吉安感知网项目-前端
shuishen
2026-02-26 039679bf092a8a6cb1f8c921c8a2cfa5306dcf73
Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web
17 files modified
8 files renamed
1 files copied
2 files added
535 ■■■■ changed files
applications/drone-command/src/api/system/user.js 3 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/defenseZone/FormDiaLog.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/FormDiaLog copy.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionRole/RolePermissionDialog.vue 44 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionUser/FormDiaLog.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionUser/index.vue 4 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue 14 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/work/workDetail/index.vue 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/api/system/user.js 3 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/deviceInvocation/deviceCallRecord/FormDiaLog.vue 142 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/deviceInvocation/deviceCallRecord/index.vue 19 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/deviceInvocation/equipmentResource/FormDiaLog.vue patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/deviceInvocation/equipmentResource/equipmentResourceApi.js patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/deviceInvocation/equipmentResource/index.vue patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue 42 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/roleMange/FormDiaLog.vue patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/roleMange/RolePermissionDialog.vue 22 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/roleMange/index.vue patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/userMange/FormDiaLog.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/userMange/index.vue 4 ●●●● patch | view | raw | blame | history
packages/utils/map/DrawPolygon.js 40 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/App.vue 3 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/hooks/useGlobalWS.js 122 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/map/index.vue 37 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/voiceCall/index.vue 4 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/static/audio/ringtone.mp3 patch | view | raw | blame | history
uniapps/work-app/src/subPackages/voiceCallDetail/index.vue 8 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/common/index.js 8 ●●●● patch | view | raw | blame | history
applications/drone-command/src/api/system/user.js
@@ -1,6 +1,6 @@
import request from '@/axios';
export const getList = (current, size,sysType, params, deptId) => {
export const getList = (current, size,params, deptId) => {
  return request({
    url: '/blade-system/user/page',
    method: 'get',
@@ -8,7 +8,6 @@
      ...params,
      current,
      size,
      sysType,
      deptId,
    },
  });
applications/drone-command/src/views/areaManage/defenseZone/FormDiaLog.vue
@@ -251,7 +251,7 @@
// 新增模式绘制
function addPolygon () {
    drawPolygonExample = new DrawPolygon(viewer)
    drawPolygonExample = new DrawPolygon()
    drawPolygonExample.initHandler(viewer)
    drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
}
@@ -261,7 +261,7 @@
    if (!formData.value?.geom) return
    pointList = geomAnalysis(formData.value.geom)
    drawPolygonExample?.destroy()
    drawPolygonExample = new DrawPolygon(viewer)
    drawPolygonExample = new DrawPolygon()
    let noClosedList = _.cloneDeep(pointList)
    noClosedList.pop()
    drawPolygonExample.initPolygon(
applications/drone-command/src/views/areaManage/partition/FormDiaLog copy.vue
@@ -352,7 +352,7 @@
// 新增模式绘制
function addPolygon () {
    drawPolygonExample = new DrawPolygon(viewer)
    drawPolygonExample = new DrawPolygon()
    drawPolygonExample.initHandler(viewer)
    drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
}
@@ -362,7 +362,7 @@
    if (!formData.value?.geom) return
    pointList = geomAnalysis(formData.value.geom)
    drawPolygonExample?.destroy()
    drawPolygonExample = new DrawPolygon(viewer)
    drawPolygonExample = new DrawPolygon()
    let pointList1 = _.cloneDeep(pointList)
    pointList1.pop()
    drawPolygonExample.initPolygon(
applications/drone-command/src/views/permissionManage/permissionRole/RolePermissionDialog.vue
@@ -20,28 +20,28 @@
                    :props="treeProps"
                />
            </el-tab-pane>
            <el-tab-pane label="任务工单">
                <el-tree
                    class="command-tree"
                    ref="workRef"
                    :data="menuGrantListWord"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObjWork"
                    :props="treeProps"
                />
            </el-tab-pane>
            <el-tab-pane label="移动app">
                <el-tree
                    class="command-tree"
                    ref="treeMenuApp"
                    :data="menuGrantListApp"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObjApp"
                    :props="treeProps"
                />
            </el-tab-pane>
<!--            <el-tab-pane label="任务工单">-->
<!--                <el-tree-->
<!--                    class="command-tree"-->
<!--                    ref="workRef"-->
<!--                    :data="menuGrantListWord"-->
<!--                    show-checkbox-->
<!--                    node-key="id"-->
<!--                    :default-checked-keys="menuTreeObjWork"-->
<!--                    :props="treeProps"-->
<!--                />-->
<!--            </el-tab-pane>-->
<!--            <el-tab-pane label="移动app">-->
<!--                <el-tree-->
<!--                    class="command-tree"-->
<!--                    ref="treeMenuApp"-->
<!--                    :data="menuGrantListApp"-->
<!--                    show-checkbox-->
<!--                    node-key="id"-->
<!--                    :default-checked-keys="menuTreeObjApp"-->
<!--                    :props="treeProps"-->
<!--                />-->
<!--            </el-tab-pane>-->
        </el-tabs>
        <template #footer>
            <el-button color="#2B2B4C" @click="visible = false">取消</el-button>
applications/drone-command/src/views/permissionManage/permissionUser/FormDiaLog.vue
@@ -195,7 +195,7 @@
    code: '',
    roleId: '',
    deptId: '',
    sysType: 5
    // sysType: 5
})
const treeProps = {
applications/drone-command/src/views/permissionManage/permissionUser/index.vue
@@ -110,7 +110,7 @@
    deptId: '',
    current: 1,
    size: 10,
    sysType: 5,
    // sysType: 5,
})
const searchParams = ref(initSearchParams())
@@ -135,7 +135,7 @@
        const res = await getUserList(
            searchParams.value.current,
            searchParams.value.size,
            searchParams.value.sysType,
            // searchParams.value.sysType,
            {
                account: searchParams.value.account,
                realName: searchParams.value.realName,
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -22,7 +22,7 @@
                <strong v-if="callEnded" class="status-text ended-status">{{ endMessage }}</strong>
            </div>
            <div class="button-group">
                <button v-if="state === 'idle' && !receiveParameters" @click="requestCall" :disabled="!connected" class="btn btn-call">呼叫</button>
                <!-- <button v-if="state === 'idle' && !receiveParameters && !callEnded" @click="requestCall" :disabled="!connected" class="btn btn-call">呼叫</button> -->
                <button v-if="state==='ringing'" @click="acceptCall" class="btn btn-accept incoming-accept">接听</button>
                <button v-if="state==='ringing'" @click="rejectCall" class="btn btn-reject incoming-reject">拒绝</button>
@@ -448,6 +448,9 @@
    send('accept', incomingFrom, null)
    log('✅ 已点击接听,发送 accept 给', incomingFrom)
    // 停止铃声
//     const transmitData = { data: { type: 'stopVoice' } }
// uni.postMessage(transmitData)
    state.value = 'calling'
    // 如果 offer 已经提前到达(我们暂存了),立刻 answer
@@ -463,6 +466,10 @@
    // 你后端没有 reject,用 busy 表示拒绝/不可接听
    send('busy', incomingFrom, null)
    log('❌ 已拒绝来电,发送 busy 给', incomingFrom)
    // 停止铃声
//     const transmitData = { data: { type: 'stopVoice' } }
// uni.postMessage(transmitData)
    incomingFrom = null
    acceptedByMe = false
@@ -514,6 +521,9 @@
}
function hangup() {
    // 停止铃声
//     const transmitData = { data: { type: 'stopVoice' } }
// uni.postMessage(transmitData)
    cleanup(true)
}
@@ -577,7 +587,7 @@
            }
            // 重置通话结束状态
            callEnded.value = false
        }, 5000)
        }, 3000)
    }
}
applications/mobile-web-view/src/appPages/work/workDetail/index.vue
@@ -69,14 +69,14 @@
    const imageArr = []
    const detail = workDetailData.value
    if (detail.eventImageUrl) {
        const smallUrl = getSmallImg(detail.eventImageUrl)
        const smallUrl = detail.eventImageUrl
        imageArr.push(smallUrl)
    }
    return imageArr
})
const openPreview = index => {
    const detail = workDetailData.value
    const showUrl = getShowImg(detail.eventImageUrl)
    const showUrl = detail.eventImageUrl
    showImagePreview({
        images: [showUrl],
        startPosition: 0,
applications/task-work-order/src/api/system/user.js
@@ -1,6 +1,6 @@
import request from '@/axios';
export const getList = (current, size, sysType, params, deptId) => {
export const getList = (current, size, params, deptId) => {
  return request({
    url: '/blade-system/user/page',
    method: 'get',
@@ -8,7 +8,6 @@
      ...params,
      current,
      size,
      sysType,
      deptId,
    },
  });
applications/task-work-order/src/views/orderView/deviceInvocation/deviceCallRecord/FormDiaLog.vue
New file
@@ -0,0 +1,142 @@
<template>
    <el-dialog class="gd-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close
                         :close-on-click-modal="false">
        <!--        <div class="detail-row-view" v-if="dialogReadonly">-->
        <el-table :data="list" class="gd-dialog-table">
            <el-table-column prop="resultCode" show-overflow-tooltip label="序号" />
            <el-table-column prop="resultCode" show-overflow-tooltip label="设备状态" />
            <el-table-column prop="shootTime" show-overflow-tooltip label="发生时间" />
        </el-table>
    </el-dialog>
</template>
<script setup>
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { add, getUser, update } from '@/api/system/user'
import { getDeptTree } from '@/api/system/dept'
import { getRoleTree } from '@/api/system/role'
import func from '@/utils/func'
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
const list = ref([])
const initForm = () => ({
    account: '',
    password: '',
    password2: '',
    realName: '',
    phone: '',
    email: '',
    sex: null,
    birthday: '',
    code: '',
    roleId: '',
    deptId: '',
    // sysType: 6
})
const treeProps = {
    label: 'title',
    children: 'children',
}
const deptTreeSelectProps = {
    label: 'name',
    value: 'id',
    children: 'children',
}
const emit = defineEmits(['success'])
const formRef = ref(null)
const formData = ref(initForm())
const visible = ref(false)
const dialogMode = ref('add')
const submitting = ref(false)
const dialogReadonly = computed(() => dialogMode.value === 'view')
const dialogTitle = computed(() => {
    if (dialogMode.value === 'edit') return '编辑'
    if (dialogMode.value === 'view') return '查看'
    return '新增'
})
const roleTree = ref([])
const deptTree = ref([])
const route = useRoute()
const validatePass = (rule, value, callback) => {
    if (value === '') {
        callback(new Error('请输入密码'))
    } else {
        callback()
    }
}
const validatePass2 = (rule, value, callback) => {
    if (value === '') {
        callback(new Error('请再次输入密码'))
    } else if (value !== formData.value.password) {
        callback(new Error('两次输入密码不一致'))
    } else {
        callback()
    }
}
async function getRoleTreeData() {
    const res = await getRoleTree('000000')
    roleTree.value = res.data.data
}
async function getDeptTreeData() {
    const res = await getDeptTree('000000')
    deptTree.value = res.data.data
}
function handleCancel() {
    visible.value = false
}
async function loadDetail() {
    if (!formData.value.id) return
    const res = await getUser(formData.value.id)
    const data = res?.data?.data ?? {}
    if (data.roleId) {
        data.roleId = func.split(data.roleId)[0] || data.roleId
    }
    formData.value = data
}
function handleClosed() {
    formData.value = initForm()
}
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    if (dialogMode.value === 'add') {
        formData.value = initForm()
        return
    }
    formData.value = { id: row.id }
    await loadDetail()
}
onMounted(() => {
    getRoleTreeData()
    getDeptTreeData()
})
defineExpose({ open })
</script>
<style scoped lang="scss">
:deep(.el-select) {
    width: 100%;
}
:deep(.el-date-editor) {
    width: 100%;
}
:deep(.el-tree-select) {
    width: 100%;
}
</style>
applications/task-work-order/src/views/orderView/deviceInvocation/deviceCallRecord/index.vue
copy from applications/task-work-order/src/views/orderView/systemMange/userMange/index.vue copy to applications/task-work-order/src/views/orderView/deviceInvocation/deviceCallRecord/index.vue
File was copied from applications/task-work-order/src/views/orderView/systemMange/userMange/index.vue
@@ -60,16 +60,17 @@
                <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="account" show-overflow-tooltip label="登录账号" />
                    <el-table-column prop="realName" show-overflow-tooltip label="用户姓名" />
                    <el-table-column prop="roleName" show-overflow-tooltip label="所属角色" />
                    <el-table-column prop="deptName" show-overflow-tooltip label="所属部门" />
                    <el-table-column prop="phone" show-overflow-tooltip width="120" label="手机号" />
                    <el-table-column prop="account" show-overflow-tooltip label="任务名称" />
                    <el-table-column prop="realName" show-overflow-tooltip label="发起任务部门" />
                    <el-table-column prop="roleName" show-overflow-tooltip label="任务发起人" />
                    <el-table-column prop="deptName" show-overflow-tooltip label="飞行时长" />
                    <el-table-column prop="phone" show-overflow-tooltip label="飞行里程(m)" />
                    <el-table-column prop="phone" show-overflow-tooltip label="执行设备" />
                    <el-table-column prop="phone" show-overflow-tooltip label="计划执行时间" />
                    <el-table-column prop="phone" show-overflow-tooltip label="实际执行时间" />
                    <el-table-column label="操作" class-name="operation-btns" width="180">
                        <template v-slot="{ row }">
                            <el-link  @click="handleView(row)">查看</el-link>
                            <el-link v-if="permission.orderUser_edit" @click="handleEdit(row)">编辑</el-link>
                            <el-link v-if="permission.orderUser_remove" type="danger" @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
                </el-table>
@@ -108,7 +109,7 @@
    deptId: '',
    current: 1,
    size: 10,
    sysType: 6
    // sysType: 6
})
const searchParams = ref(initSearchParams())
@@ -133,7 +134,7 @@
        const res = await getUserList(
            searchParams.value.current,
            searchParams.value.size,
            searchParams.value.sysType,
            // searchParams.value.sysType,
            {
                account: searchParams.value.account,
                realName: searchParams.value.realName,
applications/task-work-order/src/views/orderView/deviceInvocation/equipmentResource/FormDiaLog.vue
applications/task-work-order/src/views/orderView/deviceInvocation/equipmentResource/equipmentResourceApi.js
applications/task-work-order/src/views/orderView/deviceInvocation/equipmentResource/index.vue
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -317,6 +317,7 @@
const suddenlyEdit = ref(false)
const processList = ref([])
let viewPlane
let allDeviceList = []
const hasPatrolTaskList = computed(() => ['30', '40', '50', '60'].includes(String(formData.value.workOrderStatus)))
const gdStatusObj = {
@@ -472,7 +473,7 @@
// 新增模式绘制
function addPolygon() {
    drawPolygonExample = new DrawPolygon(viewer)
    drawPolygonExample = new DrawPolygon({ deviceList: allDeviceList })
    drawPolygonExample.initHandler(viewer)
    drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
}
@@ -494,7 +495,7 @@
    if (!formData.value?.geom) return
    pointList.value = geomAnalysis(formData.value.geom)
    drawPolygonExample?.destroy()
    drawPolygonExample = new DrawPolygon(viewer)
    drawPolygonExample = new DrawPolygon({ deviceList: allDeviceList })
    let noClosedList = _.cloneDeep(pointList.value)
    noClosedList.pop()
    drawPolygonExample.initPolygon(
@@ -550,6 +551,19 @@
    })
}
//  生成圆形坐标点
function buildCirclePositions(center, radius, segments = 120) {
    const cartographic = Cesium.Cartographic.fromCartesian(center)
    const positions = []
    for (let i = 0; i < segments; i++) {
        const angle = (i / segments) * 2 * Math.PI
        const lat = cartographic.latitude + (radius / 6378137) * Math.cos(angle)
        const lon = cartographic.longitude + (radius / (6378137 * Math.cos(cartographic.latitude))) * Math.sin(angle)
        positions.push(Cesium.Cartesian3.fromRadians(lon, lat))
    }
    return positions
}
//  渲染
function renderingAllDevice(list) {
    list.forEach(item => {
@@ -576,6 +590,27 @@
                pixelOffset: new Cesium.Cartesian2(0, -20),
            },
        })
        // 覆盖范围 - 5km圆形区域
        viewer.entities.add({
            position: position,
            ellipse: {
                semiMajorAxis: 5000,
                semiMinorAxis: 5000,
                material: Cesium.Color.fromBytes(45, 140, 240, 80),
                outline: false,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            },
        })
        // 覆盖范围描边线 - 贴地
        const circlePositions = buildCirclePositions(position, 5000)
        viewer.entities.add({
            polyline: {
                positions: [...circlePositions, circlePositions[0]],
                clampToGround: true,
                width: 2,
                material: Cesium.Color.fromBytes(45, 140, 240, 255),
            },
        })
    })
}
@@ -593,7 +628,8 @@
    initMap()
    const allDeviceRes = await gdManageDeviceListApi()
    formData.value = dialogMode.value === 'add' ? initForm() : row
    renderingAllDevice(allDeviceRes?.data?.data || [])
    allDeviceList = allDeviceRes?.data?.data || []
    renderingAllDevice(allDeviceList)
    if (dialogMode.value === 'add') {
        addPolygon()
    } else {
applications/task-work-order/src/views/orderView/organizational/roleMange/FormDiaLog.vue
applications/task-work-order/src/views/orderView/organizational/roleMange/RolePermissionDialog.vue
File was renamed from applications/task-work-order/src/views/orderView/systemMange/roleMange/RolePermissionDialog.vue
@@ -31,17 +31,17 @@
                    :props="treeProps"
                />
            </el-tab-pane>
<!--            <el-tab-pane label="移动app">-->
<!--                <el-tree-->
<!--                    class="command-tree"-->
<!--                    ref="treeMenuApp"-->
<!--                    :data="menuGrantListApp"-->
<!--                    show-checkbox-->
<!--                    node-key="id"-->
<!--                    :default-checked-keys="menuTreeObjApp"-->
<!--                    :props="treeProps"-->
<!--                />-->
<!--            </el-tab-pane>-->
            <el-tab-pane label="移动app">
                <el-tree
                    class="command-tree"
                    ref="treeMenuApp"
                    :data="menuGrantListApp"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObjApp"
                    :props="treeProps"
                />
            </el-tab-pane>
        </el-tabs>
        <template #footer>
            <el-button color="#F2F3F5" @click="visible = false">取消</el-button>
applications/task-work-order/src/views/orderView/organizational/roleMange/index.vue
applications/task-work-order/src/views/orderView/organizational/userMange/FormDiaLog.vue
File was renamed from applications/task-work-order/src/views/orderView/systemMange/userMange/FormDiaLog.vue
@@ -195,7 +195,7 @@
    code: '',
    roleId: '',
    deptId: '',
    sysType: 6
    // sysType: 6
})
const treeProps = {
applications/task-work-order/src/views/orderView/organizational/userMange/index.vue
File was renamed from applications/task-work-order/src/views/orderView/systemMange/userMange/index.vue
@@ -108,7 +108,7 @@
    deptId: '',
    current: 1,
    size: 10,
    sysType: 6
    // sysType: 6
})
const searchParams = ref(initSearchParams())
@@ -133,7 +133,7 @@
        const res = await getUserList(
            searchParams.value.current,
            searchParams.value.size,
            searchParams.value.sysType,
            // searchParams.value.sysType,
            {
                account: searchParams.value.account,
                realName: searchParams.value.realName,
packages/utils/map/DrawPolygon.js
@@ -14,7 +14,10 @@
 *  - 外部订阅/通知机制
 */
export class DrawPolygon {
    constructor() {
    constructor(options = {}) {
        // 设备覆盖范围列表(经纬度 + 5000m半径)
        this.deviceList = options.deviceList || []
        this.coverageRadius = 5000
        // 图斑预览模式标记
        this.isPureSpotPreview = false
        // 是否删除测区
@@ -398,6 +401,19 @@
    handleLeftUp() {
        if (!(this.editingMode && this.curPolygon?.positions && this.draggedEntity)) return
        // 检查松手位置是否在设备覆盖范围内
        if (this.isDragging && !this.isWithinDeviceCoverage(this.curPolygon.positions[this.draggedEntity?.customData.ind])) {
            ElMessage.warning('该位置不在设备覆盖范围内,请在设备5公里范围内绘制')
            // 回弹到拖拽前的位置
            this.draggedEntity.position = this.currentDragPointPosition
            this.curPolygon.positions[this.draggedEntity?.customData.ind] = this.currentDragPointPosition
            this.isDragging = false
            this.draggedEntity = null
            this.enableMapControl()
            this.rebuildMidPoints()
            return
        }
        if (this.currentDragPointIsValid && this.isDragging) {
            // 更新点位置
            this.draggedEntity.position = this.currentDragPointPosition
@@ -514,6 +530,12 @@
        // 添加新的点
        const cartesian = this.viewer.scene.pickPosition(click.position)
        if (!cartesian) return
        // 检查点击位置是否在设备覆盖范围内
        if (!this.isWithinDeviceCoverage(cartesian)) {
            ElMessage.warning('该位置不在设备覆盖范围内,请在设备5公里范围内绘制')
            return
        }
        let arr = [...this.curPolygon.positions]
        arr.pop()
@@ -662,7 +684,7 @@
        const menuItems =
            type === 'polygon'
                ? [{ title: '删除测区', class: 'del-planar-polygon' }]
                ? [{ title: '删除范围', class: 'del-planar-polygon' }]
                : [{ title: '删除端点', class: 'del-planar-point' }]
        menuItems.forEach(item => {
@@ -726,6 +748,20 @@
        return intersections.features.length === 0
    }
    // 检查位置是否在任意设备的覆盖范围内(5km)
    isWithinDeviceCoverage(cartesian) {
        if (!this.deviceList || this.deviceList.length === 0) return true
        const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
        const clickPoint = turf.point([Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)])
        return this.deviceList
            .filter(item => item.latitude)
            .some(item => {
                const rangePoint = turf.point([item.longitude, item.latitude])
                const distance = turf.distance(rangePoint, clickPoint, { units: 'kilometers' })
                return distance <= 5
            })
    }
    // 初始化已有多边形
    async initPolygon(viewer, positions, isPurePreview = false) {
        this.initHandler(viewer)
uniapps/work-app/src/App.vue
@@ -33,7 +33,6 @@
allowFloat()
// #endif
useGlobalWS()
onShow(async () => {
@@ -60,7 +59,7 @@
    // #ifdef APP-PLUS
    keepAliveStart()
    // 请求用户允许忽略电池优化(重要:用于保活)
    setTimeout(requestIgnoreBatteryOptimization, 120000)
    setTimeout(requestIgnoreBatteryOptimization, 10000)
    // #endif
}
uniapps/work-app/src/hooks/useGlobalWS.js
@@ -1,13 +1,17 @@
import { useUserStore } from "@/store/index.js";
import useAppStore from "../store/modules/app/index.js";
import websocketService from "@/utils/websocket.js";
// #ifdef APP-PLUS
import { openDialog, allowFloat, getBatteryCapacity } from '@/uni_modules/lgh-dialog'
// #endif
export function useGlobalWS() {
    const userStore = useUserStore();
  const appStore = useAppStore();
  const callStatus = ref(null)
  // token 存储
  const accessToken = ref('')
  // 存储铃声实例
  let ringtoneInstance = null
  // 设置默认用户ID为3
  const defaultUserId = '2021474815063486465';
@@ -23,13 +27,19 @@
    // 处理语音通话请求
    if (t === 'call') {
      console.log('📞 全局收到来电 call,来自', payload.from)
      // 播放铃声
      // playRingtone();
      // 触发震动
      triggerVibration();
      // 构建来电参数
      const callParams = {
        peerUid: payload.from,
        from: payload.from,
        type: 'incoming'
      };
// #ifdef APP-PLUS
            openDialog()
      // #endif
      // 转义参数以便在URL中传递
      const encodedParams = encodeURIComponent(JSON.stringify(callParams));
@@ -47,33 +57,91 @@
      }
      return
    }
    // 然后处理biz_code格式的消息
    // switch (bizCode) {
    //   case 'JOB_ISREFRESH':
    //     appStore.setJobUpdateKeyAdd()
    //     break
    //   case 'DEVICE_ISREFRESH':
    //     appStore.setDeviceUpdateKeyAdd()
    //     break
    //   case 'DOWNLOAD_PROGRESS':
    //     break
    //   case 'LOGOUT_USER':
    //     userStore.setUserInfo(null)
    //     uni.reLaunch({
    //       url: '/pages/login/index'
    //     })
    //     break
    //   case 'VoiceCall':
    //     // enterRoom(payload, userId.value)
    //     break
    //   default:
    //     // 记录未处理的消息
    //     console.log('未处理的WebSocket消息:', payload)
    //     break;
    // }
  }
  // 播放来电铃声 - 定义在 messageHandler 外部
  // function playRingtone() {
  //   try {
  //     if (typeof uni !== 'undefined' && uni.createInnerAudioContext) {
  //       const ringtone = uni.createInnerAudioContext();
  //       // 使用默认铃声,注意路径写法
  //       ringtone.src = '/static/audio/ringtone.mp3';
  //       ringtone.loop = true;
  //       ringtone.volume = 0.8;
  //       ringtone.play();
  //       // 存储铃声实例
  //       ringtoneInstance = ringtone; // 使用局部变量而不是window
  //     }
  //   } catch (error) {
  //   }
  // }
  // 触发震动
  function triggerVibration() {
    try {
      if (typeof uni !== 'undefined') {
        // 方法1:使用uni.vibrate(现代API)
        if (uni.vibrate) {
          // 震动400ms
          uni.vibrate({
            duration: 400,
            success: () => {
              console.log('📳 震动已触发');
            },
            fail: (error) => {
              // 方法2:使用uni.vibrateLong(兼容旧设备)
              if (uni.vibrateLong) {
                uni.vibrateLong({
                  success: () => {
                    console.log('📳 兼容模式震动已触发');
                  },
                  fail: (err) => {
                    console.error('兼容模式震动失败:', err);
                  }
                });
              }
            }
          });
        }
        // 方法3:使用plus API(H5+)
        if (typeof plus !== 'undefined' && plus.device && plus.device.vibrate) {
          plus.device.vibrate(400);
          console.log('📳 H5+ 震动已触发');
        }
      }
    } catch (error) {
      console.error('触发震动失败:', error);
    }
  }
  // 停止铃声
  // function stopRingtone() {
  //   try {
  //     if (ringtoneInstance) {
  //       ringtoneInstance.stop();
  //       ringtoneInstance.destroy();
  //       ringtoneInstance = null;
  //     }
  //   } catch (error) {
  //     // 即使出错也重置实例
  //     ringtoneInstance = null;
  //   }
  // }
  //
  // // 监听停止铃声事件
  // if (typeof uni !== 'undefined' && uni.$on) {
  //   uni.$on('stopRingtone', stopRingtone);
  //   // 监听WebView消息事件
  //   uni.$on('webViewMessage', (data) => {
  //     // 检查是否是停止铃声消息
  //     if (data?.type === 'stopVoice') {
  //       stopRingtone();
  //       console.log('📳 收到WebView停止铃声消息,已停止铃声');
  //
  //     }
  //   });
  // }
  // 初始化WebSocket连接
  function initWS() {
    const userStore = useUserStore()
uniapps/work-app/src/pages/map/index.vue
@@ -23,9 +23,6 @@
const userParams = userStore?.userInfo ? JSON.stringify(userStore.userInfo) : '{}';
const updateKey = ref(0);
// const viewUrl = computed(() => {
//   return `https://192.168.1.157:5176/mobile-web-view/#/webViewWrapper/defaultMap?params=${encodeURIComponent(userParams)}&isWeb=${envParam}&updateKey=${updateKey.value}`;
// });
const viewUrl = computed(() => {
  return getWebViewUrl("/defaultMap", {
    isWeb: envParam,
@@ -34,40 +31,6 @@
});
const onPostMessage = (data) => {
  // #ifdef MP-WEIXIN
  if (data.type === "scanCode") {
  } else if (data.type === "jumpAddWork") {
  } else if (data.type === "jumpMapNav") {
    //事件导航
  } else if (data.type === "workid") {
    //事件详情
  }
  // #endif
  // #ifndef MP-WEIXIN
  if (data.type === "scanCode") {
    uni.navigateTo({
      url: "/subPackages/qrCode/index",
    });
  } else if (data.type === "jumpAddWork") {
    //新建任务
    const encodedData = encodeURIComponent(JSON.stringify(data.rowItem));
    uni.setStorageSync("webview_params", encodedData);
    uni.navigateTo({
      url: `/subPackages/taskDetail/addTask/index`,
    });
  } else if (data.type === "jumpMapNav") {
    //事件导航
    uni.navigateTo({
      url: `/subPackages/workDetail/mapWork/index?currentItem=${data.eventNum}`,
    });
  } else if (data.type === "workid") {
    //事件详情
    uni.navigateTo({
      url: `/subPackages/workDetail/index?eventNum=${data.eventNum}`,
    });
  }
  // #endif
};
onShow(() => {
uniapps/work-app/src/pages/voiceCall/index.vue
@@ -48,8 +48,8 @@
import { getPhoneBookListApi } from '@/api/voiceCall/index.js'
import { onShow } from '@dcloudio/uni-app'
import defaultAvatar from '/static/images/defaultAvatar.svg'
  const userStore = useUserStore()
  const userId = userStore.userInfo.new_userInfo.userId
const userStore = useUserStore()
const userId = userStore.userInfo.new_userInfo.userId
// 联系人数据
const contacts = ref([])
const defaultParam = ref({
uniapps/work-app/src/static/audio/ringtone.mp3
Binary files differ
uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
@@ -74,11 +74,15 @@
  const contact = options.contact ? JSON.parse(decodeURIComponent(options.contact)) : null;
  // 构建viewUrl,将contact参数拼接到URL中
  const contactParam = contact ? encodeURIComponent(JSON.stringify(contact)) : '';
  // 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) {}
function onPostMessage(data) {
    // 将WebView消息传递给全局WebSocket处理
    // if (typeof uni !== 'undefined' && uni.$emit) {
    //     uni.$emit('webViewMessage', data);
    // }
}
</script>
uniapps/work-app/src/utils/common/index.js
@@ -72,8 +72,12 @@
  // 1. 处理用户参数
  const userParams = userStore?.userInfo ? JSON.stringify(userStore.userInfo) : '{}'
  // 2. 构建查询参数字符串
  let queryString = `params=${encodeURIComponent(userParams)}&topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}`
  let queryString = null
  if(targetUrl === '/workDetail') {
    queryString = `topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}`
  }else{
    queryString = `params=${encodeURIComponent(userParams)}&topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}`
  }
  // 3. 处理 otherParams 对象
  if (otherParams && typeof otherParams === 'object') {
    Object.keys(otherParams).forEach(key => {