Merge remote-tracking branch 'origin/master'
13 files modified
6 files added
| | |
| | | `${website.clientId}:${website.clientSecret}` |
| | | )}`; |
| | | } |
| | | // 后端的要求 |
| | | if (router.currentRoute.value.path === '/job/jobstatistics'){ |
| | | const userAreaCode = store?.state?.user?.userInfo?.detail?.areaCode |
| | | const paramsKey = config.method === 'get' ? 'params' : 'data'; |
| | | const codeKey = config.method === 'get' ? 'areaCode' : 'area_code'; |
| | | const paramCode = config?.params?.areaCode || config?.data?.area_code |
| | | try { |
| | | if (paramCode){ |
| | | if (paramCode === userAreaCode) { |
| | | config[paramsKey][codeKey] = config.method === 'get' ? '' : undefined; |
| | | } else { |
| | | if (!Array.isArray(config[paramsKey])){ |
| | | config[paramsKey] = {...config[paramsKey],[codeKey]: paramCode} |
| | | } |
| | | } |
| | | } |
| | | }catch (e) {} |
| | | } |
| | | |
| | | //headers判断请求是否携带token |
| | | const meta = config.meta || {}; |
| | |
| | | <el-form ref="formRef" :model="formData" :rules="rules" :disabled="dialogReadonly" label-width="100px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="设备名称" prop="fwDeviceDTO.deviceName"> |
| | | <el-input v-model="formData.fwDeviceDTO.deviceName" maxlength="50" placeholder="请输入" clearable /> |
| | | <el-form-item label="设备名称" prop="deviceName"> |
| | | <el-input v-model="formData.deviceName" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="设备类型" prop="fwDeviceDTO.deviceType"> |
| | | <el-select v-model="formData.fwDeviceDTO.deviceType" placeholder="请选择" clearable> |
| | | <el-form-item label="设备类型" prop="deviceType"> |
| | | <el-select v-model="formData.deviceType" placeholder="请选择" clearable> |
| | | <el-option |
| | | v-for="item in dictObj.deviceType" |
| | | :key="item.dictKey" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="设备属性" prop="fwDeviceDTO.deviceAtt"> |
| | | <el-select v-model="formData.fwDeviceDTO.deviceAtt" placeholder="请选择" clearable> |
| | | <el-form-item label="设备属性" prop="deviceAtt"> |
| | | <el-select v-model="formData.deviceAtt" placeholder="请选择" clearable> |
| | | <el-option |
| | | v-for="item in dictObj.deviceAtt" |
| | | :key="item.dictKey" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="型号" prop="fwDeviceDTO.deviceModel"> |
| | | <el-input v-model="formData.fwDeviceDTO.deviceModel" maxlength="50" placeholder="请输入" clearable /> |
| | | <el-form-item label="型号" prop="deviceModel"> |
| | | <el-input v-model="formData.deviceModel" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="规格" prop="fwDeviceDTO.deviceSpecification"> |
| | | <el-input |
| | | v-model="formData.fwDeviceDTO.deviceSpecification" |
| | | maxlength="50" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | <el-form-item label="规格" prop="deviceSpecification"> |
| | | <el-input v-model="formData.deviceSpecification" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="生产厂商" prop="fwDeviceDTO.manufacturer"> |
| | | <el-input v-model="formData.fwDeviceDTO.manufacturer" maxlength="50" placeholder="请输入" clearable /> |
| | | <el-form-item label="生产厂商" prop="manufacturer"> |
| | | <el-input v-model="formData.manufacturer" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="出库去向" prop="stockOutDestination"> |
| | | <el-input v-model="formData.stockOutDestination" maxlength="50" placeholder="请输入" clearable /> |
| | | <el-form-item label="来源" prop="source"> |
| | | <el-input v-model="formData.source" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="来源" prop="fwDeviceDTO.source"> |
| | | <el-input v-model="formData.fwDeviceDTO.source" maxlength="50" placeholder="请输入" clearable /> |
| | | <el-form-item label="用途" prop="purpose"> |
| | | <el-input v-model="formData.purpose" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="用途" prop="fwDeviceTrackDTO.purpose"> |
| | | <el-input v-model="formData.fwDeviceTrackDTO.purpose" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="所属部门" prop="fwDeviceDTO.ownerDept"> |
| | | <el-form-item label="所属部门" prop="belongDept"> |
| | | <el-tree-select |
| | | v-model="formData.fwDeviceDTO.ownerDept" |
| | | v-model="formData.belongDept" |
| | | node-key="id" |
| | | :data="deptTree" |
| | | :props="treeProps" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="负责人" prop="fwDeviceTrackDTO.charger"> |
| | | <el-select |
| | | v-model="formData.fwDeviceTrackDTO.charger" |
| | | placeholder="请选择" |
| | | :disabled="!formData.fwDeviceDTO.ownerDept" |
| | | clearable |
| | | > |
| | | <el-form-item label="负责人" prop="charger"> |
| | | <el-select v-model="formData.charger" placeholder="请选择" :disabled="!formData.belongDept" clearable> |
| | | <el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="联系电话" prop="fwDeviceTrackDTO.contactPhone"> |
| | | <el-input v-model="formData.fwDeviceTrackDTO.contactPhone" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="联系电话" prop="fwDeviceTrackDTO.contactPhone"> |
| | | <el-input v-model="formData.fwDeviceTrackDTO.contactPhone" maxlength="50" placeholder="请输入" clearable /> |
| | | <el-form-item label="联系电话" prop="contactPhone"> |
| | | <el-input v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | import { fieldRules } from '@ztzf/utils' |
| | | |
| | | const initForm = () => ({ |
| | | 'fwDeviceDTO': { |
| | | 'deviceAtt': '', |
| | | 'deviceModel': '', |
| | | 'deviceName': '', |
| | | 'deviceSpecification': '', |
| | | 'deviceType': '', |
| | | 'manufacturer': '', |
| | | 'remark': '', |
| | | 'source': '', |
| | | }, |
| | | 'fwDeviceTrackDTO': { |
| | | 'charger': '', |
| | | 'purpose': '', |
| | | 'contactPhone': '', |
| | | 'deptName': '', |
| | | 'outTarget': '', |
| | | 'outTime': '', |
| | | }, |
| | | belongDept: '', // 所属部门 |
| | | charger: '', // 负责人 |
| | | contactPhone: '', // 联系电话 |
| | | deviceAtt: '', // 设备属性 |
| | | deviceType: '', // 设备类型 |
| | | deviceModel: '', // 型号 |
| | | deviceSpecification: '', // 规格 |
| | | deviceName: '', // 设备名称 |
| | | manufacturer: '', // 生产厂商 |
| | | purpose: '', // 用途 |
| | | source: '', // 来源 |
| | | }) |
| | | const treeProps = { |
| | | label: 'name', |
| | |
| | | deviceModel: fieldRules(true, 50), |
| | | deviceSpecification: fieldRules(true, 50), |
| | | manufacturer: fieldRules(true, 50), |
| | | stockOutDestination: fieldRules(true, 50), |
| | | source: fieldRules(true, 50), |
| | | purpose: fieldRules(true, 50), |
| | | ownerDept: fieldRules(true, 50), |
| | | belongDept: fieldRules(true, 50), |
| | | contactPhone: fieldRules(true, 50), |
| | | charger: fieldRules(true, 0), |
| | | } |
| | | |
| | |
| | | <el-row :gutter="16"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="名称" prop="deviceName"> |
| | | <el-input v-model="searchParams.deviceName" placeholder="请输入" clearable @clear="getList" /> |
| | | <el-input v-model="searchParams.deviceName" placeholder="请输入" clearable @clear="handleSearch" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="类型" prop="deviceType"> |
| | | <el-input v-model="searchParams.deviceType" placeholder="请输入" clearable @clear="getList" /> |
| | | <el-select v-model="searchParams.deviceType" placeholder="请选择" clearable @change="handleSearch"> |
| | | <el-option |
| | | v-for="item in dictObj.deviceType" |
| | | :key="item.dictKey" |
| | | :label="item.dictValue" |
| | | :value="item.dictKey" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="部门" prop="belongDept"> |
| | | <el-tree-select |
| | | v-model="searchParams.belongDept" |
| | | :data="deptTree" |
| | | :props="treeProps" |
| | | node-key="id" |
| | | check-strictly |
| | | clearable |
| | | @change="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="时间"> |
| | | <el-date-picker |
| | | popper-class="ztzf-date-picker-popper" |
| | | class="ztzf-date-picker" |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | @change="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | |
| | | </el-form> |
| | | <div> |
| | | <el-button type="primary" @click="handleAdd">新增</el-button> |
| | | <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button> |
| | | <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button> |
| | | </div> |
| | | <el-table v-loading="loading" :data="list"> |
| | | <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column type="index" width="60" label="序号" /> |
| | | <el-table-column prop="deviceName" label="名称" /> |
| | | <el-table-column prop="deviceType" label="类型" /> |
| | | <el-table-column prop="deviceType" label="类型"> |
| | | <template v-slot="{ row }"> |
| | | {{ getDictLabel(row.deviceType, dictObj.deviceType) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="deviceModel" label="设备型号" /> |
| | | <el-table-column prop="deviceSpecification" label="规格" /> |
| | | <el-table-column label="操作"> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div> |
| | | <el-pagination |
| | | v-model:current-page="searchParams.current" |
| | |
| | | <script setup> |
| | | import { onMounted, ref } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { fwDevicePageApi, fwDeviceRemoveApi } from '@/views/basicManage/deviceStock/fwDevice' |
| | | import { exportFwDeviceApi, fwDevicePageApi, fwDeviceRemoveApi } from '@/views/basicManage/deviceStock/fwDevice' |
| | | import FormDiaLog from './FormDiaLog.vue' |
| | | import { getDictionaryByCode } from '@/api/system/dictbiz' |
| | | import { getDeptTree } from '@/api/system/dept' |
| | | import { blobDownload, dateRangeFormat, getDictLabel } from '@ztzf/utils' |
| | | |
| | | const initSearchParams = () => ({ |
| | | deviceName: '', // 设备名称 |
| | | deviceType: '', // 设备类型 |
| | | belongDept: '', // 所属部门 |
| | | current: 1, // 当前页 |
| | | size: 10, // 每页大小 |
| | | }) |
| | |
| | | const total = ref(0) // 总条数 |
| | | const loading = ref(false) // 列表加载中 |
| | | const list = ref([]) // 列表数据 |
| | | const selectedIds = ref([]) // 勾选的设备ID列表 |
| | | const queryParamsRef = ref(null) // 查询表单实例 |
| | | const dialogRef = ref(null) // 弹框实例 |
| | | const dictObj = ref({ |
| | | deviceType: [], |
| | | deviceAtt: [], |
| | | deviceType: [], //设备类型 |
| | | deviceAtt: [], //设备属性 |
| | | }) |
| | | const dateRange = ref([]) |
| | | const deptTree = ref([]) |
| | | |
| | | const treeProps = { |
| | | label: 'name', |
| | | children: 'children', |
| | | } |
| | | const exportLoading = ref(false) |
| | | provide('dictObj', dictObj) |
| | | provide('deptTree', deptTree) |
| | | |
| | | // 获取列表 |
| | | async function getList() { |
| | | const range = dateRangeFormat(dateRange.value) |
| | | loading.value = true |
| | | try { |
| | | const res = await fwDevicePageApi(searchParams.value) |
| | | const params = { ...searchParams.value, startTime: range[0], endTime: range[1] } |
| | | const res = await fwDevicePageApi(params) |
| | | list.value = res?.data?.data?.records ?? [] |
| | | total.value = res?.data?.data?.total ?? 0 |
| | | } finally { |
| | |
| | | |
| | | // 重置查询 |
| | | function resetForm() { |
| | | dateRange.value = [] |
| | | queryParamsRef.value?.resetFields() |
| | | searchParams.value.current = 1 |
| | | getList() |
| | |
| | | |
| | | // 删除 |
| | | async function handleDelete(row) { |
| | | await ElMessageBox.confirm('确认删除该设备吗?', '提示', { type: 'warning' }) |
| | | await fwDeviceRemoveApi({ ids: row.id }) |
| | | const tips = row ? '该条' : '选中的项' |
| | | await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' }) |
| | | const ids = row ? row.id : selectedIds.value.join(',') |
| | | await fwDeviceRemoveApi({ ids }) |
| | | ElMessage.success('删除成功') |
| | | selectedIds.value = [] |
| | | getList() |
| | | } |
| | | |
| | | // 勾选值设置 |
| | | function handleSelectionChange(rows) { |
| | | selectedIds.value = rows.map(item => item.id) |
| | | } |
| | | |
| | | function exportFile() { |
| | | exportLoading.value = true |
| | | exportFwDeviceApi() |
| | | .then(res => { |
| | | blobDownload(res) |
| | | }) |
| | | .finally(() => { |
| | | exportLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | // 获取字典 |
| | | function getDictList() { |
| | | getDictionaryByCode('deviceType,deviceAtt').then(res => { |
| | |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | //导出 |
| | | export const exportFwDeviceApi = () => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneAlarmRecord/export-fwDroneAlarmRecord`, |
| | | method: 'get', |
| | | responseType: 'blob', |
| | | }) |
| | | } |
| | |
| | | } |
| | | ]) |
| | | |
| | | const emit = defineEmits(['select-warning', 'select-equipment']) |
| | | |
| | | const warningTypeClick = (type) => { |
| | | console.log('点击了告警类型:', type) |
| | | emit('select-warning', type) |
| | | } |
| | | |
| | | const equipmentClick = (isHighlight) => { |
| | | if (isHighlight) { |
| | | console.log('点击了设备统计') |
| | | emit('select-equipment') |
| | | } |
| | | } |
| | | </script> |
| New file |
| | |
| | | <template> |
| | | <el-dialog |
| | | class="ztzf-dialog-mange data-cockpit-history-dialog" |
| | | append-to-body |
| | | v-model="visibleModel" |
| | | :title="dialogTitle" |
| | | width="1100px" |
| | | :close-on-click-modal="false" |
| | | :destroy-on-close="true" |
| | | > |
| | | <div class="dialog-body"> |
| | | <div class="device-summary"> |
| | | <div class="item"> |
| | | <span class="label">设备名称</span> |
| | | <span class="value">{{ device?.name || '反制设备名称' }}</span> |
| | | </div> |
| | | <div class="item"> |
| | | <span class="label">类型</span> |
| | | <span class="value">{{ device?.type || '-' }}</span> |
| | | </div> |
| | | <div class="item"> |
| | | <span class="label">厂商</span> |
| | | <span class="value">{{ device?.vendor || '-' }}</span> |
| | | </div> |
| | | <div class="item"> |
| | | <span class="label">状态</span> |
| | | <span class="value">{{ device?.statusText || '-' }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="table"> |
| | | <div class="thead"> |
| | | <div class="cell index">序号</div> |
| | | <div class="cell">无人机名称</div> |
| | | <div class="cell">无人机序列号</div> |
| | | <div class="cell">无人机类型</div> |
| | | <div class="cell">区域</div> |
| | | <div class="cell">数据来源</div> |
| | | <div class="cell">信号频段</div> |
| | | <div class="cell">发现时间</div> |
| | | <div class="cell">停留时间</div> |
| | | <div class="cell">反制方式</div> |
| | | </div> |
| | | <div class="tbody"> |
| | | <div class="tr" v-for="(row, index) in historyList" :key="row.id"> |
| | | <div class="cell index">{{ index + 1 }}</div> |
| | | <div class="cell">{{ row.droneName }}</div> |
| | | <div class="cell">{{ row.serialNumber }}</div> |
| | | <div class="cell">{{ row.droneType }}</div> |
| | | <div class="cell">{{ row.area }}</div> |
| | | <div class="cell">{{ row.dataSource }}</div> |
| | | <div class="cell">{{ row.frequency }}</div> |
| | | <div class="cell">{{ row.discoveredAt }}</div> |
| | | <div class="cell">{{ row.stayTime }}</div> |
| | | <div class="cell">{{ row.counterMethod }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | device: { |
| | | type: Object, |
| | | default: () => null |
| | | } |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:modelValue']) |
| | | |
| | | const visibleModel = computed({ |
| | | get: () => props.modelValue, |
| | | set: (val) => emit('update:modelValue', val) |
| | | }) |
| | | |
| | | const dialogTitle = computed(() => { |
| | | return `反制设备名称 ${props.device?.name || ''}`.trim() |
| | | }) |
| | | |
| | | const historyList = computed(() => { |
| | | const dataSource = props.device?.name || '侦测反制设备名称' |
| | | return [ |
| | | { |
| | | id: 'his_001', |
| | | droneName: '无人机名称', |
| | | serialNumber: 'XLH789456', |
| | | droneType: '微型机', |
| | | area: '古村区', |
| | | dataSource, |
| | | frequency: '5800MHZ', |
| | | discoveredAt: '2025/12/26 12:15:26', |
| | | stayTime: '00:10:50', |
| | | counterMethod: '信号干扰' |
| | | }, |
| | | { |
| | | id: 'his_002', |
| | | droneName: '无人机名称', |
| | | serialNumber: 'XLH789456', |
| | | droneType: '微型机', |
| | | area: '古村区', |
| | | dataSource, |
| | | frequency: '5800MHZ', |
| | | discoveredAt: '2025/12/26 12:18:06', |
| | | stayTime: '00:08:12', |
| | | counterMethod: '信号干扰' |
| | | }, |
| | | { |
| | | id: 'his_003', |
| | | droneName: '无人机名称', |
| | | serialNumber: 'XLH789456', |
| | | droneType: '微型机', |
| | | area: '古村区', |
| | | dataSource, |
| | | frequency: '2400MHZ', |
| | | discoveredAt: '2025/12/26 12:22:31', |
| | | stayTime: '00:06:09', |
| | | counterMethod: '诱导驱离' |
| | | }, |
| | | { |
| | | id: 'his_004', |
| | | droneName: '无人机名称', |
| | | serialNumber: 'XLH789456', |
| | | droneType: '微型机', |
| | | area: '古村区', |
| | | dataSource, |
| | | frequency: '2400MHZ', |
| | | discoveredAt: '2025/12/26 12:28:44', |
| | | stayTime: '00:12:03', |
| | | counterMethod: '诱导驱离' |
| | | }, |
| | | { |
| | | id: 'his_005', |
| | | droneName: '无人机名称', |
| | | serialNumber: 'XLH789456', |
| | | droneType: '微型机', |
| | | area: '古村区', |
| | | dataSource, |
| | | frequency: '5800MHZ', |
| | | discoveredAt: '2025/12/26 12:32:10', |
| | | stayTime: '00:04:39', |
| | | counterMethod: '信号干扰' |
| | | }, |
| | | { |
| | | id: 'his_006', |
| | | droneName: '无人机名称', |
| | | serialNumber: 'XLH789456', |
| | | droneType: '微型机', |
| | | area: '古村区', |
| | | dataSource, |
| | | frequency: '5800MHZ', |
| | | discoveredAt: '2025/12/26 12:36:42', |
| | | stayTime: '00:07:21', |
| | | counterMethod: '信号干扰' |
| | | } |
| | | ] |
| | | }) |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .dialog-body { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | color: #C3C3DD; |
| | | } |
| | | |
| | | .device-summary { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 16px 24px; |
| | | |
| | | .item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | font-size: 14px; |
| | | |
| | | .label { |
| | | color: #8B8BA7; |
| | | } |
| | | |
| | | .value { |
| | | color: #FFFFFF; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table { |
| | | border: 1px solid rgba(255, 255, 255, 0.1); |
| | | border-radius: 6px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .thead, |
| | | .tr { |
| | | display: grid; |
| | | grid-template-columns: 60px 1.1fr 1.1fr 0.9fr 0.8fr 1.2fr 0.9fr 1.3fr 0.9fr 0.9fr; |
| | | align-items: center; |
| | | } |
| | | |
| | | .thead { |
| | | background: rgba(20, 24, 48, 0.9); |
| | | color: #FFFFFF; |
| | | font-size: 13px; |
| | | height: 42px; |
| | | border-bottom: 1px solid rgba(255, 255, 255, 0.08); |
| | | } |
| | | |
| | | .tbody { |
| | | max-height: 360px; |
| | | overflow-y: auto; |
| | | background: rgba(10, 14, 34, 0.6); |
| | | } |
| | | |
| | | .tr { |
| | | font-size: 12px; |
| | | color: #C3C3DD; |
| | | height: 40px; |
| | | border-bottom: 1px solid rgba(255, 255, 255, 0.05); |
| | | |
| | | &:nth-child(2n) { |
| | | background: rgba(255, 255, 255, 0.02); |
| | | } |
| | | } |
| | | |
| | | .cell { |
| | | padding: 0 10px; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | |
| | | &.index { |
| | | text-align: center; |
| | | color: #86909C; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="left-container"> |
| | | <div class="left-container" :class="{ collapsed: collapsedModel }"> |
| | | <div class="wrapper"> |
| | | <TitleTemplate>异常情况告警</TitleTemplate> |
| | | |
| | | <div class="category-container"> |
| | | <div class="category-item" v-for="(item, ind) in categoryList" :key="ind" @click="activeId = item.id" |
| | | :class="{ active: activeId === item.id }"> |
| | | <div class="category-item" v-for="(item, ind) in categoryList" :key="ind" |
| | | @click="activeIdModel = item.id" :class="{ active: activeIdModel === item.id }"> |
| | | {{ item.name }} |
| | | <span class="badge">{{ item.badge }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="content"> |
| | | <component :is="currentComponent" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="collapse-btn" @click="toggleCollapse"> |
| | | <span class="arrow" :class="collapsedModel ? 'right' : 'left'"></span> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | import EquipmentWarning from './EquipmentWarning.vue' |
| | | import HistoryWarning from './HistoryWarning.vue' |
| | | |
| | | const activeId = ref(1) |
| | | const props = defineProps({ |
| | | activeId: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | collapsed: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:activeId', 'update:collapsed']) |
| | | |
| | | const activeIdModel = computed({ |
| | | get: () => props.activeId, |
| | | set: (val) => emit('update:activeId', val) |
| | | }) |
| | | |
| | | const collapsedModel = computed({ |
| | | get: () => props.collapsed, |
| | | set: (val) => emit('update:collapsed', val) |
| | | }) |
| | | |
| | | const categoryList = ref([ |
| | | { |
| | | id: 1, |
| | | name: '实时告警', |
| | | badge: 8, |
| | | component: RealWarning |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: '设备告警', |
| | | badge: 8, |
| | | component: EquipmentWarning |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: '历史告警', |
| | | badge: 8, |
| | | component: HistoryWarning |
| | | } |
| | | ]) |
| | | |
| | | const currentComponent = computed(() => { |
| | | return categoryList.value.find(i => i.id === activeId.value)?.component |
| | | return categoryList.value.find(i => i.id === activeIdModel.value)?.component |
| | | }) |
| | | |
| | | const toggleCollapse = () => { |
| | | collapsedModel.value = !collapsedModel.value |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .left-container { |
| | | position: relative; |
| | | transition: transform 0.3s ease-in-out; |
| | | |
| | | &.collapsed { |
| | | transform: translateX(-100%); |
| | | } |
| | | } |
| | | |
| | | .collapse-btn { |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 357px; |
| | | background: rgba(17, 23, 34, 0.8); |
| | | border: 1px solid #333355; |
| | | border-left: none; |
| | | border-radius: 0 4px 4px 0; |
| | | cursor: pointer; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #fff; |
| | | z-index: 10; |
| | | backdrop-filter: blur(5px); |
| | | transform: translateY(-50%); |
| | | |
| | | .arrow { |
| | | width: 24px; |
| | | height: 24px; |
| | | background: url('@/assets/images/dataCockpit/left-btn.png') center / 100% 100% no-repeat; |
| | | } |
| | | |
| | | &:hover { |
| | | background: rgba(40, 79, 227, 0.6); |
| | | } |
| | | } |
| | | |
| | | .category-container { |
| | | display: flex; |
| | | justify-content: space-between; |
| | |
| | | box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36); |
| | | border-radius: 5px 5px 0px 0px; |
| | | } |
| | | |
| | | .badge { |
| | | position: absolute; |
| | | top: 16px; |
| | | right: 4px; |
| | | min-width: 16px; |
| | | height: 16px; |
| | | line-height: 16px; |
| | | font-size: 12px; |
| | | color: #FFFFFF; |
| | | background: #FF3B30; |
| | | border-radius: 9px; |
| | | text-align: center; |
| | | box-shadow: 0px 2px 6px rgba(255, 59, 48, 0.6); |
| | | pointer-events: none; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | overflow-x: hidden; |
| | | overflow-y: auto; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | <!-- |
| | | <!-- |
| | | * @Author : yuan |
| | | * @Date : 2026-01-07 15:17:54 |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2026-01-08 15:58:15 |
| | | * @LastEditTime : 2026-01-08 17:04:11 |
| | | * @FilePath : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue |
| | | * @Description : |
| | | * Copyright 2026 OBKoro1, All Rights Reserved. |
| | | * 2026-01-07 15:17:54 |
| | | --> |
| | | <template> |
| | | <div class="right-container"> |
| | | <div class="right-container" :class="{ collapsed: collapsedModel }"> |
| | | <div class="wrapper"> |
| | | <TitleTemplate>侦测反制设备</TitleTemplate> |
| | | |
| | |
| | | @history="onHistory" @click="onCardClick" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="collapse-btn" @click="toggleCollapse"> |
| | | <span class="arrow" :class="collapsedModel ? 'left' : 'right'"></span> |
| | | </div> |
| | | |
| | | <DeviceHistoryDialog v-model="historyVisible" :device="historyDevice" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import TitleTemplate from './templateComponents/TitleTemplate.vue' |
| | | import RealEquipmentTemplate from './templateComponents/RealEquipmentTemplate.vue' |
| | | import DeviceHistoryDialog from './DeviceHistoryDialog.vue' |
| | | import zcsbLogo from '@/assets/images/dataCockpit/zcsb.png' |
| | | import fzsbLogo from '@/assets/images/dataCockpit/fzsb.png' |
| | | |
| | | const props = defineProps({ |
| | | collapsed: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:collapsed']) |
| | | |
| | | const collapsedModel = computed({ |
| | | get: () => props.collapsed, |
| | | set: (val) => emit('update:collapsed', val) |
| | | }) |
| | | |
| | | const categoryList = ref([ |
| | | { |
| | |
| | | { |
| | | id: 'cd_001', |
| | | name: '反制设备名称', |
| | | actionText: '侦测中', // 胶囊文案 |
| | | actionText: '侦测中', |
| | | actionType: 'detecting', // detecting | jamming | idle |
| | | type: '察打一体', |
| | | battery: 100, // 电量 % |
| | |
| | | } |
| | | ]) |
| | | |
| | | const historyVisible = ref(false) |
| | | const historyDevice = ref(null) |
| | | |
| | | const onHistory = (item) => { |
| | | console.log('历史数据:', item.id) |
| | | historyDevice.value = item |
| | | historyVisible.value = true |
| | | console.log('历史数据:', item.id) |
| | | } |
| | | |
| | | const onCardClick = (item) => { |
| | | console.log('点击卡片:', item.id) |
| | | console.log('点击卡片:', item.id) |
| | | } |
| | | const toggleCollapse = () => { |
| | | collapsedModel.value = !collapsedModel.value |
| | | } |
| | | </script> |
| | | |
| | |
| | | |
| | | .right-container { |
| | | transition: transform 0.3s ease-in-out; |
| | | /* position: relative; Removed to avoid conflict with parent absolute positioning */ |
| | | position: relative; |
| | | |
| | | &.collapsed { |
| | | transform: translateX(100%); |
| | |
| | | .collapse-btn { |
| | | position: absolute; |
| | | top: 50%; |
| | | left: -24px; |
| | | width: 24px; |
| | | height: 60px; |
| | | right: 357px; |
| | | background: rgba(17, 23, 34, 0.8); |
| | | border: 1px solid #333355; |
| | | border-right: none; |
| | |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #fff; |
| | | transform: translateY(-50%); |
| | | z-index: 10; |
| | | backdrop-filter: blur(5px); |
| | | transform: translateY(-50%); |
| | | |
| | | .arrow { |
| | | border: solid #fff; |
| | | border-width: 0 2px 2px 0; |
| | | display: inline-block; |
| | | padding: 3px; |
| | | transition: transform 0.3s; |
| | | |
| | | &.left { |
| | | transform: rotate(135deg); |
| | | } |
| | | |
| | | &.right { |
| | | transform: rotate(-45deg); |
| | | } |
| | | width: 24px; |
| | | height: 24px; |
| | | background: url('@/assets/images/dataCockpit/right-btn.png') center / 100% 100% no-repeat; |
| | | } |
| | | |
| | | &:hover { |
| | | background: rgba(40, 79, 227, 0.6); |
| | | } |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | <script setup> |
| | | defineProps({ |
| | | data: { |
| | | type: Object, |
| | | required: true, |
| | | default: () => ({}) |
| | | } |
| | | }) |
| | | |
| | | const emit = defineEmits(['history', 'click']) |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="counter-card" @click="emit('click', data)"> |
| | | <!-- 头部 --> |
| | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | defineProps({ |
| | | data: { |
| | | type: Object, |
| | | required: true, |
| | | default: () => ({}) |
| | | } |
| | | }) |
| | | |
| | | const emit = defineEmits(['history', 'click']) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .counter-card { |
| | | margin-top: 10px; |
| | |
| | | <template> |
| | | <div class="page-container"> |
| | | <MapContainer /> |
| | | <LeftContainer class="left-container" /> |
| | | <RightContainer class="right-container" /> |
| | | <CenterContainer /> |
| | | <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" /> |
| | | <RightContainer class="right-container" v-model:collapsed="rightCollapsed" /> |
| | | <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import LeftContainer from './components/LeftContainer.vue'; |
| | | import RightContainer from './components/RightContainer.vue'; |
| | | import CenterContainer from './components/CenterContainer.vue'; |
| | | |
| | | const leftActiveId = ref(1); |
| | | const leftCollapsed = ref(false); |
| | | |
| | | const rightCollapsed = ref(false); |
| | | |
| | | const onSelectWarning = (type) => { |
| | | leftActiveId.value = type; |
| | | leftCollapsed.value = false; |
| | | }; |
| | | |
| | | const onSelectEquipment = () => { |
| | | rightCollapsed.value = false; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="区域" prop="areaCode"> |
| | | <el-input v-model="searchParams.areaCode" placeholder="请输入" clearable @clear="getList" /> |
| | | <el-form-item label="区域" prop="areaName"> |
| | | <el-input v-model="searchParams.areaName" placeholder="请输入" clearable @clear="getList" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="设备类型" prop="droneType"> |
| | | <el-select v-model="searchParams.droneType" placeholder="请选择" clearable @clear="getList"> |
| | | <el-select v-model="searchParams.droneType" placeholder="请选择" clearable @change="getList"> |
| | | <el-option |
| | | v-for="item in dictObj.deviceType" |
| | | :key="item.dictKey" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="时间"> |
| | | <el-date-picker |
| | | popper-class="ztzf-date-picker-popper" |
| | | class="ztzf-date-picker" |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | @change="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item> |
| | | <el-button @click="resetForm">重置</el-button> |
| | | <el-button type="primary" @click="handleSearch">查询</el-button> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <div> |
| | | <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button> |
| | | </div> |
| | | |
| | | <el-table v-loading="loading" :data="list"> |
| | | <el-table-column type="index" width="60" label="序号" /> |
| | | <el-table-column prop="droneName" label="无人机名称" /> |
| | | <el-table-column prop="droneSerialNo" label="无人机序列号" /> |
| | | <el-table-column prop="alarmTime" label="告警时间" /> |
| | | <el-table-column prop="areaCode" label="区域" /> |
| | | <el-table-column prop="areaName" label="区域" /> |
| | | <el-table-column prop="triggerReason" label="触发原因" /> |
| | | <el-table-column prop="deviceName" label="侦测反制设备" /> |
| | | <el-table-column prop="droneType" label="设备类型"> |
| | |
| | | {{ getDeviceTypeName(row.droneType) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="deviceCode" label="设备编码"/> |
| | | <el-table-column prop="deviceCode" label="设备编码" /> |
| | | <el-table-column label="设备状态"> |
| | | <template v-slot="{ row }"> |
| | | {{ getDeviceStatus(row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="操作">--> |
| | | <!-- <template v-slot="{ row }">--> |
| | | <!-- <el-link @click="handleView(row)" type="primary">查看</el-link>--> |
| | | <!-- </template>--> |
| | | <!-- </el-table-column>--> |
| | | </el-table> |
| | | |
| | | <div> |
| | |
| | | <script setup> |
| | | import { computed, onMounted, ref } from 'vue' |
| | | import { getDictionaryByCode } from '@/api/system/dictbiz' |
| | | import { fwDroneAlarmRecordPageApi } from './fwDroneAlarmRecord' |
| | | import { exportFwDroneAlarmRecordApi, fwDroneAlarmRecordPageApi } from './fwDroneAlarmRecord' |
| | | import FormDiaLog from './FormDiaLog.vue' |
| | | import { blobDownload, dateRangeFormat } from '@ztzf/utils' |
| | | |
| | | const initSearchParams = () => ({ |
| | | droneName: '', // 无人机名称 |
| | | areaCode: '', // 区域 |
| | | areaName: '', // 区域 |
| | | droneType: '', // 设备类型 |
| | | current: 1, // 当前页 |
| | | size: 10, // 每页大小 |
| | |
| | | const queryParamsRef = ref(null) // 查询表单实例 |
| | | const dialogRef = ref(null) // 弹框实例 |
| | | const dictObj = ref({ |
| | | deviceType: [], |
| | | deviceType: [], //设备属性 |
| | | }) |
| | | const dateRange = ref([]) |
| | | const exportLoading = ref(false) |
| | | const deviceTypeMap = computed(() => { |
| | | const map = new Map() |
| | | dictObj.value.deviceType.forEach(item => { |
| | |
| | | |
| | | // 获取列表 |
| | | async function getList() { |
| | | const range = dateRangeFormat(dateRange.value) |
| | | loading.value = true |
| | | try { |
| | | const res = await fwDroneAlarmRecordPageApi(searchParams.value) |
| | | const params = { ...searchParams.value, startTime: range[0], endTime: range[1] } |
| | | const res = await fwDroneAlarmRecordPageApi(params) |
| | | list.value = res?.data?.data?.records ?? [] |
| | | total.value = res?.data?.data?.total ?? 0 |
| | | } finally { |
| | |
| | | dialogRef.value?.open({ mode: 'view', row: { ...row } }) |
| | | } |
| | | |
| | | function exportFile() { |
| | | exportLoading.value = true |
| | | exportFwDroneAlarmRecordApi() |
| | | .then(res => { |
| | | blobDownload(res) |
| | | }) |
| | | .finally(() => { |
| | | exportLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | function getDeviceStatus(row) { |
| | | return row.deviceStatus ?? row.status ?? '-' |
| | | } |
| | |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | //导出 |
| | | export const exportFwDroneAlarmRecordApi = () => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneAlarmRecord/export-fwDroneAlarmRecord`, |
| | | method: 'get', |
| | | responseType: 'blob', |
| | | }) |
| | | } |
| New file |
| | |
| | | <template> |
| | | <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close> |
| | | <div> |
| | | <div>无人机名称:{{ formData.droneName }}</div> |
| | | <div>序列号: {{ formData.serialNo }}</div> |
| | | <div>告警时间: {{ formData.alarmTime }}</div> |
| | | <div>触发原因: {{ formData.triggerType }}</div> |
| | | <div>停留时长: {{ formData.stayDuration }}</div> |
| | | </div> |
| | | <el-table :data="list"> |
| | | <el-table-column type="index" width="60" label="序号" /> |
| | | <el-table-column prop="areaCode" label="areaCode" /> |
| | | <el-table-column prop="createTime" label="createTime" /> |
| | | <el-table-column prop="latitude" label="latitude" /> |
| | | <el-table-column prop="longitude" label="longitude" /> |
| | | </el-table> |
| | | <template #footer> |
| | | <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, ref } from 'vue' |
| | | import { fwDroneFlightRecordDetailApi } from '@/views/recordManage/historyTracks/fwDroneFlightRecord' |
| | | import { fwDroneFlightRecordDetailPageApi } from '@/views/recordManage/historyTracks/flyTrajectory' |
| | | |
| | | const initForm = () => ({ |
| | | droneName: '', |
| | | serialNo: '', |
| | | alarmTime: '', |
| | | triggerType: '', |
| | | stayDuration: '', |
| | | }) |
| | | let list = ref([]) |
| | | const emit = defineEmits(['success']) |
| | | const formData = ref(initForm()) // 表单数据 |
| | | const visible = ref(false) // 弹框显隐 |
| | | const dialogMode = ref('add') // 弹框模式 |
| | | const dialogReadonly = computed(() => dialogMode.value === 'view') |
| | | const dialogTitle = computed(() => { |
| | | if (dialogMode.value === 'edit') { |
| | | return '编辑' |
| | | } else if (dialogMode.value === 'view') { |
| | | return '查看' |
| | | } else { |
| | | return '新增' |
| | | } |
| | | }) |
| | | |
| | | // 关闭弹框 |
| | | function handleCancel() { |
| | | visible.value = false |
| | | } |
| | | |
| | | // 加载详情 |
| | | async function loadDetail() { |
| | | if (!formData.value.id) return |
| | | const res = await fwDroneFlightRecordDetailApi({ id: formData.value.id }) |
| | | const res1 = await fwDroneFlightRecordDetailPageApi({ flightRecordId: formData.value.id }) |
| | | list.value = res1.data.data.records ?? [] |
| | | formData.value = res?.data?.data ?? {} |
| | | } |
| | | |
| | | // 关闭后重置 |
| | | function handleClosed() { |
| | | formData.value = initForm() |
| | | } |
| | | |
| | | // 打开弹框 |
| | | async function open({ mode, row } = {}) { |
| | | dialogMode.value = mode || 'add' |
| | | formData.value = row |
| | | visible.value = true |
| | | if (dialogMode.value === 'add') { |
| | | formData.value = initForm() |
| | | } else { |
| | | await loadDetail() |
| | | } |
| | | } |
| | | |
| | | defineExpose({ open }) |
| | | </script> |
| New file |
| | |
| | | import request from '@/axios' |
| | | |
| | | // 详情 |
| | | export const fwDroneFlightRecordDetailApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecordDetail/detail`, |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | // 分页 |
| | | export const fwDroneFlightRecordDetailPageApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecordDetail/page`, |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | // 新增或修改 |
| | | export const fwDroneFlightRecordDetailSubmitApi = data => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecordDetail/submit`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | |
| | | // 逻辑删除 |
| | | export const fwDroneFlightRecordDetailRemoveApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecordDetail/remove`, |
| | | method: 'post', |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | // 导出数据 |
| | | export const exportFwDroneFlightRecordDetailApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecordDetail/export-fwDroneFlightRecordDetail`, |
| | | method: 'get', |
| | | params, |
| | | responseType: 'blob', |
| | | }) |
| | | } |
| New file |
| | |
| | | import request from '@/axios' |
| | | |
| | | // 查list |
| | | export const fwDroneFlightRecordPageApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecord/page`, |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | // 增加或更新 |
| | | export const fwDroneFlightRecordSubmitApi = data => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecord/submit`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | |
| | | // 删除 |
| | | export const fwDroneFlightRecordRemoveApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecord/remove`, |
| | | method: 'post', |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | // 详情 |
| | | export const fwDroneFlightRecordDetailApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecord/detail`, |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | } |
| | | |
| | | // 导出 |
| | | export const exportFwDroneFlightRecordApi = params => { |
| | | return request({ |
| | | url: `/drone-fw/record/fwDroneFlightRecord/export-fwDroneFlightRecord`, |
| | | method: 'get', |
| | | params, |
| | | responseType: 'blob', |
| | | }) |
| | | } |
| | |
| | | <!-- |
| | | * @Author : yuan |
| | | * @Date : 2026-01-06 16:38:25 |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2026-01-07 11:04:23 |
| | | * @FilePath : \applications\drone-command\src\views\recordManage\historyTracks.vue |
| | | * @Description : |
| | | * Copyright 2026 OBKoro1, All Rights Reserved. |
| | | * 2026-01-06 16:38:25 |
| | | --> |
| | | <template> |
| | | <basic-container> |
| | | 历史轨迹 |
| | | </basic-container> |
| | | <basic-container> |
| | | <el-form ref="queryParamsRef" :model="searchParams"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="无人机名称" prop="droneName"> |
| | | <el-input v-model="searchParams.droneName" placeholder="请输入" clearable @clear="getList" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="区域" prop="areaName"> |
| | | <el-input v-model="searchParams.areaName" placeholder="请输入" clearable @clear="getList" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="告警时间"> |
| | | <el-date-picker |
| | | popper-class="ztzf-date-picker-popper" |
| | | class="ztzf-date-picker" |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | @change="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item> |
| | | <el-button @click="resetForm">重置</el-button> |
| | | <el-button type="primary" @click="handleSearch">查询</el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <div> |
| | | <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button> |
| | | </div> |
| | | |
| | | <el-table v-loading="loading" :data="list"> |
| | | <el-table-column type="index" width="60" label="序号" /> |
| | | <el-table-column prop="droneName" label="无人机名称" /> |
| | | <el-table-column prop="serialNo" label="序列号" /> |
| | | <el-table-column prop="alarmTime" label="告警时间" /> |
| | | <el-table-column prop="areaName" label="区域" /> |
| | | <el-table-column prop="triggerType" label="触发原因" /> |
| | | <el-table-column label="停留时长"> |
| | | <template v-slot="{ row }"> |
| | | {{ formatStayDuration(row.stayDuration) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)" type="primary">历史轨迹</el-link> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div> |
| | | <el-pagination |
| | | v-model:current-page="searchParams.current" |
| | | v-model:page-size="searchParams.size" |
| | | :total="total" |
| | | @change="getList" |
| | | /> |
| | | </div> |
| | | |
| | | <TrajectoryDiaLog ref="dialogRef" /> |
| | | </basic-container> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref } from 'vue' |
| | | import { exportFwDroneFlightRecordApi, fwDroneFlightRecordPageApi } from './fwDroneFlightRecord' |
| | | import { blobDownload, dateRangeFormat } from '@ztzf/utils' |
| | | import FormDiaLog from '@/views/basicManage/deviceStock/FormDiaLog.vue' |
| | | import TrajectoryDiaLog from '@/views/recordManage/historyTracks/TrajectoryDiaLog.vue' |
| | | |
| | | const initSearchParams = () => ({ |
| | | droneName: '', // 无人机名称 |
| | | areaName: '', // 区域 |
| | | current: 1, // 当前页 |
| | | size: 10, // 每页大小 |
| | | }) |
| | | const searchParams = ref(initSearchParams()) // 查询参数 |
| | | const total = ref(0) // 总条数 |
| | | const loading = ref(false) // 列表加载中 |
| | | const list = ref([]) // 列表数据 |
| | | const queryParamsRef = ref(null) // 查询表单实例 |
| | | const dateRange = ref([]) |
| | | const exportLoading = ref(false) |
| | | const dialogRef = ref(null) // 弹框实例 |
| | | |
| | | // 获取列表 |
| | | async function getList() { |
| | | const range = dateRangeFormat(dateRange.value) |
| | | loading.value = true |
| | | try { |
| | | const params = { ...searchParams.value, startTime: range?.[0], endTime: range?.[1] } |
| | | const res = await fwDroneFlightRecordPageApi(params) |
| | | 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() { |
| | | dateRange.value = [] |
| | | queryParamsRef.value?.resetFields() |
| | | searchParams.value.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | function formatStayDuration(value) { |
| | | if (!value) return '-' |
| | | if (typeof value === 'string') return value |
| | | const hour = String(value.hour ?? 0).padStart(2, '0') |
| | | const minute = String(value.minute ?? 0).padStart(2, '0') |
| | | const second = String(value.second ?? 0).padStart(2, '0') |
| | | return `${hour}:${minute}:${second}` |
| | | } |
| | | |
| | | function exportFile() { |
| | | const range = dateRangeFormat(dateRange.value) |
| | | exportLoading.value = true |
| | | exportFwDroneFlightRecordApi({ ...searchParams.value, start_time: range[0], end_time: range[1] }) |
| | | .then(res => { |
| | | blobDownload(res) |
| | | }) |
| | | .finally(() => { |
| | | exportLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | function handleView(row) { |
| | | dialogRef.value?.open({ mode: 'view', row: { ...row } }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | </style> |
| | | <style scoped lang="scss"></style> |
| | |
| | | max ? { max, message: `长度不超过${max}`, trigger } : {}, |
| | | ] |
| | | } |
| | | |
| | | export function getDictLabel(value, dictList) { |
| | | return dictList.find(item => item.dictKey === value)?.dictValue || value || '-' |
| | | } |
| | | |
| | | export function blobDownload(res) { |
| | | const disposition = res.headers?.['content-disposition'] || res.headers?.['Content-Disposition'] |
| | | const encodedName = disposition.split('filename=')[1] |
| | | const filename = decodeURIComponent(encodedName) |
| | | const elink = document.createElement('a') |
| | | elink.download = filename |
| | | elink.style.display = 'none' |
| | | const blob = new Blob([res.data]) |
| | | elink.href = URL.createObjectURL(blob) |
| | | document.body.appendChild(elink) |
| | | elink.click() |
| | | document.body.removeChild(elink) |
| | | } |