吉安感知网项目-前端
chenyao
2026-01-31 d60b1396df45de76c6bae6aa75c15602dd4c2570
Merge remote-tracking branch 'origin/master'
26 files modified
21 files deleted
4 files added
8724 ■■■■ changed files
applications/drone-command/src/components/map-container/common-cesium-map.vue 29 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/components/DevicePopup.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-container.vue 17 ●●●● patch | view | raw | blame | history
applications/drone-command/src/page/index/top/index.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/utils/cesium/createRouteLine.js 1361 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/api.txt 77 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/areaStatistics/index.vue 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue 89 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue 43 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue 122 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/index.vue 8 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue 155 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/CancelTaskDialog.vue 152 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/DeviceJobDetails.vue 806 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/DeviceJobDetailsMap.vue 353 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/FileTypePlay.vue 158 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/JobRelatedEvents.vue 392 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/SearchBox.vue 590 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskAlgorithmBusiness.vue 131 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskIntermediateContent/AddTask.vue 414 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue 382 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskTable.vue 252 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskTop/TaskEvent.vue 155 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskTop/TaskIndustry.vue 117 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskTop/TaskTime.vue 161 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskTop/TaskTop.vue 47 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskTop/TaskTotal.vue 157 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/executeNestDetails.vue 225 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/const/drc.js 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/jobinfo.vue 277 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/jobserver.vue 213 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/jobstatistics.vue 14 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/deploy.vue 160 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/follow.vue 199 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/manager.vue 369 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/model.vue 548 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue 120 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue 30 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue 57 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue 6 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue 46 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue 5 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js 3 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue 90 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue 5 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue 80 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js 3 ●●●● patch | view | raw | blame | history
packages/constants/package.json 8 ●●●● patch | view | raw | blame | history
pnpm-lock.yaml 60 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/common-cesium-map.vue
@@ -3,6 +3,8 @@
</template>
<script setup>
import * as Cesium from 'cesium'
import { onBeforeUnmount, onMounted, watch } from 'vue'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import { loadJaAdminBoundary, removeJaAdminBoundary } from '@/utils/cesium/adminBoundary'
@@ -172,11 +174,36 @@
    })
}
const zoomToAdminBoundary = async () => {
    if (!viewer) return
    const resolveSources = () => {
        if (adminBoundarySources) return adminBoundarySources
        const boundarySource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.boundary)?.[0] ?? null
        const lineSource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.line)?.[0] ?? null
        const labelSource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.label)?.[0] ?? null
        if (boundarySource || lineSource || labelSource) {
            adminBoundarySources = { boundarySource, lineSource, labelSource }
        }
        return adminBoundarySources
    }
    let sources = resolveSources()
    if (!sources?.boundarySource) {
        sources = await loadJaAdminBoundary(viewer, { zoomTo: false })
        adminBoundarySources = sources
    }
    const target = sources?.boundarySource
    if (!target) return
    await viewer.flyTo(target, {
        duration: 0,
        offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-75), 0),
    })
}
const getMap = () => ({ viewer, publicCesium: viewInstance })
const getViewer = () => viewer
const getPublicCesium = () => viewInstance
defineExpose({ getMap, getViewer, getPublicCesium, setAdminBoundaryVisible })
defineExpose({ getMap, getViewer, getPublicCesium, setAdminBoundaryVisible, zoomToAdminBoundary })
</script>
<style scoped lang="scss">
applications/drone-command/src/components/map-container/components/DevicePopup.vue
@@ -151,9 +151,9 @@
    return `${val}台`
})
const popupRangeText = computed(() => {
    const val = props.device?.effectiveRangeKm
    const val = Number(props.device?.effectiveRangeKm).toFixed(0) || 0
    if (val == null) return '-'
    return `${val}KM`
    return `${val}m`
})
</script>
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -50,7 +50,9 @@
import { buildEllipsePositions } from '@/utils/cesium/shapeTools'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
import { fwAreaDivideListApi } from '@/views/areaManage/partition/partitionApi'
import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
import { fwDefenseSceneManageListApi } from '@/views/areaManage/sceneManage/sceneManageApi'
import { cockpitAggregationApi } from '@/api/dataCockpit'
import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
@@ -60,6 +62,7 @@
import jaGeojsonRaw from '@/assets/geojson/ja.geojson?raw'
import DevicePopup from './components/DevicePopup.vue'
import DronePopup from './components/DronePopup.vue'
import dayjs from 'dayjs'
import {
    createDroneTrackMaterial,
    createRadialGradientMaterial,
@@ -117,6 +120,7 @@
let droneTrackTickHandler = null
let droneTrackStartTime = null
let droneTrackRuntime = []
let mapReadyHandled = false
let favoritePulseRafId = null
let favoritePulseStartAt = 0
let favoritePulseElapsed = 0
@@ -968,7 +972,10 @@
const loadPartitions = async () => {
    if (!viewer) return
    try {
        const res = await fwAreaDivideListApi()
        const res = await fwAreaDivideListApi({
            isSetSceneManage: 1,
            flyTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
        })
        await renderPartitions(res?.data?.data ?? [])
    } catch (error) {
        await renderPartitions([])
@@ -1129,7 +1136,9 @@
const loadCommandPosts = async () => {
    try {
        const res = await fwDefenseSceneListApi()
        const res = await fwDefenseSceneManageListApi({
            time: dayjs().format('YYYY-MM-DD HH:mm:ss')
        })
        renderCommandPosts(res?.data?.data ?? [])
    } catch (error) {
        renderCommandPosts([])
@@ -1201,6 +1210,8 @@
}
const handleMapReady = ({ viewer: mapViewer, publicCesium: mapPublic }) => {
    if (mapReadyHandled) return
    mapReadyHandled = true
    viewer = mapViewer
    publicCesium = mapPublic
    ensureCockpitPrimitiveLayer()
applications/drone-command/src/page/index/top/index.vue
@@ -100,8 +100,8 @@
      })
    },
    jumpMH () {
      const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL
      window.location.href = `${adminUrl}#/gatewayPage`
      // const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL
      // window.location.href = `${adminUrl}#/gatewayPage`
    },
  },
}
applications/drone-command/src/utils/cesium/createRouteLine.js
File was deleted
applications/drone-command/src/views/api.txt
@@ -1,9 +1,9 @@
## 列表-不带分页
## 列表
**接口地址**:`/area/fwAreaDivide/list`
**接口地址**:`/area/fwDefenseSceneManage/list`
**请求方式**:`GET`
@@ -15,7 +15,7 @@
**响应数据类型**:`*/*`
**接口描述**:<p>传入filterSelected或sceneId或areaTypeKeys或isSetSceneManage或flyTime</p>
**接口描述**:<p>传入time</p>
@@ -27,11 +27,7 @@
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|areaTypeKeys|区域类型key集合(逗号分隔)|query|false|string||
|filterSelected|是否过滤已被选择的数据(1过滤 0不过滤)|query|false|integer(int32)||
|flyTime|时间筛选(yyyy-MM-dd HH:mm:ss)|query|false|string(date-time)||
|isSetSceneManage|是否按场景管理过滤(1是 0或空否)|query|false|integer(int32)||
|sceneId|场景id|query|false|integer(int64)||
|time|时间筛选(yyyy-MM-dd HH:mm:ss)|query|false|string(date-time)||
**响应状态**:
@@ -39,7 +35,7 @@
| 状态码 | 说明 | schema |
| -------- | -------- | ----- | 
|200|OK|R«List«FwAreaDivideVO»»|
|200|OK|R«List«FwDefenseSceneManageVO»»|
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
@@ -51,28 +47,18 @@
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- | 
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|array|FwAreaDivideVO|
|data|承载数据|array|FwDefenseSceneManageVO|
|&emsp;&emsp;areaCode|区域编码|string||
|&emsp;&emsp;areaName|区域名称|string||
|&emsp;&emsp;areaTypeKeys|区域面的类型集合逗号隔开|string||
|&emsp;&emsp;controlLevel|管控级别:日常/重点/严密|string||
|&emsp;&emsp;deviceIds|关联设备ID,逗号分隔|string||
|&emsp;&emsp;flyDateEnd|可飞行日期时间-结束|string(date-time)||
|&emsp;&emsp;flyDateStart|可飞行日期时间-开始|string(date-time)||
|&emsp;&emsp;fwAreaDivideExtList|面数据|array|FwAreaDivideExtVO|
|&emsp;&emsp;&emsp;&emsp;areaCode|区域编码|string||
|&emsp;&emsp;&emsp;&emsp;areaDivideId|关联主表区域划分ID|integer||
|&emsp;&emsp;&emsp;&emsp;areaTypeKey|区域类型数字字典key|string||
|&emsp;&emsp;&emsp;&emsp;areaTypeValue|区域类型数字字典value|string||
|&emsp;&emsp;&emsp;&emsp;geomJson|几何JSON|string||
|&emsp;&emsp;&emsp;&emsp;id|主键id|integer||
|&emsp;&emsp;id|主键id|integer(int64)||
|&emsp;&emsp;policeStationContactPerson|派出所联系人|string||
|&emsp;&emsp;policeStationContactPhone|派出所联系电话|string||
|&emsp;&emsp;policeStationId|关联派出所id|string||
|&emsp;&emsp;policeStationName|派出所名称|string||
|&emsp;&emsp;responseMechanism|响应机制内容|string||
|&emsp;&emsp;triggerCondition|触发条件|string||
|&emsp;&emsp;defenseLeader|防控负责人|string||
|&emsp;&emsp;defenseSceneId|关联场景配置ID|integer(int64)||
|&emsp;&emsp;defenseSceneName|场景配置名称|string||
|&emsp;&emsp;effectiveDateEnd|有效时间-结束|string(date-time)||
|&emsp;&emsp;effectiveDateStart|有效时间-开始|string(date-time)||
|&emsp;&emsp;id||integer(int64)||
|&emsp;&emsp;latitude|指挥点纬度|number(double)||
|&emsp;&emsp;leaderPhone|防控负责人电话|string||
|&emsp;&emsp;longitude|指挥点经度|number(double)||
|&emsp;&emsp;sceneName|场景名称|string||
|msg|返回消息|string||
|success|是否成功|boolean||
@@ -84,29 +70,16 @@
    "data": [
        {
            "areaCode": "",
            "areaName": "",
            "areaTypeKeys": "",
            "controlLevel": "",
            "deviceIds": "",
            "flyDateEnd": "",
            "flyDateStart": "",
            "fwAreaDivideExtList": [
                {
                    "areaCode": "",
                    "areaDivideId": 0,
                    "areaTypeKey": "",
                    "areaTypeValue": "",
                    "geomJson": "",
                    "id": 0
                }
            ],
            "defenseLeader": "",
            "defenseSceneId": 0,
            "defenseSceneName": "",
            "effectiveDateEnd": "",
            "effectiveDateStart": "",
            "id": 0,
            "policeStationContactPerson": "",
            "policeStationContactPhone": "",
            "policeStationId": "",
            "policeStationName": "",
            "responseMechanism": "",
            "triggerCondition": ""
            "latitude": 0,
            "leaderPhone": "",
            "longitude": 0,
            "sceneName": ""
        }
    ],
    "msg": "",
