无人机管理后台前端(已迁走)
罗广辉
2025-07-30 d201a269290f0e9429826e4264d8420838902757
Merge branch 'dev' into test
15 files modified
3 files added
4926 ■■■■■ changed files
.env.development 10 ●●●●● patch | view | raw | blame | history
.env.jiangwu 3 ●●●●● patch | view | raw | blame | history
package-lock.json 18 ●●●●● patch | view | raw | blame | history
src/api/home/index.js 15 ●●●●● patch | view | raw | blame | history
src/api/job/task.js 27 ●●●● patch | view | raw | blame | history
src/buildConfig/buildConfig/config.default.js 4 ●●●● patch | view | raw | blame | history
src/buildConfig/buildConfig/config.jiangwu.js 8 ●●●● patch | view | raw | blame | history
src/buildConfig/layerConfig/defaultLayer.js 173 ●●●●● patch | view | raw | blame | history
src/buildConfig/layerConfig/jiangwuLayer.js 122 ●●●●● patch | view | raw | blame | history
src/components/ElTooltipCopy.vue 52 ●●●●● patch | view | raw | blame | history
src/utils/cesium/publicCesium.js 9 ●●●● patch | view | raw | blame | history
src/views/dataCenter/dataCenter.vue 10 ●●●●● patch | view | raw | blame | history
src/views/device/addDevice.vue 98 ●●●●● patch | view | raw | blame | history
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue 19 ●●●● patch | view | raw | blame | history
src/views/tickets/orderLog.vue 29 ●●●● patch | view | raw | blame | history
src/views/tickets/ticket.vue 140 ●●●●● patch | view | raw | blame | history
src/views/wel/components/backlog.vue 12 ●●●●● patch | view | raw | blame | history
yarn.lock 4177 ●●●● patch | view | raw | blame | history
.env.development
@@ -1,3 +1,13 @@
###
 # @Author       : yuan
 # @Date         : 2025-07-29 15:59:42
 # @LastEditors  : yuan
 # @LastEditTime : 2025-07-29 16:15:31
 # @FilePath     : \.env.development
 # @Description  :
 # Copyright 2025 OBKoro1, All Rights Reserved.
 # 2025-07-29 15:59:42
###
NODE_ENV = 'development'
#开发环境配置
.env.jiangwu
@@ -43,3 +43,6 @@
# 设备ip地址
VITE_APP_DEVICE_IP = '139.196.74.78:1883'
VITE_APP_TDT_TOKEN = e110584a27d506da2740edca951683f4
VITE_APP_CESIUM_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTZlNGNlYS01NTU1LTQ1MGEtYmNlZS0yNTE2NDk5YWM2MjEiLCJpZCI6MTc5Njk2LCJpYXQiOjE3MDA1NDcwMjV9.qcl4AH2731cfFd0-I1ZLUINPXqvglLkDFD-UGR2zU5M
package-lock.json
@@ -32,6 +32,7 @@
                "postcss-pxtorem": "^6.1.0",
                "reconnecting-websocket": "^4.4.0",
                "sm-crypto": "^0.3.13",
                "uuid": "^11.1.0",
                "vue": "^3.4.27",
                "vue-i18n": "^9.1.9",
                "vue-router": "^4.3.2",
