吉安感知网项目-前端
chenyao
2026-01-27 6c7cdb644f765cdb4ad923095a36550b7d46e91d
Merge remote-tracking branch 'origin/master'
18 files modified
2 files added
1086 ■■■■ changed files
applications/drone-command/src/components/map-container/device-map-container.vue 123 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/utils/cesium/DrawPolygon.js 24 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue 13 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/index.vue 9 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue 31 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionRole/index.vue 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/common/cockpit.scss 24 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/index.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/index.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue 58 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue 27 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue 30 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue 316 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue 337 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js 66 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/index.vue 8 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -119,6 +119,14 @@
let droneTrackTickHandler = null
let droneTrackStartTime = null
let droneTrackRuntime = []
let favoritePulseRafId = null
let favoritePulseStartAt = 0
let favoritePulseElapsed = 0
const favoritePulseEntities = []
const pulseBaseColor = Cesium.Color.fromCssColorString('#FF3B30')
const PULSE_MIN_RADIUS_M = 60
const PULSE_MAX_RADIUS_M = 180
const PULSE_DURATION_S = 1.6
const detailVisible = ref(true)
const clusterVisible = ref(false)
const countyCenterMap = new Map()
@@ -172,6 +180,57 @@
 */
const adminBoundaryVisible = ref(treeCheckedKeys.value.includes('admin'))
const isFavorited = item => {
    const value = item?.favorited ?? item?.isFavorite
    return value === 1 || value === '1' || value === true
}
let pulseCanvas = null
const getPulseCanvas = () => {
    if (pulseCanvas) return pulseCanvas
    const size = 256
    const canvas = document.createElement('canvas')
    canvas.width = size
    canvas.height = size
    const ctx = canvas.getContext('2d')
    const center = size / 2
    const radius = center - 2
    const gradient = ctx.createRadialGradient(center, center, radius * 0.1, center, center, radius)
    gradient.addColorStop(0, 'rgba(255,59,48,0.55)')
    gradient.addColorStop(0.35, 'rgba(255,59,48,0.35)')
    gradient.addColorStop(0.7, 'rgba(255,59,48,0.15)')
    gradient.addColorStop(1, 'rgba(255,59,48,0)')
    ctx.fillStyle = gradient
    ctx.beginPath()
    ctx.arc(center, center, radius, 0, Math.PI * 2)
    ctx.fill()
    pulseCanvas = canvas
    return canvas
}
const createFavoritePulseEntity = (billboard) => {
    if (!viewer || !billboard) return
    const material = new Cesium.ImageMaterialProperty({
        image: getPulseCanvas(),
        transparent: true,
    })
    const pulse = {
        material,
        offset: Math.random() * PULSE_DURATION_S,
        entity: null,
    }
    pulse.entity = viewer.entities.add({
        position: new Cesium.CallbackProperty(() => billboard.position, false),
        ellipse: {
            semiMajorAxis: new Cesium.CallbackProperty(() => getPulseRadius(pulse), false),
            semiMinorAxis: new Cesium.CallbackProperty(() => getPulseRadius(pulse), false),
            material,
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        },
    })
    favoritePulseEntities.push(pulse)
}
const getDevicePosition = item => {
    const longitudeRaw = item.longitude ?? item.lng ?? item.lon
@@ -311,6 +370,12 @@
    }
}
const getPulseRadius = pulse => {
    const phase = ((favoritePulseElapsed + pulse.offset) % PULSE_DURATION_S) / PULSE_DURATION_S
    const wave = phase < 0.5 ? phase * 2 : (1 - phase) * 2
    return PULSE_MIN_RADIUS_M + (PULSE_MAX_RADIUS_M - PULSE_MIN_RADIUS_M) * wave
}
const setDetailVisibility = visible => {
    detailVisible.value = visible
    if (defenseZonePrimitive) defenseZonePrimitive.show = visible
@@ -345,6 +410,11 @@
const setDroneVisibility = visible => {
    if (droneTrackBillboardCollection) droneTrackBillboardCollection.show = visible
    if (droneTrackPolylineCollection) droneTrackPolylineCollection.show = visible
    if (favoritePulseEntities.length) {
        favoritePulseEntities.forEach(pulse => {
            if (pulse?.entity) pulse.entity.show = visible
        })
    }
    if (!visible && selectedTargetType.value === 'drone') {
        closePopup()
    }
@@ -453,6 +523,7 @@
const clearDroneTrackEntities = () => {
    if (!viewer) return
    stopDroneTrackAnimation()
    stopFavoritePulseAnimation()
    if (droneTrackBillboardCollection) {
        if (!droneTrackBillboardCollection.isDestroyed?.()) {
            removeCockpitPrimitive(droneTrackBillboardCollection)
@@ -466,6 +537,12 @@
        droneTrackPolylineCollection = null
    }
    droneTrackRuntime = []
    if (favoritePulseEntities.length) {
        favoritePulseEntities.forEach(pulse => {
            if (pulse?.entity) viewer?.entities?.remove(pulse.entity)
        })
        favoritePulseEntities.length = 0
    }
    dronePickMap.clear()
    if (selectedTargetType.value === 'drone') {
        closePopup()
@@ -532,6 +609,28 @@
    droneTrackTickHandler = null
}
const startFavoritePulseAnimation = () => {
    if (!viewer || favoritePulseRafId) return
    favoritePulseStartAt = performance.now()
    const tick = now => {
        if (!viewer || viewer.isDestroyed?.() || !favoritePulseEntities.length) {
            favoritePulseRafId = null
            return
        }
        favoritePulseElapsed = (now - favoritePulseStartAt) / 1000
        viewer.scene.requestRender()
        favoritePulseRafId = requestAnimationFrame(tick)
    }
    favoritePulseRafId = requestAnimationFrame(tick)
}
const stopFavoritePulseAnimation = () => {
    if (favoritePulseRafId) {
        cancelAnimationFrame(favoritePulseRafId)
        favoritePulseRafId = null
    }
}
const renderSimulatedDroneTrack = (list) => {
    if (!viewer) return
    clearDroneTrackEntities()
@@ -576,16 +675,19 @@
            })
            billboard.id = droneId
            const speedMs = Math.round(Cesium.Cartesian3.distance(positions[0], positions[1]) / segmentDuration)
            dronePickMap.set(droneId, {
                data: {
                    ...item,
                    flightHeightM: item.flightHeightM ?? points[0].height,
                    flightSpeedMs: item.flightSpeedMs ?? speedMs,
                    longitude: item.longitude ?? points[0].longitude,
                    latitude: item.latitude ?? points[0].latitude,
                },
                billboard,
            })
        dronePickMap.set(droneId, {
            data: {
                ...item,
                flightHeightM: item.flightHeightM ?? points[0].height,
                flightSpeedMs: item.flightSpeedMs ?? speedMs,
                longitude: item.longitude ?? points[0].longitude,
                latitude: item.latitude ?? points[0].latitude,
            },
            billboard,
        })
        if (isFavorited(item)) {
            createFavoritePulseEntity(billboard)
        }
            droneTrackRuntime.push({
                positions,
                billboard,
@@ -595,6 +697,7 @@
        })
    viewer.clock.multiplier = 1
    startDroneTrackAnimation()
    startFavoritePulseAnimation()
    reorderCockpitPrimitives()
}
applications/drone-command/src/utils/cesium/DrawPolygon.js
@@ -715,7 +715,8 @@
        this.initHandler(viewer)
        this.isPureSpotPreview = isPurePreview
        this.startDrawing()
        let newPosition = positions.map(item => {
        const normalizedPositions = this.normalizePolygonPositions(positions)
        let newPosition = normalizedPositions.map(item => {
            return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item?.height || 0))
        })
        // 预览航线的时候调用
@@ -728,17 +729,14 @@
        })
        // 视角飞入区域
        const newBox = boxTransformScale(
            positions.map(item => [item.lng, item.lat]),
            5
        )
        const newBox = boxTransformScale(normalizedPositions.map(item => [item.lng, item.lat]), 5)
        viewer.camera.flyTo({
            destination: Cesium.Rectangle.fromDegrees(...newBox),
            offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 0),
            duration: 0.5,
        })
        let pointList = await getPointPositionsHeight(positions, viewer)
        let pointList = await getPointPositionsHeight(normalizedPositions, viewer)
        flyVisual({ positionsData: pointList.map(item => [item.lng, item.lat, item.ASL]), viewer })
        this.drawingMode = false
