Merge remote-tracking branch 'origin/master'
33 files modified
1 files added
| | |
| | | #开发环境代理地址(推荐本地新建文件 .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 |
| | | |
| New file |
| | |
| | | import request from '@/axios' |
| | | /** |
| | | * @description: 获取分享地址数据 |
| | | */ |
| | | |
| | | export const getSharingDetailsApi = (params) => { |
| | | return request({ |
| | | url: '/drone-gd/workOrder/gdPatrolTask/get-share-cache-data', |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | } |
| | |
| | | let locationFlag = false |
| | | let watchId = null |
| | | let userLocationMarker = null |
| | | const setMapLocation = () => { |
| | | locationFlag = true |
| | | |
| | | // 初始化实时位置监听 |
| | | const initLocationWatch = () => { |
| | | // 在WebView加载的网页中 |
| | | // 检查浏览器是否支持 geolocation |
| | | if (navigator.geolocation) { |
| | |
| | | // 停止监听位置 |
| | | navigator.geolocation.clearWatch(watchId) |
| | | } |
| | | |
| | | // 开始持续获取用户的位置 |
| | | watchId = navigator.geolocation.watchPosition( |
| | | function (position) { |
| | |
| | | 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) { |
| | | // 如果获取位置失败,执行回调 |
| | |
| | | } |
| | | ) |
| | | } 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 |
| | |
| | | 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() |
| | |
| | | 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> |
| | |
| | | <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" /> |
| | |
| | | </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() |
| | | |
| | |
| | | } |
| | | |
| | | 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> |
| | |
| | | .image-container { |
| | | position: relative; |
| | | width: 100%; |
| | | // height: 205px; |
| | | |
| | | .detailImage { |
| | | width: 100%; |
| | | height: 100%; |
| | |
| | | |
| | | .worderContainer { |
| | | padding: 0 12px; |
| | | padding-bottom: 2px; |
| | | // padding-bottom: 2px; |
| | | background: #f6f6f6; |
| | | } |
| | | |
| | |
| | | margin-top: 15px; |
| | | background-color: #fff; |
| | | border-radius: 6px; |
| | | padding: 10px; |
| | | padding: 8px 10px; |
| | | margin-bottom: 17px; |
| | | |
| | | .workOrderTitle { |
| | |
| | | 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, |
| | |
| | | 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, |
| | |
| | | methods: { |
| | | logout () { |
| | | this.$confirm(this.$t('logoutTip'), this.$t('提示'), { |
| | | customClass: 'gd-confirm-custom', |
| | | confirmButtonText: this.$t('submitText'), |
| | | cancelButtonText: this.$t('cancelText'), |
| | | type: 'warning', |
| | |
| | | }, |
| | | handleLogout() { |
| | | this.$confirm('是否退出系统, 是否继续?', '提示', { |
| | | customClass: 'gd-confirm-custom', |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | |
| | | } |
| | | |
| | | .gd-pagination-parent { |
| | | padding-top: 30px; |
| | | margin: 16px 0; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | |
| | |
| | | } |
| | | |
| | | .gd-table-container { |
| | | padding: 12px 20px 30px 20px; |
| | | padding: 25px 20px 1px; |
| | | height: 0; |
| | | flex: 1; |
| | | display: flex; |
| | |
| | | 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; |
| | | } |
| | |
| | | <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"> |
| | |
| | | </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" /> |
| | |
| | | </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> |
| | | |
| | |
| | | provide('planCycleOptions', planCycleOptions) |
| | | |
| | | // 获取列表 |
| | | async function getList() { |
| | | async function getList () { |
| | | loading.value = true |
| | | try { |
| | | const res = await fwDeviceMaintainPlanPageApi(searchParams.value) |
| | |
| | | } |
| | | |
| | | // 查询 |
| | | function handleSearch() { |
| | | function handleSearch () { |
| | | searchParams.value.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | // 重置查询 |
| | | function resetForm() { |
| | | function resetForm () { |
| | | queryParamsRef.value?.resetFields() |
| | | searchParams.value.current = 1 |
| | | getList() |
| | |
| | | |
| | | |
| | | // 新增/编辑/查看 弹框 |
| | | function openForm(mode, row) { |
| | | function openForm (mode, row) { |
| | | dialogVisible.value = true |
| | | nextTick(() => { |
| | | dialogRef.value?.open({ mode, row }) |
| | |
| | | } |
| | | |
| | | // 删除 |
| | | async function handleDelete(row) { |
| | | async function handleDelete (row) { |
| | | const tips = row ? '该条' : '选中的项' |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { |
| | | type: 'warning', |
| | |
| | | } |
| | | |
| | | // 勾选值设置 |
| | | 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(',') |
| | | } |
| | |
| | | </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"> |
| | |
| | | </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"> |
| | |
| | | </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" /> |
| | |
| | | </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="序号" /> |
| | |
| | | </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"> |
| | |
| | | </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"> |
| | |
| | | </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"> |
| | |
| | | </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"> |
| | |
| | | </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" /> |
| | |
| | | </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"> |
| | |
| | | </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" />--> |
| | |
| | | </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> |
| | |
| | | </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> |
| | |
| | | |
| | | |
| | | // 获取列表 |
| | | async function getList() { |
| | | async function getList () { |
| | | const range = dateRangeFormat(dateRange.value) |
| | | loading.value = true |
| | | try { |
| | |
| | | } |
| | | } |
| | | |
| | | 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 |
| | |
| | | } |
| | | |
| | | // 新增/编辑/查看 弹框 |
| | | function openForm(mode, row) { |
| | | function openForm (mode, row) { |
| | | dialogVisible.value = true |
| | | nextTick(() => { |
| | | dialogRef.value?.open({ mode, row }) |
| | |
| | | |
| | | // 状态: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' |
| | |
| | | } |
| | | |
| | | // 勾选值设置 |
| | | function handleSelectionChange(rows) { |
| | | function handleSelectionChange (rows) { |
| | | selectedIds.value = rows.map(item => item.id) |
| | | } |
| | | |
| | |
| | | } |
| | | 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 }) |
| | |
| | | } |
| | | |
| | | // 获取两个tab的总条数 |
| | | async function getTabTotals() { |
| | | async function getTabTotals () { |
| | | const allRes = await gdPatrolTaskPageApi({ |
| | | current: 1, |
| | | size: 1, |
| | |
| | | <!-- 运营收益 --> |
| | | <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" /> |
| | |
| | | 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> |
| | |
| | | </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' |
| | | |
| | |
| | | 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 { |
| | |
| | | </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" /> |
| | |
| | | </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" |
| | |
| | | </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' |
| | |
| | | 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', |
| | | }) |
| | |
| | | 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', |
| | | }) |
| | |
| | | </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" /> |
| | |
| | | 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() |
| | |
| | | // }, 1000) |
| | | // } |
| | | // testKeep() |
| | | |
| | | // 开启悬浮窗 |
| | | // allowFloat() |
| | | // 假设五秒后来电 |
| | | // #endif |
| | | |
| | | // #ifdef APP-PLUS |
| | | //显示悬浮窗权限 |
| | | allowFloat() |
| | | // #endif |
| | | |
| | | useGlobalWS() |
| | |
| | | function setKeepFun() { |
| | | // #ifdef APP-PLUS |
| | | keepAliveStart() |
| | | |
| | | //显示悬浮窗权限 |
| | | setTimeout(allowFloat, 5000) |
| | | // 请求用户允许忽略电池优化(重要:用于保活) |
| | | setTimeout(() => { |
| | | if (!isIgnoringBatteryOptimizations()) { |
| | | requestIgnoreBatteryOptimization() |
| | | } |
| | | }, 10000) |
| | | setTimeout(requestIgnoreBatteryOptimization, 10000) |
| | | // 引导用户开启「后台弹出界面」权限 |
| | | setTimeout(allowBackgroundPopup, 15000) |
| | | // 引导用户开启「锁屏显示」权限 |
| | | setTimeout(allowLockScreen, 20000) |
| | | |
| | | // #endif |
| | | } |
| | | |
| | |
| | | data, |
| | | }) |
| | | } |
| | | // 分享缓存数据 |
| | | export const shareCacheApi = (data) => { |
| | | return request({ |
| | | url: '/drone-gd/workOrder/gdPatrolTask/share-cache-data', |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | |
| | | 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(); |
| | |
| | | |
| | | // 消息处理 |
| | | 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('无法跳转到语音通话页面:当前环境不支持导航'); |
| | | } |
| | |
| | | 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); |
| | |
| | | } |
| | | } |
| | | |
| | | // 监听离开 |
| | | onUnload(() => { |
| | | // #ifdef APP-PLUS |
| | | cancelIncomingCallNotification() |
| | | // #endif |
| | | }) |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | |
| | | <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' |
| | | |
| | |
| | | |
| | | // 复制链接 |
| | | 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, |
| | |
| | | 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' |
| | |
| | | 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: 已在白名单中') |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 检查是否已忽略电池优化 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 请求忽略电池优化(需要用户手动确认) |
| | | * 调用此方法会弹出系统设置页面 |
| | | */ |
| | | 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 { |
| | |
| | | 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 { |
| | |
| | | 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) |
| | |
| | | } 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() |
| | | } |
| | | |
| | | /** |
| | |
| | | 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) |
| | | } |
| | | } |
| | |
| | | // 2. 构建查询参数字符串 |
| | | let queryString = null |
| | | if(targetUrl === '/workDetail') { |
| | | queryString = `topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}` |
| | | queryString = `` |
| | | }else{ |
| | | queryString = `params=${encodeURIComponent(userParams)}&topMargin=${statusBarHeight}&uniPlatform=${uniPlatform}` |
| | | } |
| | |
| | | this.socketTask.onOpen(() => { |
| | | this.connected = true |
| | | // console.log('WebSocket已连接,userId:', this.userId) |
| | | this.startPing() |
| | | // this.startPing() |
| | | if (this.onOpenCallback) { |
| | | this.onOpenCallback() |
| | | } |