applications/drone-command/src/views/areaManage/areaStatistics/index.vue
@@ -6,9 +6,9 @@
                    clearable @clear="handleSearch" />
            </el-form-item>
            <el-form-item label="区域类型" prop="areaType">
            <el-form-item label="区域类型" prop="areaTypeKeys">
                <el-select class="command-select" popper-class="command-select-popper"
                    v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
                    v-model="searchParams.areaTypeKeys" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
@@ -33,9 +33,9 @@
                <el-table class="command-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="areaName" show-overflow-tooltip label="区域名称" />
                    <el-table-column prop="areaType" show-overflow-tooltip label="区域类型">
                    <el-table-column prop="areaTypeKeys" show-overflow-tooltip label="区域类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.areaType, dictObj.areaType) }}
                            {{ getDictLabel(row.areaTypeKeys, dictObj.areaType) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="sceneName" show-overflow-tooltip label="关联场景" />
@@ -79,7 +79,7 @@
// 初始化查询参数
const initSearchParams = () => ({
    areaName: '', // 区域名称
    areaType: '', // 区域类型
    areaTypeKeys: '', // 区域类型
    sceneType: '', // 场景类型
    current: 1, // 当前页
    size: 10, // 每页大小
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -266,7 +266,7 @@
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const dialogMode = ref() // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const route = useRoute()
@@ -301,6 +301,9 @@
let shapeDisplaySource
const deviceRangePrimitiveMap = new Map()
const activeToolMode = ref('')
const suppressDeviceFly = ref(false)
let lastSelectedDeviceIds = []
let deviceRangeRenderToken = 0
const rules = {
    areaName: fieldRules(true, 50),
@@ -482,6 +485,8 @@
async function renderDeviceRanges (rows) {
    if (!viewer) return
    deviceRangeRenderToken += 1
    const renderToken = deviceRangeRenderToken
    const selectedRows = Array.isArray(rows) ? rows : []
    const nextIds = new Set(selectedRows.map(row => String(row?.id)))
    for (const [id, primitive] of deviceRangePrimitiveMap.entries()) {
@@ -504,9 +509,11 @@
            return { row, primitive }
        })
    )
    if (renderToken !== deviceRangeRenderToken) return
    primitives.forEach(({ row, primitive }) => {
        if (!primitive) return
        const id = String(row?.id)
        if (!nextIds.has(id)) return
        deviceRangePrimitiveMap.set(id, primitive)
        viewer.scene.primitives.add(primitive)
    })
@@ -668,6 +675,69 @@
                material: style.outline,
            },
        })
    })
}
function pushRadiusBoundingPoints (positions, center, radius) {
    if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return
    const resolved = resolveLngLatPoint(center) || {}
    const lng = Number(resolved.lng ?? resolved.longitude)
    const lat = Number(resolved.lat ?? resolved.latitude)
    if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
    const baseLat = Cesium.Math.toRadians(lat)
    const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius
    const deltaLat = radius / earthRadius
    const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001))
    const points = [
        { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat },
        { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat },
        { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) },
        { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) },
    ]
    points.forEach(point => {
        positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0))
    })
}
function collectShapeBoundingPositions () {
    const positions = []
    if (!viewer || !shapeList.value.length) return positions
    shapeList.value.forEach(shape => {
        let points = getShapeDisplayPoints(shape)
        if (!Array.isArray(points) || !points.length) {
            points = resolveControlPoints(shape?.meta, shape?.drawType)
        }
        if (Array.isArray(points) && points.length) {
            points.forEach(point => {
                const resolved = resolveLngLatPoint(point)
                const normalized = normalizeShapePoint(resolved)
                if (!normalized) return
                positions.push(Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height))
            })
        }
        if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) {
            const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item)))
            pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius)
        }
        if (shape?.drawType === 'ellipse' && shape?.meta?.center) {
            const major = Number(shape?.meta?.semiMajor)
            const minor = Number(shape?.meta?.semiMinor)
            const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null
            pushRadiusBoundingPoints(positions, shape.meta.center, radius)
        }
    })
    return positions
}
function fitViewToShapeList () {
    if (!viewer || !shapeList.value.length) return
    const positions = collectShapeBoundingPositions()
    if (positions.length < 2) return
    const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
    const range = Math.max(boundingSphere.radius * 2.4, 200)
    viewer.camera.flyToBoundingSphere(boundingSphere, {
        duration: 0,
        offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range),
    })
}
@@ -970,6 +1040,7 @@
    if (!viewer) return
    if (shapeList.value.length) {
        renderShapeList()
        fitViewToShapeList()
        return
    }
    if (!formData.value?.geom) return
@@ -1010,9 +1081,15 @@
    const ids = rows.map(item => item.id)
    formData.value.deviceIds = ids.join(',')
    renderDeviceRanges(rows)
    const target = rows[rows.length - 1]
    if (target) {
        void flyToDevice(target)
    if (suppressDeviceFly.value) {
        lastSelectedDeviceIds = [...ids]
        return
    }
    const prevSet = new Set(lastSelectedDeviceIds)
    const addedRow = rows.find(row => !prevSet.has(row.id))
    lastSelectedDeviceIds = [...ids]
    if (addedRow) {
        void flyToDevice(addedRow)
    }
}
@@ -1074,7 +1151,11 @@
        viewPolygon()
    }
    await nextTick()
    suppressDeviceFly.value = true
    syncDeviceSelection()
    lastSelectedDeviceIds = selectedDeviceRows.value.map(row => row.id)
    await nextTick()
    suppressDeviceFly.value = false
    if (!readonly.value) {
        renderDeviceRanges(selectedDeviceRows.value)
    }
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -111,7 +111,7 @@
</template>
<script setup>
import { computed, nextTick, ref } from 'vue'
import { computed, nextTick, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { fwPoliceStationDetailApi, fwPoliceStationSubmitApi } from './precinctInfoApi'
import { fieldRules } from '@ztzf/utils'
@@ -121,6 +121,7 @@
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
import positionIcon from '@/assets/images/areaManage/positionIcon.png'
import { MapTooltip } from '@/utils/cesium/shapeTools/Tooltip'
const initForm = () => ({
    address: '', // 位置
@@ -185,6 +186,10 @@
let viewer
let redPointEntity
let leftClickBound = false
let mouseMoveBound = false
let mapTooltip
let publicCesium
const tooltipEventKey = 'precinct-info-point-tip'
const labelParams = {
    font: '18px',
    fillColor: Cesium.Color.WHITE, // 文字颜色:白色
@@ -271,12 +276,38 @@
    await nextTick()
    const map = inlineMapRef.value?.getMap()
    viewer = map?.viewer || null
    publicCesium = map?.publicCesium || null
    if (viewer && !leftClickBound) {
        map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
        leftClickBound = true
    }
    if (viewer && !readonly.value) {
        ensurePointTooltip()
    }
    if (formData.value.longitude != null && formData.value.latitude != null) {
        setMapPoint(formData.value.longitude, formData.value.latitude)
    }
}
function ensurePointTooltip() {
    if (!viewer || !publicCesium || mouseMoveBound) return
    mapTooltip ||= new MapTooltip(viewer, { offset: { x: 16, y: 24 } })
    publicCesium.addMouseHandler?.(
        null,
        movement => {
            if (!visible.value || readonly.value) return
            mapTooltip?.show('点击地图选择派出所位置', movement.endPosition)
        },
        tooltipEventKey
    )
    mouseMoveBound = true
}
function clearPointTooltip() {
    mapTooltip?.hide()
    if (publicCesium && mouseMoveBound) {
        publicCesium.removeMouseHandler?.(tooltipEventKey)
        mouseMoveBound = false
    }
}
@@ -296,6 +327,7 @@
    formData.value = dialogMode.value === 'add' ? initForm() : row
    redPointEntity = null
    leftClickBound = false
    clearPointTooltip()
    if (dialogMode.value !== 'add') {
        await loadDetail()
    }
@@ -305,6 +337,15 @@
    }, 500)
}
watch(
    () => visible.value,
    val => {
        if (!val) {
            clearPointTooltip()
        }
    }
)
defineExpose({ open })
</script>
<style scoped lang="scss">
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
@@ -10,6 +10,7 @@
                    :terrain="true"
                    :layer-mode="4"
                    :boundary="false"
                    :zoom-to-boundary="dialogMode === 'add'"
                />
            </div>
            <div class="right-container">
@@ -23,7 +24,7 @@
                    <div class="detail-title">场景详情</div>
                    <el-row>
                        <el-col :span="24">
                            <div class="label">场景配置名称</div>
                            <div class="label">配置名称</div>
                            <div class="val">{{ formData.sceneName }}</div>
                        </el-col>
                        <el-col :span="24">
@@ -60,7 +61,7 @@
                    :disabled="readonly"
                    label-width="96px"
                >
                    <el-form-item label="场景配置名称" prop="sceneName">
                    <el-form-item label="配置名称" prop="sceneName">
                        <el-input
                            class="command-input"
                            v-model="formData.sceneName"
@@ -160,12 +161,13 @@
import * as Cesium from 'cesium'
import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi'
import { buildEllipsePositions } from '@/utils/cesium/shapeTools'
import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
const initForm = () => ({
    sceneName: '', // 场景配置名称
    sceneName: '', // 配置名称
    sceneType: '', // 场景类型
    deviceMode: '', // 设备模式
    areaDivideIds: '', // 关联区域ID
@@ -176,7 +178,7 @@
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const dialogMode = ref() // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
@@ -272,6 +274,28 @@
    return { lng: Number(lng), lat: Number(lat), height }
}
function resolveLngLatPoint(point) {
    if (!point) return null
    const lng = point?.lng ?? point?.longitude
    const lat = point?.lat ?? point?.latitude
    if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) {
        return {
            lng: Number(lng),
            lat: Number(lat),
            height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0,
        }
    }
    if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) {
        return null
    }
    const val = cartesian3Convert(point, viewer)
    return {
        lng: val.longitude,
        lat: val.latitude,
        height: Number.isFinite(val.height) ? val.height : 0,
    }
}
function buildShapePositions(points = []) {
    const normalized = points.map(normalizeShapePoint).filter(Boolean)
    return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height))
