吉安感知网项目-前端
chenyao
2026-02-27 4af871597f94631e18cc03a35bedea2679e14d9f
Merge remote-tracking branch 'origin/master'
33 files modified
1 files added
894 ■■■■■ changed files
applications/mobile-web-view/env/.env.development 2 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/work/index.js 12 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appComponents/workMap/index.vue 85 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/work/workDetail/index.vue 20 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/vite.config.mjs 7 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/index/top/index.vue 1 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/lock/index.vue 1 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/common/cockpit.scss 18 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/basicManage/maintainRecord/index.vue 80 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/appConfiguration/inventory/index.vue 8 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/appConfiguration/materials/index.vue 8 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/appConfiguration/process/index.vue 26 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/flyingHand/index.vue 8 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/index.vue 10 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue 10 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue 8 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/index.vue 8 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue 10 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue 116 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/operatingIncome/index.vue 16 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue 7 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue 12 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/roleMange/index.vue 34 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/userMange/index.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue 12 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/App.vue 25 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/api/work/index.js 8 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/hooks/useGlobalWS.js 19 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/voiceCallDetail/index.vue 9 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/index.vue 5 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/uni_modules/keep-app/utssdk/app-android/index.uts 67 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/uni_modules/lgh-dialog/utssdk/app-android/index.uts 236 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/common/index.js 2 ●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/websocket.js 2 ●●● patch | view | raw | blame | history
applications/mobile-web-view/env/.env.development
@@ -10,6 +10,8 @@
#开发环境代理地址(推荐本地新建文件 .env.development.local 来进行覆盖)
VITE_APP_XT_URL = http://218.202.104.82:8200
#工单详情(分享链接)
VITE_APP_XT_URL_GD = http://192.168.1.204
# ws地址
VITE_APP_WS_API_URL = wss://wrj.shuixiongit.com/drone-wss/api/v1/ws
applications/mobile-web-view/src/api/work/index.js
New file
@@ -0,0 +1,12 @@
import request from '@/axios'
/**
 * @description: 获取分享地址数据
 */
export const getSharingDetailsApi = (params) => {
    return request({
        url: '/drone-gd/workOrder/gdPatrolTask/get-share-cache-data',
        method: 'get',
        params,
    })
}
applications/mobile-web-view/src/appComponents/workMap/index.vue
@@ -103,9 +103,9 @@
let locationFlag = false
let watchId = null
let userLocationMarker = null
const setMapLocation = () => {
    locationFlag = true
// 初始化实时位置监听
const initLocationWatch = () => {
    // 在WebView加载的网页中
    // 检查浏览器是否支持 geolocation
    if (navigator.geolocation) {
@@ -113,7 +113,6 @@
            // 停止监听位置
            navigator.geolocation.clearWatch(watchId)
        }
        // 开始持续获取用户的位置
        watchId = navigator.geolocation.watchPosition(
            function (position) {
@@ -121,27 +120,27 @@
                const lat = position.coords.latitude // 纬度
                const lng = position.coords.longitude // 经度
                // 确保位置标记存在并更新位置
                if (userLocationMarker) {
                    userLocationMarker.setLatLng([lat, lng])
                }
                if (locationFlag) {
                } else {
                    userLocationMarker = L.marker([lat, lng], {
                        icon: L.icon({
                            iconUrl: userLocationIcon, // 图片路径
                            iconSize: [24, 24], // 图标尺寸
                            iconAnchor: [12, 12], // 锚点位置
                        }),
                                iconSize: [24, 24], // 图标尺寸
                                iconAnchor: [12, 12], // 锚点位置
                            }),
                    }).addTo(map)
                }
                // 如果是点击定位按钮触发的,将地图视图定位到当前位置
                if (locationFlag) {
                    mapSetView({
                        lat,
                        lng,
                    })
                    locationFlag = false
                }
                // 可以根据位置更新地图或界面
            },
            function (error) {
                // 如果获取位置失败,执行回调
@@ -154,20 +153,26 @@
            }
        )
    } else {
        console.log('Geolocation is not supported by this browser.')
        console.log('该浏览器不支持地理定位功能。')
    }
}
    // 获取定位信息
    // uni.getLocation({
    //     type: 'wgs84', // 返回的经纬度是 WGS84 坐标系(适合地图定位)
    //     isHighAccuracy: true,
    //     success: function (res) {
    //         location.value = res
    //     },
    //     fail: function (err) {
    //         console.log('定位失败:', err)
    //     },
    // })
const setMapLocation = () => {
    locationFlag = true
    // 如果还没有开始监听位置,先初始化监听
    if (!watchId) {
        initLocationWatch()
    } else {
        // 已经在监听位置,会自动定位到当前位置
        // 可以添加一个视觉反馈,比如位置标记闪烁效果
        if (userLocationMarker) {
            // 添加闪烁效果
            userLocationMarker.getElement().classList.add('location-blink')
            setTimeout(() => {
                userLocationMarker.getElement().classList.remove('location-blink')
            }, 1000)
        }
    }
}
let lastLocationMarker = null
@@ -312,10 +317,24 @@
    initMap()
    map.addLayer(layers[0].map)
    // 初始化实时位置监听
    initLocationWatch()
    EventBus.on('mapSetView', mapSetView)
})
onUnmounted(() => {
    // 清理位置监听
    if (watchId) {
        navigator.geolocation.clearWatch(watchId)
        watchId = null
    }
    // 移除位置标记
    if (userLocationMarker) {
        map.removeLayer(userLocationMarker)
        userLocationMarker = null
    }
    EventBus.off('mapSetView', mapSetView)
    if (map) {
        map.remove()
@@ -362,4 +381,24 @@
        bottom: 76px;
    }
}
// 位置标记闪烁效果
.location-blink {
    animation: blink 1s ease-in-out;
}
@keyframes blink {
    0% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.2);
        opacity: 0.8;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}
