视频会议
This commit is contained in:
@@ -7,8 +7,8 @@
|
|||||||
<button v-if="joinError" class="retry-btn" @click="joinMeeting">重新进入</button>
|
<button v-if="joinError" class="retry-btn" @click="joinMeeting">重新进入</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else class="call-shell">
|
<view v-else class="call-shell" :class="{ 'voice-mode': isVoiceMode }">
|
||||||
<view class="remote-stage">
|
<view v-if="!isVoiceMode" class="remote-stage">
|
||||||
<VideoTile
|
<VideoTile
|
||||||
v-if="primaryTile"
|
v-if="primaryTile"
|
||||||
class="main-video"
|
class="main-video"
|
||||||
@@ -29,7 +29,16 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="selfTile && primaryTile && !primaryTile.isLocal" class="self-preview">
|
<view v-else class="voice-stage">
|
||||||
|
<image class="voice-bg" :src="bgImage" mode="aspectFill"></image>
|
||||||
|
<view class="voice-profile">
|
||||||
|
<image v-if="voiceAvatar" class="voice-avatar" :src="voiceAvatar" mode="aspectFill"></image>
|
||||||
|
<view v-else class="voice-avatar voice-avatar-fallback">{{ voiceInitial }}</view>
|
||||||
|
<text class="voice-name">{{ voiceName }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="!isVoiceMode && selfTile && primaryTile && !primaryTile.isLocal" class="self-preview">
|
||||||
<VideoTile
|
<VideoTile
|
||||||
class="self-video"
|
class="self-video"
|
||||||
:user-id="selfTile.userId"
|
:user-id="selfTile.userId"
|
||||||
@@ -45,39 +54,81 @@
|
|||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="top-info">
|
<view v-if="!isVoiceMode" class="video-topbar">
|
||||||
<view>
|
<view class="top-window-icon"></view>
|
||||||
<text class="call-title">视频咨询</text>
|
<text class="top-duration">{{ durationText }}</text>
|
||||||
<text class="call-code">{{ formattedCode }}</text>
|
<text class="top-plus">+</text>
|
||||||
</view>
|
|
||||||
<view class="status-pill" :class="stateClass">
|
|
||||||
<view class="status-dot"></view>
|
|
||||||
<text>{{ stateText }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="timer">{{ durationText }}</view>
|
<view v-else class="voice-topbar">
|
||||||
|
<text class="voice-back" @click="hangup">‹</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="isVoiceMode" class="voice-duration">{{ durationText }}</view>
|
||||||
|
|
||||||
|
<view v-if="!isVoiceMode" class="controls video-controls">
|
||||||
|
<view class="control-item" @click="onMicToggle">
|
||||||
|
<view class="control-btn white-btn" :class="{ disabled: !audioEnabled, loading: audioLoading }">
|
||||||
|
<image class="control-image" :src="micIcon" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<text class="control-label">麦克风{{ audioEnabled ? '已开' : '已关' }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="control-item">
|
||||||
|
<view class="control-btn white-btn">
|
||||||
|
<image class="control-image" :src="speakerIcon" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<text class="control-label">扬声器已开</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="controls">
|
|
||||||
<view class="control-item" @click="onCamToggle">
|
<view class="control-item" @click="onCamToggle">
|
||||||
<view class="control-btn" :class="{ disabled: !videoEnabled, loading: videoLoading }">
|
<view class="control-btn white-btn" :class="{ disabled: !videoEnabled, loading: videoLoading }">
|
||||||
<text class="control-icon">{{ videoEnabled ? '摄' : '关' }}</text>
|
<image class="control-image" :src="cameraIcon" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
<text class="control-label">摄像头{{ videoEnabled ? '已开' : '已关' }}</text>
|
<text class="control-label">摄像头{{ videoEnabled ? '已开' : '已关' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="hangup-center" @click="hangup">
|
||||||
|
<view class="hangup-btn" :class="{ loading: leaving }">
|
||||||
|
<image class="hangup-image" :src="hangupIcon" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="controls voice-controls">
|
||||||
|
<view class="voice-control-col">
|
||||||
|
<view class="control-item" @click="onCamToggle">
|
||||||
|
<view class="control-btn dark-btn" :class="{ disabled: !videoEnabled, loading: videoLoading }">
|
||||||
|
<image class="control-image" :src="cameraIcon" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="control-item" @click="onCamToggle">
|
||||||
|
<view class="control-btn dark-btn" :class="{ disabled: !videoEnabled, loading: videoLoading }">
|
||||||
|
<image class="control-image" :src="cameraIcon" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<text class="control-label">摄像头已关</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="control-item" @click="hangup">
|
<view class="control-item" @click="hangup">
|
||||||
<view class="hangup-btn" :class="{ loading: leaving }">
|
<view class="hangup-btn" :class="{ loading: leaving }">
|
||||||
<text class="hangup-icon">挂</text>
|
<image class="hangup-image" :src="hangupIcon" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
<text class="control-label">挂断</text>
|
<text class="control-label">挂断</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="control-item" @click="onMicToggle">
|
<view class="voice-control-col">
|
||||||
<view class="control-btn" :class="{ disabled: !audioEnabled, loading: audioLoading }">
|
<view class="control-item" @click="onMicToggle">
|
||||||
<text class="control-icon">{{ audioEnabled ? '麦' : '静' }}</text>
|
<view class="control-btn dark-btn" :class="{ disabled: !audioEnabled, loading: audioLoading }">
|
||||||
|
<image class="control-image" :src="micIcon" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="control-item" @click="onMicToggle">
|
||||||
|
<view class="control-btn dark-btn" :class="{ disabled: !audioEnabled, loading: audioLoading }">
|
||||||
|
<image class="control-image" :src="micIcon" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<text class="control-label">麦克风{{ audioEnabled ? '已开' : '已关' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="control-label">麦克风{{ audioEnabled ? '已开' : '已关' }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -99,6 +150,12 @@ import {
|
|||||||
MEETING_ENDED_REASON_LABEL
|
MEETING_ENDED_REASON_LABEL
|
||||||
} from '@/constants/meeting'
|
} from '@/constants/meeting'
|
||||||
import VideoTile from '@/components/meeting/VideoTile.vue'
|
import VideoTile from '@/components/meeting/VideoTile.vue'
|
||||||
|
import bgImage from '@/static/bg.png'
|
||||||
|
import hangupIcon from '@/static/cacel.png'
|
||||||
|
import voiceOnIcon from '@/static/vocie_calling_1.png'
|
||||||
|
import voiceOffIcon from '@/static/vocie_stop.png'
|
||||||
|
import videoOnIcon from '@/static/vodie_calling_1.png'
|
||||||
|
import videoOffIcon from '@/static/vodie_stop.png'
|
||||||
|
|
||||||
const meetingStore = useMeetingStore()
|
const meetingStore = useMeetingStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@@ -130,6 +187,10 @@ const hostId = computed(() => meetingStore.currentRoom?.host_id || '')
|
|||||||
const audioEnabled = computed(() => !!meetingStore.localAudioEnabled)
|
const audioEnabled = computed(() => !!meetingStore.localAudioEnabled)
|
||||||
const videoEnabled = computed(() => !!meetingStore.localVideoEnabled)
|
const videoEnabled = computed(() => !!meetingStore.localVideoEnabled)
|
||||||
const speakingMap = computed(() => meetingStore.speakingMap || {})
|
const speakingMap = computed(() => meetingStore.speakingMap || {})
|
||||||
|
const isVoiceMode = computed(() => !videoEnabled.value)
|
||||||
|
const speakerIcon = voiceOnIcon
|
||||||
|
const cameraIcon = computed(() => videoEnabled.value ? videoOnIcon : videoOffIcon)
|
||||||
|
const micIcon = computed(() => audioEnabled.value ? voiceOnIcon : voiceOffIcon)
|
||||||
|
|
||||||
const entryInitial = computed(() => {
|
const entryInitial = computed(() => {
|
||||||
const name = userStore.userInfo?.nickname || userStore.userInfo?.username || '在'
|
const name = userStore.userInfo?.nickname || userStore.userInfo?.username || '在'
|
||||||
@@ -226,6 +287,14 @@ const tiles = computed(() => {
|
|||||||
|
|
||||||
const selfTile = computed(() => tiles.value.find(t => t.isLocal) || null)
|
const selfTile = computed(() => tiles.value.find(t => t.isLocal) || null)
|
||||||
const primaryTile = computed(() => tiles.value.find(t => !t.isLocal && t.videoTrack) || tiles.value.find(t => !t.isLocal) || selfTile.value)
|
const primaryTile = computed(() => tiles.value.find(t => !t.isLocal && t.videoTrack) || tiles.value.find(t => !t.isLocal) || selfTile.value)
|
||||||
|
const voicePeer = computed(() => participants.value.find(p => !sameUser(p.user_id, myUserId.value)) || null)
|
||||||
|
const voiceName = computed(() => {
|
||||||
|
if (voicePeer.value?.user_name) return voicePeer.value.user_name
|
||||||
|
if (primaryTile.value && !primaryTile.value.isLocal) return primaryTile.value.name
|
||||||
|
return '在线律师'
|
||||||
|
})
|
||||||
|
const voiceAvatar = computed(() => voicePeer.value?.user_avatar || '')
|
||||||
|
const voiceInitial = computed(() => String(voiceName.value || '在').slice(0, 1))
|
||||||
|
|
||||||
const isReady = computed(() => meetingStore.localState === MEETING_LOCAL_STATE_CONNECTED && !joinError.value && !ended.value)
|
const isReady = computed(() => meetingStore.localState === MEETING_LOCAL_STATE_CONNECTED && !joinError.value && !ended.value)
|
||||||
|
|
||||||
@@ -467,6 +536,51 @@ onUnload(() => {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
background: #050b1d;
|
background: #050b1d;
|
||||||
}
|
}
|
||||||
|
.voice-stage {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.voice-bg {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.voice-profile {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
margin-top: calc(126rpx + env(safe-area-inset-top));
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.voice-avatar {
|
||||||
|
width: 132rpx;
|
||||||
|
height: 132rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 4rpx solid rgba(255, 255, 255, 0.86);
|
||||||
|
box-shadow: 0 12rpx 36rpx rgba(0, 0, 0, 0.28);
|
||||||
|
}
|
||||||
|
.voice-avatar-fallback {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #43b1ff 0%, #1974e8 100%);
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 54rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.voice-name {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 1;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
.main-video {
|
.main-video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -482,13 +596,13 @@ onUnload(() => {
|
|||||||
}
|
}
|
||||||
.self-preview {
|
.self-preview {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 24rpx;
|
right: 30rpx;
|
||||||
bottom: 218rpx;
|
top: calc(104rpx + env(safe-area-inset-top));
|
||||||
width: 210rpx;
|
width: 220rpx;
|
||||||
height: 280rpx;
|
height: 310rpx;
|
||||||
border-radius: 20rpx;
|
border-radius: 10rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 2rpx solid rgba(108, 151, 255, 0.72);
|
border: 0;
|
||||||
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.38);
|
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.38);
|
||||||
background: #0b1220;
|
background: #0b1220;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
@@ -498,70 +612,78 @@ onUnload(() => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
.top-info {
|
.video-topbar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding: calc(26rpx + env(safe-area-inset-top)) 28rpx 40rpx;
|
height: calc(88rpx + env(safe-area-inset-top));
|
||||||
|
padding: env(safe-area-inset-top) 46rpx 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 20rpx;
|
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 100%);
|
background: linear-gradient(180deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
}
|
}
|
||||||
.call-title {
|
.top-window-icon {
|
||||||
display: block;
|
position: relative;
|
||||||
font-size: 32rpx;
|
width: 42rpx;
|
||||||
font-weight: 700;
|
height: 42rpx;
|
||||||
line-height: 1.2;
|
border: 4rpx solid rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 6rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.call-code {
|
.top-window-icon::after {
|
||||||
display: block;
|
content: '';
|
||||||
margin-top: 8rpx;
|
position: absolute;
|
||||||
font-size: 22rpx;
|
right: -16rpx;
|
||||||
color: rgba(255, 255, 255, 0.72);
|
bottom: -14rpx;
|
||||||
letter-spacing: 1rpx;
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
border: 4rpx solid rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 5rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.status-pill {
|
.top-duration {
|
||||||
display: inline-flex;
|
font-size: 36rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 500;
|
||||||
|
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.35);
|
||||||
|
}
|
||||||
|
.top-plus {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 56rpx;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
.voice-topbar {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
height: calc(88rpx + env(safe-area-inset-top));
|
||||||
|
padding: env(safe-area-inset-top) 26rpx 0;
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8rpx;
|
box-sizing: border-box;
|
||||||
padding: 8rpx 16rpx;
|
z-index: 6;
|
||||||
border-radius: 999rpx;
|
|
||||||
background: rgba(21, 128, 61, 0.72);
|
|
||||||
color: #baf7d0;
|
|
||||||
font-size: 22rpx;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.status-pill.connecting,
|
.voice-back {
|
||||||
.status-pill.reconnecting {
|
color: #ffffff;
|
||||||
background: rgba(37, 99, 235, 0.72);
|
font-size: 54rpx;
|
||||||
color: #dbeafe;
|
line-height: 1;
|
||||||
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
.status-pill.ended,
|
.voice-duration {
|
||||||
.status-pill.leaving {
|
|
||||||
background: rgba(107, 114, 128, 0.72);
|
|
||||||
color: #f3f4f6;
|
|
||||||
}
|
|
||||||
.status-dot {
|
|
||||||
width: 12rpx;
|
|
||||||
height: 12rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: currentColor;
|
|
||||||
box-shadow: 0 0 12rpx currentColor;
|
|
||||||
}
|
|
||||||
.timer {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
bottom: 196rpx;
|
bottom: calc(216rpx + env(safe-area-inset-bottom));
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
padding: 8rpx 20rpx;
|
padding: 10rpx 20rpx;
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
background: rgba(255, 255, 255, 0.18);
|
background: rgba(255, 255, 255, 0.16);
|
||||||
color: rgba(255, 255, 255, 0.86);
|
color: rgba(255, 255, 255, 0.78);
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
z-index: 7;
|
z-index: 7;
|
||||||
@@ -571,51 +693,88 @@ onUnload(() => {
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
padding: 34rpx 44rpx calc(34rpx + env(safe-area-inset-bottom));
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
background: linear-gradient(0deg, rgba(4, 10, 27, 0.96) 0%, rgba(4, 10, 27, 0.72) 72%, rgba(4, 10, 27, 0) 100%);
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
}
|
}
|
||||||
|
.video-controls {
|
||||||
|
padding: 28rpx 64rpx calc(122rpx + env(safe-area-inset-bottom));
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
column-gap: 46rpx;
|
||||||
|
background: linear-gradient(0deg, rgba(0, 0, 0, 0.54) 0%, rgba(0, 0, 0, 0.22) 62%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
}
|
||||||
|
.voice-controls {
|
||||||
|
padding: 0 74rpx calc(54rpx + env(safe-area-inset-bottom));
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 132rpx 1fr;
|
||||||
|
align-items: end;
|
||||||
|
column-gap: 54rpx;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.voice-control-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 36rpx;
|
||||||
|
}
|
||||||
.control-item {
|
.control-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 14rpx;
|
gap: 18rpx;
|
||||||
min-width: 150rpx;
|
|
||||||
}
|
}
|
||||||
.control-btn,
|
.control-btn,
|
||||||
.hangup-btn {
|
.hangup-btn {
|
||||||
width: 108rpx;
|
width: 124rpx;
|
||||||
height: 108rpx;
|
height: 124rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #ffffff;
|
box-sizing: border-box;
|
||||||
box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.08);
|
|
||||||
}
|
}
|
||||||
.control-btn {
|
.white-btn {
|
||||||
background: rgba(24, 64, 140, 0.9);
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
box-shadow: 0 10rpx 26rpx rgba(0, 0, 0, 0.18);
|
||||||
|
}
|
||||||
|
.dark-btn {
|
||||||
|
width: 104rpx;
|
||||||
|
height: 104rpx;
|
||||||
|
background: rgba(13, 36, 89, 0.78);
|
||||||
|
border: 1rpx solid rgba(255, 255, 255, 0.06);
|
||||||
|
box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.04);
|
||||||
}
|
}
|
||||||
.control-btn.disabled {
|
.control-btn.disabled {
|
||||||
background: rgba(18, 32, 70, 0.94);
|
opacity: 0.94;
|
||||||
color: rgba(255, 255, 255, 0.55);
|
|
||||||
}
|
}
|
||||||
.hangup-btn {
|
.hangup-btn {
|
||||||
background: #ff2727;
|
width: 132rpx;
|
||||||
box-shadow: 0 10rpx 34rpx rgba(255, 39, 39, 0.38);
|
height: 132rpx;
|
||||||
|
background: #ff2f3f;
|
||||||
|
box-shadow: 0 14rpx 36rpx rgba(255, 47, 63, 0.38);
|
||||||
}
|
}
|
||||||
.control-icon,
|
.hangup-center {
|
||||||
.hangup-icon {
|
position: absolute;
|
||||||
font-size: 34rpx;
|
left: 50%;
|
||||||
font-weight: 700;
|
bottom: calc(32rpx + env(safe-area-inset-bottom));
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
.control-image {
|
||||||
|
width: 58rpx;
|
||||||
|
height: 58rpx;
|
||||||
|
}
|
||||||
|
.dark-btn .control-image {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
}
|
||||||
|
.hangup-image {
|
||||||
|
width: 74rpx;
|
||||||
|
height: 74rpx;
|
||||||
}
|
}
|
||||||
.control-label {
|
.control-label {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: rgba(255, 255, 255, 0.74);
|
line-height: 1;
|
||||||
|
color: rgba(255, 255, 255, 0.82);
|
||||||
|
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
.loading {
|
.loading {
|
||||||
opacity: 0.68;
|
opacity: 0.68;
|
||||||
|
|||||||
Reference in New Issue
Block a user