Files
urbanLifeline/urbanLifelineWeb/packages/workcase_wechat/pages/meeting/meetingCard/MeetingCard.uvue
2025-12-26 18:55:54 +08:00

214 lines
5.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 消息会议卡片 -->
<view :class="['meeting-card', meeting.status ? `meeting-card--${meeting.status}` : '']">
<view class="meeting-card-header">
<view class="meeting-card-title">{{ meeting.meetingName || '未命名会议' }}</view>
<view class="meeting-card-status">
<text v-if="meeting.status === 'scheduled'" class="status-badge status-scheduled">预定</text>
<text v-else-if="meeting.status === 'ongoing'" class="status-badge status-ongoing">进行中</text>
<text v-else-if="meeting.status === 'ended'" class="status-badge status-ended">已结束</text>
</view>
</view>
<view class="meeting-card-time">
<text>开始时间:{{ formatDateTime(meeting.startTime) }}</text>
<text>结束时间:{{ formatDateTime(meeting.endTime) }}</text>
<text v-if="meeting.advance">提前入会:{{ meeting.advance }}分钟</text>
</view>
<view v-if="meeting.description" class="meeting-card-content">
<text class="meeting-card-desc">{{ meeting.description }}</text>
</view>
<view class="meeting-card-action">
<text class="meeting-card-countdown">{{ countdownText }}</text>
<button
type="primary"
:disabled="!canJoinMeeting"
@click="handleJoinMeeting"
size="mini"
>
<text>{{ buttonText }}</text>
</button>
</view>
</view>
</template>
<script setup lang="ts">
import type { VideoMeetingVO } from '../../../types/workcase/chatRoom'
import { ref, computed, onMounted, onUnmounted } from 'vue'
interface Props {
meeting: VideoMeetingVO
}
const props = defineProps<Props>()
console.log("meeting", JSON.stringify(props.meeting))
const emit = defineEmits<{
join: [meetingId: string]
}>()
// 当前时间,每秒更新
const currentTime = ref(Date.now())
let timer: ReturnType<typeof setInterval> | null = null
onMounted(() => {
// 每秒更新当前时间
timer = setInterval(() => {
currentTime.value = Date.now()
}, 1000)
})
onUnmounted(() => {
if (timer !== null) {
clearInterval(timer)
timer = null
}
})
/**
* 格式化日期时间
*/
function formatDateTime(dateStr?: string): string {
if (!dateStr) return ''
// iOS和小程序兼容性处理将空格替换为T
const iosCompatibleTime = dateStr.replace(' ', 'T')
const date = new Date(iosCompatibleTime)
if (isNaN(date.getTime())) return dateStr // 如果解析失败,返回原字符串
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
return `${month}-${day} ${hour}:${minute}`
}
/**
* 计算倒计时文本
*/
const countdownText = computed((): string => {
const meeting = props.meeting
if (!meeting) {
return ''
}
// 检查必要字段
if (!meeting.startTime || !meeting.endTime) {
return ''
}
// advance 默认为 0
const advanceMinutes = meeting.advance || 0
const now = currentTime.value
// iOS和小程序兼容性处理
const startTime = new Date(meeting.startTime.replace(' ', 'T')).getTime()
const endTime = new Date(meeting.endTime.replace(' ', 'T')).getTime()
// 检查时间解析是否有效
if (isNaN(startTime) || isNaN(endTime)) {
return ''
}
const advanceTime = startTime - advanceMinutes * 60 * 1000
if (meeting.status === 'ended') {
return '会议已结束'
}
if (now < advanceTime) {
// 未到提前入会时间
const leftMs = advanceTime - now
const leftMinutes = Math.floor(leftMs / 60000)
const leftSeconds = Math.floor((leftMs % 60000) / 1000)
if (leftMinutes >= 60) {
const hours = Math.floor(leftMinutes / 60)
const mins = leftMinutes % 60
return `距离入会:${hours}小时${mins}分钟`
} else if (leftMinutes > 0) {
return `距离入会:${leftMinutes}分${leftSeconds}秒`
} else {
return `距离入会:${leftSeconds}秒`
}
} else if (now < startTime) {
// 在提前入会时间窗口内,但未到开始时间
return '可以入会'
} else if (now < endTime) {
// 会议进行中
if (meeting.status === 'ongoing') {
return '会议进行中'
} else {
return '可以入会'
}
} else {
// 已超过结束时间
return '会议已超时'
}
})
/**
* 是否可以加入会议
*/
const canJoinMeeting = computed((): boolean => {
const meeting = props.meeting
if (!meeting) {
return false
}
if (!meeting.startTime || !meeting.endTime) {
return false
}
if (meeting.status === 'ended') {
return false
}
const advanceMinutes = meeting.advance || 0
const now = currentTime.value
// iOS和小程序兼容性处理
const startTime = new Date(meeting.startTime.replace(' ', 'T')).getTime()
const endTime = new Date(meeting.endTime.replace(' ', 'T')).getTime()
// 检查时间解析是否有效
if (isNaN(startTime) || isNaN(endTime)) {
return false
}
const advanceTime = startTime - advanceMinutes * 60 * 1000
// 在允许入会的时间窗口内(提前入会时间 ~ 结束时间)
return now >= advanceTime && now <= endTime
})
/**
* 按钮文本
*/
const buttonText = computed((): string => {
const meeting = props.meeting
if (meeting.status === 'ended') {
return '会议已结束'
}
if (!canJoinMeeting.value) {
return '未到入会时间'
}
return '加入会议'
})
/**
* 加入会议
*/
function handleJoinMeeting() {
if (!props.meeting.meetingId) {
uni.showToast({
title: '会议ID不存在',
icon: 'none'
})
return
}
if (!canJoinMeeting.value) {
return
}
emit('join', props.meeting.meetingId)
}
</script>
<style scoped lang="scss">
@import './MeetingCard.scss';
</style>