@@ -286,6 +310,20 @@
function getAreaTypeStyle(areaType) {
    return AREA_TYPE_STYLE_MAP?.[String(areaType)] || DEFAULT_AREA_STYLE
}
function resolveControlPoints(meta, drawType) {
    if (!meta) return []
    if (Array.isArray(meta.controlPoints) && meta.controlPoints.length) {
        return meta.controlPoints.map(resolveLngLatPoint).filter(Boolean)
    }
    if (drawType === 'buffer') {
        return [resolveLngLatPoint(meta.center)].filter(Boolean)
    }
    if (drawType === 'ellipse') {
        return [meta.center, meta.majorPoint, meta.minorPoint].map(resolveLngLatPoint).filter(Boolean)
    }
    return []
}
function parseGeomJson(geomJson) {
@@ -400,6 +438,74 @@
    })
}
function pushRadiusBoundingPoints(positions, center, radius) {
    if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return
    const resolved = resolveLngLatPoint(center) || {}
    const lng = Number(resolved.lng ?? resolved.longitude)
    const lat = Number(resolved.lat ?? resolved.latitude)
    if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
    const baseLat = Cesium.Math.toRadians(lat)
    const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius
    const deltaLat = radius / earthRadius
    const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001))
    const points = [
        { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat },
        { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat },
        { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) },
        { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) },
    ]
    points.forEach(point => {
        positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0))
    })
}
function collectSelectedAreaPositions() {
    const positions = []
    if (!viewer || !selectedAreaRows.value.length) return positions
    selectedAreaRows.value.forEach(area => {
        const shapes = resolveAreaShapes(area)
        shapes.forEach(shape => {
            let points = getShapeDisplayPoints(shape)
            if (!Array.isArray(points) || !points.length) {
                points = resolveControlPoints(shape?.meta, shape?.drawType)
            }
            if (Array.isArray(points) && points.length) {
                points.forEach(point => {
                    const resolved = resolveLngLatPoint(point)
                    const normalized = normalizeShapePoint(resolved)
                    if (!normalized) return
                    positions.push(
                        Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height)
                    )
                })
            }
            if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) {
                const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item)))
                pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius)
            }
            if (shape?.drawType === 'ellipse' && shape?.meta?.center) {
                const major = Number(shape?.meta?.semiMajor)
                const minor = Number(shape?.meta?.semiMinor)
                const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null
                pushRadiusBoundingPoints(positions, shape.meta.center, radius)
            }
        })
    })
    return positions
}
function fitViewToSelectedAreas() {
    if (!viewer || !selectedAreaRows.value.length) return
    const positions = collectSelectedAreaPositions()
    if (positions.length < 2) return
    const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
    const range = Math.max(boundingSphere.radius * 2.4, 200)
    viewer.camera.flyToBoundingSphere(boundingSphere, {
        duration: 0,
        offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range),
    })
}
watch(() => selectedAreaRows.value, renderSelectedAreas)
watch(
    () => visible.value,
@@ -468,11 +574,17 @@
    await nextTick()
    initMap()
    await getAreaList()
    if (dialogMode.value !== 'add') {
    if (dialogMode.value === 'add') {
        console.log(111)
        mapRef.value?.zoomToAdminBoundary?.()
    } else {
        await loadDetail()
    }
    await nextTick()
    await syncAreaSelection()
    await nextTick()
    fitViewToSelectedAreas()
}
defineExpose({
    open,
applications/drone-command/src/views/areaManage/sceneConfig/index.vue
@@ -1,7 +1,7 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="场景配置名称" prop="sceneName">
            <el-form-item label="配置名称" prop="sceneName">
                <el-input
                    class="command-input"
                    v-model="searchParams.sceneName"
@@ -36,7 +36,7 @@
        </el-form>
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增配置</el-button>
            <el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
@@ -46,7 +46,7 @@
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="sceneName" show-overflow-tooltip label="场景配置名称" />
                    <el-table-column prop="sceneName" show-overflow-tooltip label="配置名称" />
                    <el-table-column prop="sceneType" show-overflow-tooltip label="场景类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.sceneType, dictObj.CommandSceneType) }}
@@ -93,7 +93,7 @@
import { useRoute } from 'vue-router'
const initSearchParams = () => ({
    sceneName: '', // 场景配置名称
    sceneName: '', // 配置名称
    sceneType: '', // 场景类型
    current: 1, // 当前页
    size: 10, // 每页大小
applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
@@ -10,6 +10,7 @@
                    :terrain="true"
                    :layer-mode="4"
                    :boundary="false"
                    :zoom-to-boundary="dialogMode === 'add'"
                />
            </div>
            <div class="right-container">
@@ -110,7 +111,16 @@
                        />
                    </el-form-item>
                    <el-form-item label="指挥点位置" prop="longitude">
                        <div class="val">{{ formatLocation(formData) }}</div>
                        <el-input
                            class="command-input"
                            :model-value="commandPointText"
                            disabled
                            placeholder="请选择指挥点位置"
                        >
                            <template #suffix>
                                <span class="suffix-action">地图选点</span>
                            </template>
                        </el-input>
                    </el-form-item>
                </el-form>
                <div class="footer">
@@ -148,7 +158,9 @@
import { useRoute } from 'vue-router'
import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi'
import { buildEllipsePositions } from '@/utils/cesium/shapeTools'
import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
import { MapTooltip } from '@/utils/cesium/shapeTools/Tooltip'
// 初始化表单数据
const initForm = () => ({
@@ -162,11 +174,19 @@
    defenseSceneId: '', // 关联场景配置ID
})
const commandPointText = computed(() => {
  // 你也可以改成 `${lng}, ${lat}`
  if (!formData.value.longitude) {
    return ''
  }
  return `${formData.value.longitude}, ${formData.value.latitude}`;
});
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const dialogMode = ref() // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
@@ -179,6 +199,10 @@
let redPointEntity
let areaDisplaySource
let leftClickBound = false
let mouseMoveBound = false
let mapTooltip
let publicCesium
const tooltipEventKey = 'scene-manage-point-tip'
// 表单验证规则
const rules = {
@@ -263,6 +287,28 @@
    return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height))
}
function resolveLngLatPoint(point) {
    if (!point) return null
    const lng = point?.lng ?? point?.longitude
    const lat = point?.lat ?? point?.latitude
    if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) {
        return {
            lng: Number(lng),
            lat: Number(lat),
            height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0,
        }
    }
    if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) {
        return null
    }
    const val = cartesian3Convert(point, viewer)
    return {
        lng: val.longitude,
        lat: val.latitude,
        height: Number.isFinite(val.height) ? val.height : 0,
    }
}
function getShapeDisplayPoints(shape) {
    if (Array.isArray(shape?.displayPoints) && shape.displayPoints.length) {
        return shape.displayPoints
@@ -315,6 +361,71 @@
            }
        })
        .filter(Boolean)
}
function pushRadiusBoundingPoints(positions, center, radius) {
    if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return
    const resolved = resolveLngLatPoint(center) || {}
    const lng = Number(resolved.lng ?? resolved.longitude)
    const lat = Number(resolved.lat ?? resolved.latitude)
    if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
    const baseLat = Cesium.Math.toRadians(lat)
    const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius
    const deltaLat = radius / earthRadius
    const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001))
    const points = [
        { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat },
        { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat },
        { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) },
        { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) },
    ]
    points.forEach(point => {
        positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0))
    })
}
function collectSceneAreaPositions() {
    const positions = []
    if (!viewer || !areaList.value.length) return positions
    areaList.value.forEach(area => {
        const shapes = resolveAreaShapes(area)
        shapes.forEach(shape => {
            const points = getShapeDisplayPoints(shape)
            if (Array.isArray(points) && points.length) {
                points.forEach(point => {
                    const resolved = resolveLngLatPoint(point)
                    const normalized = normalizeShapePoint(resolved)
                    if (!normalized) return
                    positions.push(
                        Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height)
                    )
                })
            }
            if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) {
                const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item)))
                pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius)
            }
            if (shape?.drawType === 'ellipse' && shape?.meta?.center) {
                const major = Number(shape?.meta?.semiMajor)
                const minor = Number(shape?.meta?.semiMinor)
                const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null
                pushRadiusBoundingPoints(positions, shape.meta.center, radius)
            }
        })
    })
    return positions
}
function fitViewToSceneAreas() {
    if (!viewer || !areaList.value.length) return
    const positions = collectSceneAreaPositions()
    if (positions.length < 2) return
    const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
    const range = Math.max(boundingSphere.radius * 2.4, 200)
    viewer.camera.flyToBoundingSphere(boundingSphere, {
        duration: 0,
        offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range),
    })
}
function renderSceneAreas() {
@@ -449,13 +560,36 @@
// 初始化地图实例
function initMap() {
    if (viewer) return
    const map = mapRef.value?.getMap()
    if (!map?.viewer) return
    viewer = map.viewer
    viewer ||= map.viewer
    publicCesium ||= map.publicCesium
    if (!readonly.value && !leftClickBound) {
        map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
        leftClickBound = true
    }
    if (!readonly.value) ensurePointTooltip()
}
function ensurePointTooltip() {
    if (!viewer || !publicCesium || mouseMoveBound) return
    mapTooltip ||= new MapTooltip(viewer, { offset: { x: 28, y: 24 } })
    publicCesium.addMouseHandler?.(
        null,
        movement => {
            if (!visible.value || readonly.value) return
            mapTooltip?.show('点击地图选择指挥点位置', movement.endPosition)
        },
        tooltipEventKey
    )
    mouseMoveBound = true
}
function clearPointTooltip() {
    mapTooltip?.hide()
    if (publicCesium && mouseMoveBound) {
        publicCesium.removeMouseHandler?.(tooltipEventKey)
        mouseMoveBound = false
    }
}
@@ -520,13 +654,19 @@
    redPointEntity = null
    await nextTick()
    initMap()
    if (dialogMode.value === 'view') {
        clearPointTooltip()
    }
    await getSceneConfigList()
    if (dialogMode.value !== 'add') {
    if (dialogMode.value === 'add') {
        mapRef.value?.zoomToAdminBoundary?.()
    } else {
        await loadDetail()
    }
    await nextTick()
    setMapPoint(formData.value.longitude, formData.value.latitude)
    await loadAreaList(formData.value.defenseSceneId)
    fitViewToSceneAreas()
}
onMounted(() => {
@@ -543,7 +683,10 @@
watch(
    () => visible.value,
    val => {
        if (!val) clearAreaDisplay()
        if (!val) {
            clearAreaDisplay()
            clearPointTooltip()
        }
    }
)
applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js
@@ -9,6 +9,15 @@
    })
}
// 查list
export const fwDefenseSceneManageListApi = params => {
    return request({
        url: `/drone-fw/area/fwDefenseSceneManage/list`,
        method: 'get',
        params,
    })
}
// 获取场景管理详情
export const fwDefenseSceneManageDetailApi = params => {
    return request({
applications/drone-command/src/views/job/components/CancelTaskDialog.vue
File was deleted
applications/drone-command/src/views/job/components/DeviceJobDetails.vue
File was deleted
applications/drone-command/src/views/job/components/DeviceJobDetailsMap.vue
File was deleted
applications/drone-command/src/views/job/components/FileTypePlay.vue
File was deleted
applications/drone-command/src/views/job/components/JobRelatedEvents.vue
File was deleted
applications/drone-command/src/views/job/components/SearchBox.vue
File was deleted
applications/drone-command/src/views/job/components/TaskAlgorithmBusiness.vue
File was deleted
applications/drone-command/src/views/job/components/TaskIntermediateContent/AddTask.vue
File was deleted
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
File was deleted
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskTable.vue
File was deleted
applications/drone-command/src/views/job/components/TaskTop/TaskEvent.vue
File was deleted
applications/drone-command/src/views/job/components/TaskTop/TaskIndustry.vue
File was deleted
applications/drone-command/src/views/job/components/TaskTop/TaskTime.vue
File was deleted
applications/drone-command/src/views/job/components/TaskTop/TaskTop.vue
File was deleted
applications/drone-command/src/views/job/components/TaskTop/TaskTotal.vue
File was deleted
applications/drone-command/src/views/job/components/executeNestDetails.vue
File was deleted
applications/drone-command/src/views/job/const/drc.js
File was deleted
applications/drone-command/src/views/job/jobinfo.vue
File was deleted
applications/drone-command/src/views/job/jobserver.vue
File was deleted
applications/drone-command/src/views/job/jobstatistics.vue
File was deleted
applications/task-work-order/src/views/flow/deploy.vue
New file
@@ -0,0 +1,160 @@
<template>
  <basic-container>
    <avue-form
      ref="form"
      :option="option"
      v-model="form"
      :upload-before="uploadBefore"
      :upload-after="uploadAfter"
    />
  </basic-container>
</template>
<script>
import { deployUpload } from '@/api/flow/flow';
import { flowCategory } from '@/utils/flow';
export default {
  data() {
    return {
      form: {
        flowCategory: '',
        tenantId: '',
        flowFile: [],
        file: {},
      },
      option: {
        labelWidth: 120,
        menuBtn: false,
        column: [
          {
            label: '流程类型',
            prop: 'flowCategory',
            type: 'select',
            dicUrl: `/blade-system/dict/dictionary?code=flow`,
            props: {
              label: 'dictValue',
              value: 'dictKey',
            },
            row: true,
            span: 12,
            dataType: 'number',
            rules: [
              {
                required: true,
                message: '请选择流程类型',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '流程模式',
            prop: 'flowMode',
            type: 'radio',
            dicData: [
              {
                label: '通用流程',
                value: 1,
              },
              {
                label: '定制流程',
                value: 2,
              },
            ],
            value: 1,
            row: true,
            span: 12,
            rules: [
              {
                required: true,
                message: '请选择流程模式',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '所属租户',
            prop: 'tenantId',
            type: 'tree',
            multiple: true,
            dicUrl: '/blade-system/tenant/select',
            props: {
              label: 'tenantName',
              value: 'tenantId',
            },
            display: false,
            row: true,
            span: 12,
            rules: [
              {
                required: true,
                message: '请选择所属租户',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '附件上传',
            prop: 'flowFile',
            type: 'upload',
            loadText: '附件上传中,请稍等',
            span: 24,
            propsHttp: {
              res: 'data',
            },
            tip: '请上传 bpmn20.xml 标准格式文件',
            action: '/blade-flow/manager/check-upload',
          },
        ],
      },
    };
  },
  watch: {
    'form.flowMode'() {
      this.$refs.form.option.column.filter(item => {
        if (item.prop === 'tenantId') {
          item.display = this.form.flowMode === 2;
        }
      });
    },
  },
  methods: {
    uploadBefore(file, done) {
      this.$message.success('部署提交');
      this.file = file;
      done();
    },
    uploadAfter(res, done, loading) {
      if (!this.form.flowCategory) {
        this.$message.warning('清先选择流程类型');
        loading();
        return false;
      }
      if (this.form.flowMode === 2 && !this.form.tenantId) {
        this.$message.warning('清先选择对应租户');
        loading();
        return false;
      }
      if (res.success) {
        deployUpload(
          flowCategory(this.form.flowCategory),
          this.form.tenantId ? this.form.tenantId.join(',') : '',
          [this.file]
        ).then(res => {
          const data = res.data;
          if (data.success) {
            done();
          } else {
            this.$message.error(data.msg);
            loading();
          }
        });
      } else {
        this.$message.warning('请上传 bpmn20.xml 标准格式文件');
        loading();
        return false;
      }
    },
  },
};
</script>
applications/task-work-order/src/views/flow/follow.vue
New file
@@ -0,0 +1,199 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      v-model:page="page"
      :permission="permissionList"
      @row-del="rowDel"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
      <template #suspensionState="{ row }">
        <el-tag>{{ row.suspensionState === 1 ? '激活' : '挂起' }}</el-tag>
      </template>
    </avue-crud>
    <el-dialog title="流程删除" append-to-body v-model="followBox" width="20%">
      <el-form :model="form" ref="form" label-width="80px">
        <el-form-item label="删除理由">
          <el-input v-model="deleteReason" placeholder="请输入删除理由" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="followBox = false">关 闭</el-button>
          <el-button type="primary" @click="handleDelete">确 定</el-button>
        </span>
      </template>
    </el-dialog>
  </basic-container>
</template>
<script>
import { mapGetters } from 'vuex';
import { followList, deleteProcessInstance } from '@/api/flow/flow';
export default {
  data() {
    return {
      form: {},
      selectionId: '',
      processInstanceId: '',
      selectionList: [],
      query: {},
      loading: true,
      page: {
        pageSize: 10,
        currentPage: 1,
        total: 0,
      },
      followBox: false,
      deleteReason: '',
      option: {
        tip: false,
        searchShow: true,
        searchMenuSpan: 6,
        border: true,
        index: true,
        selection: true,
        editBtn: false,
        addBtn: false,
        viewBtn: false,
        dialogWidth: 900,
        menuWidth: 100,
        dialogClickModal: false,
        column: [
          {
            label: '执行id',
            prop: 'executionId',
            search: true,
            width: 320,
          },
          {
            label: '流程key',
            prop: 'processDefinitionKey',
            search: true,
          },
          {
            label: '实例id',
            prop: 'processInstanceId',
            search: true,
            width: 320,
          },
          {
            label: '状态',
            prop: 'suspensionState',
            slot: true,
            width: 80,
          },
          {
            label: '发起人',
            prop: 'startUser',
            width: 100,
          },
          {
            label: '开始时间',
            prop: 'startTime',
            width: 170,
          },
        ],
      },
      data: [],
    };
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
      return {
        delBtn: this.validData(this.permission.flow_follow_delete, false),
      };
    },
    ids() {
      let ids = [];
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
    },
  },
  methods: {
    rowDel(row) {
      this.followBox = true;
      this.selectionId = row.id;
      this.processInstanceId = row.processInstanceId;
    },
    handleDelete() {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return deleteProcessInstance({
            deleteReason: this.deleteReason,
            processInstanceId: this.processInstanceId,
          });
        })
        .then(() => {
          this.onLoad(this.page);
          this.followBox = false;
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    },
    selectionChange(list) {
      this.selectionList = list;
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    },
    onLoad(page, params = {}) {
      this.loading = true;
      followList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
    },
  },
};
</script>
<style>
.none-border {
  border: 0;
  background-color: transparent !important;
}
</style>
applications/task-work-order/src/views/flow/manager.vue
New file
@@ -0,0 +1,369 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      v-model:page="page"
      :permission="permissionList"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
      <template #menu-left>
        <el-radio-group v-model="mode">
          <el-radio-button label="1">通用流程</el-radio-button>
          <el-radio-button label="2">定制流程</el-radio-button>
        </el-radio-group>
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          icon="el-icon-refresh"
          v-if="permission.flow_manager_state"
          @click.stop="handleState(scope.row, scope.index)"
          >变更状态
        </el-button>
        <el-button
          type="primary"
          text
          icon="el-icon-search"
          v-if="permission.flow_manager_image"
          @click.stop="handleImage(scope.row, scope.index)"
          >流程图
        </el-button>
        <el-button
          type="primary"
          text
          icon="el-icon-delete"
          v-if="permission.flow_manager_remove"
          @click.stop="handleSlotDelete(scope.row, scope.index)"
          >删除
        </el-button>
      </template>
      <template #tenantId="{ row }">
        <el-tag>{{ row.tenantId === '' ? '通用' : row.tenantId }}</el-tag>
      </template>
      <template #version="{ row }">
        <el-tag>v{{ row.version }}</el-tag>
      </template>
      <template #suspensionState="{ row }">
        <el-tag>{{ row.suspensionState === 1 ? '激活' : '挂起' }}</el-tag>
      </template>
      <template #category="{ row }">
        <el-tag>{{ row.categoryName }}</el-tag>
      </template>
    </avue-crud>
    <flow-design
      v-if="this.website.design.designMode"
      is-dialog
      v-model:is-display="flowBox"
      :process-definition-id="processDefinitionId"
    ></flow-design>
    <el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
      <iframe
        :src="flowUrl"
        width="100%"
        height="700"
        title="流程图"
        frameBorder="no"
        border="0"
        marginWidth="0"
        marginHeight="0"
        scrolling="no"
        allowTransparency="yes"
      >
      </iframe>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="flowBox = false">关 闭</el-button>
        </span>
      </template>
    </el-dialog>
    <el-dialog title="流程变更" append-to-body v-model="stateBox" width="20%">
      <el-form :model="form" ref="form" label-width="80px">
        <el-form-item label="流程状态">
          <el-select v-model="flowState" placeholder="请选择" value="">
            <el-option
              v-for="item in stateOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="stateBox = false">关 闭</el-button>
          <el-button type="primary" @click="handleDoState">确 定</el-button>
        </span>
      </template>
    </el-dialog>
  </basic-container>
</template>
<script>
import { mapGetters } from 'vuex';
import { managerList, changeState, deleteDeployment } from '@/api/flow/flow';
import { flowCategory } from '@/utils/flow';
export default {
  data() {
    return {
      form: {},
      mode: '1',
      selectionId: '',
      selectionList: [],
      query: {},
      loading: true,
      page: {
        pageSize: 10,
        currentPage: 1,
        total: 0,
      },
      processDefinitionId: '',
      flowBox: false,
      flowUrl: '',
      stateBox: false,
      flowState: '',
      stateOptions: [
        {
          value: 'active',
          label: '激活',
        },
        {
          value: 'suspend',
          label: '挂起',
        },
      ],
      option: {
        tip: false,
        searchShow: true,
        searchMenuSpan: 6,
        border: true,
        index: true,
        selection: true,
        editBtn: false,
        addBtn: false,
        viewBtn: false,
        delBtn: false,
        dialogWidth: 900,
        menuWidth: 250,
        dialogClickModal: false,
        column: [
          {
            label: '租户编号',
            prop: 'tenantId',
            slot: true,
            width: 120,
          },
          {
            label: '流程主键',
            prop: 'id',
          },
          {
            label: '流程标识',
            prop: 'key',
            search: true,
            width: 150,
          },
          {
            label: '流程名称',
            prop: 'name',
            width: 150,
          },
          {
            label: '流程分类',
            type: 'select',
            row: true,
            dicUrl: '/blade-system/dict/dictionary?code=flow',
            props: {
              label: 'dictValue',
              value: 'dictKey',
            },
            dataType: 'number',
            slot: true,
            prop: 'category',
            search: true,
            width: 100,
          },
          {
            label: '流程版本',
            prop: 'version',
            slot: true,
            width: 85,
          },
          {
            label: '状态',
            prop: 'suspensionState',
            slot: true,
            width: 80,
          },
          {
            label: '部署时间',
            prop: 'deploymentTime',
            width: 170,
          },
        ],
      },
      data: [],
    };
  },
  watch: {
    mode() {
      this.onLoad(this.page);
    },
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
      return {
        delBtn: this.validData(this.permission.flow_manager_remove, false),
      };
    },
    ids() {
      let ids = [];
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
    },
    deploymentIds() {
      let ids = [];
      this.selectionList.forEach(ele => {
        ids.push(ele.deploymentId);
      });
      return ids.join(',');
    },
  },
  methods: {
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    },
    selectionChange(list) {
      this.selectionList = list;
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    },
    handleDelete() {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return deleteDeployment(this.deploymentIds);
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
          this.onLoad(this.page);
        });
    },
    handleSlotDelete(row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return deleteDeployment(row.deploymentId);
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
          this.onLoad(this.page);
        });
    },
    handleState(row) {
      this.stateBox = true;
      this.selectionId = row.id;
    },
    handleDoState() {
      if (!this.flowState) {
        this.$message({
          type: 'warn',
          message: '请先选择流程状态!',
        });
        return;
      }
      changeState({ processId: this.selectionId, state: this.flowState }).then(res => {
        const data = res.data;
        if (data.success) {
          this.$message({
            type: 'success',
            message: data.msg,
          });
          this.stateBox = false;
          this.onLoad(this.page);
        } else {
          this.$message({
            type: 'warn',
            message: data.msg,
          });
        }
      });
    },
    handleImage(row) {
      if (this.website.design.designMode) {
        this.processDefinitionId = row.id;
      } else {
        this.flowUrl = `/blade-flow/process/resource-view?processDefinitionId=${row.id}`;
      }
      this.flowBox = true;
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    },
    onLoad(page, params = {}) {
      const values = {
        ...params,
        category: params.category ? flowCategory(params.category) : null,
        mode: this.mode,
      };
      this.loading = true;
      managerList(page.currentPage, page.pageSize, Object.assign(values, this.query)).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
    },
  },
};
</script>
applications/task-work-order/src/views/flow/model.vue
New file
@@ -0,0 +1,548 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      v-model:page="page"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
      <template #menu-left>
        <el-button
          type="primary"
          size="default"
          icon="el-icon-circle-plus"
          v-if="permission.flow_model_create"
          plain
          @click="handleCreate"
          >创 建
        </el-button>
        <el-button
          type="danger"
          size="default"
          icon="el-icon-delete"
          v-if="permission.flow_model_delete"
          plain
          @click="handleDelete"
          >删 除
        </el-button>
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          size="default"
          icon="el-icon-setting"
          v-if="permission.flow_model_update"
          @click.stop="handleUpdate(scope.row, scope.index)"
          >配置
        </el-button>
        <el-button
          type="primary"
          text
          size="default"
          icon="el-icon-upload"
          v-if="permission.flow_model_deploy"
          @click.stop="handleDeploy(scope.row, scope.index)"
          >部署
        </el-button>
        <el-button
          type="primary"
          text
          size="default"
          icon="el-icon-delete"
          v-if="permission.flow_model_delete"
          @click.stop="handleSlotDelete(scope.row, scope.index)"
          >删除
        </el-button>
      </template>
      <template #version="{ row }">
        <el-tag>v{{ row.version }}</el-tag>
      </template>
    </avue-crud>
    <el-dialog
      v-if="this.website.design.designMode"
      title="流程配置"
      append-to-body
      destroy-on-close
      v-model="flowBox"
      :close-on-press-escape="false"
      :fullscreen="true"
      :before-close="handleNutflowClose"
      class="nf-dialog"
    >
      <nf-design-base
        v-if="nutflowOption.step === 1"
        class="animated fadeIn"
        style="height: calc(100vh - 108px)"
        ref="nf-design"
        :options="nutflowOption.step1"
      ></nf-design-base>
      <nf-design-base
        v-if="nutflowOption.step === 2"
        class="animated fadeIn"
        style="height: calc(100vh - 108px)"
        ref="nf-design-view"
        :options="nutflowOption.step2"
      ></nf-design-base>
      <template #footer>
        <span class="avue-dialog__footer">
          <el-button @click="handleNutflowClose(() => {}, true)">取 消</el-button>
          <el-button v-if="nutflowOption.step === 1" type="success" @click="handleStep(1)"
            >下 一 步</el-button
          >
          <el-button v-if="nutflowOption.step === 2" type="success" @click="handleStep(-1)"
            >上 一 步</el-button
          >
          <el-button v-if="nutflowOption.step === 2" type="primary" @click="handleSubmitModel"
            >确 定</el-button
          >
        </span>
      </template>
    </el-dialog>
    <el-dialog v-else title="流程配置" append-to-body v-model="flowBox" :fullscreen="true">
      <iframe
        :src="flowUrl"
        width="100%"
        height="700"
        title="流程设计器"
        frameBorder="no"
        border="0"
        marginWidth="0"
        marginHeight="0"
        scrolling="no"
        allowTransparency="yes"
      >
      </iframe>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="flowBox = false">取 消</el-button>
          <el-button type="primary" @click="handleRefresh">确 定</el-button>
        </span>
      </template>
    </el-dialog>
    <el-dialog title="流程部署" append-to-body v-model="deployBox" width="20%">
      <avue-form ref="form" :option="optionDeploy" v-model="form" @submit="handleSubmit" />
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="deployBox = false">取 消</el-button>
          <el-button type="primary" @click="handleDoDeploy" :loading="deployLoading"
            >确 定</el-button
          >
        </span>
      </template>
    </el-dialog>
  </basic-container>
</template>
<script>
import { mapGetters } from 'vuex';
import { modelList, removeModel, deployModel, submitModel, detail } from '@/api/flow/flow';
import { flowCategory } from '@/utils/flow';
export default {
  data() {
    return {
      form: {},
      optionDeploy: {
        menuBtn: false,
        column: [
          {
            label: '流程类型',
            type: 'select',
            dicUrl: '/blade-system/dict/dictionary?code=flow',
            props: {
              label: 'dictValue',
              value: 'dictKey',
            },
            dataType: 'number',
            slot: true,
            prop: 'categoryValue',
            search: true,
            span: 24,
            rules: [
              {
                required: true,
                message: '请选择流程类型',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '流程模式',
            prop: 'flowMode',
            type: 'radio',
            dicData: [
              {
                label: '通用流程',
                value: 1,
              },
              {
                label: '定制流程',
                value: 2,
              },
            ],
            value: 1,
            span: 24,
            rules: [
              {
                required: true,
                message: '请选择流程模式',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '所属租户',
            prop: 'tenantId',
            type: 'tree',
            multiple: true,
            dicUrl: '/blade-system/tenant/select',
            props: {
              label: 'tenantName',
              value: 'tenantId',
            },
            display: false,
            span: 24,
            rules: [
              {
                required: true,
                message: '请选择所属租户',
                trigger: 'blur',
              },
            ],
          },
        ],
      },
      selectionId: '',
      selectionList: [],
      query: {},
      loading: true,
      deployLoading: false,
      page: {
        pageSize: 10,
        currentPage: 1,
        total: 0,
      },
      deployBox: false,
      flowBox: false,
      flowUrl: '',
      option: {
        tip: false,
        searchShow: true,
        searchMenuSpan: 6,
        border: true,
        index: true,
        selection: true,
        editBtn: false,
        addBtn: false,
        viewBtn: false,
        delBtn: false,
        dialogWidth: 900,
        menuWidth: 220,
        dialogClickModal: false,
        size: 'default',
        searchSize: 'default',
        column: [
          {
            label: '模型主键',
            prop: 'id',
          },
          {
            label: '模型标识',
            prop: 'modelKey',
            search: true,
            overHidden: true,
          },
          {
            label: '模型名称',
            prop: 'name',
            search: true,
            overHidden: true,
          },
          {
            label: '流程版本',
            prop: 'version',
            slot: true,
            overHidden: true,
          },
          {
            label: '创建时间',
            prop: 'created',
            overHidden: true,
          },
          {
            label: '更新时间',
            prop: 'lastUpdated',
            overHidden: true,
          },
        ],
      },
      data: [],
      nutflowOption: {
        process: {},
        step: 1,
        step1: {
          toolbar: [
            'open',
            'create',
            'fit',
            'zoom-in',
            'zoom-out',
            'undo',
            'redo',
            'import',
            'preview',
          ],
        },
        step2: {
          mode: 'view',
          simulation: true,
          minimap: true,
        },
      },
    };
  },
  watch: {
    'form.flowMode'() {
      this.$refs.form.option.column.filter(item => {
        if (item.prop === 'tenantId') {
          item.display = this.form.flowMode === 2;
        }
      });
    },
  },
  computed: {
    ...mapGetters(['permission']),
    ids() {
      let ids = [];
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
    },
  },
  methods: {
    handleSubmitModel() {
      const registry = this.$refs['nf-design-view'].getElementRegistry().getAll();
      const { businessObject } = registry[0];
      const { id, name, documentation } = businessObject;
      const description = documentation && documentation.length > 0 ? documentation[0].text : null;
      const params = {
        ...this.nutflowOption.process,
        modelKey: id,
        name,
        description,
        modelEditorXml: this.nutflowOption.process.xml,
      };
      submitModel(params).then(() => {
        this.$message.success('操作成功');
        this.handleNutflowClose();
        this.onLoad(this.page, this.query);
      });
    },
    handleStep(step) {
      if (step === 1) {
        // 下一步
        this.$refs['nf-design'].getData('xml').then(data => {
          this.nutflowOption.step1.xml = data;
          this.nutflowOption.step2.xml = data;
          this.nutflowOption.process.xml = data;
          this.nutflowOption.step = 2;
        });
      } else this.nutflowOption.step = 1;
    },
    handleNutflowClose(done, flag) {
      const initOption = {
        process: {},
        step: 1,
        step1: {
          toolbar: [
            'open',
            'create',
            'fit',
            'zoom-in',
            'zoom-out',
            'undo',
            'redo',
            'import',
            'preview',
          ],
        },
        step2: {
          mode: 'view',
          simulation: true,
          minimap: true,
        },
      };
      if (done || flag) {
        this.$confirm('确定要关闭吗?关闭未保存的修改都会丢失。', '警告', {
          type: 'warning',
        })
          .then(() => {
            this.nutflowOption = initOption;
            if (typeof done == 'function') done();
            this.flowBox = false;
          })
          .catch(() => {});
      } else {
        this.nutflowOption = initOption;
        this.flowBox = false;
      }
    },
    handleSubmit(form, done) {
      this.deployLoading = true;
      deployModel({
        modelId: this.selectionId,
        category: flowCategory(form.categoryValue),
        tenantIds: form.tenantId ? form.tenantId.join(',') : '',
      }).then(res => {
        const data = res.data;
        if (data.success) {
          this.$message({
            type: 'success',
            message: data.msg,
          });
          done();
          this.$refs.form.resetForm();
          this.deployBox = false;
          this.deployLoading = false;
        } else {
          done();
          this.deployLoading = false;
          this.$message({
            type: 'warn',
            message: data.msg,
          });
        }
      });
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    },
    selectionChange(list) {
      this.selectionList = list;
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    },
    handleDelete() {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return removeModel(this.ids);
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
          this.onLoad(this.page);
        });
    },
    handleCreate() {
      if (!this.website.design.designMode) {
        this.flowUrl = `${this.website.design.designUrl}/index.html`;
      }
      this.flowBox = true;
    },
    handleRefresh() {
      this.flowBox = false;
      this.onLoad(this.page);
    },
    handleUpdate(row) {
      if (this.website.design.designMode) {
        detail({ id: row.id }).then(res => {
          const data = res.data.data;
          const { modelEditorXml } = data;
          this.nutflowOption.step1.xml = modelEditorXml;
          this.nutflowOption.process = data;
        });
      } else {
        this.flowUrl = `${this.website.design.designUrl}/index.html#/editor/${row.id}`;
      }
      this.flowBox = true;
    },
    handleDeploy(row) {
      this.deployBox = true;
      this.selectionId = row.id;
    },
    handleDoDeploy() {
      this.$refs.form.submit();
    },
    handleSlotDelete(row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return removeModel(row.id);
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
          this.onLoad(this.page);
        });
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    },
    onLoad(page, params = {}) {
      this.loading = true;
      modelList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
    },
  },
};
</script>
<style lang="scss">
.nf-dialog {
  .el-dialog__body {
    padding: 5px;
  }
  .avue-dialog__footer {
    text-align: center;
  }
}
</style>
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
@@ -28,30 +28,40 @@
            </div>
            <!-- 工单信息展示 -->
            <div class="work-order-info" v-if="selectedWorkOrder">
            <div class="work-order-info">
                <el-row class="detail-row-view">
                    <el-col :span="12">
                        <div class="info-item">
                            <span class="label">工单类型</span>
                            <span class="val">{{ getDictLabel(selectedWorkOrder.workOrderType, dictObj.workOrderType) }}</span>
                            <span class="val" v-if="selectedWorkOrder">
                                {{ getDictLabel(selectedWorkOrder.workOrderType, dictObj.workOrderType) }}
                            </span>
                            <span class="val grayText" v-else>选择工单后自动带出</span>
                        </div>
                    </el-col>
                    <el-col :span="12">
                        <div class="info-item">
                            <span class="label">执行时间范围</span>
                            <span class="val">{{ selectedWorkOrder.executeStartTime }} ~ {{ selectedWorkOrder.executeEndTime }}</span>
                            <span class="val" v-if="selectedWorkOrder">
                                {{ selectedWorkOrder.executeStartTime }} ~ {{ selectedWorkOrder.executeEndTime }}
                            </span>
                            <span class="val grayText" v-else>选择工单后自动带出</span>
                        </div>
                    </el-col>
                    <el-col :span="12">
                        <div class="info-item">
                            <span class="label">设备需求</span>
                            <span class="val">{{ getDictLabel(selectedWorkOrder.deviceLoadDemand, dictObj.deviceLoadDemand) }}</span>
                            <span class="val" v-if="selectedWorkOrder">
                                {{ getDictLabel(selectedWorkOrder.deviceLoadDemand, dictObj.deviceLoadDemand) }}
                            </span>
                            <span class="val grayText" v-else>选择工单后自动带出</span>
                        </div>
                    </el-col>
                    <el-col :span="12">
                        <div class="info-item">
                            <span class="label">工单备注</span>
                            <span class="val">{{ selectedWorkOrder.remark }}</span>
                            <span class="val" v-if="selectedWorkOrder">{{ selectedWorkOrder.remark }}</span>
                            <span class="val grayText" v-else>选择工单后自动带出</span>
                        </div>
                    </el-col>
                </el-row>
@@ -65,7 +75,11 @@
                        <el-table-column width="50" label="">
                            <template v-slot="{ $index }">{{ $index + 1 }}</template>
                        </el-table-column>
                        <el-table-column prop="patrolTaskName" label="巡查任务名称" min-width="120">
                        <el-table-column prop="patrolTaskName" min-width="120">
                            <template #header>
                                <span class="required-star">*</span>
                                巡查任务名称
                            </template>
                            <template v-slot="{ row }">
                                <el-input
                                    class="gd-input"
@@ -75,14 +89,17 @@
                                />
                            </template>
                        </el-table-column>
                        <el-table-column prop="patrolTaskType" label="巡查任务类型" min-width="140">
                        <el-table-column prop="patrolTaskType" min-width="140">
                            <template #header>
                                <span class="required-star">*</span>
                                巡查任务类型
                            </template>
                            <template v-slot="{ row, $index }">
                                <el-select
                                    class="gd-select"
                                    popper-class="gd-select-popper"
                                    v-model="row.patrolTaskType"
                                    placeholder="请选择"
                                    :disabled="dialogReadonly"
                                    @change="handlePatrolTaskTypeChange($index, $event)"
                                >
                                    <el-option
@@ -94,7 +111,11 @@
                                </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column prop="executeTime" label="任务执行时间" min-width="180">
                        <el-table-column prop="executeTime" min-width="180">
                            <template #header>
                                <span class="required-star">*</span>
                                任务执行时间
                            </template>
                            <template v-slot="{ row }">
                                <el-date-picker
                                    class="gd-date-picker"
@@ -107,7 +128,11 @@
                                />
                            </template>
                        </el-table-column>
                        <el-table-column prop="patrolRouteUrl" label="巡查任务航线" min-width="120">
                        <el-table-column prop="patrolRouteUrl" min-width="120">
                            <template #header>
                                <span class="required-star">*</span>
                                巡查任务航线
                            </template>
                            <template v-slot="{ row }">
                                <el-select
                                    class="gd-select"
@@ -120,9 +145,14 @@
                                </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column prop="recommendFlyerName" label="推荐飞手" min-width="120">
                        <el-table-column prop="recommendFlyerName" min-width="120">
                            <template #header>
                                <span class="required-star">*</span>
                                推荐飞手
                            </template>
                            <template v-slot="{ row, $index }">
                                <el-select
                                    no-data-text="暂无推荐飞手"
                                    class="gd-select"
                                    popper-class="gd-select-popper"
                                    v-model="row.recommendFlyerName"
@@ -139,7 +169,11 @@
                                </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column prop="deviceId" label="选择设备" min-width="120">
                        <el-table-column prop="deviceId" min-width="120">
                            <template #header>
                                <span class="required-star">*</span>
                                选择设备
                            </template>
                            <template v-slot="{ row }">
                                <el-select
                                    class="gd-select"
@@ -213,9 +247,7 @@
    deviceId: null,
    taskDesc: '',
})
// todo 时间线
const dictObj = inject('dictObj')
const emit = defineEmits(['success'])
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
@@ -234,12 +266,21 @@
const routeOptions = inject('routeOptions')
// 子任务列表
const taskList = ref([initTaskRow()])
const taskList = ref([initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow()])
// 处理工单选择变化
function handleWorkOrderChange(val) {
    if (val) {
        selectedWorkOrder.value = workOrderList.value.find(item => item.id === val)
        // 将工单类型赋值给所有子任务行
        const workOrderType = selectedWorkOrder.value?.workOrderType
        if (workOrderType) {
            taskList.value.forEach((task, index) => {
                task.patrolTaskType = workOrderType
                // 获取对应的飞手列表
                getFlyerList(index, workOrderType)
            })
        }
    } else {
        selectedWorkOrder.value = null
    }
@@ -247,7 +288,17 @@
// 添加任务行
function addTaskRow() {
    taskList.value.push(initTaskRow())
    const newTask = initTaskRow()
    // 如果已选择工单,自动填充工单类型
    if (selectedWorkOrder.value?.workOrderType) {
        newTask.patrolTaskType = selectedWorkOrder.value.workOrderType
    }
    const newIndex = taskList.value.length
    taskList.value.push(newTask)
    // 获取对应的飞手列表
    if (newTask.patrolTaskType) {
        getFlyerList(newIndex, newTask.patrolTaskType)
    }
}
// 删除任务行
@@ -270,7 +321,7 @@
// 获取工单列表
async function getWorkOrderList() {
    const res = await gdWorkOrderPageApi({ size: 999, workOrderStatus: dialogMode.value ==='add' ?  '20' : '' })
    const res = await gdWorkOrderPageApi({ size: 999, workOrderStatus: dialogMode.value === 'add' ? '20' : '' })
    workOrderList.value = res?.data?.data?.records ?? []
}
@@ -308,19 +359,29 @@
    }
)
// 判断一行是否已填写(至少填写了任务名称)
function isTaskRowFilled(task) {
    return !!(task.patrolTaskName || task.executeTime || task.patrolRouteUrl || task.recommendFlyerName || task.deviceId)
}
// 提交新增
async function handleSubmit() {
    if (!selectedWorkOrderId.value) {
        ElMessage.warning('请选择关联工单')
        return
    }
    if (!taskList.value.length) {
        ElMessage.warning('请添加至少一个子任务')
    // 过滤出已填写的任务行
    const filledTasks = taskList.value.filter(task => isTaskRowFilled(task))
    if (!filledTasks.length) {
        ElMessage.warning('请至少填写一个子任务')
        return
    }
    // 校验子任务
    // 只校验已填写的子任务
    for (let i = 0; i < taskList.value.length; i++) {
        const task = taskList.value[i]
        // 跳过未填写的行
        if (!isTaskRowFilled(task)) continue
        // 已填写的行需要校验完整性
        if (!task.patrolTaskName) {
            ElMessage.warning(`第${i + 1}行: 请输入巡查任务名称`)
            return
@@ -348,8 +409,9 @@
    }
    submitting.value = true
    try {
        const preID = taskList.value[0].id
        const dataList = taskList.value.map(item => ({
        const preID = filledTasks[0]?.id
        // 只提交已填写的任务行
        const dataList = filledTasks.map(item => ({
            ...item,
            workOrderId: selectedWorkOrderId.value,
            id: undefined,
@@ -371,7 +433,7 @@
    if (dialogMode.value === 'add') {
        selectedWorkOrderId.value = null
        selectedWorkOrder.value = null
        taskList.value = [initTaskRow()]
        taskList.value = [initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow()]
        flyerListMap.value = {}
    } else {
        // 编辑/查看模式 - 加载已有数据
@@ -386,9 +448,7 @@
    }
}
onMounted(() => {
})
onMounted(() => {})
defineExpose({ open })
</script>
@@ -401,4 +461,12 @@
    display: flex;
    align-items: center;
}
.required-star {
    color: #f56c6c;
    margin-right: 4px;
}
.grayText {
    color: #9f9f9f !important;
}
</style>
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
@@ -31,7 +31,7 @@
            <div class="detail-right">
                <!-- 查看模式 -->
                <div class="view-container" v-if="dialogReadonly">
                    <div class="detail-title">工单详情</div>
                    <div class="detail-title">巡查任务详情</div>
                    <el-row class="detail-row-view">
                        <el-col :span="12">
                            <div class="label">巡查任务名称</div>
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue
@@ -1,8 +1,8 @@
<template>
    <basic-container>
        <el-tabs class="gd-tabs" v-model="activeName" @tab-click="tabsClick">
            <el-tab-pane label="全部 " name="all"></el-tab-pane>
            <el-tab-pane label="我的巡查任务" name="my"></el-tab-pane>
            <el-tab-pane :label="`全部(${allTotal})`" name="all"></el-tab-pane>
            <el-tab-pane :label="`我的巡查任务(${myTotal})`" name="my"></el-tab-pane>
        </el-tabs>
        <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
            <el-form-item label="任务名称" prop="patrolTaskName">
@@ -72,7 +72,9 @@
        </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>
            <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">
@@ -98,7 +100,7 @@
                    <el-table-column prop="taskDesc" show-overflow-tooltip label="巡查任务描述" />
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link  type="primary"  @click="viewDiaLogView(row)">查看</el-link>
                            <el-link type="primary" @click="viewDiaLogView(row)">查看</el-link>
                        </template>
                    </el-table-column>
                </el-table>
@@ -154,6 +156,8 @@
const searchParams = ref(initSearchParams()) // 查询参数
const dateRange = ref([]) // 执行时间范围
const total = ref(0) // 总条数
const allTotal = ref(0) // 全部tab总条数
const myTotal = ref(0) // 我的巡查任务tab总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
@@ -258,7 +262,25 @@
    })
}
// 获取两个tab的总条数
async function getTabTotals() {
    const allRes = await gdPatrolTaskPageApi({
        current: 1,
        size: 1,
        createUser: '',
    })
    allTotal.value = allRes?.data?.data?.total ?? 0
    const myRes = await gdPatrolTaskPageApi({
        current: 1,
        size: 1,
        createUser: store.state.user.userInfo.user_id,
    })
    myTotal.value = myRes?.data?.data?.total ?? 0
}
onMounted(() => {
    getTabTotals()
    getList()
    getDictList()
    getAirList()
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -57,6 +57,7 @@
                    </el-row>
                    <div class="detail-title" :style="{ marginTop: pxToRem(20) }">已选设备</div>
                    <el-table class="setHeight gd-dialog-table" ref="deviceTableRef" :data="deviceList" row-key="id">
                        <el-table-column type="index" label="序号" />
                        <el-table-column prop="nickname" label="设备名称" />
                        <el-table-column prop="deviceName" label="设备型号" />
                        <el-table-column prop="devicePayload" label="设备负载" />
@@ -90,6 +91,7 @@
                    :disabled="dialogReadonly"
                    label-width="90px"
                >
                    <div class="detail-title">工单详情</div>
                    <el-row>
                        <el-col :span="12">
                            <el-form-item label="工单名称" prop="workOrderName">
@@ -161,7 +163,10 @@
                        </el-col>
                    </el-row>
                </el-form>
                <div class="detail-title" :style="{ marginTop: pxToRem(20) }">工单执行范围</div>
                <div class="detail-title" :style="{ marginTop: pxToRem(20) }">
                    <span class="required-star" v-if="!dialogReadonly">*</span>
                    工单执行范围
                </div>
                <CommonCesiumMap
                    ref="mapRef"
                    class="gd-cesium"
@@ -175,12 +180,17 @@
                />
            </div>
            <div class="rightBox" v-if="!dialogReadonly">
                <div class="detail-title">推荐设备</div>
                <div class="detail-title">
                    <span class="required-star" v-if="!dialogReadonly">*</span>
                    推荐设备
                </div>
                <el-table
                    class="separateTable"
                    :empty-text="(formData.deviceLoadDemand && pointList.length) ? '暂无数据' : '请先选择设备需求并在地图上绘制工单范围'"
                    :empty-text="
                        formData.deviceLoadDemand && pointList.length ? '暂无数据' : '请先选择设备需求并在地图上绘制工单范围'
                    "
                    ref="deviceTableRef"
                    :data="(formData.deviceLoadDemand && pointList.length) ? deviceList : []"
                    :data="formData.deviceLoadDemand && pointList.length ? deviceList : []"
                    row-key="id"
                    @selection-change="handleDeviceSelectionChange"
                >
@@ -193,12 +203,8 @@
        </div>
        <template #footer>
            <el-button
                v-if="['11', '21', '23','31', '60'].includes(gdStatus)"
                @click="viewDescription"
                color="#F2F3F5"
            >
                {{ gdStatusObj[gdStatus].reason }}
            <el-button v-if="['11', '21', '23', '31', '60'].includes(gdStatus)" @click="viewDescription" color="#F2F3F5">
                {{ gdStatusObj[gdStatus]?.reason }}
            </el-button>
            <template v-if="!suddenlyEdit">
@@ -212,7 +218,7 @@
                <el-button
                    color="#4C34FF"
                    @click="requestModification"
                    v-if="['20'].includes(gdStatus) && permission.order_applyNegotiation"
                    v-if="['20'].includes(gdStatus) && permission.order_applyEdit"
                >
                    申请修改
                </el-button>
@@ -239,7 +245,7 @@
            </template>
            <template v-if="permission.order_controlCancel">
                <el-button v-if="gdStatus === '21'" @click="statusChange(6)" color="#F2F3F5">驳回</el-button>
                <el-button v-if="gdStatus === '21'" @click="statusChange(5)" color="#4C34FF">同意</el-button>
                <el-button v-if="gdStatus === '21'" @click="statusChange(5)" color="#4C34FF">通过</el-button>
            </template>
            <el-button v-if="gdStatus === '30' && permission.order_applyNegotiation" @click="addDescription" color="#4C34FF">
                协商修改
@@ -326,14 +332,15 @@
const hasPatrolTaskList = computed(() => ['30', '40', '50', '60'].includes(String(formData.value.workOrderStatus)))
const gdStatusObj = {
    '10': { reason: '驳回原因', operationType: '2' },
    '11': { reason: '驳回原因' },
    '20': { reason: '取消原因', operationType: '3' },
    '10': { reason: '拒单原因', operationType: '2', title: '拒绝接单' },
    '11': { reason: '拒单原因' },
    '20': { reason: '原因', operationType: '3',title:'申请取消' },
    '21': { reason: '取消原因' },
    '23': { reason: '取消原因' },
    '30': { reason: '协商修改原因', operationType: '9' },
    '31': { reason: '协商修改原因' },
    '30': { reason: '修改原因', operationType: '9',title:'协商修改' },
    '31': { reason: '修改原因' },
    '40': { reason: '' },
    '50': { reason: '情况说明', operationType: '11' },
    '50': { reason: '情况说明', operationType: '11',title:'结算' },
    '60': { reason: '情况说明' },
}
@@ -387,6 +394,7 @@
            row: formData.value,
            type: gdStatusObj[gdStatus.value].operationType,
            formLabel: gdStatusObj[gdStatus.value].reason,
            title: gdStatusObj[gdStatus.value]?.title || '新增'
        })
    })
}
@@ -636,14 +644,14 @@
</script>
<style lang="scss" scoped>
.separateTable{
.separateTable {
    height: 0;
    flex: 1;
    :deep(){
        .el-scrollbar__view{
    :deep() {
        .el-scrollbar__view {
            height: 100%;
        }
        .el-table__empty-text{
        .el-table__empty-text {
            line-height: normal;
        }
    }
@@ -701,4 +709,9 @@
    height: 200px;
    line-height: 200px;
}
.required-star {
    color: #f56c6c;
    margin-right: 4px;
}
</style>
applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue
@@ -2,7 +2,7 @@
    <el-dialog
        class="gd-dialog"
        v-model="visible"
        :title="titleEnum[dialogMode]"
        :title="dialogTitle"
        @closed="visible = false"
        destroy-on-close
        :close-on-click-modal="false"
@@ -98,12 +98,14 @@
}
let formLabelStr = ref('原因')
let dialogTitle = ref('新增')
// 操作类型:1接单,2拒接接单,3申请取消,4申请修改, 5同意取消 6不同意取消 7.同意修改 8.不同意修改 9.协商修改
async function open({ mode = 'add', row, formLabel, type } = {}) {
async function open({ mode = 'add', row, formLabel, type,title } = {}) {
    dialogMode.value = mode
    formLabelStr.value = formLabel
    dialogTitle.value =  dialogMode.value === 'add' ? title : '查看'
    formData.value = {
        workOrderId: row.id,
        operationType: type,
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue
@@ -1,8 +1,8 @@
<template>
    <basic-container>
        <el-tabs class="gd-tabs" v-model="activeName" @tab-click="tabsClick">
            <el-tab-pane label="全部 " name="all"></el-tab-pane>
            <el-tab-pane label="我的任务工单" name="my"></el-tab-pane>
            <el-tab-pane :label="`全部(${allTotal})`" name="all"></el-tab-pane>
            <el-tab-pane :label="`我的任务工单(${myTotal})`" name="my"></el-tab-pane>
        </el-tabs>
        <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
            <el-form-item label="工单名称" prop="workOrderName">
@@ -72,25 +72,9 @@
        </el-form>
        <div class="gd-table-toolbar">
            <el-button
                v-if="permission.order_release"
                :icon="Plus"
                color="#4C34FF"
                type="primary"
                @click="openForm('add')"
            >
            <el-button v-if="permission.order_release" :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">
                新增工单
            </el-button>
<!--            <el-button
                :icon="Delete"
                color="#4C34FF"
                :disabled="!selectedIds.length"
                @click="handleDelete()"
                v-if="permission.order_delete"
            >
                删除
            </el-button>-->
        </div>
        <div class="gd-table-container" v-loading="loading">
@@ -126,8 +110,6 @@
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link type="primary" @click="openFormChange(row)">查看</el-link>
                            <!--<el-link  type="primary"  @click="openForm('edit', row)">编辑</el-link>-->
<!--                            <el-link type="primary" @click="handleDelete(row)" v-if="permission.order_delete">删除</el-link>-->
                        </template>
                    </el-table-column>
                </el-table>
@@ -182,6 +164,8 @@
const searchParams = ref(initSearchParams()) // 查询参数
const dateRange = ref([]) // 执行时间范围
const total = ref(0) // 总条数
const allTotal = ref(0) // 全部tab总条数
const myTotal = ref(0) // 我的任务工单tab总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
@@ -281,7 +265,27 @@
    })
}
// 获取两个tab的总条数
async function getTabTotals() {
    // 获取全部tab的总数
    const allRes = await gdWorkOrderPageApi({
        current: 1,
        size: 1,
        createUser: '',
    })
    allTotal.value = allRes?.data?.data?.total ?? 0
    // 获取我的任务工单tab的总数
    const myRes = await gdWorkOrderPageApi({
        current: 1,
        size: 1,
        createUser: store.state.user.userInfo.user_id,
    })
    myTotal.value = myRes?.data?.data?.total ?? 0
}
onMounted(() => {
    getTabTotals()
    getList()
    getDictList()
})
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
@@ -245,6 +245,11 @@
// 懒加载获取区域节点数据
async function loadRegionNode(node, resolve) {
  try {
      // 限制只加载两级,当前节点 level >= 1 时不加载子节点
    if (node.level >= 2) {
      resolve([])
      return
    }
    // 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
    const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
    const parentCode = node.data?.id || '360800000000'
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
@@ -57,10 +57,11 @@
}
// 导出模板
export const agenciesExportTemplateApi = () => {
export const agenciesExportTemplateApi = params => {
  return request({
    url: `/blade-system/dept/export-template`,
    method: 'get',
    params,
    responseType: 'blob',
  })
}
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
@@ -47,13 +47,14 @@
    <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>
      <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-content gd-table-content-bg">
        <el-table class="gd-table" :data="list">
        <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
          <el-table-column type="selection" width="46" />
          <el-table-column label="序号" width="80">
            <template v-slot="{ $index }">
              {{ $index + 1 }}
@@ -98,8 +99,8 @@
      class="gd-dialog"
      append-to-body
      v-model="isShowImportView"
      title="导入机构"
      :width="pxToRem(600)"
      title="机构导入"
      :width="pxToRem(500)"
      :close-on-click-modal="false"
      :destroy-on-close="true"
      @close="handleImportClose"
@@ -111,14 +112,17 @@
            type="primary" 
            @click="handleExportTemplate"
          >
            点击导出
            下载模板
          </el-button>
        </el-form-item>
        <el-form-item label="上传文件" prop="file">
          {{ importFileName }}
          <el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onImportFileBefore">
            <el-button  type="primary">点击上传</el-button>
          </el-upload>
          <div class="upload-tip">
           {{ importFileName }}
          </div>
        </el-form-item>
      </el-form>
      <template #footer>
@@ -171,6 +175,7 @@
const dialogVisible = ref(false)
const regionOptions = ref([]) // 区域选项
const dictObj = ref({}) // 字典对象
const selectedFiles = ref([]) // 勾选的文件列表
// 树形选择器配置
const treeSelectProps = {
@@ -178,8 +183,6 @@
  label: 'name',
  children: 'children'
}
// 导入机构相关
const isShowImportView = ref(false)
const importFileName = ref('')
@@ -213,6 +216,12 @@
async function loadRegionNode(node, resolve) {
  try {
    // 限制只加载两级,当前节点 level >= 1 时不加载子节点
    if (node.level >= 2) {
      resolve([])
      return
    }
    // 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
    const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
    const res = await regionLazyTreeApi(params)
@@ -315,6 +324,11 @@
  }
}
// 勾选值设置
function handleSelectionChange(rows) {
  selectedFiles.value = rows
}
// 导出模板
async function handleExportTemplate() {
  templateExportLoading.value = true
@@ -338,9 +352,6 @@
// 上传文件前处理
function onImportFileBefore(file) {
  // 提示请先导出模板
  // ElMessage.warning('请先导出模板并按模板格式填写数据')
  // 检查文件类型
  const allowedTypes = ['.xlsx', '.xls']
  const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
@@ -348,21 +359,11 @@
    ElMessage.error('请上传Excel文件(.xlsx 或 .xls)')
    return false
  }
  let data = new FormData()
  data.append('file', file)
  agenciesImportApi(data, { isCovered: importParams.value.isCovered }).then(res => {
    if (res.data.code === 200) {
      ElMessage.success('导入成功')
      // 保存文件名
      importFileName.value = file.name
      importParams.value.file = file
      isShowImportView.value = false
      getList()
    } else {
      ElMessage.error(res.msg || '导入失败')
    }
  })
  // 保存文件名和文件对象
  importFileName.value = file.name
  importParams.value.file = file
  ElMessage.success('上传成功')
  return false // 阻止组件的默认上传行为
}
@@ -378,8 +379,19 @@
          ElMessage.error('请先上传文件')
          return
        }
        // 触发上传操作
        await onImportFileBefore(importParams.value.file)
        // 构建表单数据并调用导入接口
        let data = new FormData()
        data.append('file', importParams.value.file)
        const res = await agenciesImportApi(data, { isCovered: importParams.value.isCovered })
        if (res.data.code === 200) {
          ElMessage.success('导入成功')
          isShowImportView.value = false
          getList()
        } else {
          ElMessage.error(res.msg || '导入失败')
        }
      } finally {
        importSubmitting.value = false
      }
@@ -389,15 +401,26 @@
// 导出机构
async function handleExport() {
  // 检查是否有选中的数据
  if (selectedFiles.value.length === 0) {
    ElMessage.warning('请选择要导出的机构')
    return
  }
  exportLoading.value = true
  try {
    const res = await agenciesExportApi(searchParams.value)
    const params = {}
    params.deptIds = selectedFiles.value.map(i => i.id)
    // 调用导出API
    const res = await agenciesExportApi(params)
    const blob = new Blob([res.data])
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    // `机构列表${new Date().getTime()}.xlsx`
    a.download = `机构列表.xlsx`
        // `机构列表${new Date().getTime()}.xlsx`
    const fileName = `机构列表.xlsx`
    a.download = fileName
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
@@ -416,4 +439,7 @@
</script>
<style scoped lang="scss">
.upload-tip {
margin-left: 5px;
}
</style>
applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue
@@ -276,6 +276,11 @@
// 懒加载获取区域节点数据
async function loadRegionNode(node, resolve) {
  try {
      // 限制只加载两级,当前节点 level >= 1 时不加载子节点
    if (node.level >= 2) {
      resolve([])
      return
    }
    // 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
    const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
    const parentCode = node.data?.id || '360800000000'
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
@@ -39,13 +39,14 @@
    <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>
      <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-content gd-table-content-bg">
        <el-table class="gd-table" :data="list">
        <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
          <el-table-column type="selection" width="46" />
          <el-table-column label="序号" width="80">
            <template v-slot="{ $index }">
               {{ $index + 1 }}
@@ -95,29 +96,29 @@
      class="gd-dialog"
      append-to-body
      v-model="isShowImportView"
      title="导入区划"
      :width="pxToRem(600)"
      title="区划导入"
      :width="pxToRem(500)"
      :close-on-click-modal="false"
      :destroy-on-close="true"
      @close="handleImportClose"
    >
      <el-form class="gd-dialog-form" ref="importFormRef" :model="importParams" :rules="importRules" label-width="140px">
        <el-form-item label="导出模板">
          <!-- :loading="templateExportLoading"
            :disabled="templateExportLoading" -->
          <el-button 
            type="primary" 
            color="#F2F3F5"
            @click="handleExportTemplate"
          >
            点击导出
            下载模板
          </el-button>
        </el-form-item>
        <el-form-item label="上传文件" prop="file">
          {{ importFileName }}
          <el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onImportFileBefore">
            <el-button type="primary">点击上传</el-button>
          </el-upload>
          <div class="upload-tip">
            {{ importFileName }}
          </div>
        </el-form-item>
      </el-form>
      <template #footer>
@@ -169,6 +170,7 @@
const dialogVisible = ref(false)
const regionOptions = ref([]) // 区域选项
const dictObj = ref({}) // 字典对象
const selectedFiles = ref([]) // 勾选的文件列表
// 导入区划相关
const isShowImportView = ref(false)
@@ -281,6 +283,11 @@
  }
}
// 勾选值设置
function handleSelectionChange(rows) {
  selectedFiles.value = rows
}
// 导出模板
async function handleExportTemplate() {
  templateExportLoading.value = true
@@ -304,9 +311,6 @@
// 上传文件前处理
function onImportFileBefore(file) {
  // 提示请先导出模板
  // ElMessage.warning('请先导出模板并按模板格式填写数据')
  // 检查文件类型
  const allowedTypes = ['.xlsx', '.xls']
  const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
@@ -314,21 +318,11 @@
    ElMessage.error('请上传Excel文件(.xlsx 或 .xls)')
    return false
  }
  let data = new FormData()
  data.append('file', file)
  zoningImportApi(data, { isCovered: importParams.value.isCovered }).then(res => {
    if (res.data.code === 200) {
      ElMessage.success('导入成功')
      // 保存文件名
      importFileName.value = file.name
      importParams.value.file = file
      isShowImportView.value = false
      getList()
    } else {
      ElMessage.error(res.msg || '导入失败')
    }
  })
  // 保存文件名和文件对象
  importFileName.value = file.name
  importParams.value.file = file
  ElMessage.success('上传成功')
  return false // 阻止组件的默认上传行为
}
@@ -344,8 +338,18 @@
          ElMessage.error('请先上传文件')
          return
        }
        // 触发上传操作
        await onImportFileBefore(importParams.value.file)
        // 构建表单数据并调用导入接口
        let data = new FormData()
        data.append('file', importParams.value.file)
        const res = await zoningImportApi(data, { isCovered: importParams.value.isCovered })
        if (res.data.code === 200) {
          ElMessage.success('导入成功')
          isShowImportView.value = false
          getList()
        } else {
          ElMessage.error(res.msg || '导入失败')
        }
      } finally {
        importSubmitting.value = false
      }
@@ -355,14 +359,25 @@
// 导出区划
async function handleExport() {
  // 检查是否有选中的数据
  if (selectedFiles.value.length === 0) {
    ElMessage.warning('请选择要导出的区划')
    return
  }
  exportLoading.value = true
  try {
    const res = await zoningExportApi(searchParams.value)
    const params = {}
    params.regionIds = selectedFiles.value.map(i => i.id)
    // 调用导出API
    const res = await zoningExportApi(params)
    const blob = new Blob([res.data])
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `区划列表.xlsx`
    const fileName = `区划列表.xlsx`
    a.download = fileName
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
@@ -381,4 +396,7 @@
</script>
<style scoped lang="scss">
.upload-tip {
margin-left: 5px;
}
</style>
applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
@@ -57,10 +57,11 @@
}
// 导出模板
export const zoningExportTemplateApi = () => {
export const zoningExportTemplateApi = params => {
  return request({
    url: `/blade-system/region/export-template`,
    method: 'get',
    params,
    responseType: 'blob',
  })
}
packages/constants/package.json
@@ -4,5 +4,11 @@
  "private": true,
  "description": "常量",
  "main": "index.js",
  "license": "ISC"
  "license": "ISC",
    "peerDependencies": {
        "cesium": "catalog:"
    },
    "devDependencies": {
        "cesium": "catalog:"
    }
}
pnpm-lock.yaml
@@ -854,7 +854,11 @@
  packages/components: {}
  packages/constants: {}
  packages/constants:
    devDependencies:
      cesium:
        specifier: 'catalog:'
        version: 1.126.0
  packages/hooks:
    dependencies:
@@ -13590,30 +13594,30 @@
      - '@vue/composition-api'
      - vue
  '@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)':
  '@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
    dependencies:
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      dom7: 3.0.0
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      dom7: 4.0.6
      is-url: 1.2.4
      lodash.throttle: 4.1.1
      nanoid: 3.3.11
      slate: 0.72.8
      nanoid: 5.1.6
      slate: 0.82.1
      snabbdom: 3.6.3
  '@wangeditor-next/code-highlight@1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
  '@wangeditor-next/code-highlight@1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
    dependencies:
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      dom7: 4.0.6
      prismjs: 1.30.0
      slate: 0.82.1
      snabbdom: 3.6.3
  '@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)':
  '@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
    dependencies:
      '@types/event-emitter': 0.3.5
      '@uppy/core': 2.3.4
      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
      dom7: 3.0.0
      dom7: 4.0.6
      event-emitter: 0.3.5
      html-void-elements: 3.0.0
      i18next: 23.16.8
@@ -13624,9 +13628,9 @@
      lodash.foreach: 4.5.0
      lodash.throttle: 4.1.1
      lodash.toarray: 4.4.0
      nanoid: 3.3.11
      nanoid: 5.1.6
      scroll-into-view-if-needed: 3.1.0
      slate: 0.72.8
      slate: 0.82.1
      slate-history: 0.109.0(slate@0.82.1)
      snabbdom: 3.6.3
@@ -13634,13 +13638,13 @@
    dependencies:
      '@uppy/core': 2.3.4
      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
      '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/code-highlight': 1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/list-module': 1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/table-module': 1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/upload-image-module': 1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/video-module': 1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/code-highlight': 1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/list-module': 1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/table-module': 1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/upload-image-module': 1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/video-module': 1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      dom7: 4.0.6
      is-hotkey: 0.2.0
      lodash.camelcase: 4.3.0
@@ -13653,16 +13657,16 @@
      slate: 0.82.1
      snabbdom: 3.6.3
  '@wangeditor-next/list-module@1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
  '@wangeditor-next/list-module@1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
    dependencies:
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      dom7: 4.0.6
      slate: 0.82.1
      snabbdom: 3.6.3
  '@wangeditor-next/table-module@1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
  '@wangeditor-next/table-module@1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
    dependencies:
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      dom7: 4.0.6
      lodash.debounce: 4.0.8
      lodash.throttle: 4.1.1
@@ -13670,22 +13674,22 @@
      slate: 0.82.1
      snabbdom: 3.6.3
  '@wangeditor-next/upload-image-module@1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)':
  '@wangeditor-next/upload-image-module@1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)':
    dependencies:
      '@uppy/core': 2.3.4
      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
      '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      dom7: 4.0.6
      lodash.foreach: 4.5.0
      slate: 0.82.1
      snabbdom: 3.6.3
  '@wangeditor-next/video-module@1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
  '@wangeditor-next/video-module@1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
    dependencies:
      '@uppy/core': 2.3.4
      '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
      '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
      dom7: 4.0.6
      nanoid: 5.1.6
      slate: 0.82.1