吉安感知网项目-前端
罗广辉
2026-01-21 0250ecba4022d5fc74ee1556b1e505151f81e956
Merge remote-tracking branch 'origin/master'
8 files modified
360 ■■■■ changed files
applications/drone-command/src/page/index/top/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/page/lock/index.vue 98 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/partitionApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/FormDiaLog.vue 91 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/index.vue 10 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/auditRecord.vue 142 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/page/index/top/index.vue
@@ -36,6 +36,8 @@
</template>
<script>
import { ElMessage, ElMessageBox } from 'element-plus'
import logo from '@/assets/images/topContainer/logo.png'
import { mapGetters } from 'vuex'
@@ -84,10 +86,11 @@
  },
  methods: {
    logout () {
      this.$confirm(this.$t('logoutTip'), this.$t('提示'), {
        confirmButtonText: this.$t('submitText'),
        cancelButtonText: this.$t('cancelText'),
      ElMessageBox.confirm(`是否退出系统, 是否继续?`, '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
      }).then(() => {
        this.$store.dispatch('LogOut').then(() => {
          const env = import.meta.env.VITE_APP_ENV
applications/drone-command/src/page/lock/index.vue
@@ -5,24 +5,16 @@
    </div>
    <div class="login-weaper">
      <div class="login-left animate__animated animate__fadeInLeft">
        <img class="img" src="/img/logo.png" alt=""  />
        <img class="img" src="/img/logo.png" alt="" />
        <p class="title">{{ $t('login.info') }}</p>
      </div>
      <div class="login-border animate__animated animate__fadeInRight">
        <div class="login-main">
          <div class="lock-form animate__animated animate__bounceInDown">
            <div
              class="animate__animated"
              :class="{ shake: passwdError, animate__bounceOut: pass }"
            >
            <div class="animate__animated" :class="{ shake: passwdError, animate__bounceOut: pass }">
              <h3 style="color: #333">{{ userInfo.username }}</h3>
              <el-input
                placeholder="请输入登录密码"
                type="password"
                class="input-with-select animated"
                v-model="passwd"
                @keyup.enter="handleLogin"
              >
              <el-input placeholder="请输入登录密码" type="password" class="input-with-select animated" v-model="passwd"
                @keyup.enter="handleLogin">
                <template #append>
                  <i class="icon-bofangqi-suoping" @click="handleLogin"></i>
                  &nbsp; &nbsp;
@@ -37,80 +29,82 @@
  </div>
</template>
<script>
import { mapGetters } from 'vuex';
import { removeRefreshToken, removeToken } from '@/utils/auth';
import { mapGetters } from 'vuex'
import { removeRefreshToken, removeToken } from '@/utils/auth'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
  name: 'lock',
  data() {
  data () {
    return {
      time: '',
      timeTimer: null,
      passwd: '',
      passwdError: false,
      pass: false,
    };
  },
  created() {
    this.getTime();
    if (this.timeTimer) clearInterval(this.timeTimer);
    this.timeTimer = setInterval(() => {
      this.getTime();
    }, 1000);
  },
  beforeDestroy() {
    if (this.timeTimer) {
      clearInterval(this.timeTimer);
      this.timeTimer = null;
    }
  },
  mounted() {},
  created () {
    this.getTime()
    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']),
  },
  props: [],
  methods: {
    getTime() {
      this.time = this.$dayjs().format('YYYY年MM月DD日 HH:mm:ss');
    getTime () {
      this.time = this.$dayjs().format('YYYY年MM月DD日 HH:mm:ss')
    },
    handleLogout() {
      this.$confirm('是否退出系统, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
    handleLogout () {
      ElMessageBox.confirm(`是否退出系统, 是否继续?`, '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
      }).then(() => {
        this.$store.dispatch('LogOut').then(() => {
          // 清除token信息
          removeToken();
          removeRefreshToken();
          this.$router.push({ path: '/login' });
        });
      });
          removeToken()
          removeRefreshToken()
          this.$router.push({ path: '/login' })
        })
      })
    },
    handleLogin() {
    handleLogin () {
      if (this.passwd !== this.lockPasswd) {
        this.passwd = '';
        this.passwd = ''
        this.$message({
          message: '解锁密码错误,请重新输入',
          type: 'error',
        });
        this.passwdError = true;
        })
        this.passwdError = true
        setTimeout(() => {
          this.passwdError = false;
        }, 1000);
        return;
          this.passwdError = false
        }, 1000)
        return
      }
      this.pass = true;
      this.pass = true
      setTimeout(() => {
        this.$store.commit('CLEAR_LOCK');
        this.$store.commit('CLEAR_LOCK')
        this.$router.push({
          path: this.tag.path,
        });
      }, 1000);
        })
      }, 1000)
    },
  },
  components: {},
};
}
</script>
<style lang="scss"></style>
applications/drone-command/src/views/areaManage/partition/partitionApi.js
@@ -8,7 +8,7 @@
        params: { descs: 'update_time', ...params },
    })
}
// 查list
// 查list --- 区域
export const fwAreaDivideListApi = params => {
    return request({
        url: `/drone-fw/area/fwAreaDivide/list`,
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
@@ -29,11 +29,11 @@
                </el-select>
            </el-form-item>
            <el-form-item label="设备状态" prop="deviceStatus">
            <el-form-item label="设备状态" prop="status">
                <el-select
                    class="command-select"
                    popper-class="command-select-popper"
                    v-model="searchParams.deviceStatus"
                    v-model="searchParams.status"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
@@ -124,7 +124,7 @@
const initSearchParams = () => ({
    deviceName: '', // 设备名称
    deviceType: '', // 设备类型
    deviceStatus: '', // 设备状态
    status: '', // 设备状态
    current: 1, // 当前页
    size: 10, // 每页大小
})
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/FormDiaLog.vue
@@ -12,12 +12,12 @@
                    <div class="val">{{ formData.deviceSn }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">场景</div>
                    <div class="val">{{ formData.defenseSceneName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">区域</div>
                    <div class="val">{{ formData.areaDivideName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">场景</div>
                    <div class="val">{{ formData.defenseSceneName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">调度人员</div>
@@ -71,25 +71,41 @@
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="场景" prop="defenseSceneId">
                        <el-select class="command-select" popper-class="command-select-popper"
                            v-model="formData.defenseSceneId" placeholder="请选择" clearable @change="handleSceneChange">
                            <el-option v-for="item in sceneList" :key="item.id" :label="item.sceneName"
                                :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="区域" prop="areaDivideId">
                        <el-select class="command-select" popper-class="command-select-popper"
                            v-model="formData.areaDivideId" placeholder="请先选择场景" clearable
                            :disabled="!formData.defenseSceneId" @change="handleAreaChange">
                            v-model="formData.areaDivideId" placeholder="请选择" clearable
                             @change="handleAreaChange">
                            <el-option v-for="item in areaList" :key="item.id" :label="item.areaName"
                                :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="场景" prop="defenseSceneId">
                        <el-select class="command-select" popper-class="command-select-popper"
                            v-model="formData.defenseSceneId" placeholder="请先选择区域" clearable
                            :disabled="!formData.areaDivideId" @change="handleSceneChange">
                            <el-option v-for="item in sceneList" :key="item.id" :label="item.sceneName"
                                :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="调度位置" prop="longitude">
                        <!--                        <el-button @click="selectLocation" style="width: 100%">-->
                        <el-button style="width: 100%">
                            {{
                                formData.longitude && formData.latitude ? `${formData.longitude}, ${formData.latitude}` :
                                    '选择区域后自动带出'
                            }}
                        </el-button>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="调度人员" prop="dispatchUser">
                        <el-input class="command-input" v-model="formData.dispatchUser" maxlength="50" placeholder="请输入"
@@ -103,17 +119,7 @@
                            start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss" />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="调度位置" prop="longitude">
                        <!--                        <el-button @click="selectLocation" style="width: 100%">-->
                        <el-button style="width: 100%">
                            {{
                                formData.longitude && formData.latitude ? `${formData.longitude}, ${formData.latitude}` :
                                    '选择区域后自动带出'
                            }}
                        </el-button>
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="调度内容" prop="dispatchContent">
                        <el-input class="command-input" v-model="formData.dispatchContent" type="textarea" :rows="3"
@@ -158,6 +164,7 @@
import { ElMessage } from 'element-plus'
import { fwTaskScheduleDetailApi, fwTaskScheduleSubmitApi } from './taskScheduleApi'
import { fwAreaDivideListApi } from '@/views/areaManage/partition/partitionApi'
import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
import { fieldRules } from '@ztzf/utils'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import * as Cesium from 'cesium'
@@ -186,7 +193,6 @@
})
const props = defineProps({
    sceneList: { type: Array, default: () => [] },
    deviceList: { type: Array, default: () => [] },
})
@@ -203,6 +209,7 @@
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
const tempLocation = ref({ longitude: null, latitude: null })
const areaList = ref([]) // 区域列表
const sceneList = ref([]) // 场景列表
const deviceStatus = ref('')
const rules = {
@@ -229,26 +236,32 @@
    }
}
// 场景选择变化处理
async function handleSceneChange (sceneId) {
    formData.value.areaDivideId = ''
    if (sceneId) {
        await loadAreaList(sceneId)
async function handleAreaChange (areaId) {
    formData.value.defenseSceneId = ''
    if (areaId) {
        await loadSceneList(areaId)
    } else {
        areaList.value = []
        sceneList.value = []
    }
}
function handleAreaChange (areaId) {
    const find = areaList.value.find(item => item.id === areaId)
// 场景选择变化处理
async function handleSceneChange (sceneId) {
    const find = sceneList.value.find(item => item.id === sceneId)
    formData.value.longitude = find?.longitude
    formData.value.latitude = find?.latitude
}
// 加载区域列表
async function loadAreaList (sceneId) {
    const res = await fwAreaDivideListApi({ sceneId })
async function loadAreaList () {
    const res = await fwAreaDivideListApi()
    areaList.value = res?.data?.data ?? []
}
// 加载场景列表
async function loadSceneList(areaId) {
    const res = await fwDefenseSceneListApi({areaId})
    sceneList.value = res?.data?.data ?? []
}
// 关闭弹框
@@ -385,6 +398,10 @@
}
defineExpose({ open })
onMounted(() => {
    loadAreaList()
})
</script>
<style scoped lang="scss">
#mapContainer {
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/index.vue
@@ -109,7 +109,6 @@
            @success="getList"
            v-if="dialogVisible"
            v-model="dialogVisible"
            :sceneList="sceneList"
            :deviceList="deviceList"
        />
    </basic-container>
@@ -121,7 +120,6 @@
import { nextTick, onMounted, ref, provide } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fwTaskSchedulePageApi, fwTaskScheduleRemoveApi } from './taskScheduleApi'
import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import FormDiaLog from './FormDiaLog.vue'
@@ -147,7 +145,6 @@
    deviceType: [], // 设备类型
    deviceStatus: [], // 设备状态
})
const sceneList = ref([]) // 场景列表
const deviceList = ref([]) // 设备列表
// 注入字典到子组件
@@ -225,12 +222,6 @@
    })
}
// 加载场景列表
async function loadSceneList() {
    const res = await fwDefenseSceneListApi()
    sceneList.value = res?.data?.data ?? []
}
// 加载设备列表
async function loadDeviceList() {
    const res = await fwDeviceListApi()
@@ -239,7 +230,6 @@
onMounted(() => {
    getDictList()
    loadSceneList()
    loadDeviceList()
    getList()
})
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue
@@ -420,8 +420,8 @@
.detail-container {
    padding: 20px;
    display: flex;
    align-items: flex-start;
    gap: 30px;
    align-items: stretch;
}
/* 左侧步骤条 */
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/auditRecord.vue
@@ -1,19 +1,14 @@
<template>
  <div class="audit-record-container">
    <div class="label">审批记录</div>
    <el-steps :active="activeStep" direction="vertical" align-center>
      <el-step v-for="(step, index) in displayedSteps" :key="step.status" :title="step.title">
        <template #description>
          <div class="step-person">{{ step.person }}</div>
          <div class="step-time">{{ step.time }}</div>
        </template>
      </el-step>
    <el-steps direction="vertical" :active="displayedSteps.length">
      <el-step v-for="step in displayedSteps" :key="step.status" :title="step.title" :description="`${step.person || ''}\n${step.time || ''}`" />
    </el-steps>
  </div>
</template>
<script setup>
import { ref, watch, computed } from 'vue'
import { ref, watch } from 'vue'
import { gdSupplyDemandAuditListApi } from '@/views/orderView/orderDataManage/supplyAdd/supplyAddApi'
const props = defineProps({
@@ -25,63 +20,8 @@
const detailDemandStatus = inject('detailDemandStatus')
const reasonForRejection = inject('reasonForRejection')
// 所有可能的步骤配置
const allStepConfigs = ref([
  { title: '需求申请', status: '0', person: '', time: '' },
  { title: '审核通过', status: '1', person: '', time: '' },
  { title: '拒绝申请', status: '2', person: '', time: '' }
])
// 步骤条数据
const stepResponse = ref([])
// 当前激活步骤
const activeStep = ref(0)
// 计算要显示的步骤
const displayedSteps = computed(() => {
  // 创建一个映射,将状态映射到审核记录
  const statusToRecord = {};
  stepResponse.value.forEach(record => {
    statusToRecord[record.auditStatus] = record;
  });
  // 根据审核记录筛选并构建要显示的步骤
  return allStepConfigs.value.filter(stepConfig => {
    // 如果是需求申请步骤,始终显示
    if (stepConfig.status === '0') {
      return true;
    }
    // 其他步骤只有在有对应状态的审核记录时才显示
    return statusToRecord.hasOwnProperty(stepConfig.status);
  }).map(stepConfig => {
    // 获取对应状态的审核记录
    const record = statusToRecord[stepConfig.status];
    if (record) {
      // 如果有审核记录,使用记录中的信息
      return {
        ...stepConfig,
        person: record.createUser ? `${record.createUser}` : '',
        time: record.createTime ? record.createTime : ''
      };
    }
    // 如果没有审核记录,使用默认信息
    return stepConfig;
  });
})
// 计算当前激活步骤
const calculateCurrentStep = () => {
  if (stepResponse.value.length === 0) {
    return 0;
  }
  // 获取最新的审核记录
  const latestRecord = stepResponse.value[stepResponse.value.length - 1];
  // 查找最新状态在显示步骤中的索引
  const stepIndex = displayedSteps.value.findIndex(step => step.status === latestRecord.auditStatus);
  return stepIndex !== -1 ? stepIndex : 0;
}
const displayedSteps = ref([])
// 监听 demandId 变化
watch(() => props.demandId, (newVal) => {
@@ -91,27 +31,55 @@
}, { immediate: true })
// 加载审核记录
async function loadAuditRecords(demandId) {
  try {
    const res = await gdSupplyDemandAuditListApi({ demandId })
function loadAuditRecords(demandId) {
  gdSupplyDemandAuditListApi({ demandId }).then(res => {
    const auditRecords = res?.data?.data ?? []
    
    // 按创建时间排序(最新的在最后)
    stepResponse.value = [...auditRecords].sort((a, b) => new Date(a.createTime) - new Date(b.createTime))
    // 查找拒绝原因(auditStatus为2的记录的auditOpinion)
    const rejectedRecord = stepResponse.value.find(record => record.auditStatus === '2')
    reasonForRejection.value = rejectedRecord?.auditOpinion || ''
    // 计算当前激活步骤
    activeStep.value = calculateCurrentStep()
  } catch (error) {
    const sortedRecords = [...auditRecords].sort((a, b) => new Date(a.createTime) - new Date(b.createTime))
    // 构建步骤数据
    const steps = []
    // 添加需求申请步骤
    steps.push({
      status: '0',
      title: '需求申请',
      person: sortedRecords.find(r => r.auditStatus === '0')?.createUser || '',
      time: sortedRecords.find(r => r.auditStatus === '0')?.createTime || ''
    })
    // 添加审核通过步骤(如果有)
    const approvedRecord = sortedRecords.find(r => r.auditStatus === '1')
    if (approvedRecord) {
      steps.push({
        status: '1',
        title: '审核通过',
        person: approvedRecord.createUser || '',
        time: approvedRecord.createTime || ''
      })
    }
    // 添加拒绝申请步骤(如果有)
    const rejectedRecord = sortedRecords.find(r => r.auditStatus === '2')
    if (rejectedRecord) {
      steps.push({
        status: '2',
        title: '拒绝申请',
        person: rejectedRecord.createUser || '',
        time: rejectedRecord.createTime || ''
      })
      // 查找拒绝原因
      reasonForRejection.value = rejectedRecord.auditOpinion || ''
    }
    displayedSteps.value = steps
  }).catch(error => {
    console.error('加载审核记录失败:', error)
    // 重置步骤数据
    stepResponse.value = []
    activeStep.value = 0
    displayedSteps.value = []
    reasonForRejection.value = ''
  }
  })
}
defineExpose({
@@ -134,24 +102,18 @@
}
:deep(.el-step) {
  margin-bottom: 20px;
  // margin-bottom: 20px;
}
:deep(.el-step__title) {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 8px;
}
/* 步骤描述 */
.step-person {
:deep(.el-step__description) {
  font-size: 13px;
  color: #333;
  margin-bottom: 4px;
}
.step-time {
  font-size: 12px;
  color: #909399;
  color: #666;
  white-space: pre-wrap;
  line-height: 1.4;
}
</style>