@@ -748,6 +746,20 @@
        this.rebuildMidPoints()
    }
    // 规范化多边形点位:如果首尾重复,去掉末尾闭合点
    normalizePolygonPositions(positions) {
        if (!Array.isArray(positions) || positions.length < 2) return positions || []
        const first = positions[0]
        const last = positions[positions.length - 1]
        const sameLng = Number(first?.lng) === Number(last?.lng)
        const sameLat = Number(first?.lat) === Number(last?.lat)
        const sameHeight = Number(first?.height || 0) === Number(last?.height || 0)
        if (sameLng && sameLat && sameHeight) {
            return positions.slice(0, -1)
        }
        return positions
    }
    // 初始化事件处理器
    initHandler(viewer) {
        this.viewer = viewer
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -42,7 +42,9 @@
                        </el-col>
                        <el-col :span="24">
                            <div class="label">触发条件</div>
                            <div class="val">{{ formData.triggerCondition }}</div>
                            <div class="val">
                                {{ getDictLabel(formData.triggerCondition, dictObj.triggeringCondition) }}
                            </div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">响应机制</div>
@@ -102,8 +104,11 @@
                            maxlength="200" placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="触发条件" prop="triggerCondition">
                        <el-input class="command-input" v-model="formData.triggerCondition"
                            maxlength="200" placeholder="请输入" clearable />
                        <el-select class="command-select" popper-class="command-select-popper"
                            v-model="formData.triggerCondition" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.triggeringCondition" :key="item.dictKey"
                                :label="item.dictValue" :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="管控级别" prop="controlLevel">
                        <el-select class="command-select" popper-class="command-select-popper"
@@ -410,7 +415,7 @@
// 获取设备列表
async function getDeviceList () {
    if (deviceOptions.value.length) return
    const res = await fwDeviceListApi({ isAreaSelect: 1, areaId: formData.value.id })
    const res = await fwDeviceListApi({ isAreaSelect: 1, areaId: formData.value.id, isTrack: 1 })
    deviceOptions.value = res?.data?.data ?? []
    if (dialogMode.value === 'view') {
        deviceOptions.value = deviceOptions.value.filter(item => formData.value.deviceIds.includes(item.id))
applications/drone-command/src/views/areaManage/partition/index.vue
@@ -42,7 +42,11 @@
                            {{ getDictLabel(row.areaType, dictObj.areaType) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="triggerCondition" show-overflow-tooltip width="130" label="触发条件" />
                    <el-table-column prop="triggerCondition" show-overflow-tooltip width="130" label="触发条件">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.triggerCondition, dictObj.triggeringCondition) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="responseMechanism" show-overflow-tooltip width="130" label="响应机制" />
                    <el-table-column prop="controlLevel" show-overflow-tooltip width="120" label="管控级别">
                        <template v-slot="{ row }">
@@ -168,13 +172,14 @@
const dictObj = ref({
    areaType: [], //区域类型
    controlLevel: [], //管控级别
    triggeringCondition: [], //触发条件
    deviceType: [],
})
provide('dictObj', dictObj)
// 获取字典
function getDictList() {
    getDictionaryByCode('areaType,controlLevel,deviceType').then(res => {
    getDictionaryByCode('areaType,controlLevel,deviceType,triggeringCondition').then(res => {
        dictObj.value = res.data.data
    })
}
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2026-01-08 09:29:07
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-27 12:01:37
 * @LastEditTime : 2026-01-27 14:49:56
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RealWarning.vue
 * @Description  : 
 * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -10,7 +10,7 @@
-->
<template>
    <div class="real-warning" v-loading="props.loading" element-loading-background="rgba(5, 5, 15, 0.6)"
        element-loading-text="???...">
        element-loading-text="加载中...">
        <div class="list-content">
            <EmptyState v-if="!props.data.length && !props.loading" />
            <RealTemplate v-else v-for="(item, ind) in props.data" :key="ind" :data="item"
applications/drone-command/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue
@@ -48,7 +48,7 @@
            <div class="row">
                <div class="col">
                    <span class="label">有效范围</span>
                    {{ data.range }}
                    {{ Number(data.range).toFixed(0) }}m
                </div>
                <div class="col">
                    <span class="label">设备型号</span>
applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue
@@ -71,14 +71,14 @@
                <el-col :span="12">
                    <el-form-item label="部门类型" prop="deptCategory">
                        <el-select class="command-select" popper-class="command-select-popper" v-model="formData.deptCategory" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.org_category" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
                            <el-option v-for="item in dictObj.org_category" :key="item.dictKey" :label="item.dictValue" :value="Number(item.dictKey)" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="部门性质" prop="deptNature">
                        <el-select class="command-select" popper-class="command-select-popper" v-model="formData.deptNature" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.org_nature" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
                            <el-option v-for="item in dictObj.org_nature" :key="item.dictKey" :label="item.dictValue" :value="Number(item.dictKey)" />
                        </el-select>
                    </el-form-item>
                </el-col>
@@ -137,9 +137,9 @@
    fullName: '',
    parentId: '',
    parentName: '',
    deptCategory: '',
    deptNature: '',
    areaCode: '',
    deptCategory: null,
    deptNature: null,
    areaCode: [],
    areaName: '',
    deploymentMode: 0,
    sort: 0,
@@ -154,7 +154,7 @@
const areaProps = {
    label: 'title',
    value: 'value',
    emitPath: false,
    emitPath: true,
    checkStrictly: true,
    lazy: true,
    lazyLoad(node, resolve) {
@@ -230,19 +230,26 @@
}
function getFullAreaCode(areaCode) {
    if (!areaCode) return ''
    if (!areaCode) return []
    if (Array.isArray(areaCode)) return areaCode
    const code = areaCode.toString()
    if (code.includes(',')) return code
    if (code.includes(',')) return code.split(',')
    if (code.endsWith('0000000000')) {
        return code
        return [code]
    } else if (code.endsWith('00000000') && !code.endsWith('0000000000')) {
        const provinceCode = code.substring(0, 2) + '0000000000'
        return `${provinceCode},${code}`
        return [provinceCode, code]
    } else {
        const provinceCode = code.substring(0, 2) + '0000000000'
        const cityCode = code.substring(0, 4) + '00000000'
        return `${provinceCode},${cityCode},${code}`
        return [provinceCode, cityCode, code]
    }
}
function normalizeNumber(value) {
    if (value === '' || value === null || value === undefined) return null
    const num = Number(value)
    return Number.isNaN(num) ? null : num
}
function handleCancel() {
@@ -283,6 +290,8 @@
    const data = res?.data?.data ?? {}
    formData.value = {
        ...data,
        deptCategory: normalizeNumber(data.deptCategory),
        deptNature: normalizeNumber(data.deptNature),
        areaCode: getFullAreaCode(data.areaCode),
    }
}
applications/drone-command/src/views/permissionManage/permissionRole/index.vue
@@ -36,7 +36,7 @@
                color="#1A2652"
                type="primary"
                :disabled="selectedIds.length !== 1"
                v-if="userInfo.role_name?.includes('admin')"
                v-if="permission.permissionRole_set"
                @click="handleRole"
            >
                权限设置
@@ -113,7 +113,7 @@
const dialogRef = ref(null)
const roleDialogRef = ref(null)
const store = useStore()
const userInfo = computed(() => store.getters.userInfo || {})
const permission = computed(() => store.getters.permission || {})
const route = useRoute()
async function loadList() {
applications/task-work-order/src/styles/common/cockpit.scss
@@ -475,6 +475,7 @@
    .detail-container {
      display: flex;
      justify-content: space-between;
      height: 680px;
      .detail-left {
        margin-right: 20px;
@@ -484,16 +485,21 @@
      }
      .detail-right {
        &>div {
          .detail-title {
            margin: 10px 0;
          }
        width: 920px;
        flex: 1;
        overflow: auto;
        display: flex;
        flex-direction: column;
          &:first-child {
            .detail-title {
              margin-top: 0;
            }
          }
        .view-container {
          display: flex;
          flex-direction: column;
        }
        .gd-cesium {
          width: 100%;
          min-height: 380px;
          flex: 1;
        }
      }
    }
applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/index.vue
@@ -45,7 +45,7 @@
                <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column label="序号" width="70" align="center">
                        <template #default="{ $index }">
                            {{ formatNumber((searchParams.current - 1) * searchParams.size + $index + 1) }}
                            {{ $index + 1 }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="caseName" show-overflow-tooltip label="案例名称" />
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue
@@ -45,7 +45,7 @@
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="70">
                        <template v-slot="{ $index }">
                            {{ String($index + 1).padStart(2, '0') }}
                            {{ $index + 1 }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="title" show-overflow-tooltip label="标题" />
@@ -63,7 +63,7 @@
                    <el-table-column prop="otherObjectionDetail" show-overflow-tooltip label="其他异议详情" />
                    <el-table-column prop="submitter" show-overflow-tooltip label="问题提交人" />
                    <el-table-column prop="submitterContact" show-overflow-tooltip width="120" label="提交人联系方式" />
                    <el-table-column prop="submitUnitName" show-overflow-tooltip label="提交单位" />
                    <el-table-column prop="createDeptName" show-overflow-tooltip label="提交单位" />
                    <el-table-column prop="handleUnit" show-overflow-tooltip label="处理单位" />
                    <el-table-column prop="objectionDesc" show-overflow-tooltip label="异议描述" />
                    <el-table-column prop="objectionBasis" show-overflow-tooltip label="异议依据" />
applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue
@@ -39,7 +39,7 @@
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="80">
                        <template v-slot="{ $index }">
                            {{ ((searchParams.current - 1) * searchParams.size + $index + 1).toString().padStart(2, '0') }}
                            {{ $index + 1 }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="title" show-overflow-tooltip label="标题" />
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/index.vue
@@ -44,7 +44,7 @@
                <el-table class="gd-table" :data="list">
                    <el-table-column label="序号" width="70" align="center">
                        <template #default="scope">
                            {{ ((searchParams.current - 1) * searchParams.size + scope.$index + 1).toString().padStart(2, '0') }}
                            {{ scope.$index + 1 }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="demandName" show-overflow-tooltip label="需求名称" />
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
@@ -5,11 +5,10 @@
        :title="titleEnum[dialogMode]"
        @closed="visible = false"
        destroy-on-close
        width="auto"
        :close-on-click-modal="false"
    >
        <div class="content" style="display: flex">
            <div class="processBox" v-if="dialogMode !== 'add' && processList.length">
        <div class="detail-container" style="display: flex">
            <div class="detail-left" v-if="dialogMode !== 'add' && processList.length">
                <el-timeline class="gd-timeline">
                    <el-timeline-item
                        v-for="(activity, index) in processList"
@@ -27,9 +26,10 @@
                    </el-timeline-item>
                </el-timeline>
            </div>
            <div class="leftBox">
            <div class="detail-right">
                <!-- 查看模式 -->
                <div class="detail-container" v-if="dialogReadonly">
                <div class="view-container" v-if="dialogReadonly">
                    <div class="detail-title">工单详情</div>
                    <el-row class="detail-row-view">
                        <el-col :span="12">
@@ -507,52 +507,4 @@
</script>
<style lang="scss" scoped>
.content {
    display: flex;
    gap: 0 20px;
    height: 680px;
    .leftBox {
        width: 920px;
        flex: 1;
        overflow: auto;
        display: flex;
        flex-direction: column;
        .gd-cesium {
            width: 100%;
            min-height: 380px;
            flex: 1;
        }
    }
    .processBox {
        width: 312px;
        padding: 24px 0;
        border: 1px solid #e4e4e4;
        overflow: auto;
        .item-content {
            position: relative;
            .flowName {
                width: 80px;
                position: absolute;
                left: -120px;
                top: 0px;
            }
        }
    }
    .imgBox {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        > div {
            width: calc((100% - 40px) / 5); /* 5列布局,减去4个10px间隙 */
            height: 110px;
            flex-shrink: 0;
        }
        .el-image {
            width: 100%;
            height: 100%;
        }
    }
}
</style>
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
@@ -245,8 +245,10 @@
// 懒加载获取区域节点数据
async function loadRegionNode(node, resolve) {
  try {
    const parentCode = node.data?.id  || '360800000000'
    const res = await regionLazyTreeApi({ parentCode })
    // 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
    const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
    const parentCode = node.data?.id || '360800000000'
    const res = await regionLazyTreeApi(params)
    const nodes = res?.data?.data || []
    
    // 处理返回的节点数据
@@ -273,29 +275,10 @@
    resolve([])
  }
}
// 获取初始区域数据(根节点)
async function getRegionList() {
  try {
    // 懒加载模式下,只需要初始化根节点
    const res = await regionLazyTreeApi({ parentCode: '360800000000' })
    const nodes = res?.data?.data || []
    // 处理根节点数据
    regionOptions.value = nodes.map(item => {
      return {
        id: item.id || item.value,
        name: item.name || item.title || item.label,
        hasChildren: item.hasChildren || false
      }
    })
  } catch (error) {
    console.error('获取区域数据失败:', error)
  }
}
defineExpose({ open })
onMounted(() => {
  getRegionList()
})
</script>
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
@@ -56,7 +56,7 @@
        <el-table class="gd-table" :data="list">
          <el-table-column label="序号" width="80">
            <template v-slot="{ $index }">
              {{ ((searchParams.current - 1) * searchParams.size + $index + 1).toString().padStart(2, '0') }}
              {{ $index + 1 }}
            </template>
          </el-table-column>
          <el-table-column prop="deptCode" show-overflow-tooltip label="机构编码" />
@@ -68,7 +68,7 @@
             {{ getDictLabel(row.status, dictObj.institutionStatus) }}
            </template>
          </el-table-column>
          <el-table-column label="操作" class-name="operation-btns" width="310">
          <el-table-column label="操作" class-name="operation-btns" width="400">
            <template v-slot="{ row }">
              <el-link type="primary" @click="openForm('view', row)">查看</el-link>
              <el-link type="primary"  @click="openForm('edit', row)">编辑</el-link>
@@ -197,8 +197,9 @@
async function loadRegionNode(node, resolve) {
  try {
    const parentCode = node.data?.id || '360800000000'
    const res = await regionLazyTreeApi({ parentCode })
    // 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
    const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
    const res = await regionLazyTreeApi(params)
    const nodes = res?.data?.data || []
    
    // 处理返回的节点数据
@@ -217,25 +218,7 @@
  }
}
// 获取初始区域数据(根节点)
async function getRegionList() {
  try {
    // 懒加载模式下,只需要初始化根节点
    const res = await regionLazyTreeApi({ parentCode: '360800000000' })
    const nodes = res?.data?.data || []
    // 处理根节点数据
    regionOptions.value = nodes.map(item => {
      return {
        id: item.id || item.value,
        name: item.name || item.title || item.label,
        hasChildren: item.hasChildren || false
      }
    })
  } catch (error) {
    console.error('获取区域数据失败:', error)
  }
}
// 获取列表
async function getList() {
@@ -384,7 +367,6 @@
onMounted(() => {
  getDictList()
  getRegionList()
  getList()
})
</script>
applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue
New file
@@ -0,0 +1,316 @@
<template>
  <el-dialog
    class="gd-dialog"
    v-model="visible"
    :title="titleEnum[dialogMode]"
    @closed="visible = false"
    destroy-on-close
    :close-on-click-modal="false"
  >
    <el-row class="detail-row-view" v-if="dialogReadonly">
      <el-col :span="12">
        <div class="label">区划代码</div>
        <div class="val">{{ formData.code }}</div>
      </el-col>
      <el-col :span="12">
        <div class="label">区划名称</div>
        <div class="val">{{ formData.name }}</div>
      </el-col>
      <el-col :span="12">
        <div class="label">上级区划</div>
        <div class="val">{{ formData.parentName }}</div>
      </el-col>
      <el-col :span="12">
        <div class="label">区划级别</div>
        <div class="val"> {{ getDictLabel(formData.regionLevel, dictObj.divisionLevel) }}</div>
      </el-col>
      <el-col :span="12">
        <div class="label">区划排序</div>
        <div class="val">{{ formData.sort }}</div>
      </el-col>
      <el-col :span="12">
        <div class="label">区划状态</div>
        <div class="val"> {{ getDictLabel(formData.status, dictObj.zoningStatus) }}</div>
      </el-col>
    </el-row>
    <el-form
      class="gd-dialog-form"
      v-else
      ref="formRef"
      :model="formData"
      :rules="rules"
      :disabled="dialogReadonly"
      label-width="140px"
    >
      <el-row>
        <el-col :span="12">
          <el-form-item label="区划代码" prop="code">
            <el-input class="gd-input" v-model="formData.code" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="区划名称" prop="name">
            <el-input class="gd-input" v-model="formData.name" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="上级区划" prop="parentCode">
            <el-tree-select
              class="gd-select"
              popper-class="gd-tree-select-popper"
              v-model="formData.parentCode"
              node-key="id"
              :default-expanded-keys="expandedKeys"
              :props="{
                value: 'id',
                label: 'name',
                children: 'children'
              }"
              placeholder="请选择"
              clearable
              filterable
              :check-strictly="true"
              lazy
              :load="loadRegionNode"
              :render-after-expand="false"
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="区划级别" prop="regionLevel">
            <el-select
              class="gd-select"
              popper-class="gd-select-popper"
              v-model="formData.regionLevel"
              placeholder="请选择"
              clearable
            >
              <el-option v-for="item in dictObj.divisionLevel" :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey" />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="区划排序" prop="sort">
            <el-input class="gd-input" v-model="formData.sort" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="区划状态" prop="status">
            <el-select
              class="gd-select"
              popper-class="gd-select-popper"
              v-model="formData.status"
              placeholder="请选择"
              clearable
            >
              <el-option v-for="item in dictObj.zoningStatus" :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey" />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer v-if="!dialogReadonly">
      <el-button color="#F2F3F5" @click="visible = false">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
      <el-button
        class="save-btn"
        color="#4C34FF"
        :loading="submitting"
        :disabled="submitting"
        @click="handleSubmit"
      >
        保存
      </el-button>
    </template>
  </el-dialog>
</template>
<script setup>
import { getDictLabel } from '@ztzf/utils'
import { computed, ref, inject, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { fieldRules } from '@ztzf/utils'
import {
  zoningSubmitApi,
  zoningDetailApi,
  regionLazyTreeApi
} from './zoningApi'
// 初始化表单数据
const initForm = () => ({
  code: '',
  name: '',
  parentCode: '',
  regionLevel: '',
  sort: '',
  status: '',
  id: null,
  parentName: ''
})
const dictObj = inject('dictObj')
const regionOptions = inject('regionOptions')
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
const expandedKeys = ref([]) // 树形选择器展开的节点
const regionNodeCache = ref(new Map()) // 缓存区域节点及其父节点关系
const rules = {
  code: fieldRules(true),
  name: fieldRules(true),
  parentCode: fieldRules(true),
  regionLevel: fieldRules(true),
  sort: fieldRules(true),
  status: fieldRules(true),
}
// 提交新增/编辑
async function handleSubmit() {
  const isValid = await formRef.value?.validate().catch(() => false)
  if (!isValid) return
  submitting.value = true
  try {
    await zoningSubmitApi(formData.value)
    ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
    visible.value = false
    emit('success')
  } finally {
    submitting.value = false
  }
}
// 加载详情
async function loadDetail() {
  if (!formData.value.id) return
  const res = await zoningDetailApi({ id: formData.value.id })
  const data = res?.data?.data ?? {}
  // 将 status 和 regionLevel 转换为字符串类型,与字典中的 dictKey 类型保持一致
  if (data.status !== undefined) {
    data.status = String(data.status)
  }
  if (data.regionLevel !== undefined) {
    data.regionLevel = String(data.regionLevel)
  }
  // 将 parentCode 转换为字符串类型,与 regionOptions 中的 value 类型保持一致
  if (data.parentCode !== undefined) {
    data.parentCode = String(data.parentCode)
  }
  formData.value = data
  // 如果是编辑模式,获取区域节点的路径以展开树形选择器
  if (dialogMode.value === 'edit' && data.parentCode) {
    await loadRegionPath(data.parentCode)
  }
}
// 加载区域节点路径
async function loadRegionPath(parentCode) {
  try {
    const path = []
    const queue = [{ code: '360800000000', currentPath: ['360800000000'] }]
    let found = false
    // 广度优先搜索,逐层加载节点直到找到目标节点
    while (queue.length > 0 && !found) {
      const { code, currentPath } = queue.shift()
      // 获取当前节点的子节点
      const res = await regionLazyTreeApi({ parentCode: code })
      const nodes = res?.data?.data || []
      for (const node of nodes) {
        const nodeId = node.id || node.value
        // 缓存节点及其父节点关系
        regionNodeCache.value.set(nodeId, {
          id: nodeId,
          name: node.name || node.title || node.label,
          parentId: code,
          hasChildren: node.hasChildren || false
        })
        // 检查是否找到目标节点
        if (nodeId === parentCode) {
          path.push(...currentPath, nodeId)
          found = true
          break
        }
        // 如果有子节点,加入队列继续搜索
        if (node.hasChildren) {
          queue.push({
            code: nodeId,
            currentPath: [...currentPath, nodeId]
          })
        }
      }
    }
    if (path.length > 0) {
      expandedKeys.value = path
    }
  } catch (error) {
    console.error('加载区域路径失败:', error)
  }
}
// 打开弹框
async function open({ mode = 'add', row } = {}) {
  dialogMode.value = mode
  formData.value = dialogMode.value === 'add' ? initForm() : row
  if (dialogMode.value !== 'add') {
    await loadDetail()
  }
}
// 懒加载获取区域节点数据
async function loadRegionNode(node, resolve) {
  try {
    // 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
    const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
    const parentCode = node.data?.id || '360800000000'
    const res = await regionLazyTreeApi(params)
    const nodes = res?.data?.data || []
    // 处理返回的节点数据
    const processedNodes = nodes.map(item => {
      const nodeId = item.id || item.value
      // 缓存节点及其父节点关系
      regionNodeCache.value.set(nodeId, {
        id: nodeId,
        name: item.name || item.title || item.label,
        parentId: parentCode,
        hasChildren: item.hasChildren || false
      })
      return {
        id: nodeId,
        name: item.name || item.title || item.label,
        hasChildren: item.hasChildren || false
      }
    })
    resolve(processedNodes)
  } catch (error) {
    console.error('获取区域数据失败:', error)
    resolve([])
  }
}
defineExpose({ open })
onMounted(() => {
})
</script>
<style scoped lang="scss">
</style>
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
@@ -1,19 +1,336 @@
<template>
 <div>
    区划管理
 </div>
  <basic-container>
    <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
      <el-form-item label="上级区划名称" prop="parentName">
        <el-input
          class="gd-input gray"
          v-model="searchParams.parentName"
          placeholder="请输入"
          clearable
          @clear="handleSearch"
        />
      </el-form-item>
      <el-form-item label="区划名称" prop="districtName">
        <el-input
          class="gd-input gray"
          v-model="searchParams.districtName"
          placeholder="请输入"
          clearable
          @clear="handleSearch"
        />
      </el-form-item>
      <el-form-item label="区划代码" prop="cityCode">
        <el-input
          class="gd-input gray"
          v-model="searchParams.cityCode"
          placeholder="请输入"
          clearable
          @clear="handleSearch"
        />
      </el-form-item>
      <el-form-item class="gd-search-actions">
        <el-button :icon="RefreshRight" @click="resetForm"></el-button>
        <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
      </el-form-item>
    </el-form>
    <div class="gd-table-toolbar">
      <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增区划</el-button>
      <el-button :icon="Upload" @click="handleImport">导入区划</el-button>
      <el-button :icon="Download" @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-column label="序号" width="80">
            <template v-slot="{ $index }">
               {{ $index + 1 }}
            </template>
          </el-table-column>
          <el-table-column prop="code" show-overflow-tooltip label="区划代码" />
          <el-table-column prop="name" show-overflow-tooltip label="区划名称" />
          <el-table-column prop="parentName" show-overflow-tooltip label="上级区划名称" />
          <el-table-column prop="regionLevel" show-overflow-tooltip label="区划级别">
            <template v-slot="{ row }">
              {{ getDictLabel(row.regionLevel, dictObj.divisionLevel) }}
            </template>
          </el-table-column>
          <el-table-column prop="sort" show-overflow-tooltip label="排序" />
          <el-table-column prop="status" show-overflow-tooltip label="区划状态">
            <template v-slot="{ row }">
              {{ getDictLabel(row.status, dictObj.zoningStatus) }}
            </template>
          </el-table-column>
          <el-table-column label="操作" class-name="operation-btns" width="400">
            <template v-slot="{ row }">
              <el-link type="primary" @click="openForm('view', row)">查看</el-link>
              <el-link type="primary"  @click="openForm('edit', row)">编辑</el-link>
              <el-link type="primary" @click="handleDelete(row)">删除</el-link>
              <el-link type="primary" @click="toggleStatus(row)">{{row.status === 1 ? '禁用' : '启用'}}</el-link>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <div class="gd-pagination-parent">
        <el-pagination
          popper-class="gd-select-popper"
          v-model:current-page="searchParams.current"
          v-model:page-size="searchParams.size"
          layout="total, prev, pager, next, sizes"
          :total="total"
          @change="getList"
        />
      </div>
    </div>
    <FormDiaLog ref="dialogRef" @success="getList" v-if="dialogVisible" v-model="dialogVisible" />
    <!-- 导入区划弹框 -->
    <el-dialog
      class="gd-dialog"
      append-to-body
      v-model="isShowImportView"
      title="导入区划"
      :width="pxToRem(600)"
      :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="上传文件" prop="file">
          {{ importFileName }}
          <el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onImportFileBefore">
            <el-button size="small" type="primary">点击上传</el-button>
          </el-upload>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button color="#F2F3F5" @click="isShowImportView = false">取消</el-button>
        <el-button
          class=""
          color="#4C34FF"
          :loading="importSubmitting"
          :disabled="importSubmitting"
          @click="submitImport(importFormRef)"
        >
          导入
        </el-button>
      </template>
    </el-dialog>
  </basic-container>
</template>
<script setup >
/**
 * @description index
 * @date 2026-01-26 (周一) 10:50:43
 */
defineOptions({
 name: 'index'
<script setup>
import { Search, RefreshRight, Plus, Upload, Download } from '@element-plus/icons-vue'
import { onMounted, ref, provide, nextTick } from 'vue'
import { ElMessage, ElMessageBox, ElUpload } from 'element-plus'
import FormDiaLog from './FormDiaLog.vue'
import {
  zoningPageApi,
  zoningRemoveApi,
  zoningImportApi,
  zoningExportApi,
  regionLazyTreeApi,
  zoningSubmitApi
} from './zoningApi'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDictLabel } from '@ztzf/utils'
import { pxToRem } from '@/utils/rem'
// 初始化查询参数
const initSearchParams = () => ({
  parentName: '', // 上级区划名称
  districtName: '', // 区划名称
  cityCode: '', // 区划代码
  current: 1, // 当前页
  size: 10, // 每页大小
})
const searchParams = ref(initSearchParams()) // 查询参数
const total = ref(0) // 总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dialogVisible = ref(false)
const regionOptions = ref([]) // 区域选项
const dictObj = ref({}) // 字典对象
// 导入区划相关
const isShowImportView = ref(false)
const importFileName = ref('')
const importParams = ref({
  isCovered: '0',
  file: '',
})
const importFormRef = ref()
const importSubmitting = ref(false)
const importRules = ref({
  file: [{ required: true, message: '请上传文件', trigger: ['change'] }],
})
provide('regionOptions', regionOptions)
provide('dictObj', dictObj)
// 获取字典列表
function getDictList() {
    return getDictionaryByCode('zoningStatus,divisionLevel').then(res => {
        dictObj.value = res.data.data
    })
}
// 获取列表
async function getList() {
  loading.value = true
  try {
    const res = await zoningPageApi(searchParams.value)
    list.value = res?.data?.data?.records ?? []
    total.value = res?.data?.data?.total ?? 0
  } finally {
    loading.value = false
  }
}
// 查询
function handleSearch() {
  searchParams.value.current = 1
  getList()
}
// 重置查询
function resetForm() {
  queryParamsRef.value?.resetFields()
  searchParams.value.current = 1
  getList()
}
// 新增/编辑/查看 弹框
function openForm(mode, row) {
  dialogVisible.value = true
  nextTick(() => {
    dialogRef.value?.open({ mode, row })
  })
}
// 删除
async function handleDelete(row) {
  await ElMessageBox.confirm('确认删除该条记录吗?', '提示', {
    type: 'warning',
    customClass: 'gd-confirm-custom',
    confirmButtonClass: 'gd-confirm-button',
    cancelButtonClass: 'gd-confirm-cancel-button',
  })
  await zoningRemoveApi({ id: row.id })
  ElMessage.success('删除成功')
  getList()
}
// 切换区划状态
async function toggleStatus(row) {
  // 计算目标状态:当前为1则切换为0,当前为0则切换为1
  const targetStatus = row.status === 1 ? 0 : 1
  const actionText = targetStatus === 0 ? '禁用' : '启用'
  await ElMessageBox.confirm(`确认${actionText}该区划吗?`, '提示', {
    type: 'warning',
    customClass: 'gd-confirm-custom',
    confirmButtonClass: 'gd-confirm-button',
    cancelButtonClass: 'gd-confirm-cancel-button',
  })
  try {
    await zoningSubmitApi({ ...row, status: targetStatus })
    ElMessage.success(`${actionText}成功`)
    getList()
  } catch (error) {
    ElMessage.error(`${actionText}失败`)
  }
}
// 导入区划
function handleImport() {
  isShowImportView.value = true
}
// 导入区划弹框关闭
function handleImportClose() {
  importFileName.value = ''
  importParams.value = {
    isCovered: '0',
    file: '',
  }
}
// 上传文件前处理
function onImportFileBefore(file) {
  // 检查文件类型
  const allowedTypes = ['.xlsx', '.xls']
  const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
  if (!allowedTypes.includes(fileExtension)) {
    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 || '导入失败')
    }
  })
  return false // 阻止组件的默认上传行为
}
// 提交导入
async function submitImport(formValidate) {
  if (!formValidate) return
  await formValidate.validate(async (valid, fields) => {
    if (valid) {
      importSubmitting.value = true
      try {
        if (!importParams.value.file) {
          ElMessage.error('请先上传文件')
          return
        }
        // 触发上传操作
        await onImportFileBefore(importParams.value.file)
      } finally {
        importSubmitting.value = false
      }
    }
  })
}
// 导出区划
function handleExport() {
  zoningExportApi(searchParams.value).then(res => {
    const blob = new Blob([res.data])
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `区划列表${new Date().getTime()}.xlsx`
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
  })
}
onMounted(() => {
  getDictList()
  getList()
})
</script>
<style scoped lang="scss">
applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
New file
@@ -0,0 +1,66 @@
import request from '@/axios'
// 列表
export const zoningPageApi = params => {
  return request({
    url: `/blade-system/region/page`,
    method: 'get',
    params: { ...params },
  })
}
// 新增或编辑
export const zoningSubmitApi = data => {
  return request({
    url: `/blade-system/region/submit`,
    method: 'post',
    data,
  })
}
// 删除
export const zoningRemoveApi = params => {
  return request({
    url: `/blade-system/region/remove`,
    method: 'post',
    params,
  })
}
// 详情
export const zoningDetailApi = params => {
  return request({
    url: `/blade-system/region/detail`,
    method: 'get',
    params,
  })
}
// 导入区划
export const zoningImportApi = (data,params) => {
  return request({
    url: `/blade-system/region/import-region`,
    method: 'post',
    data,
    params
  })
}
// 导出区划
export const zoningExportApi = params => {
  return request({
    url: `/blade-system/region/export-region`,
    method: 'get',
    params,
    responseType: 'blob',
  })
}
// 懒加载列表
export const regionLazyTreeApi = params => {
  return request({
    url: `/blade-system/region/lazy-tree`,
    method: 'get',
    params,
  })
}
uniapps/work-app/src/subPackages/workDetail/index.vue
@@ -158,7 +158,11 @@
    uni.showToast({
      title: '退回成功',
      icon: 'success',
      duration: 1500
    })
    setTimeout(() => {
      uni.navigateBack()
    }, 1500)
  })
}
@@ -169,7 +173,11 @@
    uni.showToast({
      title: '确认成功',
      icon: 'success',
      duration: 1500
    })
    setTimeout(() => {
      uni.navigateBack()
    }, 1500)
  })
}
// 分享模态框状态