适配微信小程序
This commit is contained in:
@@ -7,8 +7,18 @@ const { theme } = useTheme()
|
||||
|
||||
<template>
|
||||
<wd-config-provider :theme="theme">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="mp-safe-container">
|
||||
<KuRootView />
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<KuRootView />
|
||||
<!-- #endif -->
|
||||
<!-- 全局通话组件(单聊 + 群通话) -->
|
||||
<global-call-provider />
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { onLaunch, onShow } from '@dcloudio/uni-app'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { initSafeArea } from '@/composables/useSafeArea'
|
||||
import { useAuthStore, useConversationStore } from '@/stores'
|
||||
import { wsManager } from '@/api/websocket'
|
||||
import type { ChatMessage } from '@/types/api'
|
||||
@@ -37,6 +38,8 @@ async function initWebSocket() {
|
||||
|
||||
onLaunch(() => {
|
||||
console.log('App Launch')
|
||||
// 初始化安全区域(获取胶囊按钮位置等)
|
||||
initSafeArea()
|
||||
// 初始化主题
|
||||
initTheme()
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<view v-if="call.active" class="call-window" :class="{ minimized: call.minimized }">
|
||||
<!-- 最小化悬浮窗 -->
|
||||
<view v-if="call.minimized" class="minimized-bar" @click="$emit('toggleMinimize')">
|
||||
@@ -79,13 +79,13 @@
|
||||
<template v-if="call.status === 'incoming'">
|
||||
<view class="action-item" @click="$emit('reject')">
|
||||
<view class="control-btn decline">
|
||||
<wd-icon name="close-bold" size="56rpx" color="#fff" />
|
||||
<wd-icon name="close" size="56rpx" color="#fff" />
|
||||
</view>
|
||||
<text class="action-label">拒绝</text>
|
||||
</view>
|
||||
<view class="action-item" @click="$emit('accept')">
|
||||
<view class="control-btn accept">
|
||||
<wd-icon name="phone-filled" size="64rpx" color="#fff" />
|
||||
<wd-icon name="phone" size="64rpx" color="#fff" />
|
||||
</view>
|
||||
<text class="action-label">接听</text>
|
||||
</view>
|
||||
@@ -97,7 +97,7 @@
|
||||
<view class="action-item" @click="$emit('toggleMute')">
|
||||
<view class="control-btn" :class="{ active: call.muted }">
|
||||
<wd-icon
|
||||
:name="call.muted ? 'mic-off' : 'mic-on'"
|
||||
:name="call.muted ? 'mute' : 'sound'"
|
||||
size="56rpx"
|
||||
:color="call.muted ? '#000' : '#fff'"
|
||||
/>
|
||||
@@ -109,7 +109,7 @@
|
||||
<view v-if="call.type === 'video'" class="action-item" @click="$emit('toggleCamera')">
|
||||
<view class="control-btn" :class="{ active: call.camOff }">
|
||||
<wd-icon
|
||||
:name="call.camOff ? 'video-off' : 'video'"
|
||||
name="video"
|
||||
size="56rpx"
|
||||
:color="call.camOff ? '#000' : '#fff'"
|
||||
/>
|
||||
@@ -120,7 +120,7 @@
|
||||
<!-- 挂断 -->
|
||||
<view class="action-item" @click="$emit('end')">
|
||||
<view class="control-btn hangup">
|
||||
<wd-icon name="phone-off-filled" size="64rpx" color="#fff" />
|
||||
<wd-icon name="phone" size="64rpx" color="#fff" custom-style="transform: rotate(135deg);" />
|
||||
</view>
|
||||
<text class="action-label">挂断</text>
|
||||
</view>
|
||||
@@ -130,7 +130,7 @@
|
||||
<template v-else-if="call.status === 'outgoing'">
|
||||
<view class="action-item" @click="$emit('end')">
|
||||
<view class="control-btn hangup">
|
||||
<wd-icon name="phone-off-filled" size="64rpx" color="#fff" />
|
||||
<wd-icon name="phone" size="64rpx" color="#fff" custom-style="transform: rotate(135deg);" />
|
||||
</view>
|
||||
<text class="action-label">挂断</text>
|
||||
</view>
|
||||
@@ -140,11 +140,11 @@
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef H5 -->
|
||||
<!-- #ifndef H5 || APP-PLUS -->
|
||||
<view v-if="call.active" class="call-unsupported">
|
||||
<view class="unsupported-content">
|
||||
<view class="icon-box">
|
||||
<wd-icon name="video-off" size="80rpx" color="#f97316" />
|
||||
<wd-icon name="video" size="80rpx" color="#f97316" />
|
||||
</view>
|
||||
<text class="unsupported-title">暂不支持音视频通话</text>
|
||||
<text class="unsupported-desc">当前平台暂不支持音视频通话功能</text>
|
||||
@@ -207,7 +207,7 @@ function copyWebUrl() {
|
||||
}
|
||||
|
||||
watch(() => props.localStream, (stream) => {
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
nextTick(() => {
|
||||
if (localVideoRef.value && stream) {
|
||||
localVideoRef.value.srcObject = stream
|
||||
@@ -217,7 +217,7 @@ watch(() => props.localStream, (stream) => {
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => props.remoteStream, (stream) => {
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
nextTick(() => {
|
||||
if (remoteVideoRef.value && stream) {
|
||||
remoteVideoRef.value.srcObject = stream
|
||||
@@ -239,7 +239,6 @@ watch(() => props.remoteStream, (stream) => {
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
|
||||
&.minimized {
|
||||
top: calc(var(--status-bar-height, 0) + var(--mp-safe-top, 0px) + 240rpx);
|
||||
right: 32rpx;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
@@ -249,6 +248,14 @@ watch(() => props.remoteStream, (stream) => {
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.4);
|
||||
overflow: hidden;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
top: calc(var(--status-bar-height, 44px) + 88rpx + 240rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
top: calc(var(--status-bar-height, 0) + 240rpx);
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +353,6 @@ watch(() => props.remoteStream, (stream) => {
|
||||
|
||||
.minimize-btn {
|
||||
position: absolute;
|
||||
top: calc(28rpx + var(--status-bar-height, 0) + var(--mp-safe-top, 0px));
|
||||
right: 40rpx;
|
||||
z-index: 20;
|
||||
width: 72rpx;
|
||||
@@ -355,6 +361,14 @@ watch(() => props.remoteStream, (stream) => {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(8px);
|
||||
display: flex;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
top: calc(28rpx + var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
top: calc(28rpx + var(--status-bar-height, 0));
|
||||
/* #endif */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s;
|
||||
|
||||
@@ -57,13 +57,13 @@
|
||||
<!-- 顶部栏 -->
|
||||
<view class="ui-header">
|
||||
<view class="header-left">
|
||||
<view class="status-dot"></view>
|
||||
<text class="header-title">通话中</text>
|
||||
<view class="status-dot" :class="statusDotClass"></view>
|
||||
<text class="header-title">{{ headerTitle }}</text>
|
||||
<text class="header-time">{{ formatDuration(call.duration) }}</text>
|
||||
</view>
|
||||
<view class="minimize-btn" @click="toggleMinimize">
|
||||
<!-- 最小化图标 -->
|
||||
<wd-icon name="arrow-down-circle" size="60rpx" color="rgba(255,255,255,0.8)" />
|
||||
<wd-icon name="arrow-down" size="60rpx" color="rgba(255,255,255,0.8)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -73,13 +73,13 @@
|
||||
<view v-if="call.status === 'incoming'" class="incoming-actions">
|
||||
<view class="action-btn reject" @click="rejectCall">
|
||||
<view class="btn-circle reject-bg">
|
||||
<wd-icon name="close-bold" size="50rpx" color="#fff" />
|
||||
<wd-icon name="close" size="50rpx" color="#fff" />
|
||||
</view>
|
||||
<text class="action-text">拒绝</text>
|
||||
</view>
|
||||
<view class="action-btn accept" @click="acceptCall">
|
||||
<view class="btn-circle accept-bg">
|
||||
<wd-icon name="phone-filled" size="56rpx" color="#fff" />
|
||||
<wd-icon name="phone" size="56rpx" color="#fff" />
|
||||
</view>
|
||||
<text class="action-text">接听</text>
|
||||
</view>
|
||||
@@ -90,7 +90,7 @@
|
||||
<!-- 静音 -->
|
||||
<view class="ctrl-btn-wrap" @click="toggleMute">
|
||||
<view class="ctrl-btn" :class="{ active: call.muted }">
|
||||
<wd-icon :name="call.muted ? 'mic-off' : 'mic-on'" size="50rpx" :color="call.muted ? '#000' : '#fff'" />
|
||||
<wd-icon :name="call.muted ? 'mute' : 'sound'" size="50rpx" :color="call.muted ? '#000' : '#fff'" />
|
||||
</view>
|
||||
<text class="ctrl-text">静音</text>
|
||||
</view>
|
||||
@@ -98,7 +98,7 @@
|
||||
<!-- 挂断 -->
|
||||
<view class="ctrl-btn-wrap" @click="endCall">
|
||||
<view class="ctrl-btn hangup">
|
||||
<wd-icon name="phone-off-filled" size="50rpx" color="#fff" />
|
||||
<wd-icon name="phone" size="50rpx" color="#fff" custom-style="transform: rotate(135deg);" />
|
||||
</view>
|
||||
<text class="ctrl-text">挂断</text>
|
||||
</view>
|
||||
@@ -106,7 +106,7 @@
|
||||
<!-- 摄像头 -->
|
||||
<view class="ctrl-btn-wrap" @click="toggleCamera">
|
||||
<view class="ctrl-btn" :class="{ active: call.camOff }">
|
||||
<wd-icon :name="call.camOff ? 'video-off' : 'video'" size="50rpx" :color="call.camOff ? '#000' : '#fff'" />
|
||||
<wd-icon name="video" size="50rpx" :color="call.camOff ? '#000' : '#fff'" />
|
||||
</view>
|
||||
<text class="ctrl-text">摄像头</text>
|
||||
</view>
|
||||
@@ -148,6 +148,33 @@ const instance = getCurrentInstance()
|
||||
|
||||
const gridClass = computed(() => remoteStreams.value.length > 1 ? 'grid-multi' : 'grid-single')
|
||||
|
||||
// 顶部状态显示
|
||||
const headerTitle = computed(() => {
|
||||
switch (call.status) {
|
||||
case 'outgoing':
|
||||
return '正在呼叫...'
|
||||
case 'incoming':
|
||||
return '来电...'
|
||||
case 'connected':
|
||||
return '通话中'
|
||||
default:
|
||||
return call.statusText || '通话'
|
||||
}
|
||||
})
|
||||
|
||||
// 状态指示点样式
|
||||
const statusDotClass = computed(() => {
|
||||
switch (call.status) {
|
||||
case 'connected':
|
||||
return 'status-connected'
|
||||
case 'outgoing':
|
||||
case 'incoming':
|
||||
return 'status-pending'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
// 监听通话激活,初始化 Context
|
||||
watch(() => call.active, (newVal) => {
|
||||
if (newVal) {
|
||||
@@ -236,8 +263,8 @@ const onPlayerError = (e: any, userId: string) => {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
padding-top: 60rpx;
|
||||
height: calc(var(--status-bar-height, 44px) + var(--mp-navbar-height, 48px) + 20rpx);
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 20rpx);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-left: 30rpx;
|
||||
@@ -247,7 +274,19 @@ const onPlayerError = (e: any, userId: string) => {
|
||||
pointer-events: none; /* 让点击穿透到视频层(如果有交互) */
|
||||
}
|
||||
.header-left { display: flex; align-items: center; pointer-events: auto; }
|
||||
.status-dot { width: 12rpx; height: 12rpx; background: #10b981; border-radius: 50%; margin-right: 16rpx; }
|
||||
.status-dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background: #666;
|
||||
border-radius: 50%;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.status-dot.status-connected { background: #10b981; } /* 绿色 = 已连接 */
|
||||
.status-dot.status-pending { background: #f59e0b; animation: pulse 1.5s infinite; } /* 橙色 = 呼叫中 */
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
.header-title { color: #fff; font-size: 32rpx; font-weight: bold; }
|
||||
.header-time { color: rgba(255,255,255,0.8); font-size: 26rpx; margin-left: 20rpx; }
|
||||
.minimize-btn { pointer-events: auto; padding: 10rpx; }
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
@touchend="stopRecording"
|
||||
@touchcancel="cancelRecording"
|
||||
>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
|
||||
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
||||
@@ -64,7 +64,7 @@
|
||||
<view class="right-actions">
|
||||
<!-- 表情按钮 -->
|
||||
<view class="icon-btn" @click="toggleEmoji">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M8 14s1.5 2 4 2 4-2 4-2"/>
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
<!-- 发送按钮 (有文字时显示) -->
|
||||
<view v-if="inputValue.trim()" class="send-btn" @click="onSend">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="22" y1="2" x2="11" y2="13"/>
|
||||
<polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
<!-- 更多按钮 (无文字时显示) -->
|
||||
<view v-else class="icon-btn" @click="showMore = true">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="8" x2="12" y2="16"/>
|
||||
@@ -112,7 +112,7 @@
|
||||
<view class="tools-grid">
|
||||
<view class="tool-item" @click="emitAction('send-image')">
|
||||
<view class="tool-icon album">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -123,7 +123,7 @@
|
||||
</view>
|
||||
<view class="tool-item" @click="emitAction('send-camera')">
|
||||
<view class="tool-icon camera">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -134,7 +134,7 @@
|
||||
</view>
|
||||
<view class="tool-item" @click="emitAction('send-file')">
|
||||
<view class="tool-icon file">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -150,7 +150,7 @@
|
||||
<view v-if="isRecording" class="recording-overlay">
|
||||
<view class="recording-content" :class="{ 'cancel-mode': isCancelRecording }">
|
||||
<view class="recording-icon">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg v-if="!isCancelRecording" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23" stroke="currentColor" stroke-width="2"/><line x1="8" y1="23" x2="16" y2="23" stroke="currentColor" stroke-width="2"/></svg>
|
||||
<svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
|
||||
<!-- #endif -->
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:left-arrow="leftArrow"
|
||||
:fixed="fixed"
|
||||
:placeholder="placeholder"
|
||||
:safe-area-inset-top="safeAreaInsetTop"
|
||||
:safe-area-inset-top="true"
|
||||
:bordered="bordered"
|
||||
custom-class="app-nav-bar"
|
||||
@click-left="handleClickLeft"
|
||||
@@ -81,6 +81,7 @@ function handleClickRight() {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.app-nav-bar) {
|
||||
position: relative;
|
||||
// 适配暗黑模式的背景色和文字颜色
|
||||
// 假设 src/uni.scss 或 variables.scss 中定义了这些 CSS 变量
|
||||
background-color: rgba(255, 255, 255, 0.85) !important;
|
||||
@@ -101,6 +102,18 @@ function handleClickRight() {
|
||||
}
|
||||
}
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
:deep(.app-nav-bar) {
|
||||
top: 130rpx !important;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
:deep(.app-nav-bar) {
|
||||
top: 0 !important;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
// 暗黑模式下的特殊覆盖(如果 CSS 变量不够用)
|
||||
.dark {
|
||||
:deep(.app-nav-bar) {
|
||||
@@ -111,7 +124,7 @@ function handleClickRight() {
|
||||
.wd-navbar__title {
|
||||
color: #f5f5f4 !important;
|
||||
}
|
||||
|
||||
|
||||
.wd-icon {
|
||||
color: #e7e5e4 !important;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<view class="tabbar-icon">
|
||||
<!-- 消息图标 (message-circle) -->
|
||||
<template v-if="tab.name === 'messages'">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg v-if="current === tab.name" class="icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
|
||||
</svg>
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<!-- 联系人图标 (users) -->
|
||||
<template v-else-if="tab.name === 'contacts'">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg v-if="current === tab.name" class="icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="9" cy="7" r="4" fill="currentColor"/>
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
<!-- 发现图标 (compass) -->
|
||||
<template v-else-if="tab.name === 'moments'">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg v-if="current === tab.name" class="icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="10" fill="currentColor"/>
|
||||
<polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76" fill="white"/>
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<!-- 我的图标 (user) -->
|
||||
<template v-else-if="tab.name === 'profile'">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg v-if="current === tab.name" class="icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
|
||||
@@ -247,8 +247,6 @@ function handleClose() {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: calc(88rpx + var(--status-bar-height, 0) + var(--mp-safe-top, 0px));
|
||||
padding-top: calc(var(--status-bar-height, 0) + var(--mp-safe-top, 0px));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -256,6 +254,16 @@ function handleClose() {
|
||||
padding-right: 24rpx;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.6) 0%, transparent 100%);
|
||||
z-index: 10;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
height: calc(88rpx + var(--status-bar-height, 44px) + 88rpx);
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
height: calc(88rpx + var(--status-bar-height, 0));
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.close-btn,
|
||||
|
||||
@@ -113,7 +113,6 @@ function handleClick(key: string) {
|
||||
// ==========================================
|
||||
.plus-menu {
|
||||
position: fixed;
|
||||
top: calc(var(--status-bar-height, 20px) + var(--mp-safe-top, 0px) + 180rpx);
|
||||
right: 32rpx;
|
||||
width: 288rpx;
|
||||
background: var(--menu-bg);
|
||||
@@ -122,6 +121,14 @@ function handleClick(key: string) {
|
||||
z-index: 999;
|
||||
box-shadow: var(--menu-shadow);
|
||||
border: 2rpx solid var(--menu-border);
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
top: calc(var(--status-bar-height, 44px) + 88rpx + 180rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
top: calc(var(--status-bar-height, 20px) + 180rpx);
|
||||
/* #endif */
|
||||
transform-origin: top right;
|
||||
transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
overflow: hidden;
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
<view class="action-grid">
|
||||
<view class="action-item" @click="handleSendMessage">
|
||||
<view class="action-icon blue">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</svg>
|
||||
@@ -156,7 +156,7 @@
|
||||
|
||||
<view class="action-item" @click="handleAudioCall">
|
||||
<view class="action-icon green">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>
|
||||
</svg>
|
||||
@@ -170,7 +170,7 @@
|
||||
|
||||
<view class="action-item" @click="handleVideoCall">
|
||||
<view class="action-icon purple">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<polygon points="23 7 16 12 23 17 23 7"/>
|
||||
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
||||
@@ -185,7 +185,7 @@
|
||||
|
||||
<view class="action-item" @click="handleViewMoments">
|
||||
<view class="action-icon orange">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M8 14s1.5 2 4 2 4-2 4-2"/>
|
||||
|
||||
@@ -219,11 +219,34 @@ export function useMiniProgramCall() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 RTMP URL 格式
|
||||
*/
|
||||
function validateRtmpUrl(url: string): { valid: boolean; error?: string } {
|
||||
if (!url) {
|
||||
return { valid: false, error: '推流地址为空' }
|
||||
}
|
||||
// 检查是否为 RTMP 协议
|
||||
if (!url.startsWith('rtmp://')) {
|
||||
return { valid: false, error: `推流地址格式错误,期望 rtmp://` }
|
||||
}
|
||||
return { valid: true }
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入通话房间
|
||||
*/
|
||||
async function joinCallRoom() {
|
||||
if (!call.roomId || !userId.value) return
|
||||
if (!call.roomId || !userId.value) {
|
||||
console.error('[MiniProgramCall] 缺少 roomId 或 userId')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[MiniProgramCall] 加入房间:', {
|
||||
roomId: call.roomId,
|
||||
userId: userId.value,
|
||||
platform: 'miniprogram'
|
||||
})
|
||||
|
||||
try {
|
||||
const response = await callApi.joinCallRoom({
|
||||
@@ -232,18 +255,37 @@ export function useMiniProgramCall() {
|
||||
platform: 'miniprogram'
|
||||
})
|
||||
|
||||
// 设置推流地址
|
||||
console.log('[MiniProgramCall] 加入房间响应:', JSON.stringify(response, null, 2))
|
||||
|
||||
// 验证并设置推流地址
|
||||
if (response.push_url) {
|
||||
pushUrl.value = response.push_url
|
||||
const validation = validateRtmpUrl(response.push_url)
|
||||
if (validation.valid) {
|
||||
pushUrl.value = response.push_url
|
||||
console.log('[MiniProgramCall] ✅ 推流地址:', response.push_url)
|
||||
} else {
|
||||
console.error('[MiniProgramCall] ❌ 推流地址验证失败:', validation.error)
|
||||
uni.showToast({ title: validation.error || '推流地址无效', icon: 'none' })
|
||||
pushUrl.value = response.push_url
|
||||
}
|
||||
} else {
|
||||
console.error('[MiniProgramCall] ❌ 服务器未返回推流地址')
|
||||
uni.showToast({ title: '服务器未返回推流地址', icon: 'none' })
|
||||
}
|
||||
|
||||
// 设置拉流地址
|
||||
if (response.pull_urls) {
|
||||
if (response.pull_urls && response.pull_urls.length > 0) {
|
||||
remoteStreams.value = response.pull_urls.map(p => ({
|
||||
userId: p.user_id,
|
||||
pullUrl: p.url,
|
||||
flvUrl: p.flv_url,
|
||||
}))
|
||||
console.log('[MiniProgramCall] 拉流地址:', remoteStreams.value)
|
||||
}
|
||||
|
||||
// 参与者信息
|
||||
if (response.participants) {
|
||||
console.log('[MiniProgramCall] 房间参与者:', response.participants)
|
||||
}
|
||||
|
||||
// 关键修复:使用 nextTick 确保 DOM 更新,live-pusher 的 url 属性生效后再启动
|
||||
@@ -258,8 +300,9 @@ export function useMiniProgramCall() {
|
||||
call.status = 'connected'
|
||||
call.statusText = '通话中'
|
||||
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('[MiniProgramCall] 加入房间失败:', error)
|
||||
uni.showToast({ title: error?.message || '加入房间失败', icon: 'none' })
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -349,31 +392,55 @@ export function useMiniProgramCall() {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 检测是否在开发者工具中运行
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
if (systemInfo.platform === 'devtools') {
|
||||
console.warn('[MiniProgramCall] 开发者工具不支持 live-pusher,请在真机上测试')
|
||||
// 仅提示一次,避免阻塞流程
|
||||
const isDevtools = systemInfo.platform === 'devtools'
|
||||
if (isDevtools) {
|
||||
console.warn('[MiniProgramCall] ⚠️ 开发者工具不支持 live-pusher,请在真机上测试')
|
||||
uni.showToast({ title: '开发者工具不支持推流,请真机测试', icon: 'none', duration: 3000 })
|
||||
}
|
||||
|
||||
if (!pusherContext) {
|
||||
console.error('[MiniProgramCall] Pusher Context 未初始化,无法推流')
|
||||
console.error('[MiniProgramCall] ❌ Pusher Context 未初始化')
|
||||
console.error('[MiniProgramCall] 请确保:')
|
||||
console.error(' 1. 组件中有 <live-pusher id="local-pusher" ...>')
|
||||
console.error(' 2. 调用了 initPusherContext(getCurrentInstance())')
|
||||
uni.showToast({ title: 'live-pusher 组件未初始化', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 确保 URL 存在
|
||||
if (!pushUrl.value) {
|
||||
console.error('[MiniProgramCall] 推流地址为空')
|
||||
console.error('[MiniProgramCall] ❌ 推流地址为空,无法推流')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[MiniProgramCall] 开始推流,URL:', pushUrl.value)
|
||||
|
||||
pusherContext.start({
|
||||
success: () => {
|
||||
console.log('[MiniProgramCall] 推流启动成功')
|
||||
console.log('[MiniProgramCall] ✅ 推流启动成功')
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.error('[MiniProgramCall] 推流启动失败:', err)
|
||||
// 提示用户可能的原因
|
||||
if (err.errMsg?.includes('operateLivePusher:fail')) {
|
||||
uni.showToast({ title: '推流失败,请检查网络或权限', icon: 'none' })
|
||||
console.error('[MiniProgramCall] ❌ 推流启动失败:', err)
|
||||
console.error('[MiniProgramCall] 错误信息:', JSON.stringify(err))
|
||||
|
||||
// 详细的错误提示
|
||||
let errorMsg = '推流失败'
|
||||
if (err.errMsg) {
|
||||
if (err.errMsg.includes('internal error')) {
|
||||
errorMsg = '推流内部错误,请检查RTMP服务器'
|
||||
} else if (err.errMsg.includes('operateLivePusher:fail')) {
|
||||
errorMsg = '推流操作失败'
|
||||
} else if (err.errMsg.includes('permission')) {
|
||||
errorMsg = '缺少摄像头/麦克风权限'
|
||||
}
|
||||
}
|
||||
|
||||
uni.showToast({ title: errorMsg, icon: 'none', duration: 3000 })
|
||||
|
||||
// 在开发者工具中,提示用户
|
||||
if (isDevtools) {
|
||||
console.warn('[MiniProgramCall] 💡 提示:开发者工具不支持 live-pusher')
|
||||
console.warn('[MiniProgramCall] 请使用手机扫码在真机预览中测试')
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -517,29 +584,87 @@ export function useMiniProgramCall() {
|
||||
|
||||
/**
|
||||
* 处理推流状态变化
|
||||
* 状态码参考:https://developers.weixin.qq.com/miniprogram/dev/component/live-pusher.html
|
||||
*/
|
||||
function onPusherStateChange(e: any) {
|
||||
const code = e.detail?.code
|
||||
console.log('[MiniProgramCall] 推流状态:', code)
|
||||
const message = e.detail?.message || ''
|
||||
console.log('[MiniProgramCall] 推流状态:', code, message)
|
||||
|
||||
switch (code) {
|
||||
case 1002: // 已成功连接到服务器
|
||||
// 成功状态
|
||||
case 1001: // 已连接到云端推流服务器
|
||||
console.log('[MiniProgramCall] ✅ 已连接到推流服务器')
|
||||
break
|
||||
case 1002: // 已与云端推流服务器握手完毕
|
||||
console.log('[MiniProgramCall] ✅ 握手完成,开始推流')
|
||||
call.statusText = '通话中'
|
||||
break
|
||||
case 1003: // 已开始推流
|
||||
case 1003: // 已成功打开摄像头
|
||||
console.log('[MiniProgramCall] ✅ 摄像头已打开')
|
||||
break
|
||||
case 1004: // 推流断开,正在重连
|
||||
call.statusText = '重新连接中...'
|
||||
case 1004: // 自动调整分辨率
|
||||
break
|
||||
case -1301: // 打开摄像头失败
|
||||
uni.showToast({ title: '摄像头打开失败', icon: 'none' })
|
||||
case 1005: // 成功打开麦克风
|
||||
console.log('[MiniProgramCall] ✅ 麦克风已打开')
|
||||
break
|
||||
case -1302: // 打开麦克风失败
|
||||
uni.showToast({ title: '麦克风打开失败', icon: 'none' })
|
||||
|
||||
// 警告状态
|
||||
case 1101: // 网络状况不佳:上行带宽不足
|
||||
call.statusText = '网络不佳...'
|
||||
break
|
||||
case -1307: // 推流连接断开
|
||||
case 1102: // 视频编码器启动失败
|
||||
console.warn('[MiniProgramCall] ⚠️ 视频编码器启动失败')
|
||||
break
|
||||
case 1103: // 摄像头被占用
|
||||
uni.showToast({ title: '摄像头被占用', icon: 'none' })
|
||||
break
|
||||
case 3001: // RTMP 服务器建立连接失败
|
||||
console.error('[MiniProgramCall] ❌ RTMP 服务器连接失败')
|
||||
call.statusText = '服务器连接失败'
|
||||
uni.showToast({ title: 'RTMP服务器连接失败', icon: 'none' })
|
||||
break
|
||||
case 3002: // RTMP 服务器握手失败
|
||||
console.error('[MiniProgramCall] ❌ RTMP 服务器握手失败')
|
||||
uni.showToast({ title: 'RTMP服务器握手失败', icon: 'none' })
|
||||
break
|
||||
case 3003: // RTMP 服务器连接已断开
|
||||
console.warn('[MiniProgramCall] ⚠️ RTMP 连接断开')
|
||||
call.statusText = '连接已断开'
|
||||
break
|
||||
case 3004: // RTMP 推流地址格式不正确
|
||||
console.error('[MiniProgramCall] ❌ RTMP 推流地址格式错误:', pushUrl.value)
|
||||
uni.showToast({ title: '推流地址格式错误', icon: 'none' })
|
||||
break
|
||||
case 3005: // RTMP 服务器连接异常断开
|
||||
console.error('[MiniProgramCall] ❌ RTMP 连接异常断开')
|
||||
call.statusText = '连接异常断开'
|
||||
break
|
||||
|
||||
// 错误状态
|
||||
case -1301: // 打开摄像头失败
|
||||
console.error('[MiniProgramCall] ❌ 摄像头打开失败')
|
||||
uni.showToast({ title: '摄像头打开失败,请检查权限', icon: 'none' })
|
||||
break
|
||||
case -1302: // 打开麦克风失败
|
||||
console.error('[MiniProgramCall] ❌ 麦克风打开失败')
|
||||
uni.showToast({ title: '麦克风打开失败,请检查权限', icon: 'none' })
|
||||
break
|
||||
case -1303: // 视频编码失败
|
||||
console.error('[MiniProgramCall] ❌ 视频编码失败')
|
||||
break
|
||||
case -1304: // 音频编码失败
|
||||
console.error('[MiniProgramCall] ❌ 音频编码失败')
|
||||
break
|
||||
case -1307: // 推流连接断开
|
||||
console.error('[MiniProgramCall] ❌ 推流连接断开')
|
||||
call.statusText = '连接已断开'
|
||||
break
|
||||
|
||||
default:
|
||||
if (code < 0) {
|
||||
console.error('[MiniProgramCall] ❌ 未知错误:', code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
139
src/composables/useSafeArea.ts
Normal file
139
src/composables/useSafeArea.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* 安全区域和导航栏尺寸组合式函数
|
||||
* 用于获取微信小程序的状态栏、胶囊按钮、导航栏高度
|
||||
*/
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
||||
// 胶囊按钮信息
|
||||
interface MenuButtonInfo {
|
||||
width: number
|
||||
height: number
|
||||
top: number
|
||||
right: number
|
||||
bottom: number
|
||||
left: number
|
||||
}
|
||||
|
||||
// 全局单例状态(避免重复计算)
|
||||
const systemInfo = ref<UniApp.GetSystemInfoResult | null>(null)
|
||||
const menuButtonInfo = ref<MenuButtonInfo | null>(null)
|
||||
const isInitialized = ref(false)
|
||||
|
||||
/**
|
||||
* 初始化安全区域数据
|
||||
*/
|
||||
function initSafeArea() {
|
||||
if (isInitialized.value) return
|
||||
|
||||
try {
|
||||
// 获取系统信息
|
||||
systemInfo.value = uni.getSystemInfoSync()
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 获取微信小程序胶囊按钮位置信息
|
||||
try {
|
||||
const rect = uni.getMenuButtonBoundingClientRect()
|
||||
menuButtonInfo.value = {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
left: rect.left
|
||||
}
|
||||
console.log('📱 [SafeArea] 胶囊按钮信息:', menuButtonInfo.value)
|
||||
} catch (e) {
|
||||
console.warn('⚠️ [SafeArea] 获取胶囊按钮信息失败:', e)
|
||||
}
|
||||
// #endif
|
||||
|
||||
isInitialized.value = true
|
||||
} catch (e) {
|
||||
console.error('❌ [SafeArea] 初始化失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
export function useSafeArea() {
|
||||
// 状态栏高度
|
||||
const statusBarHeight = computed(() => {
|
||||
return systemInfo.value?.statusBarHeight || 0
|
||||
})
|
||||
|
||||
// 导航栏内容高度(不含状态栏)
|
||||
const navBarContentHeight = computed(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (menuButtonInfo.value) {
|
||||
// 导航栏高度 = (胶囊按钮距顶部距离 - 状态栏高度) * 2 + 胶囊按钮高度
|
||||
const paddingTop = menuButtonInfo.value.top - statusBarHeight.value
|
||||
return paddingTop * 2 + menuButtonInfo.value.height
|
||||
}
|
||||
return 48 // 微信小程序默认值
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
return 44 // H5/App 默认值
|
||||
// #endif
|
||||
})
|
||||
|
||||
// 导航栏总高度(状态栏 + 导航栏内容)
|
||||
const navBarTotalHeight = computed(() => {
|
||||
return statusBarHeight.value + navBarContentHeight.value
|
||||
})
|
||||
|
||||
// 胶囊按钮右侧边距(用于计算导航栏右侧安全区域)
|
||||
const menuButtonRight = computed(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (menuButtonInfo.value && systemInfo.value) {
|
||||
// 屏幕宽度 - 胶囊按钮右边距
|
||||
return systemInfo.value.windowWidth - menuButtonInfo.value.right
|
||||
}
|
||||
return 10 // 默认 10px
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
return 0
|
||||
// #endif
|
||||
})
|
||||
|
||||
// 安全区域右边距(避开胶囊按钮的宽度)
|
||||
const safeAreaRight = computed(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (menuButtonInfo.value && systemInfo.value) {
|
||||
// 胶囊按钮宽度 + 右边距 + 额外间距
|
||||
return menuButtonInfo.value.width + menuButtonRight.value + 10
|
||||
}
|
||||
return 100 // 默认 100px
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
return 0
|
||||
// #endif
|
||||
})
|
||||
|
||||
// 初始化(在组件挂载时调用)
|
||||
onMounted(() => {
|
||||
initSafeArea()
|
||||
})
|
||||
|
||||
// 也可以手动初始化
|
||||
function init() {
|
||||
initSafeArea()
|
||||
}
|
||||
|
||||
return {
|
||||
systemInfo,
|
||||
menuButtonInfo,
|
||||
statusBarHeight,
|
||||
navBarContentHeight,
|
||||
navBarTotalHeight,
|
||||
menuButtonRight,
|
||||
safeAreaRight,
|
||||
init
|
||||
}
|
||||
}
|
||||
|
||||
// 导出初始化函数,可在 App.vue 中调用
|
||||
export { initSafeArea }
|
||||
|
||||
export default useSafeArea
|
||||
|
||||
@@ -74,7 +74,7 @@ export function useWebRTC() {
|
||||
|
||||
function playRingtone() {
|
||||
stopRingtone()
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
try {
|
||||
const AudioContext = window.AudioContext || (window as any).webkitAudioContext
|
||||
const ctx = new AudioContext()
|
||||
@@ -230,7 +230,7 @@ export function useWebRTC() {
|
||||
|
||||
// 初始化媒体设备
|
||||
async function initMedia(videoEnabled: boolean): Promise<void> {
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
try {
|
||||
if (localStream.value) {
|
||||
localStream.value.getTracks().forEach(t => t.stop())
|
||||
@@ -247,7 +247,7 @@ export function useWebRTC() {
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// #ifndef H5 || APP-PLUS
|
||||
throw new Error('当前平台暂不支持音视频通话')
|
||||
// #endif
|
||||
}
|
||||
@@ -277,7 +277,7 @@ export function useWebRTC() {
|
||||
|
||||
// 创建 RTCPeerConnection
|
||||
async function createPC(): Promise<void> {
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
// 优先使用服务器配置的 ICE 服务器
|
||||
let servers: RTCIceServer[] = sfuIceServers.length > 0
|
||||
? sfuIceServers
|
||||
@@ -562,7 +562,7 @@ export function useWebRTC() {
|
||||
call.remoteCamOff = false
|
||||
stopCallTimer()
|
||||
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (pc) {
|
||||
pc.close()
|
||||
pc = null
|
||||
@@ -609,7 +609,7 @@ export function useWebRTC() {
|
||||
function toggleMute() {
|
||||
call.muted = !call.muted
|
||||
sendSyncState('mic-toggle', call.muted)
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (localStream.value) localStream.value.getAudioTracks().forEach(t => t.enabled = !call.muted)
|
||||
// #endif
|
||||
}
|
||||
@@ -618,7 +618,7 @@ export function useWebRTC() {
|
||||
function toggleCamera() {
|
||||
call.camOff = !call.camOff
|
||||
sendSyncState('cam-toggle', call.camOff)
|
||||
// #ifdef H5
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (localStream.value) localStream.value.getVideoTracks().forEach(t => t.enabled = !call.camOff)
|
||||
// #endif
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<view class="navbar-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<view class="back-btn">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<view class="nav-actions">
|
||||
<view class="icon-btn" @click="startAudioCall">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -34,7 +34,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="icon-btn" @click="startVideoCall">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -43,7 +43,7 @@
|
||||
</view>
|
||||
<!-- 群聊:显示群成员图标 -->
|
||||
<view v-if="isGroupChat" class="icon-btn" @click="goGroupInfo">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -52,7 +52,7 @@
|
||||
</view>
|
||||
<!-- 单聊:显示更多图标 -->
|
||||
<view v-else class="icon-btn" @click="showChatInfo">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -145,7 +145,7 @@
|
||||
<view class="dialog-header">确认发送</view>
|
||||
<view class="file-preview-box">
|
||||
<view class="preview-icon">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
@@ -165,9 +165,6 @@
|
||||
</wd-popup>
|
||||
|
||||
<wd-toast />
|
||||
|
||||
<!-- 全局通话组件 -->
|
||||
<global-call-provider />
|
||||
</view>
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
@@ -194,7 +191,6 @@ import type { ChatMessage, GroupMember } from '@/types/api'
|
||||
import MessageBubble from '@/components/chat/MessageBubble.vue'
|
||||
import MessageInput from '@/components/chat/MessageInput.vue'
|
||||
import GroupCallBanner from '@/components/call/GroupCallBanner.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
import { useGroupWebRTC } from '@/composables/useGroupWebRTC'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
@@ -668,6 +664,7 @@ function cancelRecording() { isCancelRecording.value = true; stopRecording() }
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg-base);
|
||||
padding-top: 130rpx;
|
||||
|
||||
&.theme-dark {
|
||||
--bg-base: #0c0a09;
|
||||
@@ -689,7 +686,16 @@ function cancelRecording() { isCancelRecording.value = true; stopRecording() }
|
||||
/* 导航栏 */
|
||||
.glass-navbar {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
padding-top: calc(var(--status-bar-height) + var(--mp-safe-top, 0px));
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
/* 微信小程序:状态栏 + 胶囊按钮区域 */
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
/* H5/App:仅状态栏 */
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* #endif */
|
||||
.navbar-bg {
|
||||
position: absolute; inset: 0; background: var(--nav-bg);
|
||||
backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
|
||||
@@ -723,10 +729,17 @@ function cancelRecording() { isCancelRecording.value = true; stopRecording() }
|
||||
/* 群通话 Banner 位置 */
|
||||
.call-banner-wrapper {
|
||||
position: fixed;
|
||||
top: calc(88rpx + var(--status-bar-height) + var(--mp-safe-top, 0px));
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
top: calc(88rpx + var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
top: calc(88rpx + var(--status-bar-height, 0));
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* 消息列表 */
|
||||
@@ -737,15 +750,29 @@ function cancelRecording() { isCancelRecording.value = true; stopRecording() }
|
||||
|
||||
&.has-banner {
|
||||
.message-feed {
|
||||
padding-top: calc(88rpx + var(--status-bar-height) + var(--mp-safe-top, 0px) + 160rpx);
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding-top: calc(88rpx + var(--status-bar-height, 44px) + 88rpx + 160rpx);
|
||||
/* #endif */
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding-top: calc(88rpx + var(--status-bar-height, 0) + 160rpx);
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
}
|
||||
.message-feed {
|
||||
padding: 32rpx;
|
||||
padding-top: calc(88rpx + var(--status-bar-height) + var(--mp-safe-top, 0px) + 32rpx);
|
||||
/* 增加底部 Padding 以防输入框遮挡 */
|
||||
padding-bottom: 240rpx;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
/* 微信小程序:导航栏高度 + 状态栏 + 胶囊区域 */
|
||||
padding-top: calc(88rpx + var(--status-bar-height, 44px) + 88rpx + 32rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
/* H5/App:导航栏高度 + 状态栏 */
|
||||
padding-top: calc(88rpx + var(--status-bar-height, 0) + 32rpx);
|
||||
/* #endif */
|
||||
}
|
||||
.feed-status { text-align: center; padding: 24rpx; margin-bottom: 24rpx; .link-text { color: var(--color-primary); font-size: 24rpx; } }
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- 导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-btn" @click="goBack">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7">
|
||||
<polyline points="15 18 9 12 15 6"></polyline>
|
||||
</svg>
|
||||
@@ -30,7 +30,7 @@
|
||||
</view>
|
||||
<text class="nav-title">添加好友</text>
|
||||
<view class="nav-btn">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6">
|
||||
<circle cx="12" cy="12" r="1"></circle>
|
||||
<circle cx="19" cy="12" r="1"></circle>
|
||||
@@ -53,7 +53,7 @@
|
||||
<view class="search-wrapper">
|
||||
<view class="search-box" @click="focusSearch">
|
||||
<view class="search-icon-wrapper">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
@@ -74,7 +74,7 @@
|
||||
@confirm="doSearch"
|
||||
/>
|
||||
<view v-if="searchKeyword" class="clear-btn" @click.stop="clearSearch">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
@@ -96,7 +96,7 @@
|
||||
<!-- 扫一扫卡片 -->
|
||||
<view class="scan-card animate-fade-in-up" @click="handleScan">
|
||||
<view class="scan-icon-box">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6 text-white">
|
||||
<path d="M3 7V5a2 2 0 0 1 2-2h2"></path>
|
||||
<path d="M17 3h2a2 2 0 0 1 2 2v2"></path>
|
||||
@@ -113,7 +113,7 @@
|
||||
<text class="scan-desc">扫描二维码名片</text>
|
||||
</view>
|
||||
<view class="scan-arrow">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 text-white">
|
||||
<polyline points="15 18 9 12 15 6"></polyline>
|
||||
</svg>
|
||||
@@ -168,7 +168,7 @@
|
||||
<template v-else-if="viewState === 'empty'">
|
||||
<view class="state-card animate-fade-in-up">
|
||||
<view class="empty-icon-box">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
@@ -504,8 +504,17 @@ function goUserDetail(user: User) {
|
||||
.header-content {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
padding-top: calc(var(--status-bar-height) + var(--mp-safe-top, 0px));
|
||||
padding-bottom: 40rpx;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
/* 微信小程序:状态栏 + 胶囊按钮区域 */
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
/* H5/App:仅状态栏 */
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<!-- 导航栏 (与设计稿一致) -->
|
||||
<view class="nav-header">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M15 18l-6-6 6-6"/>
|
||||
</svg>
|
||||
@@ -24,7 +24,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="nav-more">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="1"/>
|
||||
<circle cx="19" cy="12" r="1"/>
|
||||
@@ -71,7 +71,7 @@
|
||||
<text class="row-label">设置备注</text>
|
||||
<view class="row-right">
|
||||
<text class="row-value">{{ contact.remark_name || '未设置' }}</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -170,7 +170,7 @@
|
||||
<view class="dock-buttons">
|
||||
<!-- 电话: flex-1 -->
|
||||
<view class="dock-btn secondary" @click="startAudioCall">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.05 12.05 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.05 12.05 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>
|
||||
</svg>
|
||||
@@ -181,7 +181,7 @@
|
||||
</view>
|
||||
<!-- 发消息: flex-[2] -->
|
||||
<view class="dock-btn primary" @click="goChat">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</svg>
|
||||
@@ -193,7 +193,7 @@
|
||||
</view>
|
||||
<!-- 视频: flex-1 -->
|
||||
<view class="dock-btn secondary" @click="startVideoCall">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polygon points="23 7 16 12 23 17 23 7"/>
|
||||
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
||||
@@ -214,7 +214,7 @@
|
||||
<!-- 底部悬浮操作栏 - 陌生人状态 -->
|
||||
<view class="floating-dock" v-else-if="!isFriend && strangerUser">
|
||||
<view class="add-friend-btn" @click="addFriend">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="8.5" cy="7" r="4"/>
|
||||
@@ -445,11 +445,18 @@ async function deleteFriend() { try { await messageBox.confirm({ title: '提示'
|
||||
.nav-header {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
padding: calc(var(--status-bar-height) + var(--mp-safe-top, 0px) + 24rpx) 32rpx 16rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 44px) + 88rpx + 24rpx) 32rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 0) + 24rpx) 32rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
.nav-back, .nav-more {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</view>
|
||||
<!-- 右侧: 添加好友按钮 -->
|
||||
<view class="icon-btn" @click="goAddFriend">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="add-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="8.5" cy="7" r="4"/>
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<view class="search-box-wrap">
|
||||
<view class="search-inner" @click="goSearch">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
@@ -85,7 +85,7 @@
|
||||
<view v-if="activeTab === 'groups'" class="feature-grid">
|
||||
<view class="feature-card" @click="goFriendRequests">
|
||||
<view class="icon-wrap is-orange">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="8.5" cy="7" r="4"/>
|
||||
@@ -104,7 +104,7 @@
|
||||
</view>
|
||||
<view class="feature-card" @click="goGroupNotify">
|
||||
<view class="icon-wrap is-blue">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
||||
@@ -131,7 +131,7 @@
|
||||
<view class="group-section-header">
|
||||
<text class="section-title">我的分组</text>
|
||||
<view class="add-group-btn" @click="showCreateGroupModal = true">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
@@ -148,7 +148,7 @@
|
||||
<view v-if="ungroupedContacts.length" class="collapse-item animate-fade-in-up">
|
||||
<view class="collapse-header" @click="toggleCollapse(0)">
|
||||
<view class="collapse-arrow" :class="{ collapsed: collapsedIds.includes(0) }">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
@@ -175,7 +175,7 @@
|
||||
<view v-for="(group, index) in contactGroups" :key="group.id" class="collapse-item animate-fade-in-up" :style="{ animationDelay: `${(index + 1) * 50}ms` }">
|
||||
<view class="collapse-header" @click="toggleCollapse(group.id)" @longpress="handleGroupLongPress(group)">
|
||||
<view class="collapse-arrow" :class="{ collapsed: collapsedIds.includes(group.id) }">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
@@ -219,7 +219,7 @@
|
||||
<view v-if="createdGroups.length" class="collapse-item animate-fade-in-up">
|
||||
<view class="collapse-header" @click="toggleCollapse('created')">
|
||||
<view class="collapse-arrow" :class="{ collapsed: collapsedIds.includes('created') }">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
@@ -242,7 +242,7 @@
|
||||
<view v-if="managedGroups.length" class="collapse-item animate-fade-in-up" style="animation-delay: 50ms;">
|
||||
<view class="collapse-header" @click="toggleCollapse('managed')">
|
||||
<view class="collapse-arrow" :class="{ collapsed: collapsedIds.includes('managed') }">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
@@ -265,7 +265,7 @@
|
||||
<view v-if="joinedGroups.length" class="collapse-item animate-fade-in-up" style="animation-delay: 100ms;">
|
||||
<view class="collapse-header" @click="toggleCollapse('joined')">
|
||||
<view class="collapse-arrow" :class="{ collapsed: collapsedIds.includes('joined') }">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
@@ -344,9 +344,6 @@
|
||||
<wd-toast />
|
||||
<wd-message-box />
|
||||
<app-tab-bar current="contacts" />
|
||||
|
||||
<!-- 全局通话组件 -->
|
||||
<global-call-provider />
|
||||
</view>
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
@@ -362,7 +359,6 @@ import * as roomApi from '@/api/modules/room'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
import AppTabBar from '@/components/common/AppTabBar.vue'
|
||||
import AppDrawer from '@/components/common/AppDrawer.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
import type { Contact, ContactGroup } from '@/types/api'
|
||||
|
||||
// --- 逻辑完全不变 ---
|
||||
@@ -492,9 +488,18 @@ async function logout() { try { await messageBox.confirm({ title: '提示', msg:
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: var(--bg-page);
|
||||
padding-top: calc(var(--status-bar-height) + var(--mp-safe-top, 0px));
|
||||
// 与消息页一致,不要下边框
|
||||
border-bottom: none;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
// 微信小程序:状态栏 + 胶囊按钮区域
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
// H5/App:仅状态栏
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* #endif */
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
@@ -641,8 +646,15 @@ async function logout() { try { await messageBox.confirm({ title: '提示', msg:
|
||||
|
||||
// 滚动区
|
||||
.main-scroll {
|
||||
margin-top: calc(var(--status-bar-height) + var(--mp-safe-top, 0px) + 290rpx);
|
||||
height: calc(100vh - var(--status-bar-height) - var(--mp-safe-top, 0px) - 290rpx);
|
||||
/* #ifdef MP-WEIXIN */
|
||||
margin-top: calc(var(--status-bar-height, 44px) + 88rpx + 290rpx);
|
||||
height: calc(100vh - var(--status-bar-height, 44px) - 88rpx - 290rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
margin-top: calc(var(--status-bar-height, 0) + 290rpx);
|
||||
height: calc(100vh - var(--status-bar-height, 0) - 290rpx);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.scroll-spacer {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<!-- 右侧: 加号按钮 -->
|
||||
<view class="nav-right">
|
||||
<view class="icon-btn-circle" @click="showPlusMenu = true">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="plus-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
@@ -49,7 +49,7 @@
|
||||
<!-- 2. 搜索栏 (扁平化设计) -->
|
||||
<view class="search-wrapper">
|
||||
<view class="search-bar" @click="goSearch">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
@@ -72,7 +72,7 @@
|
||||
</view>
|
||||
|
||||
<view v-else-if="conversations.length === 0" class="empty-state">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
<line x1="9" y1="10" x2="15" y2="10"/>
|
||||
@@ -124,7 +124,7 @@
|
||||
</view>
|
||||
<view class="row-bottom">
|
||||
<text class="msg-preview text-ellipsis">{{ item.last_message || '暂无消息' }}</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg v-if="item.is_muted" class="mute-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M11 5L6 9H2v6h4l5 4V5z"/>
|
||||
<line x1="23" y1="9" x2="17" y2="15"/>
|
||||
@@ -141,7 +141,7 @@
|
||||
<template #right>
|
||||
<view class="swipe-actions">
|
||||
<view class="action-btn top" @click.stop="toggleTop(item)">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z"/>
|
||||
</svg>
|
||||
@@ -151,7 +151,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="action-btn delete" @click.stop="deleteConversation(item)">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||
@@ -193,9 +193,6 @@
|
||||
<wd-toast />
|
||||
<wd-message-box :z-index="11000" />
|
||||
<app-tab-bar current="messages" />
|
||||
|
||||
<!-- 全局通话组件 -->
|
||||
<global-call-provider />
|
||||
</view>
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
@@ -213,7 +210,6 @@ import AppTabBar from '@/components/common/AppTabBar.vue'
|
||||
import AppDrawer from '@/components/common/AppDrawer.vue'
|
||||
import PlusMenu from '@/components/common/PlusMenu.vue'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
import type { Conversation } from '@/types/conversation'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
@@ -406,10 +402,19 @@ async function logout() {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
padding-top: calc(var(--status-bar-height, 0) + var(--mp-safe-top, 0px));
|
||||
// 确保没有任何边框
|
||||
border: none;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
// 微信小程序:状态栏 + 胶囊按钮区域 (约88rpx)
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
// H5/App:仅状态栏
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* #endif */
|
||||
|
||||
.navbar-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<view class="nav-right">
|
||||
<view class="nav-icon-btn" @click="goNotifications">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="icon" :style="{ color: iconColor }" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
||||
@@ -53,7 +53,7 @@
|
||||
</view>
|
||||
|
||||
<view v-else-if="moments.length === 0" class="empty-state">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M8 14s1.5 2 4 2 4-2 4-2"/>
|
||||
@@ -113,7 +113,7 @@
|
||||
<view class="feed-footer">
|
||||
<text class="time">{{ formatTime(moment.created_at) }}</text>
|
||||
<view class="action-btn" @click="showActionMenu(moment)">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="5" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="19" cy="12" r="2"/>
|
||||
</svg>
|
||||
@@ -130,7 +130,7 @@
|
||||
|
||||
<!-- 点赞列表 -->
|
||||
<view v-if="moment.likes && moment.likes.length > 0" class="likes-list">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="heart-icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
|
||||
</svg>
|
||||
@@ -167,7 +167,7 @@
|
||||
|
||||
<!-- 悬浮发布按钮 -->
|
||||
<view class="fab-btn" @click="goPublish">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
|
||||
<circle cx="12" cy="13" r="4"/>
|
||||
@@ -190,9 +190,6 @@
|
||||
<wd-message-box />
|
||||
|
||||
<app-tab-bar current="moments" />
|
||||
|
||||
<!-- 全局通话组件 -->
|
||||
<global-call-provider />
|
||||
</view>
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
@@ -208,7 +205,6 @@ import { parseMediaUrls } from '@/types/moment'
|
||||
import { useToast, useMessage } from 'wot-design-uni'
|
||||
import AppTabBar from '@/components/common/AppTabBar.vue'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
import type { Moment } from '@/types/moment'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
@@ -394,7 +390,6 @@ function getImageSize(moment: Moment): string {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: calc(88rpx + var(--status-bar-height) + var(--mp-safe-top, 0px));
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@@ -403,6 +398,16 @@ function getImageSize(moment: Moment): string {
|
||||
transition: background 0.3s;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
// 微信小程序:导航栏 + 状态栏 + 胶囊区域
|
||||
height: calc(88rpx + var(--status-bar-height, 44px) + 88rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
// H5/App:导航栏 + 状态栏
|
||||
height: calc(88rpx + var(--status-bar-height, 0));
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.nav-content {
|
||||
|
||||
@@ -178,9 +178,16 @@ async function save() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: calc(var(--status-bar-height) + var(--mp-safe-top, 0px) + 16rpx) 24rpx 16rpx;
|
||||
background: var(--bg-page);
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 44px) + 88rpx + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 0) + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
.nav-back {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<view class="header-top">
|
||||
<text class="page-title">我的</text>
|
||||
<view class="icon-btn" @click="uni.navigateTo({ url: '/pages/settings/index' })">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
@@ -27,7 +27,7 @@
|
||||
<text class="nickname">{{ userInfo.nickname || '未设置昵称' }}</text>
|
||||
<text class="account">账号: {{ userInfo.account || '--' }}</text>
|
||||
</view>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -59,7 +59,7 @@
|
||||
<view class="menu-group animate-fade-in-up" style="animation-delay: 150ms;">
|
||||
<view class="menu-item" @click="uni.navigateTo({ url: '/pages/moment/my' })">
|
||||
<view class="icon-box indigo">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M8 14s1.5 2 4 2 4-2 4-2"/>
|
||||
@@ -72,7 +72,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<text class="menu-text">我的动态</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="menu-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -83,7 +83,7 @@
|
||||
</view>
|
||||
<view class="menu-item" @click="uni.navigateTo({ url: '/pages/collection/index' })">
|
||||
<view class="icon-box amber">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
||||
</svg>
|
||||
@@ -93,7 +93,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<text class="menu-text">我的收藏</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="menu-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
</view>
|
||||
<view class="menu-item" @click="uni.navigateTo({ url: '/pages/album/index' })">
|
||||
<view class="icon-box emerald">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
||||
@@ -116,7 +116,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<text class="menu-text">相册</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="menu-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -129,9 +129,6 @@
|
||||
</view>
|
||||
|
||||
<app-tab-bar current="profile" />
|
||||
|
||||
<!-- 全局通话组件 -->
|
||||
<global-call-provider />
|
||||
</view>
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
@@ -142,7 +139,6 @@ import { useTheme } from '@/composables/useTheme'
|
||||
import { useAuthStore } from '@/stores'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
import AppTabBar from '@/components/common/AppTabBar.vue'
|
||||
import GlobalCallProvider from '@/components/call/GlobalCallProvider.vue'
|
||||
|
||||
const { isDark } = useTheme()
|
||||
const authStore = useAuthStore()
|
||||
@@ -197,10 +193,17 @@ function goProfileEdit() { uni.navigateTo({ url: '/pages/profile/edit' }) }
|
||||
// 头部
|
||||
.profile-header {
|
||||
background: var(--bg-surface);
|
||||
padding: calc(var(--status-bar-height) + var(--mp-safe-top, 0px) + 24rpx) 40rpx 60rpx;
|
||||
border-bottom-left-radius: 80rpx;
|
||||
border-bottom-right-radius: 80rpx;
|
||||
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 44px) + 88rpx + 24rpx) 40rpx 60rpx;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 0) + 24rpx) 40rpx 60rpx;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.header-top {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-header">
|
||||
<view class="search-box">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
@@ -21,7 +21,7 @@
|
||||
@confirm="doSearch"
|
||||
/>
|
||||
<view v-if="keyword" class="clear-btn" @click="clearKeyword">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>
|
||||
</svg>
|
||||
@@ -39,7 +39,7 @@
|
||||
<view class="section-header">
|
||||
<text class="title">搜索历史</text>
|
||||
<view class="clear" @click="clearHistory">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
<!-- 空结果 -->
|
||||
<view v-else-if="getCurrentResults.length === 0" class="empty-state">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
@@ -120,7 +120,7 @@
|
||||
<text class="name" v-html="highlightKeyword(item.remark_name || item.user?.name || '')"></text>
|
||||
<text class="desc">{{ item.user?.desc || '暂无签名' }}</text>
|
||||
</view>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -153,7 +153,7 @@
|
||||
<text class="desc">{{ item.member_count }}人</text>
|
||||
</view>
|
||||
<view class="group-badge">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="9" cy="7" r="4"/>
|
||||
@@ -165,7 +165,7 @@
|
||||
<wd-icon name="user" size="28rpx" />
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -436,11 +436,18 @@ function formatTime(time: string): string {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
padding: calc(var(--status-bar-height) + var(--mp-safe-top, 0px) + 16rpx) 24rpx 16rpx;
|
||||
background: var(--bg-page);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 44px) + 88rpx + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 0) + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.search-box {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<!-- 导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M15 18l-6-6 6-6"/>
|
||||
</svg>
|
||||
@@ -24,7 +24,7 @@
|
||||
<view class="group-card">
|
||||
<view class="setting-item" @click="goTo('/pages/settings/account')">
|
||||
<view class="icon-box blue">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>
|
||||
@@ -34,7 +34,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<text class="item-text">账号管理</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -45,7 +45,7 @@
|
||||
</view>
|
||||
<view class="setting-item" @click="goTo('/pages/settings/privacy')">
|
||||
<view class="icon-box purple">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
@@ -56,7 +56,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<text class="item-text">隐私设置</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -74,7 +74,7 @@
|
||||
<view class="group-card">
|
||||
<view class="setting-item" @click="goTo('/pages/settings/notification')">
|
||||
<view class="icon-box red">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
||||
@@ -85,7 +85,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<text class="item-text">新消息通知</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -96,7 +96,7 @@
|
||||
</view>
|
||||
<view class="setting-item" @click="goTo('/pages/settings/theme')">
|
||||
<view class="icon-box indigo">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="5"/>
|
||||
<line x1="12" y1="1" x2="12" y2="3"/>
|
||||
@@ -115,7 +115,7 @@
|
||||
</view>
|
||||
<text class="item-text">主题设置</text>
|
||||
<text class="item-value">{{ themeText }}</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -126,7 +126,7 @@
|
||||
</view>
|
||||
<view class="setting-item" @click="handleClearCache">
|
||||
<view class="icon-box green">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
@@ -138,7 +138,7 @@
|
||||
</view>
|
||||
<text class="item-text">清理缓存</text>
|
||||
<text class="item-value">{{ cacheSize }}</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -156,7 +156,7 @@
|
||||
<view class="group-card">
|
||||
<view class="setting-item" @click="goTo('/pages/settings/feedback')">
|
||||
<view class="icon-box orange">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</svg>
|
||||
@@ -166,7 +166,7 @@
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<text class="item-text">帮助与反馈</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -177,7 +177,7 @@
|
||||
</view>
|
||||
<view class="setting-item" @click="goTo('/pages/settings/about')">
|
||||
<view class="icon-box gray">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||
@@ -190,7 +190,7 @@
|
||||
</view>
|
||||
<text class="item-text">关于我们</text>
|
||||
<text class="item-value">v1.0.0</text>
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
@@ -305,9 +305,16 @@ function handleClearCache() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: calc(var(--status-bar-height) + var(--mp-safe-top, 0px) + 16rpx) 24rpx 16rpx;
|
||||
background: var(--bg-page);
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 44px) + 88rpx + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 0) + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
.nav-back {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
|
||||
@@ -169,9 +169,16 @@ function selectTheme(mode: 'system' | 'light' | 'dark') {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: calc(var(--status-bar-height) + var(--mp-safe-top, 0px) + 16rpx) 24rpx 16rpx;
|
||||
background: var(--bg-page);
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 44px) + 88rpx + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding: calc(var(--status-bar-height, 0) + 16rpx) 24rpx 16rpx;
|
||||
/* #endif */
|
||||
|
||||
.nav-back {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
|
||||
@@ -2,10 +2,20 @@
|
||||
* 应用全局状态管理
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export type TabType = 'chat' | 'contact' | 'moment'
|
||||
|
||||
// 微信小程序胶囊按钮信息类型
|
||||
export interface MenuButtonInfo {
|
||||
width: number
|
||||
height: number
|
||||
top: number
|
||||
right: number
|
||||
bottom: number
|
||||
left: number
|
||||
}
|
||||
|
||||
export const useAppStore = defineStore('app', () => {
|
||||
// 当前激活的 Tab
|
||||
const currentTab = ref<TabType>('chat')
|
||||
@@ -16,9 +26,30 @@ export const useAppStore = defineStore('app', () => {
|
||||
// 系统信息
|
||||
const systemInfo = ref<UniApp.GetSystemInfoResult | null>(null)
|
||||
|
||||
// 微信小程序胶囊按钮信息
|
||||
const menuButtonInfo = ref<MenuButtonInfo | null>(null)
|
||||
|
||||
// 左侧抽屉是否打开
|
||||
const drawerVisible = ref(false)
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = computed(() => systemInfo.value?.statusBarHeight || 0)
|
||||
|
||||
// 导航栏高度(不含状态栏)
|
||||
const navBarHeight = computed(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (menuButtonInfo.value) {
|
||||
// 导航栏高度 = (胶囊按钮距顶部距离 - 状态栏高度) * 2 + 胶囊按钮高度
|
||||
const paddingTop = menuButtonInfo.value.top - statusBarHeight.value
|
||||
return paddingTop * 2 + menuButtonInfo.value.height
|
||||
}
|
||||
// #endif
|
||||
return 44 // 默认导航栏高度
|
||||
})
|
||||
|
||||
// 导航栏总高度(含状态栏)
|
||||
const navBarTotalHeight = computed(() => statusBarHeight.value + navBarHeight.value)
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
@@ -26,6 +57,25 @@ export const useAppStore = defineStore('app', () => {
|
||||
// 获取系统信息
|
||||
systemInfo.value = uni.getSystemInfoSync()
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 获取微信小程序胶囊按钮位置信息
|
||||
try {
|
||||
const rect = uni.getMenuButtonBoundingClientRect()
|
||||
menuButtonInfo.value = {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
left: rect.left
|
||||
}
|
||||
console.log('📱 [App] 胶囊按钮信息:', menuButtonInfo.value)
|
||||
console.log('📱 [App] 状态栏高度:', statusBarHeight.value, '导航栏高度:', navBarHeight.value)
|
||||
} catch (e) {
|
||||
console.warn('⚠️ [App] 获取胶囊按钮信息失败:', e)
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 初始化主题
|
||||
initTheme()
|
||||
|
||||
@@ -34,6 +84,22 @@ export const useAppStore = defineStore('app', () => {
|
||||
if (cachedTab && ['chat', 'contact', 'moment'].includes(cachedTab)) {
|
||||
currentTab.value = cachedTab as TabType
|
||||
}
|
||||
|
||||
// 注入 CSS 变量到页面
|
||||
injectCSSVariables()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入 CSS 变量(用于微信小程序安全区域)
|
||||
*/
|
||||
function injectCSSVariables() {
|
||||
// #ifdef MP-WEIXIN
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 0) {
|
||||
// 通过 page 元素设置 CSS 变量无法直接操作,但可以通过 JS 计算后在组件中使用
|
||||
console.log('📱 [App] CSS 变量 - statusBarHeight:', statusBarHeight.value, 'navBarHeight:', navBarHeight.value)
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,6 +193,10 @@ export const useAppStore = defineStore('app', () => {
|
||||
currentTab,
|
||||
isDark,
|
||||
systemInfo,
|
||||
menuButtonInfo,
|
||||
statusBarHeight,
|
||||
navBarHeight,
|
||||
navBarTotalHeight,
|
||||
drawerVisible,
|
||||
init,
|
||||
initTheme,
|
||||
|
||||
@@ -185,17 +185,30 @@
|
||||
--safe-area-left: env(safe-area-inset-left, 0px);
|
||||
--safe-area-right: env(safe-area-inset-right, 0px);
|
||||
// 微信小程序额外安全距离(胶囊按钮区域)
|
||||
// 默认值:胶囊按钮高度约32px,上下间距约8px,总计约48px
|
||||
--mp-safe-top: 0px;
|
||||
// 微信小程序导航栏内容高度(不含状态栏)
|
||||
--mp-navbar-height: 44px;
|
||||
}
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
page {
|
||||
--mp-safe-top: env(safe-area-inset-top, 20px);
|
||||
// 微信小程序:状态栏 + 导航栏区域
|
||||
// 胶囊按钮通常距状态栏底部约 4-8px,高度约 32px
|
||||
// 导航栏高度 = (胶囊top - 状态栏高度) * 2 + 胶囊高度 ≈ 8 * 2 + 32 = 48px
|
||||
--mp-safe-top: 8px; // 胶囊按钮上方间距
|
||||
--mp-navbar-height: 48px; // 导航栏内容区高度
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.safe-area-top {
|
||||
padding-top: calc(var(--status-bar-height, 0) + var(--mp-safe-top, 0px));
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 95rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.safe-area-bottom {
|
||||
@@ -207,6 +220,11 @@ page {
|
||||
height: calc(160rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
// 微信小程序导航栏占位(含状态栏 + 导航栏)
|
||||
.mp-navbar-spacer {
|
||||
height: calc(var(--status-bar-height, 0) + var(--mp-navbar-height, 44px));
|
||||
}
|
||||
|
||||
// 响应式断点
|
||||
@mixin respond-to($breakpoint) {
|
||||
@if $breakpoint == 'sm' {
|
||||
@@ -449,14 +467,28 @@ page {
|
||||
background: var(--nav-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
padding-top: calc(var(--status-bar-height, 0) + var(--mp-safe-top, 0px));
|
||||
border-bottom: 1rpx solid var(--border-subtle);
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
// 微信小程序:状态栏 + 胶囊按钮区域
|
||||
padding-top: calc(var(--status-bar-height, 44px) + 20rpx);
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
// H5/App:仅状态栏高度
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* #endif */
|
||||
|
||||
.navbar-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 88rpx;
|
||||
height: var(--mp-navbar-height, 88rpx);
|
||||
padding: 0 32rpx;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
// 微信小程序:避开右侧胶囊按钮,预留约 100px 空间
|
||||
padding-right: 200rpx;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
// 图片资源基础域名
|
||||
const IMAGE_BASE_URL = 'http://127.0.0.1:12080'
|
||||
const IMAGE_BASE_URL = 'https://g-ws.nailaoyun.cn'
|
||||
|
||||
/**
|
||||
* 解析图片 URL,为相对路径添加域名前缀
|
||||
|
||||
Reference in New Issue
Block a user