@@ -5358,6 +5359,18 @@
            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
            "license": "MIT"
        },
        "node_modules/uuid": {
            "version": "11.1.0",
            "resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
            "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
            "funding": [
                "https://github.com/sponsors/broofa",
                "https://github.com/sponsors/ctavan"
            ],
            "bin": {
                "uuid": "dist/esm/bin/uuid"
            }
        },
        "node_modules/vite": {
            "version": "5.2.12",
            "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz",
@@ -9537,6 +9550,11 @@
            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
        },
        "uuid": {
            "version": "11.1.0",
            "resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
            "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="
        },
        "vite": {
            "version": "5.2.12",
            "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz",
src/api/home/index.js
@@ -96,17 +96,14 @@
  });
};
// 获取事件工单或者智飞工单的信息
export const getOrderOrEventApi = (type,areaCode) => {
export const getOrderOrEventApi = data => {
  return request({
    url: '/drone-device-core/jobEvent/getOrderOrEvent',
    method: 'get',
    params: {
      type,
      areaCode
    },
  });
};
    method: 'post',
    data: data,
  })
}
// 任务成果
export const getdisposeApi = data => {
    return request({
src/api/job/task.js
@@ -11,26 +11,47 @@
}
// 其他任务统计
export const jobStatistics = data => {
    let requestParams = {
        ...data,
        status_list: data.status
    }
    delete requestParams.status
    return request({
        url: '/drone-device-core/wayline/waylineJobInfo/jobStatistics',
        method: 'post',
        data: data,
        data: requestParams,
    })
}
// 行业任务统计
export const industryJobNumPieChart = data => {
    let requestParams = {
        ...data,
        status_list: data.status
    }
    delete requestParams.status
    return request({
        url: '/drone-device-core/wayline/waylineJobInfo/industryJobNumPieChart',
        method: 'post',
        data: data,
        data: requestParams,
    })
}
// 时间任务统计
export const jobNumBar = data => {
    let requestParams = {
        ...data,
        status_list: data.status
    }
    delete requestParams.status
    return request({
        url: '/drone-device-core/wayline/waylineJobInfo/jobNumBar',
        method: 'post',
        data: data,
        data: requestParams,
    })
}
// 事件任务统计
src/buildConfig/buildConfig/config.default.js
@@ -1,5 +1,9 @@
import defaultLayer from '@/buildConfig/layerConfig/defaultLayer'
export default function getBaseConfig () {
    return {
        loginTitle: '中图智飞低空智能感知网平台',  //登陆页面标题
        ...defaultLayer
    }
}
src/buildConfig/buildConfig/config.jiangwu.js
@@ -1,10 +1,14 @@
import jiangwuLayer from '@/buildConfig/layerConfig/jiangwuLayer'
export default function getBaseConfig () {
    return {
        userLoginTitle (key) {
            return '大吉山钨业无人机安防监测平台'
        },
    routerHash: true, //使用hash路由
        routerHash: true, //使用hash路由
        loginTitle: '大吉山钨业无人机安防监测平台',
      hideMenuTopLogo: true, // 是否隐藏菜单顶部logo
        hideMenuTopLogo: true, // 是否隐藏菜单顶部logo
        ...jiangwuLayer,
    }
}
src/buildConfig/layerConfig/defaultLayer.js
New file
@@ -0,0 +1,173 @@
/*
 * @Author       : yuan
 * @Date         : 2025-06-13 09:48:50
 * @LastEditors  : yuan
 * @LastEditTime : 2025-07-04 18:04:58
 * @FilePath     : \src\buildConfig\layerConfig\defaultLayer.js
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-06-13 09:48:50
 */
import * as Cesium from 'cesium'
import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme'
let TDT_Token = import.meta.env.VITE_APP_TDT_TOKEN
const TDT_IMG_C =
    'https://{s}.tianditu.gov.cn/img_c/wmts?service=wmts&request=GetTile&version=1.0.0' +
    '&LAYER=img&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}' +
    '&style=default&format=tiles&tk=' +
    TDT_Token
// 天地图注记
const TDT_ZJ =
    'https://{s}.tianditu.gov.cn/cia_c/wmts?service=wmts&request=GetTile&version=1.0.0' +
    '&LAYER=cia&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}' +
    '&style=default&format=tiles&tk=' +
    TDT_Token
// 天地图矢量底图
const imageryProvider_stand = new Cesium.UrlTemplateImageryProvider({
    url: `https://t{s}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${TDT_Token}`,
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    // format: 'image/jpeg',
    // show: true,
    maximumLevel: 18,
    credit: 'stand_tc',
})
// 天地图矢量注记
const imageryProvider_standZh = new Cesium.UrlTemplateImageryProvider({
    url: `https://t{s}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=${TDT_Token}`,
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
    credit: 'stand_zj',
})
// 天地图影像
const imageryProvider_tdt_img_w = new Cesium.WebMapTileServiceImageryProvider({
    url: TDT_IMG_C,
    layer: 'tdtImg_c',
    style: 'default',
    format: 'tiles',
    tileMatrixSetID: 'c',
    subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
    tilingScheme: new Cesium.GeographicTilingScheme(),
    tileMatrixLabels: [
        '1',
        '2',
        '3',
        '4',
        '5',
        '6',
        '7',
        '8',
        '9',
        '10',
        '11',
        '12',
        '13',
        '14',
        '15',
        '16',
        '17',
        '18',
        '19',
    ],
    maximumLevel: 17,
})
// 天地图影像注记
const imageryProvider_tdt_cia_w = new Cesium.WebMapTileServiceImageryProvider({
    url: TDT_ZJ,
    layer: 'tdtZwImg_c',
    style: 'default',
    format: 'tiles',
    tileMatrixSetID: 'c',
    subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
    tilingScheme: new Cesium.GeographicTilingScheme(),
    tileMatrixLabels: [
        '1',
        '2',
        '3',
        '4',
        '5',
        '6',
        '7',
        '8',
        '9',
        '10',
        '11',
        '12',
        '13',
        '14',
        '15',
        '16',
        '17',
        '18',
        '19',
    ],
    maximumLevel: 50,
})
//高德矢量,自带注记
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    layer: 'tdtVecBasicLayer',
    style: 'default',
    format: 'image/png',
    tileMatrixSetID: 'GoogleMapsCompatible',
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
    tilingScheme: new AmapMercatorTilingScheme(),
    credit: 'amap_SL',
})
//高德影像,自带注记
const imageryProvider_ammap = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
    layer: 'tdtVecBasicLayer',
    style: 'default',
    format: 'image/png',
    tileMatrixSetID: 'GoogleMapsCompatible',
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
    tilingScheme: new AmapMercatorTilingScheme(),
    credit: 'amap_stand',
})
// 高德影像,注记
const imageryProvider_ammapBz = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8',
    tilingScheme: new AmapMercatorTilingScheme(),
    minimumLevel: 3,
})
// 天地图矢量底图
const blueImageryProviderVec = new Cesium.UrlTemplateImageryProvider({
    url: `https://t{s}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${TDT_Token}`,
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    // format: 'image/jpeg',
    // show: true,
    maximumLevel: 18,
    credit: 'stand_tc',
})
// 天地图矢量注记-蓝色
const blueImageryProviderCva = new Cesium.UrlTemplateImageryProvider({
    url: `https://t{s}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=${TDT_Token}`,
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
    credit: 'stand_zj',
})
export default {
    imageryProvider_stand,
    imageryProvider_standZh,
    imageryProvider_tdt_img_w,
    imageryProvider_tdt_cia_w,
    imageryProvider_ammapSL,
    imageryProvider_ammap,
    imageryProvider_ammapBz,
    blueImageryProviderVec,
    blueImageryProviderCva
}
src/buildConfig/layerConfig/jiangwuLayer.js
New file
@@ -0,0 +1,122 @@
/*
 * @Author       : yuan
 * @Date         : 2025-06-13 09:49:01
 * @LastEditors  : yuan
 * @LastEditTime : 2025-06-13 14:02:59
 * @FilePath     : \src\buildConfig\layerConfig\jiangwuLayer.js
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-06-13 09:49:01
 */