</style>
applications/mobile-web-view/src/appPages/work/workDetail/index.vue
@@ -2,7 +2,7 @@
    <div class="workDetailContainer">
        <div class="detailTop">
            <div class="image-container">
                <van-swipe class="detailSwipe" :autoplay="3000" indicator-color="#4C85FF">
                <van-swipe :autoplay="3000" indicator-color="#4C85FF">
                <van-swipe-item v-for="(img, index) in getImageList" :key="index">
                    <van-image class="detailImage" :src="img" fit="cover" width="100%" height="235px"
                        @click="openPreview(index)" preview-visible="false" />
@@ -55,10 +55,11 @@
</template>
<script setup>
import {getSharingDetailsApi} from '@/api/work/index'
import { showToast, showNotify, showImagePreview } from 'vant'
import { getShowImg, getSmallImg } from '@/utils/util'
import { useRoute,useRouter } from 'vue-router'
const keyword = ref('')
const route = useRoute()
const router = useRouter()
@@ -93,7 +94,14 @@
}
onMounted(async () => {
workDetailData.value = JSON.parse(route.query.workDetailData)
    keyword.value = JSON.parse(route.query.workDetailData)
    try {
        const res = await getSharingDetailsApi({ cacheKey: keyword.value })
        workDetailData.value = res.data.data
    } catch (error) {
        showToast('分享链接失效')
    }
})
</script>
<style lang="scss" scoped>
@@ -102,8 +110,6 @@
        .image-container {
            position: relative;
            width: 100%;
            // height: 205px;
            .detailImage {
                width: 100%;
                height: 100%;
@@ -115,7 +121,7 @@
    .worderContainer {
        padding: 0 12px;
        padding-bottom: 2px;
        // padding-bottom: 2px;
        background: #f6f6f6;
    }
@@ -123,7 +129,7 @@
        margin-top: 15px;
        background-color: #fff;
        border-radius: 6px;
        padding: 10px;
        padding: 8px 10px;
        margin-bottom: 17px;
        .workOrderTitle {
applications/mobile-web-view/vite.config.mjs
@@ -21,6 +21,7 @@
    VITE_APP_BASE,
    VITE_APP_API_URL,
    VITE_APP_XT_URL,
    VITE_APP_XT_URL_GD,
    VITE_APP_RECTANGLE,
    VITE_APP_ARCGIS_URL,
    VITE_APP_MAP_TILE_URL,
@@ -119,6 +120,12 @@
          rewrite: path => path.replace(/^\/webservice/, '/webservice'),
        },
        '/drone-gd': {
          target: VITE_APP_XT_URL_GD,
          changeOrigin: true,
          rewrite: path => path.replace(/^\/drone-gd/, '/drone-gd'),
        },
        '/api': {
          target: VITE_APP_API_URL,
          changeOrigin: true,
applications/task-work-order/src/page/index/top/index.vue
@@ -82,6 +82,7 @@
  methods: {
    logout () {
      this.$confirm(this.$t('logoutTip'), this.$t('提示'), {
        customClass: 'gd-confirm-custom',
        confirmButtonText: this.$t('submitText'),
        cancelButtonText: this.$t('cancelText'),
        type: 'warning',
applications/task-work-order/src/page/lock/index.vue
@@ -67,6 +67,7 @@
    },
    handleLogout() {
      this.$confirm('是否退出系统, 是否继续?', '提示', {
        customClass: 'gd-confirm-custom',
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
applications/task-work-order/src/styles/common/cockpit.scss
@@ -721,7 +721,7 @@
}
.gd-pagination-parent {
  padding-top: 30px;
  margin: 16px 0;
  display: flex;
  justify-content: flex-end;
@@ -924,7 +924,7 @@
}
.gd-table-container {
  padding: 12px 20px 30px 20px;
  padding: 25px 20px 1px;
  height: 0;
  flex: 1;
  display: flex;
@@ -945,12 +945,24 @@
  padding-bottom: 20px;
  .el-button {
    min-width: 88px;
    padding: 4px 16px;
    color: #383874;
    border: none;
    height: 32px;
  }
  .el-button:first-child {
    color: #FFFFFF;
  }
  .el-button.is-disabled,
  .el-button.is-disabled:hover {
    background: #ECECF4;
    border: none;
  }
  .el-button:first-child.is-disabled,
  .el-button:first-child.is-disabled:hover {
    background: #4C34FF;
    border: none;
  }
applications/task-work-order/src/views/basicManage/maintainRecord/index.vue
@@ -2,45 +2,21 @@
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input
                    class="gd-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
                <el-input class="gd-input" v-model="searchParams.deviceName" placeholder="请输入" clearable
                    @clear="handleSearch" />
            </el-form-item>
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="gd-select"
                    popper-class="gd-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                <el-select class="gd-select" popper-class="gd-select-popper" v-model="searchParams.deviceType"
                    placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
            </el-form-item>
            <el-form-item label="所属部门" prop="belongDept">
                <el-tree-select
                    class="gd-select"
                    popper-class="gd-tree-select-popper"
                    v-model="searchParams.belongDept"
                    :data="deptTree"
                    :props="treeProps"
                    node-key="id"
                    check-strictly
                    clearable
                    @change="handleSearch"
                />
                <el-tree-select class="gd-select" popper-class="gd-tree-select-popper" v-model="searchParams.belongDept"
                    :data="deptTree" :props="treeProps" node-key="id" check-strictly clearable @change="handleSearch" />
            </el-form-item>
            <el-form-item class="gd-search-actions">
@@ -49,12 +25,13 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增</el-button>
            <el-button :icon="Delete" color="#4C34FF" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增</el-button>
                <el-button :icon="Delete" color="#ECECF4" :disabled="!selectedIds.length"
                    @click="handleDelete()">删除</el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
@@ -85,14 +62,9 @@
            </div>
            <div class="gd-pagination-parent">
                <el-pagination
                    popper-class="gd-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
                    :total="total"
                    @change="getList"
                />
                <el-pagination popper-class="gd-select-popper" v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size" layout="total, prev, pager, next, sizes" :total="total"
                    @change="getList" />
            </div>
        </div>
@@ -145,7 +117,7 @@
provide('planCycleOptions', planCycleOptions)
// 获取列表
async function getList() {
async function getList () {
    loading.value = true
    try {
        const res = await fwDeviceMaintainPlanPageApi(searchParams.value)
@@ -157,13 +129,13 @@
}
// 查询
function handleSearch() {
function handleSearch () {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm() {
function resetForm () {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
@@ -171,7 +143,7 @@
// 新增/编辑/查看 弹框
function openForm(mode, row) {
function openForm (mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
@@ -179,7 +151,7 @@
}
// 删除
async function handleDelete(row) {
async function handleDelete (row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
@@ -195,25 +167,25 @@
}
// 勾选值设置
function handleSelectionChange(rows) {
function handleSelectionChange (rows) {
    selectedIds.value = rows.map(item => item.id)
}
// 获取字典
function getDictList() {
function getDictList () {
    getDictionaryByCode('deviceType,deviceAtt').then(res => {
        dictObj.value = res.data.data
    })
}
// 获取部门树
function getDeptTreeFun() {
function getDeptTreeFun () {
    getDeptTree().then(res => {
        deptTree.value = res.data.data
    })
}
function getPlanCycleLabel(row) {
function getPlanCycleLabel (row) {
    const item = planCycleOptions.find(option => option.value === row.planCycleType)
    return item?.label + '-' + row.planCycleValue.join(',')
}
applications/task-work-order/src/views/orderView/appConfiguration/inventory/index.vue
@@ -45,11 +45,11 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增</el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="80">
applications/task-work-order/src/views/orderView/appConfiguration/materials/index.vue
@@ -17,11 +17,11 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增材料</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增材料</el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="80">
applications/task-work-order/src/views/orderView/appConfiguration/process/index.vue
@@ -27,20 +27,20 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary"  @click="handleCreate">
                创建
            </el-button>
            <el-button
                :icon="Delete"
                @click="handleBatchDelete"
            >
                删除
            </el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary"  @click="handleCreate">
                    创建
                </el-button>
                <el-button
                    :icon="Delete"
                      color="#ECECF4"
                    @click="handleBatchDelete"
                >
                    删除
                </el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list" @selection-change="selectionChange">
                    <el-table-column type="selection" width="55" />
applications/task-work-order/src/views/orderView/flyingHand/index.vue
@@ -48,11 +48,11 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="addFlyingHand">飞手建档</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="addFlyingHand">飞手建档</el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list">
                    <el-table-column type="index" width="64" label="序号" />
applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/index.vue
@@ -35,12 +35,12 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">应用创新新增</el-button>
            <!-- <el-button :icon="Delete" color="#4C34FF" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button> -->
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">应用创新新增</el-button>
                <!-- <el-button :icon="Delete" color="#4C34FF" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button> -->
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column label="序号" width="70" align="center">
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue
@@ -35,12 +35,12 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">数据异议申请</el-button>
            <!--            -->
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">数据异议申请</el-button>
                <!--            -->
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="70">
applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue
@@ -31,11 +31,11 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增评价</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增评价</el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="80">
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/index.vue
@@ -35,11 +35,11 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar" v-if="permissionList.addBtn">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">供需填报新增</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar" v-if="permissionList.addBtn">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">供需填报新增</el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="70" align="center">
applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue
@@ -36,12 +36,12 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Download" color="#4C34FF" type="primary" @click="handleDownload">文档下载</el-button>
            <el-button :icon="Upload" color="#4C34FF" type="primary" @click="handleUpload">文档上传</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button :icon="Download" color="#4C34FF" type="primary" @click="handleDownload">文档下载</el-button>
                <el-button :icon="Upload" color="#ECECF4" type="primary" @click="handleUpload">文档上传</el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue
@@ -6,63 +6,30 @@
        </el-tabs>
        <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
            <el-form-item label="任务名称" prop="patrolTaskName">
                <el-input
                    class="gd-input gray"
                    v-model="searchParams.patrolTaskName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
                <el-input class="gd-input gray" v-model="searchParams.patrolTaskName" placeholder="请输入" clearable
                    @clear="handleSearch" />
            </el-form-item>
            <el-form-item label="任务类型" prop="patrolTaskType">
                <el-select
                    class="gd-select gray"
                    popper-class="gd-select-popper"
                    v-model="searchParams.patrolTaskType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.workOrderType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                <el-select class="gd-select gray" popper-class="gd-select-popper" v-model="searchParams.patrolTaskType"
                    placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.workOrderType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
            </el-form-item>
            <el-form-item label="任务状态" prop="taskStatus">
                <el-select
                    class="gd-select gray"
                    popper-class="gd-select-popper"
                    v-model="searchParams.taskStatus"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.taskStatus"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                <el-select class="gd-select gray" popper-class="gd-select-popper" v-model="searchParams.taskStatus"
                    placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.taskStatus" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
            </el-form-item>
            <el-form-item label="执行时间" prop="executeTime">
                <el-date-picker
                    class="gd-date-picker gray"
                    popper-class="gd-date-picker-popper"
                    v-model="dateRange"
                    type="daterange"
                    range-separator="-"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    value-format="YYYY-MM-DD HH:mm:ss"
                    @change="handleSearch"
                />
                <el-date-picker class="gd-date-picker gray" popper-class="gd-date-picker-popper" v-model="dateRange"
                    type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
                    value-format="YYYY-MM-DD HH:mm:ss" @change="handleSearch" />
            </el-form-item>
            <el-form-item class="gd-search-actions">
@@ -71,13 +38,14 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button v-if="permission.flyOrder_add" :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">
                拆分工单
            </el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-toolbar">
                <el-button v-if="permission.flyOrder_add" :icon="Plus" color="#4C34FF" type="primary"
                    @click="openForm('add')">
                    拆分工单
                </el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
                    <!--                    <el-table-column type="selection" width="46" />-->
@@ -91,7 +59,7 @@
                    </el-table-column>
                    <el-table-column prop="taskStatus" show-overflow-tooltip label="巡查任务状态">
                        <template v-slot="{ row }">
                            <span :style="{color: colors?.[row?.taskStatus]}">
                            <span :style="{ color: colors?.[row?.taskStatus] }">
                                {{ getDictLabel(row.taskStatus, dictObj.taskStatus) }}
                            </span>
                        </template>
@@ -109,25 +77,15 @@
            </div>
            <div class="gd-pagination-parent">
                <el-pagination
                    popper-class="gd-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
                    :total="total"
                    @change="getList"
                />
                <el-pagination popper-class="gd-select-popper" v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size" layout="total, prev, pager, next, sizes" :total="total"
                    @change="getList" />
            </div>
        </div>
        <FormDiaLog ref="dialogRef" @success="getList" v-if="dialogVisible" v-model="dialogVisible" />
        <ViewDiaLog
            ref="viewDiaLogRef"
            @success="getList"
            @refusalAccept="refusalAccept"
            v-if="viewDiaLogVisible"
            v-model="viewDiaLogVisible"
        />
        <ViewDiaLog ref="viewDiaLogRef" @success="getList" @refusalAccept="refusalAccept" v-if="viewDiaLogVisible"
            v-model="viewDiaLogVisible" />
    </basic-container>
</template>
<script setup>
@@ -190,7 +148,7 @@
// 获取列表
async function getList() {
async function getList () {
    const range = dateRangeFormat(dateRange.value)
    loading.value = true
    try {
@@ -207,20 +165,20 @@
    }
}
function tabsClick(tab, event) {
function tabsClick (tab, event) {
    activeName.value = tab.props.name
    resetForm()
    handleSearch()
}
// 查询
function handleSearch() {
function handleSearch () {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm() {
function resetForm () {
    queryParamsRef.value?.resetFields()
    dateRange.value = []
    searchParams.value.current = 1
@@ -228,7 +186,7 @@
}
// 新增/编辑/查看 弹框
function openForm(mode, row) {
function openForm (mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
@@ -237,7 +195,7 @@
// 状态:0待签收、1拒绝签收、2已撤回、3待审核、4审核驳回
// 5审核通过、6待验收、7拒绝验收、8验收通过
function viewDiaLogView(row) {
function viewDiaLogView (row) {
    viewDiaLogVisible.value = true
    nextTick(() => {
        let mode = ['1', '4'].includes(row.taskStatus) && permission.value.flyOrder_add ? 'edit' : 'view'
@@ -246,7 +204,7 @@
}
// 勾选值设置
function handleSelectionChange(rows) {
function handleSelectionChange (rows) {
    selectedIds.value = rows.map(item => item.id)
}
@@ -257,20 +215,20 @@
}
provide('routeOptions', routeOptions)
provide('getAirName', getAirName)
function getAirList() {
function getAirList () {
    airlineListApi({}).then(res => {
        routeOptions.value = res.data.data || []
    })
}
// 获取字典
function getDictList() {
function getDictList () {
    getDictionaryByCode('workOrderType,deviceLoadDemand,taskStatus').then(res => {
        dictObj.value = res.data.data
    })
}
function refusalAccept(row) {
function refusalAccept (row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode: 'edit', row })
@@ -278,7 +236,7 @@
}
// 获取两个tab的总条数
async function getTabTotals() {
async function getTabTotals () {
    const allRes = await gdPatrolTaskPageApi({
        current: 1,
        size: 1,
applications/task-work-order/src/views/orderView/orderManage/operatingIncome/index.vue
@@ -1,11 +1,12 @@
<!-- 运营收益 -->
<template>
  <basic-container>
    <div class="gd-table-toolbar">
      <el-button :icon="Plus" color="#4C34FF" type="primary" @click="handleAdd">新增</el-button>
      <el-button :icon="Delete" color="#4C34FF" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
    </div>
    <div class="gd-table-container" v-loading="loading">
      <div class="gd-table-toolbar">
        <el-button :icon="Plus" color="#4C34FF" type="primary" @click="handleAdd">新增</el-button>
        <el-button :icon="Delete" color="#ECECF4" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
      </div>
      <div class="gd-table-content gd-table-content-bg">
        <el-table class="gd-table" :data="tableList" @selection-change="handleSelectionChange">
          <el-table-column type="selection" width="46" />
@@ -32,11 +33,11 @@
          v-model:page-size="params.size" layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
      </div>
    </div>
    <el-dialog class="gd-dialog" append-to-body v-model="isShowEditView" :title="titleTxt"
      :close-on-click-modal="false" :destroy-on-close="true" @close="handleClose">
    <el-dialog class="gd-dialog" append-to-body v-model="isShowEditView" :title="titleTxt" :close-on-click-modal="false"
      :destroy-on-close="true" @close="handleClose">
      <el-form class="gd-dialog-form" ref="ruleFormRef" :model="editParams" :rules="rules" label-width="160px">
        <el-row>
                <el-col :span="12">
          <el-col :span="12">
            <el-form-item label="营业收入(元)" prop="operatingIncome">
              <el-input class="gd-input" v-model="editParams.operatingIncome" placeholder="请输入" />
            </el-form-item>
@@ -79,6 +80,7 @@
  </basic-container>
</template>
<script setup>
import { Delete, Plus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import { operatingIncomePage, operatingIncomeUpdate, operatingIncomeAdd, operatingIncomeBatchDelete } from './operatingIncomeApi'
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -721,11 +721,18 @@
        width: 350px;
        display: flex;
        flex-direction: column;
        .title {
            font-weight: 500;
            font-size: 14px;
            color: #272e37;
        }
        ::v-deep(.el-table) {
            tr th {
                font-weight: normal;
            }
        }
    }
    .processBox {
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
@@ -38,13 +38,13 @@
      </el-form-item>
    </el-form>
    <div class="gd-table-toolbar">
      <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增机构</el-button>
      <el-button :icon="Upload" @click="handleImport">机构导入</el-button>
      <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">机构导出</el-button>
    </div>
    <div class="gd-table-container" v-loading="loading">
      <div class="gd-table-toolbar">
        <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增机构</el-button>
        <el-button :icon="Upload" color="#ECECF4" @click="handleImport">机构导入</el-button>
        <el-button :icon="Download" color="#ECECF4" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">机构导出</el-button>
      </div>
      <div class="gd-table-content gd-table-content-bg">
        <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
          <el-table-column type="selection" width="46" />
applications/task-work-order/src/views/orderView/organizational/roleMange/index.vue
@@ -27,21 +27,23 @@
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="handleAdd">新增</el-button>
            <el-button :icon="Delete"  :disabled="!selectedIds.length" @click="handleDelete()">
                删除
            </el-button>
            <el-button
                :disabled="selectedIds.length !== 1"
                v-if="permission.orderRole_set"
                @click="handleRole"
            >
                权限设置
            </el-button>
        </div>
        <div class="gd-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="gd-table-toolbar">
                <el-button :icon="Plus" color="#4C34FF" type="primary" @click="handleAdd">新增</el-button>
                <el-button :icon="Delete"  color="#ECECF4" :disabled="!selectedIds.length" @click="handleDelete()">
                    删除
                </el-button>
                <el-button
                    :icon="Setting"
                    color="#ECECF4"
                    :disabled="selectedIds.length !== 1"
                    v-if="permission.orderRole_set"
                    @click="handleRole"
                >
                    权限设置
                </el-button>
            </div>
            <div class="gd-table-content gd-table-content-bg">
                <el-table
                    class="gd-table"
@@ -75,7 +77,7 @@
</template>
<script setup>
import { Delete, Plus, RefreshRight, Search } from '@element-plus/icons-vue'
import { Delete, Plus, Setting, RefreshRight, Search } from '@element-plus/icons-vue'
import { computed, onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getRoleListNew as getRolePage, remove } from '@/api/system/role'
@@ -153,7 +155,7 @@
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        customClass: 'gd-confirm-custom',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
applications/task-work-order/src/views/orderView/organizational/userMange/index.vue
@@ -175,7 +175,7 @@
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        customClass: 'gd-confirm-custom',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
@@ -37,13 +37,13 @@
      </el-form-item>
    </el-form>
    <div class="gd-table-toolbar">
      <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增区划</el-button>
      <el-button :icon="Upload" @click="handleImport">区划导入</el-button>
      <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">区划导出</el-button>
    </div>
    <div class="gd-table-container" v-loading="loading">
      <div class="gd-table-toolbar">
        <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增区划</el-button>
        <el-button :icon="Upload" color="#ECECF4" @click="handleImport">区划导入</el-button>
        <el-button :icon="Download" color="#ECECF4" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">区划导出</el-button>
      </div>
      <div class="gd-table-content gd-table-content-bg">
        <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
          <el-table-column type="selection" width="46" />
uniapps/work-app/src/App.vue
@@ -4,7 +4,7 @@
import { useGlobalWS } from '@/hooks/useGlobalWS.js'
// #ifdef APP-PLUS
import { keepAliveStart, requestIgnoreBatteryOptimization, isIgnoringBatteryOptimizations } from '@/uni_modules/keep-app'
import { openDialog, allowFloat, getBatteryCapacity } from '@/uni_modules/lgh-dialog'
import { openDialog, allowFloat, getBatteryCapacity, allowBackgroundPopup, allowLockScreen } from '@/uni_modules/lgh-dialog'
// #endif
const appStore = useAppStore()
@@ -22,15 +22,6 @@
//     }, 1000)
// }
// testKeep()
// 开启悬浮窗
// allowFloat()
// 假设五秒后来电
// #endif
// #ifdef APP-PLUS
//显示悬浮窗权限
allowFloat()
// #endif
useGlobalWS()
@@ -58,12 +49,16 @@
function setKeepFun() {
    // #ifdef APP-PLUS
    keepAliveStart()
    //显示悬浮窗权限
    setTimeout(allowFloat, 5000)
    // 请求用户允许忽略电池优化(重要:用于保活)
    setTimeout(() => {
        if (!isIgnoringBatteryOptimizations()) {
            requestIgnoreBatteryOptimization()
        }
    }, 10000)
    setTimeout(requestIgnoreBatteryOptimization, 10000)
    // 引导用户开启「后台弹出界面」权限
    setTimeout(allowBackgroundPopup, 15000)
    //  引导用户开启「锁屏显示」权限
    setTimeout(allowLockScreen, 20000)
    // #endif
}
uniapps/work-app/src/api/work/index.js
@@ -56,3 +56,11 @@
    data,
  })
}
// 分享缓存数据
export const shareCacheApi = (data) => {
  return request({
    url: '/drone-gd/workOrder/gdPatrolTask/share-cache-data',
    method: 'post',
    data,
  })
}
uniapps/work-app/src/hooks/useGlobalWS.js
@@ -2,7 +2,7 @@
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'
import { openDialog, allowFloat, getBatteryCapacity, showIncomingCallNotification, cancelIncomingCallNotification } from '@/uni_modules/lgh-dialog'
// #endif
export function useGlobalWS() {
    const userStore = useUserStore();
@@ -35,34 +35,35 @@
    // 消息处理
  function messageHandler(payload) {
    // 先尝试直接处理消息(适用于mobile-web-view的voiceCallDetail页面的消息格式)
    const t = (payload.type || '').toString()
    callStatus.value = t
    // 处理语音通话请求
    if (t === 'call') {
      console.log('📞 全局收到来电 call,来自', payload.from)
      // 播放铃声
      // playRingtone();
      // 触发震动
      triggerVibration();
      // triggerVibration();
      // 构建来电参数
      const callParams = {
        peerUid: payload.from,
        from: payload.from,
        type: 'incoming'
      };
// #ifdef APP-PLUS
      // #ifdef APP-PLUS
      // 发送来电通知(锁屏/后台都会弹出 + 亮屏)
      showIncomingCallNotification(payload.from || '未知来电')
      // 拉起应用到前台
            openDialog()
      // #endif
      // 转义参数以便在URL中传递
      const encodedParams = encodeURIComponent(JSON.stringify(callParams));
      try {
        // 优先使用uni-app的导航API(适用于同应用内跳转)
        if (typeof uni !== 'undefined' && uni.navigateTo) {
          uni.navigateTo({
            url: `/subPackages/voiceCallDetail/index?voiceparams=${encodedParams}`,
          });
            uni.navigateTo({
              url: `/subPackages/voiceCallDetail/index?voiceparams=${encodedParams}`,
            });
        } else {
          console.error('无法跳转到语音通话页面:当前环境不支持导航');
        }
uniapps/work-app/src/subPackages/voiceCallDetail/index.vue
@@ -11,6 +11,8 @@
import { useUserStore } from "@/store/index.js"
import { getWebViewUrl } from "@/utils/index.js";
import { onLoad } from "@dcloudio/uni-app";
import {cancelIncomingCallNotification } from '@/uni_modules/lgh-dialog'
const userStore = useUserStore()
const userParams = userStore?.userInfo ? JSON.stringify(userStore.userInfo) : '{}'
const sWebViewRef = ref(null);
@@ -84,6 +86,13 @@
    }
}
// 监听离开
onUnload(() => {
    // #ifdef APP-PLUS
    cancelIncomingCallNotification()
    // #endif
})
</script>
<style lang="scss" scoped>
uniapps/work-app/src/subPackages/workDetail/index.vue
@@ -103,7 +103,7 @@
<script setup>
// import { getShowImg, getSmallImg } from '@/utils/util'
import {getGddetailedData,backGdApi} from '/src/api/work/index.js'
import {getGddetailedData,backGdApi,shareCacheApi} from '/src/api/work/index.js'
import { getWebViewUrl } from '@/utils/index.js'
import WebViewPlus from '@/components/WebViewPlus.vue'
@@ -226,7 +226,8 @@
// 复制链接
const copyLink = async () => {
  const shareUrl =getWebViewUrl("/workDetail", { workDetailData: JSON.stringify(workDetailData.value) })
  const res = await shareCacheApi(workDetailData.value)
  const shareUrl = getWebViewUrl("/workDetail", { workDetailData: JSON.stringify(res.data.data) })
    try {
        await uni.setClipboardData({
            data: shareUrl,
uniapps/work-app/src/uni_modules/keep-app/utssdk/app-android/index.uts
@@ -11,6 +11,8 @@
import PowerManager from 'android.os.PowerManager'
import BatteryManager from 'android.os.BatteryManager'
import { UTSAndroid } from 'io.dcloud.uts'
import AlertDialog from 'android.app.AlertDialog'
import DialogInterface from 'android.content.DialogInterface'
// 保活服务的通知渠道ID
const CHANNEL_ID = 'keep_alive_channel'
@@ -102,38 +104,7 @@
  return '0%'
}
/**
 * 请求忽略电池优化(需要用户手动确认)
 * 调用此方法会弹出系统设置页面
 */
export function requestIgnoreBatteryOptimization(): void {
  const context = UTSAndroid.getAppContext()
  if (context == null) {
    return
  }
  if (Build.VERSION.SDK_INT >= 23) {
    // Android 6.0+
    const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
    const packageName = context.getPackageName()
    if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
      // 给个提示
      uni.showToast({  title: '请将应用添加到电池优化白名单'})
      try {
        const intent = new Intent()
        intent.setAction('android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS')
        intent.setData(android.net.Uri.parse('package:' + packageName))
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        context.startActivity(intent)
      } catch (e) {
        console.error('requestIgnoreBatteryOptimization: 打开设置失败', e)
      }
    } else {
      console.log('requestIgnoreBatteryOptimization: 已在白名单中')
    }
  }
}
/**
 * 检查是否已忽略电池优化
@@ -154,6 +125,40 @@
}
/**
 * 请求忽略电池优化(需要用户手动确认)
 * 调用此方法会弹出系统设置页面
 */
export function requestIgnoreBatteryOptimization(): void {
  if (isIgnoringBatteryOptimizations()) return
  const activity = UTSAndroid.getUniActivity()
  if (activity == null) return
  AlertDialog.Builder(activity)
    .setTitle('权限提示')
    .setMessage('为了在后台收到来电时能弹出通话界面,请将应用添加到电池优化白名单')
    .setPositiveButton('去设置', (_dialog : DialogInterface, _which : Int) => {
      const context = UTSAndroid.getAppContext()
      if (context == null) return
      if (Build.VERSION.SDK_INT >= 23) {
        const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
        const packageName = context.getPackageName()
        if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
          try {
            const intent = new Intent()
            intent.setAction('android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS')
            intent.setData(android.net.Uri.parse('package:' + packageName))
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context.startActivity(intent)
          } catch (e) {
            console.error('requestIgnoreBatteryOptimization: 打开设置失败', e)
          }
        }
      }
    })
    .setNegativeButton('暂不', null)
    .show()
}
/**
 * 创建通知渠道 (Android 8.0+ 必需)
 */
function createNotificationChannel(context: Context): void {
uniapps/work-app/src/uni_modules/lgh-dialog/utssdk/app-android/index.uts
@@ -4,6 +4,18 @@
import Settings from 'android.provider.Settings'
import { UTSAndroid } from 'io.dcloud.uts'
import BatteryManager from 'android.os.BatteryManager'
import NotificationChannel from 'android.app.NotificationChannel'
import NotificationManager from 'android.app.NotificationManager'
import Notification from 'android.app.Notification'
import PendingIntent from 'android.app.PendingIntent'
import PowerManager from 'android.os.PowerManager'
import KeyguardManager from 'android.app.KeyguardManager'
import AlertDialog from 'android.app.AlertDialog'
import DialogInterface from 'android.content.DialogInterface'
// 来电通知相关常量
const CALL_CHANNEL_ID = 'incoming_call_channel'
const CALL_NOTIFICATION_ID: Int = 2001
// 拉起应用
export function openDialog(): void {
@@ -27,12 +39,14 @@
    if (context == null) return
    // 1. 检查悬浮窗权限 (Android 6.0+)
    const noneFloat = Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(context)
    if (noneFloat) {
        uni.showToast({ title: '请允许应用悬浮在屏幕上', icon: 'none' })
    } else {
        setTimeout(() => {
    if (!noneFloat) return
    const activity = UTSAndroid.getUniActivity()
    if (activity == null) return
    AlertDialog.Builder(activity)
        .setTitle('权限提示')
        .setMessage('为了在后台收到来电时能弹出通话界面,请开启"悬浮窗"权限')
        .setPositiveButton('去设置', (_dialog : DialogInterface, _which : Int) => {
            try {
                // 无权限,跳转到权限设置页面
                const intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
                intent.setData(android.net.Uri.parse('package:' + context.getPackageName()))
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -40,45 +54,71 @@
            } catch (e) {
                console.error(e)
            }
        }, 2000)
    }
        })
        .setNegativeButton('暂不', null)
        .show()
}
// 跳到后台弹出界面权限页面(应用详情设置)
export function allowBackgroundPopup() {
    const context = UTSAndroid.getAppContext()
    if (context == null) return
    try {
        const intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
        intent.setData(android.net.Uri.parse('package:' + context.getPackageName()))
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        context.startActivity(intent)
    } catch (e) {
        console.error(e)
    }
  const context = UTSAndroid.getAppContext()
  if (context == null) return
  // 后台弹出界面无标准API,用悬浮窗权限作为代理判断(有悬浮窗权限通常也允许后台弹出)
  const nonePermission = Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(context)
  if (!nonePermission) return
  const activity = UTSAndroid.getUniActivity()
  if (activity == null) return
  AlertDialog.Builder(activity)
    .setTitle('权限提示')
    .setMessage('为了在后台收到来电时能弹出通话界面,请开启"后台弹出界面"权限')
    .setPositiveButton('去设置', (_dialog : DialogInterface, _which : Int) => {
      try {
        const intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
        intent.setData(android.net.Uri.parse('package:' + context.getPackageName()))
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        context.startActivity(intent)
      } catch (e) {
        console.error(e)
      }
    })
    .setNegativeButton('暂不', null)
    .show()
}
// 跳到锁屏显示权限页面(通知设置)
export function allowLockScreen() {
    const context = UTSAndroid.getAppContext()
    if (context == null) return
    try {
        if (Build.VERSION.SDK_INT >= 26) {
            // Android 8.0+ 跳转到应用通知设置
            const intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
            intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName())
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context.startActivity(intent)
        } else {
            // 低版本跳转到应用详情
            const intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
            intent.setData(android.net.Uri.parse('package:' + context.getPackageName()))
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            context.startActivity(intent)
        }
    } catch (e) {
        console.error(e)
    }
  const context = UTSAndroid.getAppContext()
  if (context == null) return
  // 锁屏显示依赖通知权限,检查通知是否开启
  const nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  const nonePermission = !nm.areNotificationsEnabled()
  if (!nonePermission) return
  const activity = UTSAndroid.getUniActivity()
  if (activity == null) return
  AlertDialog.Builder(activity)
    .setTitle('权限提示')
    .setMessage('为了在锁屏时能显示来电通知,请开启"锁屏显示通知"权限')
    .setPositiveButton('去设置', (_dialog : DialogInterface, _which : Int) => {
      try {
        if (Build.VERSION.SDK_INT >= 26) {
          // Android 8.0+ 跳转到应用通知设置
          const intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
          intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName())
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
          context.startActivity(intent)
        } else {
          // 低版本跳转到应用详情
          const intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
          intent.setData(android.net.Uri.parse('package:' + context.getPackageName()))
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
          context.startActivity(intent)
        }
      } catch (e) {
        console.error(e)
      }
    })
    .setNegativeButton('暂不', null)
    .show()
}
/**
@@ -100,3 +140,127 @@
    return '0%'
}
/**
 * 显示来电通知(锁屏/后台可弹出)
 * 利用高优先级通知 + fullScreenIntent 实现锁屏/后台来电提醒
 * @param callerName 来电者名称
 */
export function showIncomingCallNotification(callerName: string): void {
    const context = UTSAndroid.getAppContext()
    if (context == null) return
    try {
        // 1. 创建来电通知渠道(IMPORTANCE_HIGH → Heads-up 弹出)
        if (Build.VERSION.SDK_INT >= 26) {
            const channel = new NotificationChannel(
                CALL_CHANNEL_ID,
                '来电通知',
                NotificationManager.IMPORTANCE_HIGH
            )
            channel.setDescription('语音来电通知')
            channel.enableVibration(true)
            channel.enableLights(true)
            channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC)
            channel.setBypassDnd(true)
            const nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            nm.createNotificationChannel(channel)
        }
        // 2. 构建点击通知后打开应用的 Intent
        const pm = context.getPackageManager()
        const launchIntent = pm.getLaunchIntentForPackage(context.getPackageName())
        if (launchIntent != null) {
            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        }
        let pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT
        if (Build.VERSION.SDK_INT >= 31) {
            pendingIntentFlags = pendingIntentFlags | PendingIntent.FLAG_IMMUTABLE
        }
        const contentIntent = PendingIntent.getActivity(context, 0, launchIntent, pendingIntentFlags)
        // fullScreenIntent 用于锁屏时全屏展示
        const fullScreenIntent = PendingIntent.getActivity(context, 1, launchIntent, pendingIntentFlags)
        // 3. 构建通知
        const appInfo = context.getApplicationInfo()
        const appIcon = appInfo.icon
        let builder: Notification.Builder
        if (Build.VERSION.SDK_INT >= 26) {
            builder = new Notification.Builder(context, CALL_CHANNEL_ID)
        } else {
            builder = new Notification.Builder(context)
        }
        builder
            .setSmallIcon(appIcon)
            .setContentTitle('语音来电')
            .setContentText(callerName + ' 正在呼叫您')
            .setContentIntent(contentIntent)
            .setFullScreenIntent(fullScreenIntent, true)  // 🔑 锁屏全屏弹出
            .setOngoing(true)
            .setAutoCancel(false)
            .setPriority(Notification.PRIORITY_MAX)
            .setVisibility(Notification.VISIBILITY_PUBLIC) // 锁屏显示完整内容
        if (Build.VERSION.SDK_INT >= 21) {
            builder.setCategory(Notification.CATEGORY_CALL) // 标记为来电类型
        }
        const notification = builder.build()
        // 设置通知标志:保持常亮、不可清除
        notification.flags = notification.flags | Notification.FLAG_INSISTENT | Notification.FLAG_NO_CLEAR
        // 4. 发送通知
        const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(CALL_NOTIFICATION_ID, notification)
        // 5. 如果在锁屏状态,尝试亮屏
        wakeUpScreen(context)
        console.log('showIncomingCallNotification: 来电通知已发送,来电者:', callerName)
    } catch (e) {
        console.error('showIncomingCallNotification: 发送来电通知失败', e)
    }
}
/**
 * 取消来电通知
 * 在接听或拒接后调用
 */
export function cancelIncomingCallNotification(): void {
    const context = UTSAndroid.getAppContext()
    if (context == null) return
    try {
        const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.cancel(CALL_NOTIFICATION_ID)
        console.log('cancelIncomingCallNotification: 来电通知已取消')
    } catch (e) {
        console.error('cancelIncomingCallNotification: 取消来电通知失败', e)
    }
}
/**
 * 唤醒屏幕(锁屏时亮屏)
 */
function wakeUpScreen(context: Context): void {
    try {
        const powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
        if (!powerManager.isInteractive()) {
            // 屏幕关闭,唤醒屏幕
            const screenLock = powerManager.newWakeLock(
                PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
                'IncomingCall::WakeUp'
            )
            screenLock.acquire(10000) // 保持亮屏 10 秒
        }
    } catch (e) {
        console.error('wakeUpScreen: 唤醒屏幕失败', e)
    }
}
uniapps/work-app/src/utils/common/index.js
@@ -74,7 +74,7 @@
  // 2. 构建查询参数字符串
  let queryString = null
  if(targetUrl === '/workDetail') {
    queryString = `topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}`
    queryString = ``
  }else{
    queryString = `params=${encodeURIComponent(userParams)}&topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}`
  }
uniapps/work-app/src/utils/websocket.js
@@ -50,7 +50,7 @@
      this.socketTask.onOpen(() => {
        this.connected = true
        // console.log('WebSocket已连接,userId:', this.userId)
        this.startPing()
        // this.startPing()
        if (this.onOpenCallback) {
          this.onOpenCallback()
        }