applications/drone-command/public/fonts/DinAB.TTFBinary files differ
applications/drone-command/public/fonts/font.css
@@ -52,3 +52,10 @@ font-display: swap; } @font-face { font-family: "DinAB"; src: url("DinAB.TTF") format("truetype"); font-weight: normal; font-style: normal; font-display: swap; } applications/drone-command/src/components/map-container/device-map-container.vue
@@ -22,7 +22,15 @@ <div v-if="popupVisible" class="device-popup" :style="popupStyle" @click.stop> <div class="device-popup__header"> <span class="device-popup__title">{{ popupTitle }}</span> <el-tooltip class="device-popup__title-tooltip" :content="popupTitle" placement="top" effect="dark" :show-after="200" > <span class="device-popup__title">{{ popupTitleDisplay }}</span> </el-tooltip> <img class="device-popup__close" :src="popupClose" alt="" @click.stop="closePopup"> </div> @@ -579,7 +587,13 @@ top: `${popupPosition.value.y}px`, })) const POPUP_TITLE_MAX = 12 const popupTitle = computed(() => selectedDevice.value?.deviceName || selectedDevice.value?.deviceModel || '-') const popupTitleOverflow = computed(() => popupTitle.value.length > POPUP_TITLE_MAX) const popupTitleDisplay = computed(() => { if (!popupTitleOverflow.value) return popupTitle.value return `${popupTitle.value.slice(0, POPUP_TITLE_MAX)}……` }) const popupStatusText = computed(() => { const status = selectedDevice.value?.status if (status === 0) return '在线' @@ -828,6 +842,20 @@ border-radius: 0px 0px 5px 5px; } .device-popup__title-tooltip { flex: 1; min-width: 0; display: block; } .device-popup__title { display: inline-block; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .device-popup__close { cursor: pointer; } applications/drone-command/src/mac/index.vue
@@ -69,6 +69,7 @@ return { app: false, timeString: '', timeTimer: null, } }, computed: { @@ -116,6 +117,12 @@ this.startTimer() this.$store.dispatch('GetMenu') }, beforeDestroy () { if (this.timeTimer) { clearInterval(this.timeTimer) this.timeTimer = null } }, methods: { switchTheme () { this.$store.commit('SET_THEME_NAME', '') applications/drone-command/src/page/index/top/top-full.vue
@@ -6,11 +6,22 @@ import { fullscreenToggel, listenfullscreen } from 'utils/util'; export default { data() { return { fullscreenCleanup: null, }; }, computed: { ...mapGetters(['isFullScren']), }, mounted() { listenfullscreen(this.setScreen); this.fullscreenCleanup = listenfullscreen(this.setScreen); }, beforeDestroy() { if (this.fullscreenCleanup) { this.fullscreenCleanup(); this.fullscreenCleanup = null; } }, methods: { setScreen() { applications/drone-command/src/page/lock/index.vue
@@ -45,6 +45,7 @@ data() { return { time: '', timeTimer: null, passwd: '', passwdError: false, pass: false, @@ -52,10 +53,17 @@ }, created() { this.getTime(); setInterval(() => { if (this.timeTimer) clearInterval(this.timeTimer); this.timeTimer = setInterval(() => { this.getTime(); }, 1000); }, beforeDestroy() { if (this.timeTimer) { clearInterval(this.timeTimer); this.timeTimer = null; } }, mounted() {}, computed: { ...mapGetters(['userInfo', 'tag', 'lockPasswd']), applications/drone-command/src/page/login/index.vue
@@ -56,6 +56,7 @@ }, website: website, time: '', timeTimer: null, activeName: 'user', socialForm: { tenantId: '000000', @@ -82,6 +83,12 @@ computed: { ...mapGetters(['tagWel']), }, beforeDestroy() { if (this.timeTimer) { clearInterval(this.timeTimer); this.timeTimer = null; } }, props: [], methods: { // 动态获取系统名称 @@ -93,7 +100,8 @@ }) }, getTime() { setInterval(() => { if (this.timeTimer) clearInterval(this.timeTimer); this.timeTimer = setInterval(() => { this.time = this.$dayjs().format('YYYY-MM-DD HH:mm:ss'); }, 1000); }, applications/drone-command/src/styles/common/cockpit.scss
@@ -1065,24 +1065,37 @@ } .command-page-view-dialog { padding: 30px; margin: 0; padding: 20px; display: flex; flex-direction: column; width: 770px; background: #1A1A2A; border-radius: 6px 6px 6px 6px; border: 1px solid #2E2E46; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); .el-dialog__header { position: relative; span { font-family: Source Han Sans CN, Source Han Sans CN; font-weight: bold; font-size: 18px; color: #FFFFFF; line-height: 40px; text-align: left; font-style: normal; text-transform: none; } .el-button { width: auto; height: auto; display: flex; align-items: center; } .el-icon { @@ -1145,6 +1158,7 @@ height: 360px; .command-table-pagination { padding-bottom: 20px; justify-content: center; } } @@ -1165,6 +1179,10 @@ .el-row { .el-col { margin-bottom: 0; .el-form-item { margin-bottom: 20px; } } } @@ -1288,6 +1306,7 @@ } .el-dialog__footer { padding-top: 0; display: flex; justify-content: center; @@ -1353,7 +1372,9 @@ } .command-page-map-view-dialog { padding: 30px; margin: 0; padding: 20px; display: flex; flex-direction: column; width: 80%; @@ -1362,6 +1383,11 @@ border-radius: 6px 6px 6px 6px; border: 1px solid #2E2E46; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); .el-dialog__header, .el-dialog__footer { margin: 0; applications/drone-command/src/utils/cesium/DrawPolygon.js
@@ -785,6 +785,7 @@ this.handler.removeInputAction(type) }) this.handler.destroy() this.handler = null } } applications/drone-command/src/utils/util.js
@@ -102,22 +102,21 @@ * esc监听全屏 */ export const listenfullscreen = callback => { function listen () { const handler = () => { callback() } document.addEventListener('fullscreenchange', function () { listen() }) document.addEventListener('mozfullscreenchange', function () { listen() }) document.addEventListener('webkitfullscreenchange', function () { listen() }) document.addEventListener('msfullscreenchange', function () { listen() }) document.addEventListener('fullscreenchange', handler) document.addEventListener('mozfullscreenchange', handler) document.addEventListener('webkitfullscreenchange', handler) document.addEventListener('msfullscreenchange', handler) return () => { document.removeEventListener('fullscreenchange', handler) document.removeEventListener('mozfullscreenchange', handler) document.removeEventListener('webkitfullscreenchange', handler) document.removeEventListener('msfullscreenchange', handler) } } /** * 获取缩略图路径 applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -1,7 +1,7 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]"> <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false"> <div class="detail-container" v-if="readonly"> <div class="detail-title">派出所详情</div> <el-row> <el-col :span="12"> <div class="label">派出所名称</div> applications/drone-command/src/views/basicManage/deviceScrap/FormDiaLog.vue
@@ -1,6 +1,6 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close> destroy-on-close :close-on-click-modal="false"> <div class="detail-container" v-if="dialogReadonly"> <el-row> <el-col :span="12"> applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue
@@ -1,5 +1,6 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close :close-on-click-modal="false"> <el-form class="dialog-form" ref="formRef" :model="formData" :rules="rules" label-width="100px"> <el-row> <el-col :span="24"> applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue
@@ -1,5 +1,6 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close :close-on-click-modal="false"> <el-form class="dialog-form" ref="formRef" :model="formData" :rules="rules" label-width="100px"> <el-row> <el-col :span="12"> applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue
@@ -1,6 +1,6 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> destroy-on-close :close-on-click-modal="false"> <div class="detail-container" v-if="dialogReadonly"> <div class="detail-title">设备详情</div> <el-row> applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue
@@ -1,6 +1,6 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close> destroy-on-close :close-on-click-modal="false"> <div class="detail-container" v-if="dialogReadonly"> <div class="detail-title">设备详情</div> <el-row> applications/drone-command/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue
@@ -5,9 +5,9 @@ :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close :close-on-click-modal="false" > <div class="detail-container" v-if="dialogReadonly"> <div class="detail-title">设备详情</div> <el-row> <el-col :span="12"> <div class="label">设备名称</div> applications/drone-command/src/views/dataCenter/components/dataCenterMap.vue
@@ -72,8 +72,11 @@ const removeDom = () => { const dom = document.querySelector('#mapPopUpBox'); if (dom && dom.parentNode) { dom.parentNode.removeChild(dom); if (dom) { render(null, dom); if (dom.parentNode) { dom.parentNode.removeChild(dom); } } }; // 移除弹框标签 @@ -290,6 +293,7 @@ onMounted(() => {}); onBeforeUnmount(() => { removeLabel(); if (viewer) { viewer.destroy(); } applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue
@@ -25,7 +25,7 @@ <div class="list"> <div class="item" v-for="(item, ind) in equipmentList" :key="ind"> <div class="val" :class="{highlight: item.isHighlight}" @click="equipmentClick(item.isHighlight)"> <div class="val" :class="{ highlight: item.isHighlight }" @click="equipmentClick(item.isHighlight)"> {{ item.value }} </div> @@ -158,7 +158,7 @@ display: flex; align-items: center; height: 100px; background: rgba(0,0,0,0.3); background: rgba(0, 0, 0, 0.3); box-sizing: border-box; border-radius: 16px 16px 16px 16px; pointer-events: auto; @@ -172,7 +172,7 @@ height: 100%; z-index: -1; background: rgba(17, 23, 34, 0.56); backdrop-filter: blur(2px); backdrop-filter: blur(2px); border-radius: 16px 16px 16px 16px; } @@ -208,7 +208,7 @@ } .val { font-family: Source Han Sans CN, Source Han Sans CN; font-family: DinAB; font-weight: bold; font-size: 30px; text-align: left; applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -2,7 +2,7 @@ * @Author : yuan * @Date : 2026-01-07 15:17:54 * @LastEditors : yuan * @LastEditTime : 2026-01-19 14:05:35 * @LastEditTime : 2026-01-20 09:19:16 * @FilePath : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue * @Description : * Copyright 2026 OBKoro1, All Rights Reserved. @@ -233,7 +233,7 @@ .val { margin-top: 10px; font-family: DIN, DIN; font-family: DinAB; font-weight: bold; font-size: 22px; color: #FFFFFF; applications/drone-command/src/views/dataCockpit/components/templateComponents/RealTemplate.vue
@@ -65,8 +65,8 @@ </div> <div class="footer"> <el-button v-if="showSignalBtn(data)" class="general" color="#2B2B4C" @click="emit('signal')">信号干扰</el-button> <el-button v-if="showCounterBtn(data)" color="#284FE3" type="primary" @click="emit('counter')">诱导驱离</el-button> <el-button class="general" color="#2B2B4C" @click="emit('signal')">信号干扰</el-button> <el-button color="#284FE3" type="primary" @click="emit('counter')">诱导驱离</el-button> </div> </div> </template> applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/FormDiaLog.vue
@@ -8,7 +8,6 @@ destroy-on-close > <div v-if="readonly" class="detail-container"> <div class="detail-title">配置详情</div> <el-row> <el-col :span="12"> <div class="label">起始扇区</div> @@ -94,7 +93,7 @@ /> </el-form-item> </el-col> <el-col :span="24"><h3 style="color: #ffffff">工作模式</h3></el-col> <el-col :span="24"><h3 style="color: #ffffff">设备参数</h3></el-col> <el-col :span="12"> <el-form-item label="伺服模式" prop="servoMode"> <el-select applications/drone-command/src/views/detectionCountermeasure/taskSchedule/FormDiaLog.vue
@@ -8,7 +8,6 @@ @closed="handleClosed" > <div v-if="readonly" class="detail-container"> <div class="detail-title">调度详情</div> <el-row> <el-col :span="12"> <div class="label">设备名称</div> applications/drone-command/src/views/job/jobinfo.vue
@@ -45,13 +45,21 @@ selectionList: [], option: option, data: [], searchChangeHandler: null, } }, created() { this.$bus?.on('triggerSearchChange', (params) => { this.searchChangeHandler = (params) => { this.searchChange(params, () => {}); }); }; this.$bus?.on('triggerSearchChange', this.searchChangeHandler); }, beforeDestroy() { if (this.searchChangeHandler) { this.$bus?.off('triggerSearchChange', this.searchChangeHandler); this.searchChangeHandler = null; } }, computed: { ...mapGetters(['permission']), permissionList () { applications/drone-command/src/views/layerManagement/components/utils.js
@@ -600,6 +600,7 @@ this.handler.removeInputAction(type) }) this.handler.destroy() this.handler = null } } applications/drone-command/src/views/layerManagement/index.vue
@@ -488,14 +488,15 @@ isPageLoading.value = status; }; provide('layerParams', layerParams); const focusOnNodeHandler = nodeData => { focusOnNode(nodeData); }; onMounted(() => { initMap(); cesiumContextMenu(); getdataFolderApi(); // 监听定位事件 EventBus.on('focusOnNode', (nodeData) => { focusOnNode(nodeData); }); EventBus.on('focusOnNode', focusOnNodeHandler); EventBus.on('deleteMapEntityById', deleteMapEntityById); EventBus.on('deleteMapEntitiesByFolderId', deleteMapEntitiesByFolderId); }); @@ -523,6 +524,7 @@ onBeforeUnmount(() => { destroyMap(); cesiumContextMenu(false); EventBus.off('focusOnNode', focusOnNodeHandler); EventBus.off('deleteMapEntityById', deleteMapEntityById); EventBus.off('deleteMapEntitiesByFolderId', deleteMapEntitiesByFolderId); }); applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue
@@ -1,7 +1,7 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close :close-on-click-modal="false"> <div class="detail-container" v-if="dialogReadonly"> <div class="detail-title">机构详情</div> <el-row> <el-col :span="12"> <div class="label">部门名称</div> applications/drone-command/src/views/permissionManage/permissionRole/FormDiaLog.vue
@@ -1,7 +1,7 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close :close-on-click-modal="false"> <div class="detail-container" v-if="dialogReadonly"> <div class="detail-title">角色详情</div> <el-row> <el-col :span="12"> <div class="label">角色名称</div> applications/drone-command/src/views/permissionManage/permissionUser/FormDiaLog.vue
@@ -1,5 +1,6 @@ <template> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close :close-on-click-modal="false"> <div class="detail-container" v-if="dialogReadonly"> <div class="detail-title">用户详情</div> <el-row> applications/drone-command/src/views/recordManage/historyTracks/index.vue
@@ -39,12 +39,22 @@ </el-form> <div class="command-table-toolbar"> <el-button :icon="Download" color="#1A2652" type="primary" @click="exportFile" :loading="exportLoading">导出</el-button> <el-button :icon="Download" color="#1A2652" type="primary" @click="exportFile" :loading="exportLoading" :disabled="!selectionList.length" > 导出 </el-button> </div> <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)"> <div class="command-table-content command-table-content-bg"> <el-table class="command-table" :data="list"> <el-table class="command-table" :data="list" ref="tableRef" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="48" /> <el-table-column type="index" show-overflow-tooltip width="64" label="序号" /> <el-table-column prop="droneName" show-overflow-tooltip width="140" label="无人机名称" /> <el-table-column prop="serialNo" show-overflow-tooltip label="无人机序列号" /> @@ -82,7 +92,7 @@ <script setup> import { Search, RefreshRight, Download } from '@element-plus/icons-vue' import { onMounted, ref } from 'vue' import { onMounted, ref, nextTick } from 'vue' import { exportFwDroneFlightRecordApi, fwDroneFlightRecordPageApi } from './fwDroneFlightRecord' import { blobDownload, dateRangeFormat } from '@ztzf/utils' import TrajectoryDiaLog from '@/views/recordManage/historyTracks/TrajectoryDiaLog.vue' @@ -97,6 +107,8 @@ const total = ref(0) // 总条数 const loading = ref(true) // 列表加载中 const list = ref([]) // 列表数据 const tableRef = ref(null) const selectionList = ref([]) const queryParamsRef = ref(null) // 查询表单实例 const dateRange = ref([]) const exportLoading = ref(false) @@ -111,6 +123,9 @@ const res = await fwDroneFlightRecordPageApi(params) list.value = res?.data?.data?.records ?? [] total.value = res?.data?.data?.total ?? 0 selectionList.value = [] await nextTick() tableRef.value?.clearSelection() } finally { loading.value = false } @@ -141,8 +156,14 @@ function exportFile() { const range = dateRangeFormat(dateRange.value) const ids = selectionList.value.map(item => item.id).filter(Boolean) exportLoading.value = true exportFwDroneFlightRecordApi({ ...searchParams.value, start_time: range[0], end_time: range[1] }) exportFwDroneFlightRecordApi({ ...searchParams.value, start_time: range?.[0], end_time: range?.[1], id: ids.join(','), }) .then(res => { blobDownload(res) }) @@ -151,6 +172,10 @@ }) } function handleSelectionChange(selection) { selectionList.value = selection || [] } function handleView(row) { dialogRef.value?.open({ mode: 'view', row: { ...row } }) } applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue
@@ -1,164 +1,154 @@ <template> <el-dialog class="gd-dialog" v-model="visible" :title="'查看申请'" @closed="visible = false" destroy-on-close > <div class="detail-container"> <!-- 左侧步骤条 --> <div class="detail-left"> <el-steps :active="activeStep" direction="vertical" align-center> <el-step v-for="(step, index) in steps" :key="index" :title="step.title"> <template #description> <div class="step-person">{{ step.person }}</div> <div class="step-time">{{ step.time }}</div> </template> </el-step> </el-steps> </div> <!-- 右侧表单数据 --> <div class="detail-right"> <div> <div class="label">需求详情</div> <div class="requirementsDetailsBox"> <div class="detail-table"> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">需求名称</span> <span class="cell-value">{{ formData.demandName }}</span> </div> <div class="detail-cell"> <span class="cell-label">需求类型</span> <span class="cell-value">{{ getDictLabel(formData.demandType, dictObj.requirementType) }}</span> <el-dialog class="gd-dialog" v-model="visible" :title="'查看申请'" @closed="visible = false" destroy-on-close> <div class="detail-container"> <!-- 左侧审批记录 --> <div class="detail-left"> <div class="label">审批记录</div> <el-steps :active="activeStep" direction="vertical" align-center> <el-step v-for="(step, index) in steps" :key="index" :title="step.title"> <template #description> <div class="step-person">{{ step.person }}</div> <div class="step-time">{{ step.time }}</div> </template> </el-step> </el-steps> </div> <!-- 右侧表单数据 --> <div class="detail-right"> <div> <div class="label">需求详情</div> <div class="requirementsDetailsBox"> <div class="detail-table"> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">需求名称</span> <span class="cell-value">{{ formData.demandName }}</span> </div> <div class="detail-cell"> <span class="cell-label">需求类型</span> <span class="cell-value">{{ getDictLabel(formData.demandType, dictObj.requirementType) }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">需求联系人</span> <span class="cell-value">{{ formData.contactPerson }}</span> </div> <div class="detail-cell"> <span class="cell-label">需求联系人电话</span> <span class="cell-value">{{ formData.contactPhone }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">需求邮箱</span> <span class="cell-value">{{ formData.contactEmail }}</span> </div> <div class="detail-cell"> <span class="cell-label">更新周期</span> <span class="cell-value">{{ formData.updateCycle }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">申请依据</span> <span class="cell-value">{{ formData.applyBasis }}</span> </div> <div class="detail-cell"> <span class="cell-label">共享类型</span> <span class="cell-value">{{ getDictLabel(formData.shareType, dictObj.sharedType) }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">应用场景类型</span> <span class="cell-value">{{ getDictLabel(formData.applicationScene, dictObj.appSceneType) }}</span> </div> <div class="detail-cell"> <span class="cell-label">责任部门</span> <span class="cell-value">{{ formData.responsibleDeptName }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">数据来源依据</span> <span class="cell-value">{{ formData.dataSource }}</span> </div> <div class="detail-cell"> <span class="cell-label">需求信息项</span> <span class="cell-value">{{ formData.demandInfo }}</span> </div> </div> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">需求联系人</span> <span class="cell-value">{{ formData.contactPerson }}</span> </div> <div class="detail-cell"> <span class="cell-label">需求联系人电话</span> <span class="cell-value">{{ formData.contactPhone }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">需求邮箱</span> <span class="cell-value">{{ formData.contactEmail }}</span> </div> <div class="detail-cell"> <span class="cell-label">更新周期</span> <span class="cell-value">{{ formData.updateCycle }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">申请依据</span> <span class="cell-value">{{ formData.applyBasis }}</span> </div> <div class="detail-cell"> <span class="cell-label">共享类型</span> <span class="cell-value">{{ getDictLabel(formData.shareType, dictObj.sharedType) }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">应用场景类型</span> <span class="cell-value">{{ getDictLabel(formData.applicationScene, dictObj.appSceneType) }}</span> </div> <div class="detail-cell"> <span class="cell-label">责任部门</span> <span class="cell-value">{{ formData.responsibleDeptName }}</span> </div> </div> <div class="detail-row"> <div class="detail-cell"> <span class="cell-label">数据来源依据</span> <span class="cell-value">{{ formData.dataSource }}</span> </div> <div class="detail-cell"> <span class="cell-label">需求信息项</span> <span class="cell-value">{{ formData.demandInfo }}</span> </div> <div> <div class="label">数据上传</div> <div class="upload-container"> <el-upload ref="uploadRef" :auto-upload="false" :limit="1" :on-change="handleFileChange" :on-remove="handleFileRemove" :before-upload="beforeFileUpload" :file-list="fileList" accept=".zip,.rar,.7z,.tar,.gz" class="upload-demo" > <template #trigger> <el-button type="primary">选择文件</el-button> </template> <template #tip> <div class="el-upload__tip">请上传压缩文件 (.zip/.rar/.7z/.tar/.gz)</div> </template> </el-upload> <el-button type="success" :disabled="!selectedFile" @click="handleUpload" style="margin-top: 10px"> 上传文件 </el-button> <!-- 已上传文件列表 --> <div v-if="uploadedFiles.length > 0" class="uploaded-files"> <h4>已上传文件:</h4> <div v-for="(file, index) in uploadedFiles" :key="index" class="file-item"> <span>{{ file.originalName }}</span> <el-button type="text" @click="viewFile(file)" style="margin-left: 10px">查看</el-button> <el-button type="text" @click="downloadFile(file)" style="margin-left: 5px">下载</el-button> </div> </div> </div> </div> </div> </div> <div> <div class="label">数据上传</div> <div class="upload-container"> <el-upload ref="uploadRef" :auto-upload="false" :limit="1" :on-change="handleFileChange" :on-remove="handleFileRemove" :before-upload="beforeFileUpload" :file-list="fileList" accept=".zip,.rar,.7z,.tar,.gz" class="upload-demo" > <template #trigger> <el-button type="primary">选择文件</el-button> </template> <template #tip> <div class="el-upload__tip"> 请上传压缩文件 (.zip/.rar/.7z/.tar/.gz) </div> </template> </el-upload> <el-button type="success" :disabled="!selectedFile" @click="handleUpload" style="margin-top: 10px;" > 上传文件 </el-button> <!-- 已上传文件列表 --> <div v-if="uploadedFiles.length > 0" class="uploaded-files"> <h4>已上传文件:</h4> <div v-for="(file, index) in uploadedFiles" :key="index" class="file-item"> <span>{{ file.originalName }}</span> <el-button type="text" @click="viewFile(file)" style="margin-left: 10px;">查看</el-button> <el-button type="text" @click="downloadFile(file)" style="margin-left: 5px;">下载</el-button> </div> </div> </div> </div> </div> </div> <template #footer> <el-button class="save-btn" color="#4C34FF" @click="rejectTheApplication" > 拒绝申请 </el-button> <el-button color="#F2F3F5" @click="approvedByTheReview">审核通过</el-button> <el-button class="save-btn" color="#4C34FF" @click="rejectTheApplication">拒绝申请</el-button> <el-button color="#F2F3F5" @click="approvedByTheReview">审核通过</el-button> </template> </el-dialog> <!-- 拒绝申请弹框 --> <reasonForRejectionDialog v-if="rejectionDialogVisible" v-model="rejectionDialogVisible" @confirm="handleRejectionConfirm" /> </template> <script setup> import { ref, inject } from 'vue' import { getDictLabel } from '@ztzf/utils' import { gdSupplyDemandDetailApi, putFileAttachApi,gdSupplyDemandAuditPassApi,gdSupplyDemandAuditRejectApi } from '@/views/orderView/orderDataManage/supplyAdd/supplyAddApi' import { ElMessage, ElMessageBox } from 'element-plus' import { gdSupplyDemandDetailApi, putFileAttachApi, gdSupplyDemandAuditPassApi, gdSupplyDemandAuditRejectApi, gdSupplyDemandAuditListApi } from '@/views/orderView/orderDataManage/supplyAdd/supplyAddApi' import { ElMessage } from 'element-plus' import reasonForRejectionDialog from './reasonForRejectionDialog.vue' const emit = defineEmits(['success']) const dictObj = inject('dictObj') const deptTree = inject('deptTree') const demandId = ref('') const visible = defineModel() // 弹框显隐 const formData = ref({}) // 表单数据 const uploadRef = ref() // 上传组件引用 @@ -167,131 +157,132 @@ const uploadedFiles = ref([]) // 已上传文件列表 const uploadLoading = ref(false) // 上传加载状态 const responseData = ref([]) // 拒绝申请弹框控制 const rejectionDialogVisible = ref(false) // 拒绝申请弹框显隐 // 步骤条数据 const steps = ref([ { title: '需求申请', person: '张三', time: '2025-03-31 12:23:30' }, { title: '审核通过', person: '', time: '' }, { title: '需求申请', person: '', time: '' }, { title: '审核通过', person: '', time: '' } ]) // 当前激活步骤 const activeStep = ref(0) const activeStep = ref(0) // 文件选择变化 const handleFileChange = (file, fileList) => { if (fileList.length > 0) { selectedFile.value = file.raw } else { selectedFile.value = null } if (fileList.length > 0) { selectedFile.value = file.raw } else { selectedFile.value = null } } // 文件移除 const handleFileRemove = () => { selectedFile.value = null selectedFile.value = null } // 上传前验证 const beforeFileUpload = (file) => { const allowedExtensions = ['.zip', '.rar', '.7z', '.tar', '.gz'] const fileExtension = '.' + file.name.split('.').pop().toLowerCase() if (!allowedExtensions.includes(fileExtension)) { ElMessage.error('只能上传压缩文件格式:.zip/.rar/.7z/.tar/.gz') return false } // 文件大小限制,这里限制为100MB const maxSize = 100 * 1024 * 1024 // 100MB if (file.size > maxSize) { ElMessage.error('文件大小不能超过100MB') return false } return true const beforeFileUpload = file => { const allowedExtensions = ['.zip', '.rar', '.7z', '.tar', '.gz'] const fileExtension = '.' + file.name.split('.').pop().toLowerCase() if (!allowedExtensions.includes(fileExtension)) { ElMessage.error('只能上传压缩文件格式:.zip/.rar/.7z/.tar/.gz') return false } // 文件大小限制,这里限制为100MB const maxSize = 100 * 1024 * 1024 // 100MB if (file.size > maxSize) { ElMessage.error('文件大小不能超过100MB') return false } return true } // 处理文件上传 const handleUpload = async () => { if (!selectedFile.value) { ElMessage.warning('请先选择文件') return } if (!selectedFile.value) { ElMessage.warning('请先选择文件') return } // 再次验证文件类型 if (!beforeFileUpload(selectedFile.value)) { return } // 再次验证文件类型 if (!beforeFileUpload(selectedFile.value)) { return } try { uploadLoading.value = true // 创建FormData对象 const formData = new FormData() formData.append('file', selectedFile.value) // 调用上传接口 const response = await putFileAttachApi(formData) if (response.data && response.data.success) { const fileData = response.data.data console.log('成功数据',fileData); responseData.value = fileData // 添加到已上传文件列表 uploadedFiles.value.push({ name: fileData.name, originalName: fileData.originalName, link: fileData.link, attachId: fileData.attachId }) ElMessage.success('文件上传成功!') // 清空上传列表 uploadRef.value.clearFiles() selectedFile.value = null // 这里可以将附件ID保存到表单数据中,供后续提交使用 // formData.value.attachmentId = fileData.attachId } else { ElMessage.error(response.data?.msg || '文件上传失败') } } catch (error) { console.error('文件上传失败:', error) ElMessage.error('文件上传失败,请重试') } finally { uploadLoading.value = false } try { uploadLoading.value = true // 创建FormData对象 const formData = new FormData() formData.append('file', selectedFile.value) // 调用上传接口 const response = await putFileAttachApi(formData) if (response.data && response.data.success) { const fileData = response.data.data console.log('成功数据', fileData) responseData.value = fileData // 添加到已上传文件列表 uploadedFiles.value.push({ name: fileData.name, originalName: fileData.originalName, link: fileData.link, attachId: fileData.attachId, }) ElMessage.success('文件上传成功!') // 清空上传列表 uploadRef.value.clearFiles() selectedFile.value = null // 这里可以将附件ID保存到表单数据中,供后续提交使用 // formData.value.attachmentId = fileData.attachId } else { ElMessage.error(response.data?.msg || '文件上传失败') } } catch (error) { console.error('文件上传失败:', error) ElMessage.error('文件上传失败,请重试') } finally { uploadLoading.value = false } } // 查看文件 const viewFile = (file) => { if (file.link) { window.open(file.link, '_blank') } else { ElMessage.warning('文件链接无效') } const viewFile = file => { if (file.link) { window.open(file.link, '_blank') } else { ElMessage.warning('文件链接无效') } } // 下载文件 const downloadFile = (file) => { if (file.link) { // 创建一个隐藏的a标签来触发下载 const a = document.createElement('a') a.href = file.link a.download = file.originalName || file.name document.body.appendChild(a) a.click() document.body.removeChild(a) } else { ElMessage.warning('文件链接无效') } const downloadFile = file => { if (file.link) { // 创建一个隐藏的a标签来触发下载 const a = document.createElement('a') a.href = file.link a.download = file.originalName || file.name document.body.appendChild(a) a.click() document.body.removeChild(a) } else { ElMessage.warning('文件链接无效') } } // 根据部门ID获取部门名称 function getDeptNameById(deptId, deptList) { // 处理类型转换,确保比较的是相同类型 const targetId = String(deptId) // 递归遍历部门树 function findDept(deptList) { for (const dept of deptList) { @@ -305,11 +296,11 @@ } return '' } if (!targetId || !Array.isArray(deptList) || deptList.length === 0) { return '' } return findDept(deptList) } @@ -317,8 +308,8 @@ async function open({ row }) { if (row?.id) { await loadDetail(row.id) // 可以在这里加载已上传的文件列表 // loadUploadedFiles(row.id) demandId.value = row.id console.log('row.id', demandId.value) } visible.value = true } @@ -332,36 +323,132 @@ data.responsibleDeptName = getDeptNameById(data.responsibleDeptId, deptTree.value) } formData.value = data // 加载审核记录 await loadAuditRecords(id) } // 如果需要加载已上传的文件列表 const loadUploadedFiles = async (id) => { try { // 这里根据实际情况调用接口获取已上传的文件列表 // const response = await getFileListApi({ id }) // if (response.data?.success) { // uploadedFiles.value = response.data.data || [] // } } catch (error) { console.error('加载已上传文件失败:', error) } // 加载审核记录 async function loadAuditRecords(demandId) { try { const res = await gdSupplyDemandAuditListApi({ demandId }) const auditRecords = res?.data?.data ?? [] // 处理审核记录为步骤格式 processAuditRecords(auditRecords) } catch (error) { console.error('加载审核记录失败:', error) ElMessage.error('加载审核记录失败,请重试') // 保持默认步骤,不做清空 activeStep.value = 0 } } // 处理审核记录为步骤格式 function processAuditRecords(records) { // 重置为默认步骤结构 steps.value = [ { title: '需求申请', person: '', time: '' }, { title: '审核通过', person: '', time: '' } ] if (!records || records.length === 0) { // 没有审核记录,保持默认步骤 activeStep.value = 0 return } // 按创建时间排序(最新的在最后) const sortedRecords = [...records].sort((a, b) => new Date(a.createTime) - new Date(b.createTime)) // 处理需求申请步骤(第一个步骤) const demandRecord = sortedRecords.find(record => record.auditStatus === '0') || sortedRecords[0] if (demandRecord) { steps.value[0] = { title: '需求申请', person: demandRecord.createUser ? `${demandRecord.createUser}` : '', time: demandRecord.createTime ? demandRecord.createTime : '' } } // 处理审核步骤(第二个步骤) const latestRecord = sortedRecords[sortedRecords.length - 1] if (latestRecord) { // 根据最新审核结果设置标题 let auditTitle = '审核通过' if (latestRecord.auditStatus === '2') { auditTitle = '审核驳回' // 更新第二个步骤的标题为审核驳回 steps.value[1].title = auditTitle } // 添加审核意见(如果有) if (latestRecord.auditOpinion) { steps.value[1].title += ` - ${latestRecord.auditOpinion}` } // 更新审核步骤的信息 steps.value[1].person = latestRecord.createUser ? `${latestRecord.createUser}` : '' steps.value[1].time = latestRecord.createTime ? latestRecord.createTime : '' // 根据最新审核结果设置activeStep switch (latestRecord.auditStatus) { case '0': // 审核中,高亮第一个步骤 activeStep.value = 0 break case '1': // 审核通过,高亮第二个步骤 activeStep.value = 1 break case '2': // 审核驳回,高亮第二个步骤 activeStep.value = 1 break default: // 默认高亮第一个步骤 activeStep.value = 0 } } } // 拒绝申请 const rejectTheApplication = () => { // const id = ['2013182736046530562'] if (responseData.value.length === 0) { return ElMessage.warning('请上传数据') } // 打开拒绝申请弹框 rejectionDialogVisible.value = true } // 处理拒绝申请确认 const handleRejectionConfirm = reason => { const id = [responseData.value.attachId] console.log('responseData.value',responseData.value) gdSupplyDemandAuditRejectApi({ attachIds:id }).then(() => { ElMessage.success('拒绝申请成功') visible.value = false }) console.log('demandId.value', demandId.value) gdSupplyDemandAuditRejectApi({ attachIds: id, auditOpinion: reason, demandId: demandId.value }) .then(() => { ElMessage.success('拒绝申请成功') visible.value = false emit('success') }) .catch(error => { console.error('拒绝申请失败:', error) ElMessage.error('拒绝申请失败,请重试') emit('success') }) } // 审核通过 const approvedByTheReview = () => { if (responseData.value.length === 0) { return ElMessage.warning('请上传数据') } const id = [responseData.value.attachId] gdSupplyDemandAuditPassApi({ id }).then(() => { gdSupplyDemandAuditPassApi({ attachIds: id, demandId: demandId.value }).then(() => { ElMessage.success('审核通过成功') visible.value = false emit('success') }) } @@ -436,7 +523,7 @@ } :deep(.el-step) { margin-bottom: 20px; margin-bottom: 90px; } :deep(.el-step__title) { applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/FormDiaLog.vue
@@ -66,7 +66,7 @@ :model="formData" :rules="rules" :disabled="dialogReadonly" label-width="120px" label-width="130px" > <el-row> <el-col :span="12"> @@ -172,13 +172,11 @@ <el-input class="gd-input" v-model="formData.dataSource" placeholder="请输入" clearable /> </el-form-item> </el-col> <el-col :span="24"> <el-col :span="12"> <el-form-item label="需求信息项" prop="demandInfo"> <el-input class="gd-input" v-model="formData.demandInfo" type="textarea" :rows="4" placeholder="请输入" clearable /> applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/index.vue
@@ -75,10 +75,11 @@ <el-table-column prop="demandInfo" show-overflow-tooltip label="需求信息项" /> <el-table-column label="操作" class-name="operation-btns"> <template v-slot="{ row }"> <el-link @click="openForm('view', row)">查看</el-link> <el-link @click="openForm('edit', row)" v-if="row.demandStatus !== '1'">编辑</el-link> <el-link @click="handleDelete(row)" v-if="row.demandStatus !== '1'">删除</el-link> </template> <el-link @click="openForm('view', row)">查看</el-link> <!-- 只对非审批状态显示编辑和删除按钮 --> <el-link @click="openForm('edit', row)" v-if="!['1', '2', '3'].includes(row.demandStatus)">编辑</el-link> <el-link @click="handleDelete(row)" v-if="!['1', '2', '3'].includes(row.demandStatus)">删除</el-link> </template> </el-table-column> </el-table> </div> @@ -96,7 +97,7 @@ </div> <FormDiaLog ref="dialogRef" @success="getList" v-if="dialogVisible" v-model="dialogVisible" /> <ApplyViewDialog ref="applyDialogRef" v-if="applyDialogVisible" v-model="applyDialogVisible" /> <ApplyViewDialog ref="applyDialogRef" @success="getList" v-if="applyDialogVisible" v-model="applyDialogVisible" /> </basic-container> </template> <script setup> @@ -192,8 +193,9 @@ // 新增/编辑/查看 弹框 function openForm(mode, row) { if (mode === 'view' && row?.demandStatus === '1') { // 如果是查看申请中的记录,使用申请详情弹框 // 对于所有审批相关状态(申请中、审核通过、审核拒绝)都使用申请详情弹框 if (mode === 'view' && ['1', '2', '3'].includes(row?.demandStatus)) { // 使用申请详情弹框 openApplyViewDialog(row) } else { // 其他情况使用原有弹框 applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/reasonForRejectionDialog.vue
New file @@ -0,0 +1,91 @@ <template> <el-dialog class="gd-dialog" v-model="visible" :title="'拒绝申请'" @closed="visible = false" destroy-on-close width="550px" align-center > <div class="detail-container"> <div class="detail-cell"> <span class="cell-label">拒绝原因:</span> <el-input v-model="reasonForRejection" style="width: 380px" type="textarea" placeholder="请输入" /> </div> </div> <template #footer> <!-- <el-button @click="handleCancel">取消</el-button> --> <el-button class="save-btn" color="#4C34FF" @click="handleConfirm">确认</el-button> </template> </el-dialog> </template> <script setup> import { ref, watch } from 'vue' import { ElMessage } from 'element-plus' const props = defineProps({ modelValue: { type: Boolean, default: false, }, }) const emit = defineEmits(['update:modelValue', 'confirm']) const visible = ref(props.modelValue) const reasonForRejection = ref('') // 监听visible变化,同步到父组件 watch( () => visible.value, newVal => { emit('update:modelValue', newVal) } ) // 监听父组件modelValue变化 watch( () => props.modelValue, newVal => { visible.value = newVal } ) // 取消按钮点击事件 const handleCancel = () => { visible.value = false reasonForRejection.value = '' } // 确认按钮点击事件 const handleConfirm = () => { if (!reasonForRejection.value.trim()) { return ElMessage.warning('请输入拒绝原因') } emit('confirm', reasonForRejection.value) visible.value = false reasonForRejection.value = '' } </script> <style scoped lang="scss"> .detail-container { padding: 20px; display: flex; align-items: flex-start; gap: 30px; .detail-cell { display: flex; justify-content: center; .cell-label { min-width: 80px; flex-shrink: 0; } :deep(.el-textarea__inner) { height: 100px !important; } } } </style> applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/supplyAddApi.js
@@ -63,3 +63,12 @@ }) } // 获取供需需求审核记录 export const gdSupplyDemandAuditListApi = params => { return request({ url: `/drone-gd/orderdata/gdSupplyDemandAudit/list`, method: 'get', params, }) }