import * as Cesium from 'cesium'
import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme'
let TDT_Token = import.meta.env.VITE_APP_TDT_TOKEN
const TDT_IMG_C =
    'https://{s}.tianditu.gov.cn/img_c/wmts?service=wmts&request=GetTile&version=1.0.0' +
    '&LAYER=img&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}' +
    '&style=default&format=tiles&tk=' +
    TDT_Token
// 天地图注记
const TDT_ZJ =
    'https://{s}.tianditu.gov.cn/cia_c/wmts?service=wmts&request=GetTile&version=1.0.0' +
    '&LAYER=cia&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}' +
    '&style=default&format=tiles&tk=' +
    TDT_Token
// 天地图矢量底图
const imageryProvider_stand = new Cesium.UrlTemplateImageryProvider({
    url: `${import.meta.env.VITE_APP_OFFLINE_MAP_URL}/slLayer/{z}/{y}/{x}.png`, // 本地瓦片路径
    minimumLevel: 0,  // 最小层级
    maximumLevel: 17, // 最大层级
    tileWidth: 256,   // 瓦片宽度(默认256)
    tileHeight: 256,  // 瓦片高度(默认256)
})
// 天地图矢量注记
const imageryProvider_standZh = new Cesium.UrlTemplateImageryProvider({
    url: `${import.meta.env.VITE_APP_OFFLINE_MAP_URL}/slBzLayer/{z}/{y}/{x}.png`, // 本地瓦片路径
    minimumLevel: 0,  // 最小层级
    maximumLevel: 17, // 最大层级
    tileWidth: 256,   // 瓦片宽度(默认256)
    tileHeight: 256,  // 瓦片高度(默认256)
})
// 天地图影像
const imageryProvider_tdt_img_w = new Cesium.UrlTemplateImageryProvider({
    url: `${import.meta.env.VITE_APP_OFFLINE_MAP_URL}/yxLayer/{z}/{y}/{x}.png`, // 本地瓦片路径
    minimumLevel: 0,  // 最小层级
    maximumLevel: 17, // 最大层级
    tileWidth: 256,   // 瓦片宽度(默认256)
    tileHeight: 256,  // 瓦片高度(默认256)
})
// 天地图影像注记
const imageryProvider_tdt_cia_w = new Cesium.UrlTemplateImageryProvider({
    url: `${import.meta.env.VITE_APP_OFFLINE_MAP_URL}/yxBzLayer/{z}/{y}/{x}.png`, // 本地瓦片路径
    minimumLevel: 0,  // 最小层级
    maximumLevel: 17, // 最大层级
    tileWidth: 256,   // 瓦片宽度(默认256)
    tileHeight: 256,  // 瓦片高度(默认256)
})
//高德矢量,自带注记
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    layer: 'tdtVecBasicLayer',
    style: 'default',
    format: 'image/png',
    tileMatrixSetID: 'GoogleMapsCompatible',
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
    tilingScheme: new AmapMercatorTilingScheme(),
    credit: 'amap_SL',
})
//高德影像,自带注记
const imageryProvider_ammap = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
    layer: 'tdtVecBasicLayer',
    style: 'default',
    format: 'image/png',
    tileMatrixSetID: 'GoogleMapsCompatible',
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
    tilingScheme: new AmapMercatorTilingScheme(),
    credit: 'amap_stand',
})
// 高德影像,注记
const imageryProvider_ammapBz = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8',
    tilingScheme: new AmapMercatorTilingScheme(),
    minimumLevel: 3,
})
// 天地图矢量底图-蓝色
const blueImageryProviderVec = new Cesium.UrlTemplateImageryProvider({
    url: `${import.meta.env.VITE_APP_OFFLINE_MAP_URL}/slLayer/{z}/{y}/{x}.png`, // 本地瓦片路径
    minimumLevel: 0,  // 最小层级
    maximumLevel: 17, // 最大层级
    tileWidth: 256,   // 瓦片宽度(默认256)
    tileHeight: 256,  // 瓦片高度(默认256)
})
// 天地图矢量注记-蓝色
const blueImageryProviderCva = new Cesium.UrlTemplateImageryProvider({
    url: `${import.meta.env.VITE_APP_OFFLINE_MAP_URL}/slBzLayer/{z}/{y}/{x}.png`, // 本地瓦片路径
    minimumLevel: 0,  // 最小层级
    maximumLevel: 17, // 最大层级
    tileWidth: 256,   // 瓦片宽度(默认256)
    tileHeight: 256,  // 瓦片高度(默认256)
})
export default {
    imageryProvider_stand,
    imageryProvider_standZh,
    imageryProvider_tdt_img_w,
    imageryProvider_tdt_cia_w,
    imageryProvider_ammapSL,
    imageryProvider_ammap,
    imageryProvider_ammapBz,
    blueImageryProviderVec,
    blueImageryProviderCva
}
src/components/ElTooltipCopy.vue
New file
@@ -0,0 +1,52 @@
<template>
    <el-tooltip :show-after="200" placement="top" effect="dark">
    <div class="defaultDisplay" :style="{textAlign}">
      <slot name="default" />
    </div>
        <template #content>
            <span class="popUpContent" @click="clickTest">{{ showCopyText ? '复制' : content }}</span>
        </template>
    </el-tooltip>
