吉安感知网项目-前端
chenyao
2026-06-03 b4f08a561973b998036f6ffa9e843cfd5ee877be
Merge remote-tracking branch 'origin/master'
13 files modified
456 ■■■■ changed files
AGENTS.md 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/api/system/dept.js 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/api/zkxt/index.js 17 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/common/cockpit.scss 16 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/clueEvents/ViewDiaLog.vue 85 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/clueEvents/index.vue 30 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue 102 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue 105 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue 14 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue 51 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue 14 ●●●● patch | view | raw | blame | history
packages/utils/common/index.js 10 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/login/index.vue 4 ●●●● patch | view | raw | blame | history
AGENTS.md
@@ -30,8 +30,7 @@
- 文件与目录保持 kebabCase,遵循现有应用与包的命名风格。
## 测试指南
- 当前未配置测试框架(`pnpm test` 会直接退出)。
- 若新增测试,请在对应应用 `README.md` 说明测试框架,并补充根目录脚本。
- 无需任何测试
## 提交与合并请求规范
- 提交历史使用简短的约定式主题(如 `feat: ...`)。
@@ -54,3 +53,4 @@
  Prefer minimal changes.
- 未被明确要求时,禁止大规模重构。
  Avoid large refactors unless requested.
- 修改完不需要走打包编译校验
applications/task-work-order/src/api/system/dept.js
@@ -67,12 +67,12 @@
  });
};
export const getDeptTree = tenantId => {
export const getDeptTree = id => {
  return request({
    url: '/blade-system/dept/getTree',
    method: 'get',
    params: {
      tenantId,
            id,
    },
  });
};
applications/task-work-order/src/api/zkxt/index.js
@@ -53,4 +53,21 @@
    })
}
// ai算法
export const algorithmGroupedApi = (params) => {
    return request({
        url: `/webservice/webservice/algorithm/grouped`,
        method: 'get',
        params,
    })
}
// 获取字典下拉 task_inspection_type
export const getDictListApi = (str) => {
    return request({
        url: `/system/dict/data/type/${str}`,
        method: 'get',
    })
}
applications/task-work-order/src/styles/common/cockpit.scss
@@ -1223,22 +1223,6 @@
}
.gd-dialog-form {
  // 最后一排不需要设置 margin-bottom
  // border: 1px solid red;
  // 倒数第二个也设置为0
  // .el-col:nth-last-child(2) {
  //   margin-bottom: 0;
  // }
  // .el-col:nth-last-child(1) {
  //   margin-bottom: 0;
  // }
  // .el-form-item:nth-last-child(2) {
  //   margin-bottom: 0;
  // }
  //  .el-form-item:nth-last-child(1) {
  //   margin-bottom: 0;
  // }
  .gd-select {
    .el-select__wrapper {
      height: 36px;
applications/task-work-order/src/views/orderView/orderManage/clueEvents/ViewDiaLog.vue
@@ -5,9 +5,9 @@
                <el-table-column label="线索缩略图" width="120">
                    <template v-slot="{ row }">
                        <el-image
                            v-if="row.resultUrl"
                            :src="row.resultUrl"
                            :preview-src-list="[row.resultUrl]"
                            v-if="row.aiImg || row.resultUrl"
                            :src="row.aiImg || row.resultUrl"
                            :preview-src-list="[row.aiImg || row.resultUrl]"
                            fit="cover"
                            style="width: 80px; height: 60px"
                            preview-teleported
@@ -101,11 +101,90 @@
    try {
        const res = await gdTaskResultListApi({ patrolTaskId: currentRow.value.id })
        list.value = res?.data?.data ?? []
        list.value = await Promise.all(list.value.map(async i => {
            const aiImg = await getAiImg(i.resultUrl)
            return { ...i, aiImg }
        }))
    } finally {
        loading.value = false
    }
}
const aiFrame = [
    "{\"score\":0.91357421875,\"bbox\":{\"x_cen\":1246.0,\"y_cen\":209.0,\"width\":166.0,\"height\":334.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"}",
    "{\"score\":0.89697265625,\"bbox\":{\"x_cen\":370.0,\"y_cen\":694.5,\"width\":162.0,\"height\":331.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"}",
    "{\"score\":0.89501953125,\"bbox\":{\"x_cen\":396.0,\"y_cen\":343.0,\"width\":168.0,\"height\":330.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"}",
    "{\"score\":0.79296875,\"bbox\":{\"x_cen\":409.5,\"y_cen\":52.5,\"width\":167.0,\"height\":105.0},\"class_name\":\"car\",\"algorithmId\":\"e71116098eeb1d60cfebd04d30653b151\"}"
]
function getAiImg(url) {
    if (!url) return ''
    const img = new Image()
    img.crossOrigin = 'anonymous';
    return new Promise(resolve => {
        img.onload = () => {
            if (!img.naturalWidth || !img.naturalHeight) {
                resolve('')
                return
            }
            const canvas = document.createElement('canvas')
            const ctx = canvas.getContext('2d')
            if (!ctx) {
                resolve('')
                return
            }
            canvas.width = img.naturalWidth
            canvas.height = img.naturalHeight
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
            aiFrame.forEach(item => {
                let target = item
                try {
                    target = typeof item === 'string' ? JSON.parse(item) : item
                } catch (error) {
                    return
                }
                const { x_cen, y_cen, width, height } = target.bbox || {}
                if ([x_cen, y_cen, width, height].some(value => typeof value !== 'number')) return
                const x = x_cen - width / 2
                const y = y_cen - height / 2
                const label = target.class_name || ''
                const fontSize = Math.max(18, Math.round(canvas.width / 80))
                const labelHeight = fontSize + 10
                const labelY = y - labelHeight >= 0 ? y - labelHeight : y
                ctx.strokeStyle = '#FF3B30'
                ctx.lineWidth = Math.max(3, Math.round(canvas.width / 640))
                ctx.strokeRect(x, y, width, height)
                if (label) {
                    ctx.font = `${fontSize}px Arial`
                    const labelWidth = ctx.measureText(label).width + 16
                    ctx.fillStyle = '#FF3B30'
                    ctx.fillRect(x, labelY, labelWidth, labelHeight)
                    ctx.fillStyle = '#FFFFFF'
                    ctx.textBaseline = 'middle'
                    ctx.fillText(label, x + 8, labelY + labelHeight / 2)
                }
            })
            try {
                resolve(canvas.toDataURL('image/jpeg', 0.92))
            } catch (error) {
                console.log(error)
                resolve('')
            }
        }
        img.onerror = () => resolve('')
        img.src = url
    })
}
// 打开分发弹框
function openDistributeDialog(row) {
    distributeDialogVisible.value = true
applications/task-work-order/src/views/orderView/orderManage/clueEvents/index.vue
@@ -22,10 +22,10 @@
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.workOrderType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                        v-for="item in workOrderTypeXT"
                        :key="item.dictValue"
                        :label="item.dictLabel"
                        :value="item.dictValue"
                    />
                </el-select>
            </el-form-item>
@@ -76,7 +76,7 @@
                    <el-table-column prop="taskNo" show-overflow-tooltip label="巡查任务编号" />
                    <el-table-column prop="patrolTaskType" show-overflow-tooltip label="巡查任务类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.patrolTaskType, dictObj.workOrderType) }}
                            {{ getDictLabelXT(row.patrolTaskType, workOrderTypeXT) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="taskStatus" show-overflow-tooltip label="巡查任务状态">
@@ -92,7 +92,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>
@@ -115,12 +115,14 @@
</template>
<script setup>
import { Search, RefreshRight } from '@element-plus/icons-vue'
import { onMounted, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { dateRangeFormat, getDictLabel } from '@ztzf/utils'
import { dateRangeFormat, getDictLabel, getDictLabelXT } from '@ztzf/utils'
import { getDictListApi } from '@/api/zkxt'
import { gdPatrolTaskPageApi } from '../inspectionRequest/inspectionRequestApi'
import { getDeptTree } from '@/api/system/dept'
import ViewDiaLog from './ViewDiaLog.vue'
import { useStore } from 'vuex'
// 初始化查询参数
const initSearchParams = () => ({
@@ -149,8 +151,10 @@
    workOrderType: [], // 巡查任务类型
    taskStatus: [], // 巡查任务状态
})
const workOrderTypeXT = ref([])
provide('dictObj', dictObj)
provide('workOrderTypeXT', workOrderTypeXT)
provide('deptTree', deptTree)
provide('treeProps', treeProps)
@@ -163,7 +167,7 @@
            ...searchParams.value,
            startTime: range[0],
            endTime: range[1],
            taskStatus: 8
            taskStatus: 8,
        })
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
@@ -199,11 +203,17 @@
    getDictionaryByCode('workOrderType,taskStatus').then(res => {
        dictObj.value = res.data.data
    })
    getDictListApi('task_inspection_type').then(res => {
        workOrderTypeXT.value = res.data.data || []
    })
}
const store = useStore()
const userInfo = computed(() => store.state.user.userInfo)
// 获取部门树
function getDeptTreeFun() {
    getDeptTree().then(res => {
    getDeptTree(userInfo.value.dept_id).then(res => {
        deptTree.value = res.data.data
    })
}
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
@@ -5,10 +5,10 @@
        :title="titleEnum[dialogMode]"
        @closed="visible = false"
        destroy-on-close
        width="75%"
        width="90%"
        :close-on-click-modal="false"
    >
        <div class="gd-dialog-form">
        <div>
            <!-- 关联工单选择 -->
            <div class="work-order-section">
                <div class="detail-title" :style="{ marginBottom: 0, marginRight: pxToRem(10) }">选择工单</div>
@@ -33,7 +33,7 @@
                        <div class="info-item">
                            <span class="label">工单类型</span>
                            <span class="val" v-if="selectedWorkOrder">
                                {{ getDictLabel(selectedWorkOrder.workOrderType, dictObj.workOrderType) }}
                                {{ getDictLabelXT(selectedWorkOrder.workOrderType, workOrderTypeXT) }}
                            </span>
                            <span class="val grayText" v-else>选择工单后自动带出</span>
                        </div>
@@ -78,7 +78,7 @@
                        <el-table-column width="50" label="">
                            <template v-slot="{ $index }">{{ $index + 1 }}</template>
                        </el-table-column>
                        <el-table-column prop="patrolTaskName" min-width="120">
                        <el-table-column prop="patrolTaskName" min-width="100">
                            <template #header>
                                <span class="required-star">*</span>
                                巡查任务名称
@@ -92,7 +92,7 @@
                                />
                            </template>
                        </el-table-column>
                        <el-table-column prop="patrolTaskType" min-width="140">
                        <el-table-column prop="patrolTaskType" min-width="100">
                            <template #header>
                                <span class="required-star">*</span>
                                巡查任务类型
@@ -106,15 +106,15 @@
                                    @change="handlePatrolTaskTypeChange($index, $event)"
                                >
                                    <el-option
                                        v-for="item in dictObj.workOrderType"
                                        :key="item.dictKey"
                                        :label="item.dictValue"
                                        :value="item.dictKey"
                                        v-for="item in workOrderTypeXT"
                                        :key="item.dictValue"
                                        :label="item.dictLabel"
                                        :value="item.dictValue"
                                    />
                                </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column prop="executeTime" min-width="180">
                        <el-table-column prop="executeTime" width="200">
                            <template #header>
                                <span class="required-star">*</span>
                                任务执行时间
@@ -122,7 +122,7 @@
                            <template v-slot="{ row }">
                                <!--                                gd-date-picker-popper-->
                                <el-date-picker
                                    :style="{width: pxToRem(160)}"
                                    :style="{width: pxToRem(165)}"
                                    class="gd-date-picker"
                                    popper-class=""
                                    v-model="row.executeTime"
@@ -133,7 +133,7 @@
                                />
                            </template>
                        </el-table-column>
                        <el-table-column prop="patrolRouteUrl" min-width="120">
                        <el-table-column prop="patrolRouteUrl" min-width="100">
                            <template #header>
                                <span class="required-star">*</span>
                                巡查任务航线
@@ -152,7 +152,7 @@
                                </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column prop="recommendFlyerName" min-width="120">
                        <el-table-column prop="deviceFlyerId" min-width="100">
                            <template #header>
                                <span class="required-star">*</span>
                                推荐飞手
@@ -162,16 +162,17 @@
                                    no-data-text="暂无推荐飞手"
                                    class="gd-select"
                                    popper-class="gd-select-popper"
                                    v-model="row.recommendFlyerName"
                                    v-model="row.deviceFlyerId"
                                    placeholder="请选择"
                                    filterable
                                    :disabled="dialogReadonly || !taskList[$index].patrolTaskType"
                                >
                                    <el-option
                                        v-for="item in flyerListMap[$index] || []"
                                        :key="item.id"
                                        :key="item.flyerId"
                                        :label="item.flyerName"
                                        :value="item.flyerName"
                                        :value="item.flyerId"
                                    >
                                        <template #default>
                                        <!-- 匹配区域=1 显示红星号 -->
@@ -182,7 +183,7 @@
                                    </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column prop="deviceId" min-width="120">
                        <el-table-column prop="deviceId" min-width="100">
                            <template #header>
                                <span class="required-star">*</span>
                                选择设备
@@ -200,7 +201,31 @@
                                </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column prop="taskDesc" label="巡查任务描述" min-width="120">
                        <el-table-column prop="algorithmIds" min-width="160">
                            <template #header>
                                算法
                            </template>
                            <template v-slot="{ row }">
                                <el-tree-select
                                    class="gd-select"
                                    popper-class="gd-tree-select-popper"
                                    v-model="row.algorithmIds"
                                    :data="algorithmTreeData"
                                    :props="{ label: 'name', children: 'children', value: 'id' }"
                                    node-key="id"
                                    multiple
                                    collapse-tags
                                    collapse-tags-tooltip
                                    check-strictly
                                    filterable
                                    placeholder="请选择"
                                    :disabled="dialogReadonly"
                                />
                            </template>
                        </el-table-column>
                        <el-table-column prop="taskDesc" label="巡查任务描述">
                            <template v-slot="{ row }">
                                <el-input class="gd-input" v-model="row.taskDesc" placeholder="请输入" :disabled="dialogReadonly" />
                            </template>
@@ -240,15 +265,16 @@
</template>
<script setup>
import { computed, ref, onMounted } from 'vue'
import { computed, ref, onMounted, inject } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Delete } from '@element-plus/icons-vue'
import { geomAnalysis, getDictLabel } from '@ztzf/utils'
import { geomAnalysis, getDictLabel, getDictLabelXT } from '@ztzf/utils'
import { gdPatrolTaskSaveApi, gdFlyerPageApi, gdPatrolTaskAuditApi } from './inspectionRequestApi'
import { gdWorkOrderPageApi } from '../orderManage/orderManageApi'
import { gdManageDeviceListApi } from '../orderManage/gdManageDeviceApi'
import { pxToRem } from '@/utils/rem'
import { airlineListApi } from '@/api/zkxt'
import { airlineListApi, algorithmGroupedApi } from '@/api/zkxt'
// 初始化任务行
const initTaskRow = () => ({
@@ -256,8 +282,9 @@
    patrolTaskType: '',
    executeTime: '',
    patrolRouteUrl: '',
    recommendFlyerName: '',
    deviceFlyerId: '',
    deviceId: null,
    algorithmIds: [],
    taskDesc: '',
})
const dictObj = inject('dictObj')
@@ -278,6 +305,27 @@
const deviceList = ref([]) // 设备列表
const routeOptions = ref([]) // 航线选项(根据工单范围获取)
const routeLoading = ref(false) // 航线加载中
const algorithmTreeData = ref([]) // 算法树形数据
const workOrderTypeXT = inject('workOrderTypeXT', ref([]))
// 获取算法分组数据
async function getAlgorithmList() {
    try {
        const res = await algorithmGroupedApi()
        const list = res?.data?.data ?? []
        algorithmTreeData.value = list.map(group => ({
            id: 'group_' + group.type,
            name: group.type,
            disabled: true,
            children: (group.algorithms || []).map(alg => ({
                id: alg.id,
                name: alg.name,
            }))
        }))
    } catch (e) {
        console.error('获取算法列表失败', e)
    }
}
// 子任务列表
const taskList = ref([initTaskRow()])
@@ -369,10 +417,12 @@
// 处理巡查任务类型变化
async function handlePatrolTaskTypeChange(index, skilledTaskType) {
    // 清空该行已选择的飞手
    taskList.value[index].recommendFlyerName = ''
    taskList.value[index].deviceFlyerId = ''
    // 获取对应的飞手列表
    await getFlyerList(index, skilledTaskType)
}
import * as turf from '@turf/turf'
@@ -416,7 +466,7 @@
// 判断一行是否已填写(至少填写了任务名称)
function isTaskRowFilled(task) {
    return !!(task.patrolTaskName || task.executeTime || task.patrolRouteUrl || task.recommendFlyerName || task.deviceId)
    return !!(task.patrolTaskName || task.executeTime || task.patrolRouteUrl || task.deviceFlyerId || task.deviceId)
}
// 提交新增
@@ -453,7 +503,7 @@
            ElMessage.warning(`第${i + 1}行: 请选择巡查任务航线`)
            return
        }
        if (!task.recommendFlyerName) {
        if (!task.deviceFlyerId) {
            ElMessage.warning(`第${i + 1}行: 请选择推荐飞手`)
            return
        }
@@ -485,6 +535,8 @@
async function open({ mode = 'add', row } = {}) {
    dialogMode.value = mode
    await getWorkOrderList()
    getAlgorithmList()
    if (dialogMode.value === 'add') {
        selectedWorkOrderId.value = null
        selectedWorkOrder.value = null
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
@@ -42,7 +42,7 @@
                        </el-col>
                        <el-col :span="12">
                            <div class="label">巡查任务类型</div>
                            <div class="val">{{ getDictLabel(formData.patrolTaskType, dictObj.workOrderType) }}</div>
                            <div class="val">{{ getDictLabelXT(formData.patrolTaskType, workOrderTypeXT) }}</div>
                        </el-col>
                        <el-col :span="12">
                            <div class="label">任务执行时间</div>
@@ -58,7 +58,7 @@
                        </el-col>
                        <el-col :span="12">
                            <div class="label">推荐飞手</div>
                            <div class="val">{{ formData.recommendFlyerName }}</div>
                            <div class="val">{{ getFlyerName(formData.deviceFlyerId) }}</div>
                        </el-col>
                        <el-col :span="12">
                            <div class="label">选择设备</div>
@@ -71,6 +71,10 @@
                        <el-col :span="12">
                            <div class="label">巡查任务描述</div>
                            <div class="val">{{ formData.taskDesc }}</div>
                        </el-col>
                        <el-col :span="12">
                            <div class="label">算法</div>
                            <div class="val">{{ getAlgorithmNames(formData.algorithmIds) }}</div>
                        </el-col>
                    </el-row>
                    <template v-if="['6', '7', '8'].includes(taskStatus)">
@@ -115,10 +119,10 @@
                                    clearable
                                >
                                    <el-option
                                        v-for="item in dictObj.workOrderType"
                                        :key="item.dictKey"
                                        :label="item.dictValue"
                                        :value="item.dictKey"
                                        v-for="item in workOrderTypeXT"
                                        :key="item.dictValue"
                                        :label="item.dictLabel"
                                        :value="item.dictValue"
                                    />
                                </el-select>
                            </el-form-item>
@@ -149,16 +153,16 @@
                            </el-form-item>
                        </el-col>
                        <el-col :span="12">
                            <el-form-item label="推荐飞手" prop="recommendFlyerName">
                            <el-form-item label="推荐飞手" prop="deviceFlyerId">
                                <el-select
                                    class="gd-select"
                                    popper-class="gd-select-popper"
                                    v-model="formData.recommendFlyerName"
                                    v-model="formData.deviceFlyerId"
                                    placeholder="请选择"
                                    filterable
                                    clearable
                                >
                                    <el-option v-for="item in flyerList" :key="item.id" :label="item.flyerName" :value="item.flyerName" />
                                    <el-option v-for="item in flyerList" :key="item.id" :label="item.flyerName" :value="item.flyerId" />
                                </el-select>
                            </el-form-item>
                        </el-col>
@@ -179,6 +183,26 @@
                        <el-col :span="12">
                            <el-form-item label="关联工单">
                                <span>{{ getWorkOrderName(formData.workOrderId) }}</span>
                            </el-form-item>
                        </el-col>
                        <!-- 算法下拉 -->
                        <el-col :span="12">
                            <el-form-item label="算法" prop="algorithmIds">
                                <el-tree-select
                                    class="gd-select"
                                    popper-class="gd-tree-select-popper"
                                    v-model="formData.algorithmIds"
                                    :data="algorithmTreeData"
                                    :props="{ label: 'name', children: 'children', value: 'id' }"
                                    node-key="id"
                                    multiple
                                    collapse-tags
                                    collapse-tags-tooltip
                                    check-strictly
                                    filterable
                                    clearable
                                    placeholder="请选择"
                                />
                            </el-form-item>
                        </el-col>
                        <el-col :span="24">
@@ -212,7 +236,7 @@
            </div>
        </div>
        <template #footer>
        <template #footer v-if="detailLoaded">
            <el-button v-if="['1', '4', '7'].includes(taskStatus)" @click="viewDescription" color="#F2F3F5">
                {{ gdStatusObj[taskStatus].reason }}
            </el-button>
@@ -260,9 +284,9 @@
</template>
<script setup>
import { computed, ref, onMounted } from 'vue'
import { computed, ref, onMounted, inject } from 'vue'
import { ElMessage } from 'element-plus'
import { fieldRules, flyVisual, getDictLabel, geomAnalysis } from '@ztzf/utils'
import { fieldRules, flyVisual, getDictLabel, getDictLabelXT, geomAnalysis } from '@ztzf/utils'
import {
    gdPatrolTaskRepublish,
    gdFlyerPageApi,
@@ -276,7 +300,7 @@
import { gdTaskResultListApi } from '@/views/orderView/orderManage/clueEvents/achievementApi'
import RefuseOrderDialog1 from '@/views/orderView/orderManage/inspectionRequest/RefuseOrderDialog1.vue'
import { Check } from '@element-plus/icons-vue'
import { queryAirById, airlineListApi } from '@/api/zkxt'
import { queryAirById, airlineListApi, algorithmGroupedApi } from '@/api/zkxt'
import * as Cesium from 'cesium'
import { useStore } from 'vuex'
@@ -287,8 +311,9 @@
    patrolTaskType: '',
    executeTime: '',
    patrolRouteUrl: '',
    recommendFlyerName: '',
    deviceFlyerId: null,
    deviceId: null,
    algorithmIds: [],
    taskDesc: '',
    workOrderId: null,
})
@@ -304,6 +329,7 @@
const visible = defineModel()
const dialogMode = ref('view')
const submitting = ref(false)
const detailLoaded = ref(false)
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看' })
const taskStatus = computed(() => formData.value.taskStatus)
@@ -316,6 +342,38 @@
const getAirName = id => {
    const item = routeOptions.value.find(item => item.id === id)
    return item ? item.name : id
}
const algorithmTreeData = ref([]) // 算法树形数据
const workOrderTypeXT = inject('workOrderTypeXT', ref([]))
// 获取算法分组数据
async function getAlgorithmList() {
    try {
        const res = await algorithmGroupedApi()
        const list = res?.data?.data ?? []
        algorithmTreeData.value = list.map(group => ({
            id: 'group_' + group.type,
            name: group.type,
            disabled: true,
            children: (group.algorithms || []).map(alg => ({
                id: alg.id,
                name: alg.name,
            }))
        }))
    } catch (e) {
        console.error('获取算法列表失败', e)
    }
}
// 根据 algorithmIds 数组获取算法名称,逗号分割回显
function getAlgorithmNames(algorithmIds) {
    if (!algorithmIds || !algorithmIds.length) return ''
    const allAlgorithms = algorithmTreeData.value.flatMap(group => group.children || [])
    return algorithmIds.map(id => {
        const item = allAlgorithms.find(alg => alg.id === id)
        return item ? item.name : id
    }).join(', ')
}
const gdStatusObj = {
@@ -338,7 +396,7 @@
    patrolTaskType: fieldRules(true),
    executeTime: fieldRules(true),
    patrolRouteUrl: fieldRules(true),
    recommendFlyerName: fieldRules(true),
    deviceFlyerId: fieldRules(true),
    deviceId: fieldRules(true),
}
@@ -382,6 +440,12 @@
function getDeviceName(id) {
    const item = deviceList.value.find(item => item.id === id)
    return item ? item.nickname : id
}
// 获取飞手名称
function getFlyerName(id) {
    const item = flyerList.value.find(item => item.flyerId === id)
    return item ? item.flyerName : id
}
// 获取工单名称
@@ -515,9 +579,20 @@
// 打开弹框
async function open({ mode = 'view', row } = {}) {
    detailLoaded.value = false
    dialogMode.value = mode
    const res = await gdPatrolTaskDetailApi({ id: row.id })
    formData.value = { ...initForm(), ...res.data.data }
    // 确保 algorithmIds 是数组
    if (formData.value.algorithmIds && typeof formData.value.algorithmIds === 'string') {
        formData.value.algorithmIds = formData.value.algorithmIds.split(',').filter(Boolean)
    } else if (!formData.value.algorithmIds) {
        formData.value.algorithmIds = []
    }
    detailLoaded.value = true
    // 获取算法列表
    getAlgorithmList()
    // 获取工单详情并根据geom范围获取航线列表
    await getWorkOrderDetail(formData.value.workOrderId)
    ;['6', '7', '8'].includes(row.taskStatus) && await getTaskResultList()
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue
@@ -13,8 +13,8 @@
            <el-form-item label="任务类型" prop="patrolTaskType">
                <el-select class="gd-select gray" popper-class="gd-select-popper" v-model="searchParams.patrolTaskType"
                    placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.workOrderType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                    <el-option v-for="item in workOrderTypeXT" :key="item.dictValue" :label="item.dictLabel"
                        :value="item.dictValue" />
                </el-select>
            </el-form-item>
@@ -55,7 +55,7 @@
                    <el-table-column prop="taskNo" show-overflow-tooltip label="巡查任务编号" />
                    <el-table-column prop="patrolTaskType" show-overflow-tooltip label="巡查任务类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.patrolTaskType, dictObj.workOrderType) }}
                            {{ getDictLabelXT(row.patrolTaskType, workOrderTypeXT) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="taskStatus" show-overflow-tooltip label="巡查任务状态">
@@ -93,7 +93,8 @@
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { dateRangeFormat, getDictLabel } from '@ztzf/utils'
import { dateRangeFormat, getDictLabel, getDictLabelXT } from '@ztzf/utils'
import { getDictListApi } from '@/api/zkxt'
import FormDiaLog from './FormDiaLog.vue'
import { gdPatrolTaskPageApi, gdPatrolTaskRemoveApi } from './inspectionRequestApi'
import ViewDiaLog from '@/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue'
@@ -130,8 +131,10 @@
    deviceLoadDemand: [], // 设备负载需求
    taskStatus: [], // 巡查任务状态
})
const workOrderTypeXT = ref([])
const activeName = ref('all')
provide('dictObj', dictObj)
provide('workOrderTypeXT', workOrderTypeXT)
const colors = {
    0: '#F6A000',//0待签收
@@ -213,6 +216,9 @@
    getDictionaryByCode('workOrderType,deviceLoadDemand,taskStatus').then(res => {
        dictObj.value = res.data.data
    })
    getDictListApi('task_inspection_type').then(res => {
        workOrderTypeXT.value = res.data.data || []
    })
}
function refusalAccept (row) {
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog
        class="gd-dialog"
        class="gd-dialog orderManageFormDlg"
        v-model="visible"
        :title="titleEnum[dialogMode]"
        @closed="visible = false"
@@ -31,7 +31,7 @@
                        </el-col>
                        <el-col :span="12">
                            <div class="label">工单类型</div>
                            <div class="val">{{ getDictLabel(formData.workOrderType, dictObj.workOrderType) }}</div>
                            <div class="val">{{ getDictLabelXT(formData.workOrderType, workOrderTypeXT) }}</div>
                        </el-col>
                        <el-col :span="12">
                            <div class="label">执行时间范围</div>
@@ -62,7 +62,7 @@
                            <el-table-column prop="patrolTaskName" show-overflow-tooltip label="巡查任务名称" />
                            <el-table-column prop="patrolTaskType" show-overflow-tooltip label="巡查任务类型">
                                <template v-slot="{ row }">
                                    {{ getDictLabel(row.patrolTaskType, dictObj.workOrderType) }}
                                    {{ getDictLabelXT(row.patrolTaskType, workOrderTypeXT) }}
                                </template>
                            </el-table-column>
                            <el-table-column prop="taskStatus" show-overflow-tooltip label="任务状态">
@@ -102,10 +102,10 @@
                                    clearable
                                >
                                    <el-option
                                        v-for="item in dictObj.workOrderType"
                                        :key="item.dictKey"
                                        :label="item.dictValue"
                                        :value="item.dictKey"
                                        v-for="item in workOrderTypeXT"
                                        :key="item.dictValue"
                                        :label="item.dictLabel"
                                        :value="item.dictValue"
                                    />
                                </el-select>
                            </el-form-item>
@@ -156,7 +156,7 @@
                        </el-col>
                    </el-row>
                </el-form>
                <div class="detail-title" :style="{ marginTop: pxToRem(20) }">
                <div class="detail-title" :style="{ marginTop: pxToRem(10) }">
                    <span class="required-star" v-if="!dialogReadonly">*</span>
                    工单执行范围
                </div>
@@ -195,7 +195,7 @@
            </div>
        </div>
        <template #footer v-if="activeName === 'basic'">
        <template #footer v-if="activeName === 'basic' && detailLoaded">
            <el-button v-if="['11', '21', '23', '31', '60'].includes(gdStatus)" @click="viewDescription" color="#F2F3F5">
                {{ gdStatusObj[gdStatus]?.reason }}
            </el-button>
@@ -262,9 +262,9 @@
</template>
<script setup>
import { computed, ref, nextTick } from 'vue'
import { computed, ref, nextTick, inject } from 'vue'
import { ElMessage } from 'element-plus'
import { dateRangeFormat, fieldRules, flyVisual, geomAnalysis, getDictLabel } from '@ztzf/utils'
import { dateRangeFormat, fieldRules, flyVisual, geomAnalysis, getDictLabel, getDictLabelXT } from '@ztzf/utils'
import {
    gdWorkOrderDetailApi,
    gdWorkOrderFlowListApi,
@@ -286,6 +286,7 @@
import { useStore } from 'vuex'
import OrderStepBar from '@/views/orderView/orderManage/orderManage/OrderStepBar.vue'
import OutcomeData from './outcomeData.vue'
import { getDictListApi } from '@/api/zkxt'
const store = useStore()
const permission = computed(() => store.state.user.permission)
const activeName = ref('basic')
@@ -305,6 +306,7 @@
const mapRef = ref(null)
const rejectVisible = ref(false)
const dictObj = inject('dictObj')
const workOrderTypeXT = inject('workOrderTypeXT', ref([]))
const dateRange = ref([])
const patrolTaskList = ref([])
const emit = defineEmits(['success'])
@@ -313,6 +315,7 @@
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const detailLoaded = ref(false) // 详情加载完成
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
const deviceList = ref([]) // 设备列表
@@ -631,6 +634,7 @@
// 打开弹框
async function open({ mode = 'add', row } = {}) {
    detailLoaded.value = false
    dialogMode.value = mode
    getPayloadList()
    initMap()
@@ -645,6 +649,7 @@
        hasPatrolTaskList.value && gdPatrolTaskPage()
        dialogMode.value === 'edit' ? editPolygon() : viewPolygon()
    }
    detailLoaded.value = true
    await nextTick()
    await getDeviceList()
@@ -691,7 +696,20 @@
defineExpose({ open })
</script>
<style lang="scss" scoped>
<style lang="scss">
.orderManageFormDlg{
        .el-dialog__body{
            display: flex;
            flex-direction: column;
            .content{
                height: 0;
                flex: 1;
            }
        }
}
</style>
<style scoped lang="scss">
.separateTable {
    height: 0;
    flex: 1;
@@ -704,10 +722,12 @@
        }
    }
}
.content {
    display: flex;
    gap: 0 20px;
    height: 630px;
    //height: 630px;
    .leftBox {
        width: 0;
@@ -718,10 +738,9 @@
        .gd-cesium {
            position: relative;
            overflow: hidden;
            //overflow: hidden;
            width: 100%;
            min-height: 360px;
            flex: 1;
            height: 390px;
        }
    }
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue
@@ -13,8 +13,8 @@
            <el-form-item label="工单类型" prop="workOrderType">
                <el-select class="gd-select gray" popper-class="gd-select-popper" v-model="searchParams.workOrderType"
                    placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.workOrderType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                    <el-option v-for="item in workOrderTypeXT" :key="item.dictValue" :label="item.dictLabel"
                        :value="item.dictValue" />
                </el-select>
            </el-form-item>
@@ -53,7 +53,7 @@
                    <el-table-column prop="workOrderCode" show-overflow-tooltip label="工单编号" />
                    <el-table-column prop="workOrderType" show-overflow-tooltip label="工单类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.workOrderType, dictObj.workOrderType) }}
                            {{ getDictLabelXT(row.workOrderType, workOrderTypeXT) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="status" show-overflow-tooltip label="阶段状态">
@@ -102,7 +102,8 @@
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { dateRangeFormat, getDictLabel } from '@ztzf/utils'
import { dateRangeFormat, getDictLabel, getDictLabelXT } from '@ztzf/utils'
import { getDictListApi } from '@/api/zkxt'
import FormDiaLog from './FormDiaLog.vue'
import { gdWorkOrderPageApi, gdWorkOrderRemoveApi } from './orderManageApi'
import dayjs from 'dayjs'
@@ -143,7 +144,9 @@
    workOrderType: [], // 工单类型
    workOrderStatus: [], // 工单状态
})
const workOrderTypeXT = ref([])
provide('dictObj', dictObj)
provide('workOrderTypeXT', workOrderTypeXT)
// 获取列表
async function getList () {
@@ -246,6 +249,9 @@
    getDictionaryByCode('deviceLoadDemand,workOrderType,workOrderStatus,taskStatus').then(res => {
        dictObj.value = res.data.data
    })
    getDictListApi('task_inspection_type').then(res => {
        workOrderTypeXT.value = res.data.data || []
    })
}
// 获取两个tab的总条数
packages/utils/common/index.js
@@ -38,6 +38,16 @@
        .join(',');
}
// id转label xt
export function getDictLabelXT(value, dictList = []) {
    if (value === null || value === undefined || value === '') return '';
    const values = String(value).split(',');
    return dictList
        .filter(item => values.includes(String(item.dictValue)))
        .map(item => item.dictLabel)
        .join(',');
}
export function blobDownload(res) {
    const disposition = res.headers?.['content-disposition'] || res.headers?.['Content-Disposition']
    const encodedName = disposition.split('filename=')[1]
uniapps/work-app/src/pages/login/index.vue
@@ -201,8 +201,8 @@
    tenantId: "000000",
    deptId: "",
    roleId: "",
    username: loginFormGd.value.username,
    password: loginFormGd.value.password,
    username: loginForm.value.username,
    password: loginForm.value.username === 'admin' ? 'admin' : 'jadk@2026',
    type: "account",
    code: "",
    key: "",