电话咨询

This commit is contained in:
duoaohui
2026-06-04 14:32:52 +08:00
parent edda31e8bf
commit b89b930c41
4 changed files with 134 additions and 20 deletions

View File

@@ -169,6 +169,9 @@ const consultId = ref('')
const miniProgramReturnUrl = ref('')
const cloudLawApiBase = ref('')
const cloudLawToken = ref('')
// 调用方角色:'law' = 律师端(接单端),其余/空 = 用户端applet
// 律师为参会者(非主持人),结束会议走 /api/law/voiceCall/{callId}/end用户端默认 /api/applet/voiceConnection。
const cloudLawRole = ref('')
const callMode = ref('video')
const joining = ref(false)
const joinError = ref('')
@@ -209,6 +212,9 @@ const audioEnabled = computed(() => !!meetingStore.localAudioEnabled)
const videoEnabled = computed(() => !!meetingStore.localVideoEnabled)
const speakingMap = computed(() => meetingStore.speakingMap || {})
const isVoiceMode = computed(() => callMode.value === 'voice')
const isLawRole = computed(() => String(cloudLawRole.value).toLowerCase() === 'law')
// 结束会议接口基址:律师端 /api/law/voiceCall用户端 /api/applet/voiceConnection
const voiceEndBase = computed(() => isLawRole.value ? '/api/law/voiceCall' : '/api/applet/voiceConnection')
const cameraIcon = computed(() => videoEnabled.value ? videoOnIcon : videoOffIcon)
const micIcon = computed(() => audioEnabled.value ? voiceOnIcon : voiceOffIcon)
const speakerIcon = computed(() => speakerMuted.value ? speakerOffIcon : speakerOnIcon)
@@ -403,7 +409,7 @@ const notifyCloudLawEnd = () => {
if (!callId.value || !cloudLawApiBase.value) return Promise.resolve()
return new Promise(resolve => {
uni.request({
url: resolveCloudLawApiUrl(`/api/applet/voiceConnection/${encodeURIComponent(callId.value)}/end`),
url: resolveCloudLawApiUrl(`${voiceEndBase.value}/${encodeURIComponent(callId.value)}/end`),
method: 'POST',
data: {},
header: cloudLawToken.value ? { 'Content-Type': 'application/json', token: `Bearer ${cloudLawToken.value}` } : { 'Content-Type': 'application/json' },
@@ -499,6 +505,8 @@ const pollCommandsOnce = async () => {
}
const startCommandPolling = () => {
// 律师端(接单端)无 publicCommands 远程指令通道,跳过轮询避免无谓 404
if (isLawRole.value) return
if (!callId.value || commandPollHandle) return
pollCommandsOnce()
commandPollHandle = setInterval(pollCommandsOnce, 1200)
@@ -840,21 +848,34 @@ const hangup = async () => {
leaving.value = true
stopCommandPolling()
try {
// 先通知云律后端结束本次通话:
// - 用户端:/api/applet/voiceConnection/{callId}/end
// - 律师端:/api/law/voiceCall/{callId}/end → 后端 CloudLawEndMeeting 结束会议(双方都收到 room.ended
await notifyCloudLawEnd()
// 云律视频咨询:发起人(小程序端用户)在 CreateRoomForInternal 时被加成 host
// 用户挂机 = 咨询结束,无条件 endMeeting 让律师 iframe 收到 room.ended 自动退出。
// 不依赖 meetingStore.isHosthost_id/userId 偶发类型不一致导致误判 false 只走 leave
// 表现就是"用户端挂机后 PC 还停在会议里")。
console.log('[CloudLawVideoCall] hangup -> endMeeting', {
roomCode: meetingStore.currentRoom?.room_code,
hostId: meetingStore.currentRoom?.host_id,
myUserId: userStore.userInfo?.id || userStore.userInfo?.user_id,
isHostComputed: meetingStore.isHost
})
await meetingStore.endMeeting()
if (isLawRole.value) {
// 律师为参会者非主持人EndRoom 是 host-only。会议已由上面的 /end 在后端结束,
// 这里仅本地离会即可(直接 endMeeting 会因非主持人 403徒增一次失败请求
console.log('[CloudLawVideoCall] hangup(law) -> backend end + local leave', {
roomCode: meetingStore.currentRoom?.room_code,
callId: callId.value
})
await meetingStore.leave().catch(() => {})
} else {
// 云律视频咨询:发起人(小程序端用户)在 CreateRoomForInternal 时被加成 host
// 用户挂机 = 咨询结束,无条件 endMeeting 让律师端收到 room.ended 自动退出。
// 不依赖 meetingStore.isHosthost_id/userId 偶发类型不一致导致误判 false 只走 leave
// 表现就是"用户端挂机后 PC 还停在会议里")。
console.log('[CloudLawVideoCall] hangup -> endMeeting', {
roomCode: meetingStore.currentRoom?.room_code,
hostId: meetingStore.currentRoom?.host_id,
myUserId: userStore.userInfo?.id || userStore.userInfo?.user_id,
isHostComputed: meetingStore.isHost
})
await meetingStore.endMeeting()
}
} catch (err) {
console.warn('[CloudLawVideoCall] endMeeting failed, fallback leave', err)
// endMeeting 失败时兜底 leave至少自己先退出避免卡死
console.warn('[CloudLawVideoCall] end/hangup failed, fallback leave', err)
// 失败时兜底 leave至少自己先退出避免卡死
try { await meetingStore.leave() } catch (leaveErr) { console.warn('[CloudLawVideoCall] fallback leave failed', leaveErr) }
} finally {
ended.value = true
@@ -871,6 +892,7 @@ onLoad(query => {
miniProgramReturnUrl.value = query?.miniProgramReturnUrl || query?.mini_program_return_url || ''
cloudLawApiBase.value = query?.cloudLawApiBase || query?.cloud_law_api_base || ''
cloudLawToken.value = query?.cloudLawToken || query?.cloud_law_token || ''
cloudLawRole.value = query?.cloudLawRole || query?.cloud_law_role || ''
const mode = String(query?.mode || query?.callType || query?.type || '').toLowerCase()
callMode.value = mode === 'voice' || mode === 'audio' ? 'voice' : 'video'
joinMeeting()