</template>
<script setup>
import { ElMessage } from 'element-plus'
const props = defineProps({
    content: {
        type: String,
        required: true
    },
    showCopyText: {
        type: Boolean,
        default: false
    },
  textAlign:{
    type: String,
    default: 'center'
  }
})
function clickTest(e) {
    navigator.clipboard
        .writeText(props.content)
        .then(() => {
            ElMessage.success('复制成功!')
        })
        .catch(() => {
            ElMessage.error('复制失败,请重试!')
        })
}
</script>
<style scoped lang="scss">
.popUpContent{
    cursor: pointer;
}
.defaultDisplay{
  display: inline-block;
  width: 100%;
  white-space:nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style>
src/utils/cesium/publicCesium.js
@@ -1,7 +1,14 @@
import EventBus from './eventBus'
import { Cartesian3, Terrain, Viewer } from 'cesium'
import { imageryProvider_stand, imageryProvider_standZh } from './cesiumLayer'
import getBaseConfig from '@/buildConfig/config'
const {
    imageryProvider_stand,
    imageryProvider_standZh
} = getBaseConfig()
import { addBlueFilter } from './common'
import * as Cesium from 'cesium'
src/views/dataCenter/dataCenter.vue
@@ -35,7 +35,14 @@
          </el-table-column>
          <el-table-column property="nestName" label="所属机巢" />
          <el-table-column property="jobName" label="任务名称" show-overflow-tooltip />
          <el-table-column prop="nickName" label="文件名称" show-overflow-tooltip />
          <el-table-column prop="nickName" label="文件名称" width="160">
            <template #default="scope">
              <el-tooltip-copy :content="scope.row.nickName" :showCopyText="true">
                {{scope.row.nickName}}
              </el-tooltip-copy>
            </template>
          </el-table-column>
          <el-table-column property="link" label="缩略图" width="120">
            <template #default="scope">
              <img
@@ -304,6 +311,7 @@
import { getShowImg, getSmallImg, getzsSmallImg } from '@/utils/util';
import { onMounted, watch } from 'vue';
import dayjs from 'dayjs';
import ElTooltipCopy from '@/components/ElTooltipCopy.vue';
function bytesToMB(bytes, decimalPlaces = 2) {
  if (typeof bytes !== 'number' || bytes < 0) return '0';
src/views/device/addDevice.vue
@@ -194,6 +194,64 @@
                        format: 'YYYY-MM-DD HH:mm:ss',
                    },
                    {
                        label: '行政区划',
                        prop: 'area_code',
                        type: 'cascader',
                        labelWidth: 130,
                        hide: true,
                        editDisplay: true,
                        viewDisplay: false,
                        props: {
                        label: 'title',
                        value: 'value',
                        emitPath: false,
                        checkStrictly: true,
                        multiple: false,
                        expandTrigger: 'hover',
                        },
                        dataType: 'string',
                        rules: [
                        {
                            required: true,
                            message: '请选择行政区划',
                            trigger: 'change',
                        },
                        ],
                        lazy: true,
                        lazyLoad (node, resolve) {
                        let level = node.level
                        let list = []
                        let callback = () => {
                            resolve(
                            (list || []).map(ele => ({
                                ...ele,
                                value: ele.value,
                                leaf: level >= 2,
                            }))
                            )
                        }
                        if (level === 0) {
                            getLazyTree('000000000000').then(res => {
                            list = res.data.data
                            callback()
                            })
                        } else if (level === 1) {
                            getLazyTree(node.value).then(res => {
                            list = res.data.data
                            callback()
                            })
                        } else if (level === 2) {
                            getLazyTree(node.value).then(res => {
                            list = res.data.data
                            callback()
                            })
                        } else {
                            callback()
                        }
                        },
                    },
                ],
            },
