From e4439e5d0a2cba9983f013bd044fe4e22c7b7077 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Wed, 11 Feb 2026 16:35:06 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
applications/task-work-order/src/utils/auth.js | 5
applications/drone-command/src/assets/images/active-menu-item.png | 0
applications/drone-command/src/page/index/top/index.vue | 340 +++++++++++++++++++++++++------------
uniapps/work-app/src/api/voiceCall/index.js | 2
applications/drone-command/src/utils/auth.js | 4
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue | 45 +++-
applications/drone-command/src/assets/images/menu-item.png | 0
uniapps/work-app/src/subPackages/voiceCallDetail/index.vue | 83 ++++++--
uniapps/work-app/src/pages/voiceCall/index.vue | 2
9 files changed, 326 insertions(+), 155 deletions(-)
diff --git a/applications/drone-command/src/assets/images/active-menu-item.png b/applications/drone-command/src/assets/images/active-menu-item.png
new file mode 100644
index 0000000..39fd486
--- /dev/null
+++ b/applications/drone-command/src/assets/images/active-menu-item.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/menu-item.png b/applications/drone-command/src/assets/images/menu-item.png
new file mode 100644
index 0000000..3b24a46
--- /dev/null
+++ b/applications/drone-command/src/assets/images/menu-item.png
Binary files differ
diff --git a/applications/drone-command/src/page/index/top/index.vue b/applications/drone-command/src/page/index/top/index.vue
index e7f7b93..d090627 100644
--- a/applications/drone-command/src/page/index/top/index.vue
+++ b/applications/drone-command/src/page/index/top/index.vue
@@ -1,38 +1,49 @@
<template>
- <div class="avue-top">
- <div class="top-bar__title">
- <img :src="logoUrl" alt="">
- <span>低空飞行监管子系统</span>
- </div>
+ <header class="header-container">
+ <div class="content-wrap">
+ <div class="logo-title-wrap">
+ <img :src="logoUrl" alt="Logo"></img>
+ <p class="title">低空飞行监管子系统</p>
+ </div>
- <div class="top-bar__right">
- <div class="top-user">
- <div class="icon-box">
- <img class="gateway" @click="jumpMH" src="@/assets/images/mh.png" alt="进入门户" title="进入门户">
- </div>
+ <div class="header-right">
+ <nav class="nav-menu">
+ <div v-for="(item, index) in topMenus" :key="index" class="nav-item"
+ :class="{ active: item.active }" @click="handleMenuClick(item)">
+ <span>{{ item.label }}</span>
+ </div>
+ </nav>
- <el-dropdown popper-class="command-custom-dropdown">
- <span class="el-dropdown-link">
- <img class="top-bar__img" :src="userInfo.avatar" alt="" />
- </span>
+ <div class="icon-group">
+ <div class="top-user">
+ <div class="icon-box">
+ <img class="gateway" @click="jumpMH" src="@/assets/images/mh.png" alt="进入门户" title="进入门户">
+ </div>
- <template #dropdown>
- <el-dropdown-menu>
- <!-- <el-dropdown-item>
+ <el-dropdown popper-class="command-custom-dropdown">
+ <span class="el-dropdown-link">
+ <img class="top-bar__img" :src="userInfo.avatar" alt="" />
+ </span>
+
+ <template #dropdown>
+ <el-dropdown-menu>
+ <!-- <el-dropdown-item>
<router-link to="/">{{ $t('navbar.dashboard') }}</router-link>
</el-dropdown-item>-->
- <!-- <el-dropdown-item>
+ <!-- <el-dropdown-item>
<router-link to="/info/index">{{ $t('navbar.userinfo') }}</router-link>
</el-dropdown-item> -->
- <el-dropdown-item @click="logout" divided>{{ $t('navbar.logOut') }}
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- <top-setting></top-setting>
+ <el-dropdown-item @click="logout" divided>{{ $t('navbar.logOut') }}
+ </el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+ <top-setting></top-setting>
+ </div>
+ </div>
</div>
</div>
- </div>
+ </header>
</template>
<script>
@@ -69,6 +80,35 @@
data () {
return {
logoUrl: logo,
+ activeMenu: 'drone-control',
+ topMenus: [
+ {
+ key: 'twin-supervision',
+ label: '孪生监管',
+ path: '/flight-supervision/#/brain',
+ },
+ {
+ key: 'airspace-collaboration',
+ label: '空域协同',
+ path: '/flight-supervision/#/space',
+ },
+ {
+ key: 'air-traffic',
+ label: '空中交通',
+ path: '/flight-supervision/#/activity',
+ },
+ {
+ key: 'information-service',
+ label: '信息服务',
+ path: '/flight-supervision/#/infoService',
+ },
+ {
+ key: 'drone-control',
+ label: '无人机管控',
+ path: '',
+ active: true,
+ },
+ ],
}
},
filters: {},
@@ -93,115 +133,189 @@
cancelButtonClass: 'command-message-box-cancel',
}).then(() => {
this.$store.dispatch('LogOut').then(() => {
- const {VITE_APP_PARENT_SYSTEM,VITE_APP_ENV} = import.meta.env
- const isDev = VITE_APP_ENV === 'development'
- isDev
- ? this.$router.push({ path: '/login' })
- : window.location.replace(`${VITE_APP_PARENT_SYSTEM}/#/login`)
+ const { VITE_APP_PARENT_SYSTEM, VITE_APP_ENV } = import.meta.env
+ const isDev = VITE_APP_ENV === 'development'
+ isDev
+ ? this.$router.push({ path: '/login' })
+ : window.location.replace(`${VITE_APP_PARENT_SYSTEM}/#/login`)
})
})
},
jumpMH () {
-
+ window.open(`${window.location.origin}/droneWeb/#/gateway`, '_blank')
+ },
+ handleMenuClick (item) {
+ this.activeMenu = item.key
+ if (!item.path) return
+ window.open(`${window.location.origin}${item.path}`, '_blank')
},
},
}
</script>
<style lang="scss" scoped>
-.avue-top {
- display: flex;
- justify-content: space-between;
- height: 100%;
+.header-container {
+ position: relative;
+ height: 110px;
background: url('@/assets/images/topContainer/top-bg.png') center / 100% 100% no-repeat !important;
+ background-repeat: no-repeat;
+ background-size: cover;
pointer-events: none;
-}
+ z-index: 2;
-.top-bar__left {
- flex: 0 0 auto;
-}
-
-.top-bar__title {
- margin-top: 16px;
- margin-left: 30px;
- flex: 1;
- display: flex;
- align-items: center;
- height: pxToVh(48);
-
- img {
- margin-right: 17px;
- width: 50px;
- height: 50px;
+ .bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ pointer-events: none;
}
- span {
- width: 353px;
- height: 43px;
- font-family: PangMen;
- font-weight: 400;
- font-size: 36px;
- color: #FFFFFF;
- letter-spacing: 3px;
- text-shadow: 3px 3px 0px rgba(0, 13, 42, 0.27);
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
-}
-
-.top-bar__right {
- margin-right: 28px;
- padding-top: 16px;
- flex: 0 0 auto;
- display: flex !important;
- align-items: flex-start;
- height: 100%;
- pointer-events: auto;
- box-sizing: border-box;
-
- .icon-box {
- margin-top: 6px;
- margin-right: 30px;
+ .content-wrap {
+ position: absolute;
+ inset: 0;
display: flex;
align-items: center;
- gap: 0 20px;
+ z-index: 1;
+ padding: 0 40px;
- .gateway {
- width: 21px;
- height: 18px;
+ .logo-title-wrap {
+ display: flex;
+ align-items: center;
+ transform: translateY(-10px);
+
+ img {
+ width: 70px;
+ height: 63px;
+ vertical-align: middle;
+ }
+
+ .title {
+ margin-left: 16px;
+ margin-top: 0;
+ margin-bottom: 0;
+
+ width: 353px;
+ height: 43px;
+ font-family: PangMen;
+ font-weight: 400;
+ font-size: 36px;
+ color: #FFFFFF;
+ letter-spacing: 3px;
+ text-shadow: 3px 3px 0px rgba(0, 13, 42, 0.27);
+ text-align: left;
+ font-style: normal;
+ text-transform: none;
+ }
}
- >* {
- cursor: pointer;
+ .header-right {
+ display: flex;
+ align-items: center;
+ margin-left: auto;
+ margin-top: -25px;
+ pointer-events: auto;
+
+ .nav-menu {
+ display: flex;
+ margin-right: 30px;
+ gap: 0px;
+
+ .nav-item {
+ width: 136px;
+ height: 34px;
+ box-sizing: border-box;
+ text-align: center;
+ color: #afafe0;
+ text-decoration: none;
+ font-size: 20px;
+ transition: color 0.3s;
+ padding: 0;
+ background: url('@/assets/images/menu-item.png') no-repeat center / 136px 34px;
+ cursor: pointer;
+ margin: 0 10px;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding-bottom: 10px;
+ line-height: 1;
+
+ font-family: YouSheBiaoTiHei;
+
+ &:hover {
+ color: #fff;
+ }
+
+ &.active {
+ color: #fff;
+ background: url('@/assets/images/active-menu-item.png') no-repeat center / 136px 34px;
+
+ span {
+ background: linear-gradient(180deg,
+ #fff 22.11%,
+ #ffffff 86.69%);
+ background-size: contain;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ text-shadow: 0px 0px 13px 0px #5da6ef73;
+ }
+ }
+ }
+ }
+
+ .icon-group {
+ display: flex;
+ align-items: center;
+ margin-right: 20px;
+ gap: 15px;
+
+
+ .icon-box {
+ margin-top: 6px;
+ margin-right: 30px;
+ display: flex;
+ align-items: center;
+ gap: 0 20px;
+
+ .gateway {
+ width: 21px;
+ height: 18px;
+ }
+
+ >* {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .top-bar__item {
+ margin-right: 15px;
+ display: inline-block !important;
+ }
+
+ .top-user {
+ display: flex;
+ align-items: center;
+ }
+
+ .top-bar__img {
+ margin-top: 5px;
+ width: 20px;
+ height: 20px;
+ border: 2px solid #383874;
+ border-radius: 50%;
+ }
+
+ .el-dropdown-link {
+ cursor: pointer;
+ color: #606266;
+ }
+
+ .el-dropdown-link:hover {
+ color: #409EFF;
+ }
}
}
-}
-
-.top-bar__item {
- margin-right: 15px;
- display: inline-block !important;
-}
-
-.top-user {
- display: flex;
- align-items: center;
-}
-
-.top-bar__img {
- margin-top: 5px;
- width: 20px;
- height: 20px;
- border: 2px solid #383874;
- border-radius: 50%;
-}
-
-.el-dropdown-link {
- cursor: pointer;
- color: #606266;
-}
-
-.el-dropdown-link:hover {
- color: #409EFF;
}
</style>
diff --git a/applications/drone-command/src/utils/auth.js b/applications/drone-command/src/utils/auth.js
index 4171132..e82ec6a 100644
--- a/applications/drone-command/src/utils/auth.js
+++ b/applications/drone-command/src/utils/auth.js
@@ -10,8 +10,8 @@
*/
import Cookies from 'js-cookie';
-const TokenKey = 'saber3-access-token';
-const RefreshTokenKey = 'saber3-refresh-token';
+const TokenKey = 'command-access-token';
+const RefreshTokenKey = 'command-refresh-token';
const SessionId = 'JSESSIONID';
const UserId = 'b-user-id';
diff --git a/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue b/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
index 6a2134c..ec529b9 100644
--- a/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
+++ b/applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -41,8 +41,8 @@
import defaultAvatar from '@/appDataSource/appwork/defaultAvatar.svg'
const route = useRoute()
/** ✅ 你的 WS 地址前缀(后面拼 userId) */
-const WS_BASE = 'wss://wrj.shuixiongit.com/ws/chat?userId='
-
+// const WS_BASE = 'ws://218.202.104.82:38201/ws/chat?userId='
+const WS_BASE = 'ws://218.202.104.82:38201/ws/chat'
// 解析URL参数
const parseUrlParams = () => {
// 1. 优先从route.query.voiceparams获取参数
@@ -105,11 +105,11 @@
if (userId) {
return userId
}
- return '8' // 默认值
+ return '3' // 默认值
}
-const uid = ref('8')
-const peerUid = ref('7')
+const uid = ref('3')
+const peerUid = ref('2')
// 联系人名称
const contactName = ref('张三')
@@ -121,6 +121,8 @@
const callEnded = ref(false)
const endMessage = ref('对方已挂断')
const endTimer = ref(null)
+// token 存储
+const accessToken = ref('')
let ws = null
let pc = null
@@ -227,7 +229,17 @@
}
try {
- localStream = await navigator.mediaDevices.getUserMedia({ audio: true })
+ localStream = await navigator.mediaDevices.getUserMedia({
+ audio: {
+ echoCancellation: true,
+ noiseSuppression: true,
+ autoGainControl: true,
+ // 移动端特定配置
+ channelCount: 1,
+ sampleRate: 16000,
+ sampleSize: 16
+ }
+ })
log('✅ 已获取本地麦克风')
return localStream
} catch (e) {
@@ -378,8 +390,10 @@
if (ws) ws.close()
- ws = new WebSocket(WS_BASE + encodeURIComponent(uid.value))
-
+ // 使用从参数中获取的token或默认token
+ const token = accessToken.value
+ // ws = new WebSocket(WS_BASE + encodeURIComponent(uid.value))
+ ws = new WebSocket(WS_BASE, token)
ws.onopen = () => {
connected.value = true
log('WS 已连接 userId=', uid.value)
@@ -558,8 +572,19 @@
onMounted(() => {
console.log('收拾收拾',route.query.voiceparams);
- console.log('接收参数', JSON.parse(decodeURIComponent(receiveParameters.value)))
-
+ console.log('接收参数', receiveParameters)
+ if (route.query && route.query.params) {
+ try {
+ const params = JSON.parse(decodeURIComponent(route.query.params));
+ // 获取token
+ if (params.access_token) {
+ accessToken.value = params.access_token;
+ log('🔗 从params获取access_token:', accessToken.value);
+ }
+ } catch (e) {
+ log('❌ 解析params参数失败:', String(e));
+ }
+ }
// 解析并使用contact参数
if (receiveParameters.value) {
try {
diff --git a/applications/task-work-order/src/utils/auth.js b/applications/task-work-order/src/utils/auth.js
index 4e41090..22a03a6 100644
--- a/applications/task-work-order/src/utils/auth.js
+++ b/applications/task-work-order/src/utils/auth.js
@@ -1,8 +1,9 @@
import Cookies from 'js-cookie';
-const TokenKey = 'saber3-access-token';
-const RefreshTokenKey = 'saber3-refresh-token';
+const TokenKey = 'work-access-token';
+const RefreshTokenKey = 'work-refresh-token';
const SessionId = 'JSESSIONID';
+
const UserId = 'b-user-id';
export function getToken() {
diff --git a/uniapps/work-app/src/api/voiceCall/index.js b/uniapps/work-app/src/api/voiceCall/index.js
index 448689d..2b7a26e 100644
--- a/uniapps/work-app/src/api/voiceCall/index.js
+++ b/uniapps/work-app/src/api/voiceCall/index.js
@@ -2,7 +2,7 @@
export const getPhoneBookListApi = (data) => {
return request({
- url: '/webservice/jaUserContact/pageInfo',
+ url: '/system/user/listCommunication',
method: 'post',
data,
})
diff --git a/uniapps/work-app/src/pages/voiceCall/index.vue b/uniapps/work-app/src/pages/voiceCall/index.vue
index 0a6b987..96f466d 100644
--- a/uniapps/work-app/src/pages/voiceCall/index.vue
+++ b/uniapps/work-app/src/pages/voiceCall/index.vue
@@ -20,7 +20,7 @@
<!-- 联系人信息 -->
<div class="contactInfo">
- <div class="contactName">{{ contact.friendNickName || '未知联系人' }}</div>
+ <div class="contactName">{{ contact.nickName || '未知联系人' }}</div>
<div class="contactDept">{{ contact.deptName || '未知部门' }}</div>
</div>
diff --git a/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue b/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
index 32fde49..86c915e 100644
--- a/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
+++ b/uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
@@ -15,7 +15,62 @@
const userParams = userStore?.userInfo ? JSON.stringify(userStore.userInfo) : '{}'
const sWebViewRef = ref(null);
const viewUrl = ref("");
-onLoad((options) => {
+
+// #ifdef APP-PLUS
+/**
+ * 请求 Android 麦克风权限
+ * @returns {Promise<boolean>} 权限是否授予
+ */
+async function requestAndroidMicPermission() {
+ try {
+ // 使用 uni 的权限 API(需要 HBuilderX 3.4.0+)
+ if (uni.requestPermissions) {
+ const result = await uni.requestPermissions({
+ permissions: ['android.permission.RECORD_AUDIO']
+ })
+
+ console.log('权限请求结果:', result)
+
+ if (result[0].granted) {
+ console.log('✅ 麦克风权限已授予')
+ return true
+ } else {
+ console.warn('⚠️ 麦克风权限被拒绝')
+
+ // 可以提示用户
+ uni.showModal({
+ title: '权限提示',
+ content: '语音通话需要麦克风权限,请前往设置开启',
+ showCancel: false,
+ success: () => {
+ // 跳转到应用设置
+ plus.runtime.openURL(plus.runtime.getProperty('package') + '://settings')
+ }
+ })
+ return false
+ }
+ }
+
+ // 降级方案:使用原生方法
+ return await requestAndroidMicPermissionNative()
+
+ } catch (error) {
+ console.error('❌ 请求麦克风权限失败:', error)
+ return false
+ }
+}
+// #endif
+
+onLoad(async (options) => {
+ // #ifdef APP-PLUS
+ // 在加载 WebView 前先请求麦克风权限
+ console.log('📞 语音通话页面加载,请求麦克风权限')
+ const hasPermission = await requestAndroidMicPermission()
+ if (!hasPermission) {
+ console.warn('⚠️ 未获得麦克风权限,通话功能可能无法使用')
+ }
+ // #endif
+
// 解析传递过来的contact参数
const contact = options.contact ? JSON.parse(decodeURIComponent(options.contact)) : null;
// 构建viewUrl,将contact参数拼接到URL中
@@ -23,32 +78,8 @@
// viewUrl.value = `https://192.168.1.157:5176/mobile-web-view/#/webViewWrapper/voiceCallDetail?params=${encodeURIComponent(userParams)}&contact=${contactParam}`;
viewUrl.value = getWebViewUrl("/voiceCallDetail", { contact: options.contact ,voiceparams: options.voiceparams});
});
+
function onPostMessage(data) {}
-// #ifdef APP-PLUS
-function requestAndroidMicPermission() {
- return new Promise((resolve) => {
- const main = plus.android.runtimeMainActivity()
- const Build = plus.android.importClass('android.os.Build')
- if (Build.VERSION.SDK_INT < 23) return resolve(true)
-
- const Manifest = plus.android.importClass('android.Manifest')
- const ActivityCompat = plus.android.importClass('androidx.core.app.ActivityCompat')
-
- const permission = Manifest.permission.RECORD_AUDIO
- ActivityCompat.requestPermissions(main, [permission], 1001)
-
- // 简化:延迟检查一次(更严谨可写原生回调插件)
- setTimeout(() => {
- const PackageManager = plus.android.importClass('android.content.pm.PackageManager')
- const granted = ActivityCompat.checkSelfPermission(main, permission) === PackageManager.PERMISSION_GRANTED
- resolve(granted)
- }, 800)
- })
-}
-
-
-requestAndroidMicPermission()
-// #endif
</script>
--
Gitblit v1.9.3