电话咨询
This commit is contained in:
@@ -96,7 +96,17 @@
|
||||
</view>
|
||||
|
||||
<view class="controls-row controls-row-bottom">
|
||||
<view class="control-item" @click="takeSnapshot" @tap="takeSnapshot">
|
||||
<!-- 律师端:底部左侧为共享屏幕(替代通用会议页「聊天」能力) -->
|
||||
<view v-if="isLawRole" class="control-item" @click="onScreenToggle" @tap="onScreenToggle">
|
||||
<view
|
||||
class="control-btn dark-btn text-icon-btn"
|
||||
:class="{ active: screenSharingByMe, loading: screenLoading, disabled: screenDisabled && !screenSharingByMe }"
|
||||
>
|
||||
<text class="text-icon">{{ screenSharingByMe ? '停' : '屏' }}</text>
|
||||
</view>
|
||||
<text class="control-label">{{ screenSharingByMe ? '停止共享' : '共享屏幕' }}</text>
|
||||
</view>
|
||||
<view v-else class="control-item" @click="takeSnapshot" @tap="takeSnapshot">
|
||||
<view class="control-btn dark-btn text-icon-btn">
|
||||
<image v-if="!captureIconError" class="control-image" :src="captureIcon" mode="aspectFit" @error="captureIconError = true"></image>
|
||||
<text v-else class="text-icon">截</text>
|
||||
@@ -177,6 +187,7 @@ const joining = ref(false)
|
||||
const joinError = ref('')
|
||||
const audioLoading = ref(false)
|
||||
const videoLoading = ref(false)
|
||||
const screenLoading = ref(false)
|
||||
const leaving = ref(false)
|
||||
const startedAt = ref(0)
|
||||
const nowTs = ref(Date.now())
|
||||
@@ -244,7 +255,56 @@ const participants = computed(() => {
|
||||
|
||||
const localTrackVersion = computed(() => {
|
||||
const producers = meetingStore.localProducers || {}
|
||||
return `${producers.audio || ''}-${producers.video || ''}`
|
||||
return `${producers.audio || ''}-${producers.video || ''}-${producers.screen || ''}`
|
||||
})
|
||||
|
||||
const screenSharingActive = computed(() => !!meetingStore.screenShare?.ownerUserId)
|
||||
const screenSharingByMe = computed(() => {
|
||||
const owner = meetingStore.screenShare?.ownerUserId
|
||||
return !!owner && sameUser(owner, myUserId.value)
|
||||
})
|
||||
const screenSupported = computed(() => {
|
||||
// #ifdef H5
|
||||
return typeof navigator !== 'undefined' &&
|
||||
!!navigator.mediaDevices &&
|
||||
typeof navigator.mediaDevices.getDisplayMedia === 'function'
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
return false
|
||||
// #endif
|
||||
})
|
||||
const screenDisabled = computed(() => {
|
||||
if (!screenSupported.value) return true
|
||||
if (screenSharingActive.value && !screenSharingByMe.value) return true
|
||||
return false
|
||||
})
|
||||
const screenDisabledReason = computed(() => {
|
||||
if (!screenSupported.value) return '当前环境不支持屏幕共享'
|
||||
if (screenSharingActive.value && !screenSharingByMe.value) return '已有成员正在共享屏幕'
|
||||
return ''
|
||||
})
|
||||
|
||||
const screenTile = computed(() => {
|
||||
const owner = meetingStore.screenShare?.ownerUserId
|
||||
if (!owner) return null
|
||||
localTrackVersion.value
|
||||
remoteTrackVersion.value
|
||||
const isLocal = sameUser(owner, myUserId.value)
|
||||
const track = isLocal
|
||||
? meetingStore.getLocalTrack('screen')
|
||||
: meetingStore.getRemoteTrack(owner, 'screen')
|
||||
if (!track) return null
|
||||
return {
|
||||
key: `screen_${owner}`,
|
||||
userId: owner,
|
||||
name: isLocal ? '你' : nameOf(owner),
|
||||
isLocal,
|
||||
isHost: sameUser(owner, hostId.value),
|
||||
audioEnabled: false,
|
||||
videoEnabled: true,
|
||||
audioTrack: null,
|
||||
videoTrack: track
|
||||
}
|
||||
})
|
||||
|
||||
const remoteTrackVersion = computed(() => {
|
||||
@@ -252,7 +312,7 @@ const remoteTrackVersion = computed(() => {
|
||||
const map = meetingStore.remoteConsumers || {}
|
||||
Object.keys(map).forEach(uid => {
|
||||
const slot = map[uid]
|
||||
parts.push(`${uid}:${slot?.audio?.id || ''}:${slot?.video?.id || ''}`)
|
||||
parts.push(`${uid}:${slot?.audio?.id || ''}:${slot?.video?.id || ''}:${slot?.screen?.id || ''}`)
|
||||
})
|
||||
return parts.join('|')
|
||||
})
|
||||
@@ -315,6 +375,7 @@ const tiles = computed(() => {
|
||||
const selfTile = computed(() => tiles.value.find(t => t.isLocal) || null)
|
||||
const remoteTile = computed(() => tiles.value.find(t => !t.isLocal && t.videoTrack) || tiles.value.find(t => !t.isLocal) || null)
|
||||
const primaryTile = computed(() => {
|
||||
if (screenTile.value) return screenTile.value
|
||||
if (preferSelfMain.value && selfTile.value) return selfTile.value
|
||||
return remoteTile.value || selfTile.value
|
||||
})
|
||||
@@ -650,6 +711,28 @@ const flipCamera = () => {
|
||||
flipCameraByCommand()
|
||||
}
|
||||
|
||||
const onScreenToggle = async () => {
|
||||
if (screenLoading.value) return
|
||||
if (screenDisabled.value && !screenSharingByMe.value) {
|
||||
uni.showToast({ title: screenDisabledReason.value || '无法共享屏幕', icon: 'none' })
|
||||
return
|
||||
}
|
||||
screenLoading.value = true
|
||||
try {
|
||||
if (screenSharingByMe.value) {
|
||||
await meetingStore.stopScreenShare()
|
||||
} else {
|
||||
await meetingStore.startScreenShare()
|
||||
}
|
||||
} catch (err) {
|
||||
if (err?.name !== 'NotAllowedError') {
|
||||
uni.showToast({ title: err?.message || '屏幕共享失败', icon: 'none' })
|
||||
}
|
||||
} finally {
|
||||
screenLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const executeCloudLawCommand = async (cmd = {}) => {
|
||||
const command = String(cmd.command || cmd.type || '').toLowerCase()
|
||||
const value = cmd.value
|
||||
@@ -893,6 +976,13 @@ onLoad(query => {
|
||||
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 || ''
|
||||
// #ifdef H5
|
||||
if (String(cloudLawRole.value).toLowerCase() === 'law') {
|
||||
try {
|
||||
window.sessionStorage.setItem('echo_cloud_law_meeting_role', 'law')
|
||||
} catch (e) {}
|
||||
}
|
||||
// #endif
|
||||
const mode = String(query?.mode || query?.callType || query?.type || '').toLowerCase()
|
||||
callMode.value = mode === 'voice' || mode === 'audio' ? 'voice' : 'video'
|
||||
joinMeeting()
|
||||
@@ -1233,6 +1323,10 @@ onUnload(() => {
|
||||
.control-btn.disabled {
|
||||
opacity: 0.94;
|
||||
}
|
||||
.control-btn.active {
|
||||
background: linear-gradient(180deg, #4f8dff, #2563eb);
|
||||
border-color: rgba(79, 141, 255, 0.55);
|
||||
}
|
||||
.hangup-btn {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
|
||||
Reference in New Issue
Block a user