@@ -258,6 +316,14 @@
        },
        rowSave (row, done, loading) {
            let areaCode = row.area_code
            if (Array.isArray(areaCode)) {
                areaCode = areaCode[areaCode.length - 1]
            } else if (typeof areaCode === 'string' && areaCode.includes(',')) {
                const codes = areaCode.split(',')
                areaCode = codes[codes.length - 1]
            }
            row.area_code = areaCode || null
            add(row).then(
                () => {
                    this.onLoad(this.page)
@@ -274,7 +340,11 @@
            )
        },
        rowUpdate (row, index, done, loading) {
            update(row).then(
            const submitData = {
                ...row,
                area_code: row.area_code.split(',').pop(),
            }
            update(submitData).then(
                () => {
                    this.onLoad(this.page)
                    this.$message({
@@ -345,10 +415,32 @@
                    this.$refs.crud.toggleSelection()
                })
        },
        async getFullAreaCode (areaCode) {
            if (!areaCode) return ''
            const code = areaCode.toString()
            if (code.includes(',')) return code
            if (code.endsWith('0000000000')) {
                return code
            } else if (code.endsWith('00000000') && !code.endsWith('0000000000')) {
                const provinceCode = code.substring(0, 2) + '0000000000'
                return `${provinceCode},${code}`
            } else {
                const provinceCode = code.substring(0, 2) + '0000000000'
                const cityCode = code.substring(0, 4) + '00000000'
                return `${provinceCode},${cityCode},${code}`
            }
        },
        beforeOpen (done, type) {
            if (['edit', 'view'].includes(type)) {
                getDetail(this.form.id).then(res => {
                    this.form = res.data.data
                getDetail(this.form.id).then(async res => {
                    const data = res.data.data
                     this.form = {
                        ...data,
                        area_code: await this.getFullAreaCode(data.area_code),
                     }
                })
            }
            done()
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -3,15 +3,27 @@
    <div class="task-intermediate-content">
        <SearchBox @search="searchClick" @addTask="handleAddTask"></SearchBox>
        <div class="task-table">
            <el-table border :data="jobListData" class="custom-header" @cell-click="handleCellClick">
            <el-table border :data="jobListData" class="custom-header">
                <el-table-column label="序号" type="index" width="60">
                    <template #default="{ $index }">
                        {{ ($index + 1 + (jobListParams.current - 1) * jobListParams.size).toString().padStart(2,
                            '0') }}
                    </template>
                </el-table-column>
                <el-table-column prop="job_info_num" label="任务编号" show-overflow-tooltip align="center" />
                <el-table-column prop="name" label="任务名称" show-overflow-tooltip align="center" />
        <el-table-column prop="job_info_num" label="任务编号" width="160">
          <template #default="scope">
            <el-tooltip-copy :content="scope.row.job_info_num" :showCopyText="true">
              {{scope.row.job_info_num}}
            </el-tooltip-copy>
          </template>
        </el-table-column>
        <el-table-column prop="name" label="任务编号" width="160">
          <template #default="scope">
            <el-tooltip-copy :content="scope.row.name" :showCopyText="true">
              {{scope.row.name}}
            </el-tooltip-copy>
          </template>
        </el-table-column>
                <el-table-column prop="dept_name" label="所属部门" width="200" align="center" />
                <el-table-column prop="device_names" label="所属机巢" />
                <el-table-column prop="ai_type_str" label="关联算法" show-overflow-tooltip align="center" />
@@ -108,6 +120,7 @@
import { useStore } from 'vuex'
import { cloneDeep } from 'lodash'
import { inject, onBeforeUnmount } from 'vue';
import ElTooltipCopy from '@/components/ElTooltipCopy.vue';
const store = useStore()
const singleUavHome = computed(() => store.state.home.singleUavHome)
const jobListParams = reactive({
src/views/tickets/orderLog.vue
@@ -92,7 +92,19 @@
          <!-- 表格部分 -->
          <avue-crud class="ztzf-public-general-avue-crud" :data="tableData" :option="option" v-model:page="page"
            ref="crud" :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange"
            @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" @cell-click="handleCellClick" v-if="activeTab === tab.name">
            @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name">
            <template #job_info_num="{row}">
              <el-tooltip-copy :content="row.job_info_num" :showCopyText="true" textAlign="left">
                {{ row.job_info_num }}
              </el-tooltip-copy>
            </template>
            <template #name="{ row }">
              <el-tooltip-copy :content="row.name" :showCopyText="true" textAlign="left">
                {{ row.name }}
              </el-tooltip-copy>
            </template>
            <template #menu-left>
              <el-button v-if="hasAddBtnPermission() && activeTab != 'WAIT_AUDIT'" type="primary" icon="el-icon-plus"
                @click="handleAdd">新建工单</el-button>
@@ -508,11 +520,13 @@
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn'; // 导入中文语言包
import weekday from 'dayjs/plugin/weekday';
import elTooltipCopy from '@/components/ElTooltipCopy.vue'
dayjs.extend(weekday);
dayjs.locale('zh-cn');
export default {
  components: { elTooltipCopy },
  name: 'TicketPage',
  data () {
    return {
@@ -582,8 +596,7 @@
        column: [
          {
            label: '工单编号', prop: 'job_info_num', width: 100, ellipsis: true, overHidden: true,
            showOverflowTooltip: true,
            label: '工单编号', prop: 'job_info_num', width: 150,
          },
          {
            label: '工单名称', prop: 'name', width: 100, ellipsis: true, overHidden: true,
@@ -602,7 +615,7 @@
            showOverflowTooltip: true,
          },
          {
            label: '已执行次数', prop: 'job_num', width: 96, ellipsis: true,
            label: '已执行次数', prop: 'job_num', width: 70, ellipsis: true,
            showOverflowTooltip: true,
          },
          {
@@ -887,7 +900,7 @@
          //     if (selectedTime < now) {
          //       return this.$message.warning('任务时间不能小于当前时间')
          //     }
          //   }
          // }
@@ -1228,6 +1241,8 @@
      this.$message.success('审核通过')
      this.detailVisibleCopy = false
      this.detailVisible = false
      this.page.currentPage = 1
      this.onLoad(this.page, this.query)
      // this.handleViewDetail()
@@ -1244,6 +1259,8 @@
        let data = response.data.data
        this.$message.success('驳回成功')
        this.detailVisibleCopy = false
        // this.detailVisible = false
        this.page.currentPage = 1
        this.onLoad(this.page, this.query)
@@ -1347,7 +1364,7 @@
}
:deep(.el-textarea__inner) {
  resize: none;
  resize: none;
}
.action-bar {
src/views/tickets/ticket.vue
@@ -98,22 +98,20 @@
          </div>
          <!-- 表格部分 -->
          <avue-crud
            class="ztzf-public-general-avue-crud"
            ref="avueCrud"
            v-model="tableData"
            :option="option"
            :data="tableData"
            v-model:page="page"
            @size-change="sizeChange"
            @current-change="handleCurrentChange"
            @refresh-change="refreshChange"
            :table-loading="loading"
            @selection-change="handleSelectionChange"
            @cell-click="handleCellClick"
            :permission="permissionList"
            v-if="activeTab === tab.name"
          >
          <avue-crud class="ztzf-public-general-avue-crud" ref="avueCrud" v-model="tableData" :option="option"
            :data="tableData" v-model:page="page" @size-change="sizeChange" @current-change="handleCurrentChange"
            @refresh-change="refreshChange" :table-loading="loading" @selection-change="handleSelectionChange"
            :permission="permissionList" v-if="activeTab === tab.name">
            <template #orderNumber="{ row }">
              <el-tooltip-copy :content="row.orderNumber" :showCopyText="true" textAlign="left">
                {{ row.orderNumber }}
              </el-tooltip-copy>
            </template>
            <template #orderName="{ row }">
              <el-tooltip-copy :content="row.orderName" :showCopyText="true" textAlign="left">
                {{ row.orderName }}
              </el-tooltip-copy>
            </template>
            <template #menu-left>
              <el-button
                v-if="(activeTab === 'all' || activeTab === 'myTickets') && permissionList.addBtn"
@@ -210,7 +208,8 @@
            </el-col>
            <el-col :span="12">
              <el-form-item label="工单类型" prop="type">
                <el-select v-model="form.type" placeholder="请选择工单类型" class="full-width">
                <el-select  @change="handleTypeChange" v-model="form.type" placeholder="请选择工单类型" class="full-width" >
                  <el-option
                    v-for="item in types"
                    :key="item.value"
@@ -267,6 +266,7 @@
                  multiple
                  placeholder="请选择关联算法"
                  class="full-width"
                  :disabled="!form.type"
                >
                  <el-option
                    v-for="item in algorithms"
@@ -365,7 +365,9 @@
          <el-button type="infoprimary" plain :loading="draftLoading" @click="saveDraft"
            >存草稿</el-button
          >
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button  @click="handleCancel">取 消</el-button>
        </div>
      </template>
    </el-dialog>
@@ -866,17 +868,19 @@
  getStepInfo,
  getReviewById,
  getCreateEventJob,
} from '@/api/tickets/ticket';
import { export_json_to_excel } from '@/utils/exportExcel';
import geoJson from '@/assets/geoJson.json';
import { mapGetters } from 'vuex';
import { getAdcodeObj } from '@/utils/disposeData';
function regExp(label, name) {
  var reg = new RegExp(label + '=([^&]*)(&|$)', 'g');
  return name.match(reg)[0].split('=')[1];
} from '@/api/tickets/ticket'
import { export_json_to_excel } from '@/utils/exportExcel'
import geoJson from '@/assets/geoJson.json'
import { mapGetters } from 'vuex'
import { getAdcodeObj } from '@/utils/disposeData'
import elTooltipCopy from '@/components/ElTooltipCopy.vue'
function regExp (label, name) {
  var reg = new RegExp(label + '=([^&]*)(&|$)', 'g')
  return name.match(reg)[0].split('=')[1]
}
export default {
  components: { elTooltipCopy },
  name: 'TicketPage',
  data() {
    return {
@@ -911,6 +915,7 @@
      },
      departments: [],
      types: [],
      allAlgorithms: [],
      handlers: [
        { label: '处理人A', value: 'handlerA' },
        { label: '处理人B', value: 'handlerB' },
@@ -949,15 +954,15 @@
        column: [
          // { label: "序号", prop: "id", width: 70 },
          { label: '工单编号', prop: 'orderNumber', width: 120, overHidden: true, tooltip: true },
          { label: '工单编号', prop: 'orderNumber', width: 170 },
          { label: '工单名称', prop: 'orderName', width: 150, overHidden: true, tooltip: true },
          { label: '所属单位', prop: 'department', overHidden: true, tooltip: true },
          { label: '所属单位', prop: 'department',width: 150, overHidden: true, tooltip: true },
          { label: '发起时间', prop: 'startTime', width: 160 },
          { label: '关联算法', prop: 'aiType', width: 150, overHidden: true, tooltip: true },
          {
            label: '工单类型',
            prop: 'type',
            width: 130,
            width: 110,
            overHidden: true,
            tooltip: true,
            type: 'select',
@@ -970,8 +975,8 @@
            width: 152,
            overHidden: true,
          },
          { label: '创建人', prop: 'creator', width: 100 },
          { label: '处理人', prop: 'handler', width: 100 },
          { label: '创建人', prop: 'creator', width: 70 },
          { label: '处理人', prop: 'handler', width: 70 },
          {
            slot: true,
            hide: false,
@@ -1131,7 +1136,7 @@
        this.$nextTick(() => {
          this.isShowInfo = true;
        });
        console.log('orderNumber', orderNumber);
      }
    }
@@ -1260,14 +1265,16 @@
        { label: '工单内容', value: this.currentDetail.content },
      ];
      // 过滤掉值为 '/' 的列
      const filteredFields = fields.filter(field => field.value !== '/');
      // 将字段分成两列
      const formattedFields = [];
      for (let i = 0; i < fields.length; i += 2) {
      for (let i = 0; i < filteredFields.length; i += 2) {
        formattedFields.push({
          label1: fields[i]?.label || '',
          value1: fields[i]?.value || '暂无数据',
          label2: fields[i + 1]?.label || '',
          value2: fields[i + 1]?.value || '暂无数据',
          label1: filteredFields[i]?.label || '',
          value1: filteredFields[i]?.value || (filteredFields[i]?.label ? '暂无数据' : ''),
          label2: filteredFields[i + 1]?.label || '',
          value2: filteredFields[i + 1]?.value || (filteredFields[i + 1]?.label ? '暂无数据' : ''),
        });
      }
      return formattedFields;
@@ -1391,7 +1398,7 @@
        const subAreaCode = areaCode ? areaCode.substring(0, 6) : '';
        const adcodeObj = getAdcodeObj(geoJson, 'adcode', subAreaCode);
        console.log('区域代码:', subAreaCode);
       // console.log('区域代码:', subAreaCode);
        // 直接从返回对象中获取正确的路径
        const center = adcodeObj?.payload?.objects?.collection?.geometries?.[0]?.properties?.center;
@@ -1477,7 +1484,8 @@
    async fetchDropdownData() {
      try {
        const response = await getTicketInfo();
        const { dept_data, event_type, ai_type } = response.data.data;
        const { dept_data, event_type, ai_type,info } = response.data.data;
        this.departments = dept_data.map(item => ({
          label: item.dept_name,
@@ -1496,16 +1504,19 @@
        const columnType = this.findObject(this.option.column, 'type');
        columnType.dicData = this.types;
        this.allAlgorithms  = info
        // console.log('工单类型',this.types);
        // console.log('关联算法',this.allAlgorithms );
        // 确保算法数据的映射一致
        this.algorithms =
          ai_type?.map(item => ({
            dict_key: item.dict_key,
            dict_value: item.dict_value,
            // 同时添加 label 和 value 以兼容两处使用
            label: item.dict_value,
            value: item.dict_key,
          })) || [];
        // this.algorithms =
        //   ai_type?.map(item => ({
        //     dict_key: item.dict_key,
        //     dict_value: item.dict_value,
        //     // 同时添加 label 和 value 以兼容两处使用
        //     label: item.dict_value,
        //     value: item.dict_key,
        //   })) || [];
        // 构建用户ID和名称的映射关系
        this.userNameToIdMap = {};
@@ -1517,6 +1528,33 @@
      } catch (error) {
        this.$message.error('加载下拉框数据失败');
      }
    },
    // 工单类型变化时触发
    handleTypeChange(typeValue) {
      if (!typeValue) {
        // 未选择类型时清空算法列表
        this.algorithms = [];
        return;
      }
      const matchedCategory = this.allAlgorithms.find(
        category => category.dict_key === typeValue
      );
      if (!matchedCategory || !matchedCategory.algorithms || matchedCategory.algorithms.length === 0) {
        // 无匹配的算法时清空
        this.algorithms = [];
        this.$message.info('该工单类型暂无关联算法');
        return;
      }
      this.algorithms = matchedCategory.algorithms.map(algo => ({
        label: algo.dict_value,
        value: algo.dict_key,
        dict_key: algo.dict_key,
        dict_value: algo.dict_value
      }));
    },
    async fetchTableData() {
@@ -1771,7 +1809,10 @@
        this.draftLoading = false;
      }
    },
    handleCancel (){
      this.resetForm();
       this.dialogVisible = false;
},
    handleLocationChange(val) {
      let locationValue = val.value;
      if (locationValue && locationValue.length >= 2) {
@@ -2432,8 +2473,7 @@
      // 获取工单类型值 - 从types中找到匹配的值
      const typeValue =
        this.types.find(t => t.label === row.type)?.value || row.work_order_type_dict_key;
        this.types.find(t => t.value === row.type)?.value || row.work_order_type_dict_key;
      // 获取处理人ID - 使用userNameToIdMap映射
      const handlerId = this.userNameToIdMap[row.handler] || row.handler;
src/views/wel/components/backlog.vue
@@ -49,7 +49,7 @@
</template>
<script setup>
import st7 from '@/assets/images/workbench/st7.svg'
import db1 from '@/assets/images/workbench/db1.svg'
import db2 from '@/assets/images/workbench/db2.svg'
import db3 from '@/assets/images/workbench/db3.svg'
@@ -59,8 +59,7 @@
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
const zfTotal = ref(0)
const sjTotal = ref(0)
const router = useRouter()
const store = useStore()
const permission = computed(() => store.getters.permission)
@@ -216,9 +215,12 @@
  if (todosCache[checked.value].length > 0) {
    todos.value = todosCache[checked.value]
  }
  const todosParams = {
    type:type,
    area_code: userInfo.value.detail.areaCode,
  }
  try {
    const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode)
    const res = await getOrderOrEventApi(todosParams)
    const data = res.data.data || []
    todos.value = data
    todosCache[checked.value] = data
yarn.lock
Diff too large