Merge branch 'dev' of http://139.196.74.78:10010/r/drone/drone-web-manage into dev
18 files modified
4 files added
| | |
| | | import request from '@/axios'; |
| | | import request from '@/axios' |
| | | |
| | | export const getList = (current, size, params) => { |
| | | return request({ |
| | |
| | | current, |
| | | size, |
| | | }, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const remove = ids => { |
| | | return request({ |
| | |
| | | params: { |
| | | ids, |
| | | }, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const add = row => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/add', |
| | | method: 'post', |
| | | data: row, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const update = row => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/update', |
| | | method: 'post', |
| | | data: row, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const operatePasswordUpdate = row => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/operatePasswordUpdate', |
| | | method: 'post', |
| | | data: row, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const getDetail = id => { |
| | | return request({ |
| | |
| | | params: { |
| | | id, |
| | | }, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const getOperatePassword = id => { |
| | | return request({ |
| | |
| | | params: { |
| | | id, |
| | | }, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const getDeviceUpgradeInfo = (params) => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/workspaces/firmware-release-notes/latest', |
| | | method: 'get', |
| | | params: params |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const ota = (workspace_id,row) => { |
| | | export const ota = (workspace_id, row) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/${workspace_id}/devices/ota`, |
| | | method: 'post', |
| | | data: row, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const getDeviceFirmwareList = (current, size, params) => { |
| | | return request({ |
| | |
| | | current, |
| | | size, |
| | | }, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const upload = (workspace_id,formDatas) => { |
| | | export const upload = (workspace_id, formDatas) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/workspaces/${workspace_id}/firmwares/file/upload`, |
| | | method: 'post', |
| | | data: formDatas, |
| | | headers: { "Content-Type": "multipart/form-data" }, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const updateFirmware = (row) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/workspaces/firmwares/update`, |
| | | method: 'post', |
| | | data: row, |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const deleteFirmware = ids => { |
| | | return request({ |
| | |
| | | params: { |
| | | ids |
| | | } |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const getDevices = (workspace_id,device_sn) => { |
| | | export const getDevices = (workspace_id, device_sn) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/${workspace_id}/devices/${device_sn}`, |
| | | method: 'get', |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | export const getAirportList = () => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/getAirportList', |
| | | method: 'get', |
| | | }); |
| | | }; |
| | | }) |
| | | } |
| | | |
| | | //可飞行机巢列表 |
| | | export const getFlyingNestBy = (param) => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/getFlyingNestBy', |
| | | method: 'post', |
| | | data:param |
| | | }); |
| | | }; |
| | | data: param |
| | | }) |
| | | } |
| | | |
| | | // 设备下线 |
| | | export const deviceOffline = (dockSn) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/deviceOffline?dockSn=${dockSn}`, |
| | | method: 'get', |
| | | }) |
| | | } |
| | |
| | | data, |
| | | }); |
| | | }; |
| | | // 飞行统计111 |
| | | export const tongjiNestApi = data => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/getAllDeviceStatistics`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | // 飞行时长 里程 任务数 |
| | | export const getFlyTime = data => { |
| | | return request({ |
| | |
| | | areaCode |
| | | }, |
| | | }); |
| | | }; |
| | | }; |
| | | |
| | | // 任务成果 |
| | | export const getdisposeApi = data => { |
| | | return request({ |
| | | url: `/drone-device-core/jobEvent/eventStatusLine`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | // 任务成果 |
| | | export const gettaskOutcomApi = data => { |
| | | return request({ |
| | | url: `/blade-resource/attach/getManageAttachTypeStatistics`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| New file |
| | |
| | | <svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| | | <g id="Frame" clip-path="url(#clip0_1421_9332)"> |
| | | <path id="Vector" d="M14.3527 3.77465C14.2357 3.77465 14.1234 3.82113 14.0407 3.90387C13.958 3.98661 13.9115 4.09882 13.9115 4.21583V5.2774C13.23 4.11678 12.1947 3.20492 10.9573 2.67538C9.71995 2.14584 8.34552 2.02646 7.03539 2.33472C5.72525 2.64299 4.54825 3.36271 3.67683 4.38843C2.80541 5.41416 2.28535 6.69199 2.19285 8.03472C2.18815 8.09269 2.19495 8.15102 2.21287 8.20636C2.23079 8.26169 2.25947 8.31293 2.29727 8.35714C2.33507 8.40134 2.38124 8.43764 2.43312 8.46393C2.48499 8.49023 2.54156 8.50601 2.59956 8.51036C2.71083 8.5107 2.81812 8.46898 2.89993 8.39356C2.98175 8.31814 3.03204 8.21459 3.04073 8.10365C3.12938 6.92628 3.60019 5.81008 4.38155 4.92489C5.1629 4.0397 6.21203 3.43398 7.36929 3.19987C8.52656 2.96576 9.72863 3.11609 10.7926 3.62798C11.8566 4.13988 12.7242 4.98531 13.2635 6.03566H12.0917C11.9737 6.03566 11.8606 6.0825 11.7773 6.16589C11.6939 6.24927 11.647 6.36236 11.647 6.48028C11.647 6.5982 11.6939 6.71129 11.7773 6.79467C11.8606 6.87805 11.9737 6.9249 12.0917 6.9249H14.3527C14.4697 6.9249 14.5819 6.87842 14.6646 6.79568C14.7474 6.71295 14.7938 6.60073 14.7938 6.48373V4.21583C14.7938 4.09882 14.7474 3.98661 14.6646 3.90387C14.5819 3.82113 14.4697 3.77465 14.3527 3.77465ZM14.3527 8.50347C14.2947 8.49876 14.2364 8.50556 14.181 8.52348C14.1257 8.5414 14.0744 8.57009 14.0302 8.60789C13.986 8.64568 13.9497 8.69185 13.9234 8.74373C13.8972 8.79561 13.8814 8.85217 13.877 8.91017C13.7884 10.0875 13.3176 11.2037 12.5362 12.0889C11.7548 12.9741 10.7057 13.5799 9.54846 13.814C8.39119 14.0481 7.18912 13.8977 6.12515 13.3858C5.06118 12.8739 4.19354 12.0285 3.65424 10.9782H4.90193C5.01985 10.9782 5.13294 10.9313 5.21632 10.8479C5.2997 10.7646 5.34654 10.6515 5.34654 10.5335C5.34654 10.4156 5.2997 10.3025 5.21632 10.2192C5.13294 10.1358 5.01985 10.0889 4.90193 10.0889H2.64092C2.58298 10.0889 2.52562 10.1003 2.47209 10.1225C2.41856 10.1447 2.36993 10.1772 2.32896 10.2181C2.288 10.2591 2.2555 10.3077 2.23333 10.3613C2.21116 10.4148 2.19975 10.4722 2.19975 10.5301V12.7911C2.19975 12.909 2.24659 13.0221 2.32997 13.1055C2.41335 13.1889 2.52645 13.2357 2.64437 13.2357C2.76229 13.2357 2.87538 13.1889 2.95876 13.1055C3.04214 13.0221 3.08898 12.909 3.08898 12.7911V11.7226C3.77051 12.8833 4.80578 13.7951 6.04315 14.3247C7.28052 14.8542 8.65495 14.9736 9.96509 14.6653C11.2752 14.357 12.4522 13.6373 13.3236 12.6116C14.1951 11.5859 14.7151 10.3081 14.8076 8.96532C14.8104 8.90624 14.8013 8.84721 14.7808 8.79173C14.7603 8.73626 14.7288 8.68546 14.6883 8.64238C14.6478 8.59929 14.5991 8.56479 14.5449 8.54093C14.4908 8.51707 14.4325 8.50433 14.3733 8.50347H14.3527Z" fill="#1441FF"/> |
| | | </g> |
| | | <defs> |
| | | <clipPath id="clip0_1421_9332"> |
| | | <rect width="17" height="17" fill="white"/> |
| | | </clipPath> |
| | | </defs> |
| | | </svg> |
| | |
| | | store.commit('SET_IS_MENU', isMenu === undefined) |
| | | |
| | | const menuAll = getStore({ name: 'menuAll' }) |
| | | |
| | | if (getToken()) { |
| | | if (!menuAll) { |
| | | if (!menuAll?.length) { |
| | | store.dispatch('GetMenu').then(data => { |
| | | if (data.length !== 0) { |
| | | router.$avueRouter.formatRoutes(data, true) |
| | |
| | | getRoutes(tenantId).then(res => { |
| | | const data = res.data.data; |
| | | let menu = deepClone(data); |
| | | menu.forEach(ele => formatPath(ele, true)); |
| | | menu?.forEach(ele => formatPath(ele, true)); |
| | | commit('SET_MENU', menu); |
| | | commit('SET_MENU_ALL', menu); |
| | | dispatch('GetButtons'); |
| | |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | } |
| | | |
| | | .ztzf-public-general-avue-crud { |
| | | .el-table { |
| | | tr { |
| | | min-height: 30px !important; |
| | | line-height: 30px !important; |
| | | box-sizing: border-box !important; |
| | | } |
| | | |
| | | tr th, |
| | | tr td { |
| | | padding: 6px 0 !important; |
| | | min-height: 30px !important; |
| | | line-height: 30px !important; |
| | | box-sizing: border-box !important; |
| | | |
| | | div.cell { |
| | | min-height: 30px !important; |
| | | line-height: 30px !important; |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | }; |
| | | // 日期选择 |
| | | const changeselect = () => { |
| | | params.value.start_date = taskData.value.length ? `${taskData.value[0]} 00:00:00` : null; |
| | | params.value.end_date = taskData.value.length ? `${taskData.value[1]} 23:59:59` : null; |
| | | params.value.start_date = taskData.value?.length ? `${taskData.value[0]} 00:00:00` : null; |
| | | params.value.end_date = taskData.value?.length ? `${taskData.value[1]} 23:59:59` : null; |
| | | }; |
| | | // 处理日期清空 |
| | | const handleDateClear = () => { |
| | |
| | | getList(); |
| | | }; |
| | | const handleSearch = () => { |
| | | // console.log('taskData',taskData.value); |
| | | if(taskData.value === null){ |
| | | params.value.start_date = null; // 重置开始日期参数 |
| | | params.value.end_date = null; // 重置结束日期参数 |
| | | } |
| | | |
| | | getList(); |
| | | |
| | | }; |
| | |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | :disabled="searchForm.photoType !== '' && item.value === '4'" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | { |
| | | value: '5', |
| | | label: '全景', |
| | | |
| | | }, |
| | | { |
| | | value: '4', |
| | | label: '正射', |
| | | |
| | | }, |
| | | ]; |
| | | const CategoryOption = [ |
| | |
| | | <template> |
| | | <basic-main-content> |
| | | <avue-crud |
| | | :option="option" |
| | | :table-loading="loading" |
| | | :data="data" |
| | | v-model:page="page" |
| | | ref="crud" |
| | | @row-del="rowDel" |
| | | v-model="form" |
| | | :permission="permissionList" |
| | | @row-update="rowUpdate" |
| | | @row-save="rowSave" |
| | | :before-open="beforeOpen" |
| | | @search-change="searchChange" |
| | | @search-reset="searchReset" |
| | | @selection-change="selectionChange" |
| | | @current-change="currentChange" |
| | | @size-change="sizeChange" |
| | | @refresh-change="refreshChange" |
| | | @on-load="onLoad" |
| | | @tree-load="treeLoad" |
| | | > |
| | | <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel" |
| | | v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave" :before-open="beforeOpen" |
| | | @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange" |
| | | @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad" |
| | | @tree-load="treeLoad"> |
| | | <template #menu-left> |
| | | <el-button |
| | | type="danger" |
| | | icon="el-icon-delete" |
| | | plain |
| | | v-if="permission.device_delete" |
| | | @click="handleDelete" |
| | | >删 除 |
| | | <el-button type="danger" icon="el-icon-delete" plain v-if="permission.device_delete" @click="handleDelete">删 除 |
| | | </el-button> |
| | | </template> |
| | | <template #dept_name="{ row }"> |
| | |
| | | <template #firmware_status="{ row }"> |
| | | <div v-if="row.firmware_status == 1" class="onlineStatus1"> |
| | | <el-tag type="info">无需升级</el-tag> |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)" |
| | | >版本管理</el-tag |
| | | > |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)">版本管理</el-tag> |
| | | <!-- <div style="background-color: #f1f1f1; border-radius: 4px">无需升级</div> --> |
| | | </div> |
| | | <div v-if="row.firmware_status == 2" class="onlineStatus1"> |
| | | <!-- <el-tag type="success" effect="dark" class="active-element" @click="updateFirmware(row)" |
| | | >升级固件</el-tag |
| | | > --> |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)" |
| | | >版本管理</el-tag |
| | | > |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)">版本管理</el-tag> |
| | | <!-- <div @click="updateFirmware(row)" class="firmware_status">升级固件</div> --> |
| | | </div> |
| | | <div v-if="row.firmware_status == 3" class="onlineStatus"> |
| | | <el-tag type="success" effect="dark" class="active-element" @click="updateFirmware(row)" |
| | | >一致性升级</el-tag |
| | | > |
| | | <el-tag type="success" effect="dark" class="active-element" @click="updateFirmware(row)">一致性升级</el-tag> |
| | | <!-- <div @click="updateFirmware(row)" class="firmware_status">一致性升级</div> --> |
| | | </div> |
| | | <div v-if="row.firmware_status == 4" class="onlineStatus"> |
| | | <el-tag effect="dark" class="active-element" @click="updateFirmware(row)">升级中</el-tag> |
| | | <el-progress |
| | | v-if="row.firmware_progress > 0 && row.firmware_progress < 100" |
| | | :percentage="row.firmware_progress" |
| | | ></el-progress> |
| | | <el-progress v-if="row.firmware_progress > 0 && row.firmware_progress < 100" |
| | | :percentage="row.firmware_progress"></el-progress> |
| | | </div> |
| | | </template> |
| | | <template #mode_code="{ row }"> |
| | | <span |
| | | class="text" |
| | | v-if="row.domain == 3" |
| | | :style="row.mode_code != '-1' ? 'color: #00ee8b' : 'color: #de5e5e'" |
| | | > |
| | | <span class="text" v-if="row.domain == 3" :style="row.mode_code != '-1' ? 'color: #00ee8b' : 'color: #de5e5e'"> |
| | | {{ getDockModeText(row.mode_code) }} |
| | | </span> |
| | | </template> |
| | | <template #menu="scope"> |
| | | <el-button |
| | | type="primary" |
| | | text |
| | | icon="el-icon-paperclip" |
| | | v-if="permission.oss_set" |
| | | @click.stop="handleOpenOssSet(scope.row, scope.index)" |
| | | >存储配置 |
| | | <el-button type="primary" text icon="el-icon-paperclip" v-if="permission.oss_set" |
| | | @click.stop="handleOpenOssSet(scope.row, scope.index)">存储配置 |
| | | </el-button> |
| | | <el-button |
| | | type="primary" |
| | | text |
| | | icon="el-icon-share" |
| | | v-if="permission.per_share && scope.row.domain == 3" |
| | | @click.stop="handleOpenDevicePerShare(scope.row, scope.index)" |
| | | >机场授权 |
| | | <el-button type="primary" text icon="el-icon-share" v-if="permission.per_share && scope.row.domain == 3" |
| | | @click.stop="handleOpenDevicePerShare(scope.row, scope.index)">机场授权 |
| | | </el-button> |
| | | |
| | | <el-button |
| | | type="primary" |
| | | text |
| | | icon="el-icon-position" |
| | | :disabled="!scope.row.status" |
| | | <el-button type="primary" text icon="el-icon-position" :disabled="!scope.row.status" |
| | | v-if="permission.rang_con && scope.row.domain == 3" |
| | | @click.stop="handleOpenRemoteDebugging(scope.row, scope.index)" |
| | | >远程调试 |
| | | @click.stop="handleOpenRemoteDebugging(scope.row, scope.index)">远程调试 |
| | | </el-button> |
| | | |
| | | <el-button type="primary" text icon="el-icon-setting" v-if="permission.fly_device_offline" |
| | | @click.stop="handleDeviceOffline(scope.row)">设备下线 |
| | | </el-button> |
| | | </template> |
| | | <!-- 添加行政区划显示模板 --> |
| | |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <el-dialog |
| | | title="存储配置" |
| | | append-to-body |
| | | v-model="ossSetBox" |
| | | @close="handleOssSetClose" |
| | | width="600px" |
| | | > |
| | | <el-dialog title="存储配置" append-to-body v-model="ossSetBox" @close="handleOssSetClose" width="600px"> |
| | | <div class="onlineStatus"> |
| | | 存储对象配置: |
| | | <el-select v-model="ossEnd" clearable placeholder="请选择存储对象" style="width: 450px"> |
| | | <el-option |
| | | v-for="item in ossList" |
| | | :key="item.id" |
| | | :label="item.name + ': ' + item.endpoint" |
| | | :value="item.id" |
| | | > |
| | | <el-option v-for="item in ossList" :key="item.id" :label="item.name + ': ' + item.endpoint" :value="item.id"> |
| | | <span style="float: left">{{ item.name + ': ' + item.endpoint }}</span> |
| | | <span v-if="item.status == 1" class="status1">{{ item.categoryName }}</span> |
| | | <span v-if="item.status == 2" class="status2">{{ item.categoryName }}</span> |
| | |
| | | <el-dialog title="固件版本管理" append-to-body v-model="rollFirmwareBox" width="455px"> |
| | | <div class="onlineStatus"> |
| | | 固件版本升级/回退: |
| | | <el-select |
| | | v-model="rollFirmVersion" |
| | | clearable |
| | | placeholder="请选择固件版本" |
| | | style="width: 240px" |
| | | > |
| | | <el-option |
| | | v-for="item in firmList" |
| | | :key="item.firmware_version" |
| | | :label="item.firmware_version" |
| | | :value="item.firmware_version" |
| | | /> |
| | | <el-select v-model="rollFirmVersion" clearable placeholder="请选择固件版本" style="width: 240px"> |
| | | <el-option v-for="item in firmList" :key="item.firmware_version" :label="item.firmware_version" |
| | | :value="item.firmware_version" /> |
| | | </el-select> |
| | | </div> |
| | | <template #footer> |
| | |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <el-drawer |
| | | class="ztzf-drawer-body-basic-container" |
| | | title="机场授权管理" |
| | | size="60%" |
| | | append-to-body |
| | | v-model="devicePerShareVisible" |
| | | :direction="'rtl'" |
| | | > |
| | | <el-drawer class="ztzf-drawer-body-basic-container" title="机场授权管理" size="60%" append-to-body |
| | | v-model="devicePerShareVisible" :direction="'rtl'"> |
| | | <DevicePerShare ref="devicePerShare" /> |
| | | </el-drawer> |
| | | |
| | | <el-drawer :title="operateTitle" append-to-body v-model="remoteDebuggingShow" size="40%"> |
| | | <DockControlPanel |
| | | v-if="remoteDebuggingShow" |
| | | :sn="curDeviceInfo.device_sn" |
| | | :deviceInfo="curDeviceInfo" |
| | | > |
| | | <DockControlPanel v-if="remoteDebuggingShow" :sn="curDeviceInfo.device_sn" :deviceInfo="curDeviceInfo"> |
| | | </DockControlPanel> |
| | | </el-drawer> |
| | | </basic-main-content> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | import { |
| | | getList, |
| | | remove, |
| | |
| | | ota, |
| | | getDeviceFirmwareList, |
| | | getDevices, |
| | | } from '@/api/device/device'; |
| | | import { deleteByOssId, addOrUpdate as addOrUpdateOssBind } from '@/api/device/ossBind'; |
| | | import { getListPage as getOssList } from '@/api/resource/oss'; |
| | | import { getLazyTree } from '@/api/base/region'; |
| | | import { EDockModeText, EDockModeCode } from '@/types/device'; |
| | | import { mapGetters } from 'vuex'; |
| | | import FirmwareManage from './components/firmwareManage.vue'; |
| | | import DevicePerShare from './components/devicePerShare.vue'; |
| | | import DockControlPanel from './components/DockControlPanel.vue'; |
| | | import { getWebsocketUrl } from '@/websocket/util/config'; |
| | | import ConnectWebSocket from '@/websocket'; |
| | | deviceOffline |
| | | } from '@/api/device/device' |
| | | import { deleteByOssId, addOrUpdate as addOrUpdateOssBind } from '@/api/device/ossBind' |
| | | import { getListPage as getOssList } from '@/api/resource/oss' |
| | | import { getLazyTree } from '@/api/base/region' |
| | | import { EDockModeText, EDockModeCode } from '@/types/device' |
| | | import { mapGetters } from 'vuex' |
| | | import FirmwareManage from './components/firmwareManage.vue' |
| | | import DevicePerShare from './components/devicePerShare.vue' |
| | | import DockControlPanel from './components/DockControlPanel.vue' |
| | | import { getWebsocketUrl } from '@/websocket/util/config' |
| | | import ConnectWebSocket from '@/websocket' |
| | | export default { |
| | | components: { |
| | | FirmwareManage, |
| | | DevicePerShare, |
| | | DockControlPanel, |
| | | }, |
| | | data() { |
| | | data () { |
| | | return { |
| | | bindOssId: null, |
| | | deviceSn: '', |
| | |
| | | trigger: 'change', |
| | | }, |
| | | ], |
| | | change: ({ value }) => { |
| | | if (!value) { |
| | | this.form.area_code = null; |
| | | } else { |
| | | if (typeof value === 'string' && value.includes(',')) { |
| | | const codes = value.split(','); |
| | | this.form.area_code = codes[codes.length - 1]; |
| | | } else { |
| | | this.form.area_code = value; |
| | | } |
| | | } |
| | | }, |
| | | lazy: true, |
| | | lazyLoad(node, resolve) { |
| | | let level = node.level; |
| | | let list = []; |
| | | lazyLoad (node, resolve) { |
| | | let level = node.level |
| | | let list = [] |
| | | let callback = () => { |
| | | resolve( |
| | | (list || []).map(ele => ({ |
| | |
| | | value: ele.value, |
| | | leaf: level >= 2, |
| | | })) |
| | | ); |
| | | }; |
| | | ) |
| | | } |
| | | |
| | | if (level === 0) { |
| | | getLazyTree('000000000000').then(res => { |
| | | list = res.data.data; |
| | | callback(); |
| | | }); |
| | | list = res.data.data |
| | | callback() |
| | | }) |
| | | } else if (level === 1) { |
| | | getLazyTree(node.value).then(res => { |
| | | list = res.data.data; |
| | | callback(); |
| | | }); |
| | | list = res.data.data |
| | | callback() |
| | | }) |
| | | } else if (level === 2) { |
| | | getLazyTree(node.value).then(res => { |
| | | list = res.data.data; |
| | | callback(); |
| | | }); |
| | | list = res.data.data |
| | | callback() |
| | | }) |
| | | } else { |
| | | callback(); |
| | | callback() |
| | | } |
| | | }, |
| | | }, |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | { |
| | | label: '在线状态', |
| | | prop: 'cnstatus', |
| | | hide: true, |
| | |
| | | prop: 'status', |
| | | addDisplay: false, |
| | | editDisplay: false, |
| | | viewDisplay: false, |
| | | viewDisplay: false, |
| | | labelWidth: 130, |
| | | slot: true, |
| | | width: 100, |
| | |
| | | ConsistencyUpgrade: 3, // 一致性升级 |
| | | DuringUpgrade: 4, // 升级中 |
| | | }, |
| | | }; |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapGetters(['permission']), |
| | | permissionList() { |
| | | permissionList () { |
| | | return { |
| | | addBtn: this.validData(this.permission.air_add, true), |
| | | viewBtn: this.validData(this.permission.air_view, true), |
| | | delBtn: this.validData(this.permission.air_delete, true), |
| | | editBtn: this.validData(this.permission.air_edit, true), |
| | | }; |
| | | } |
| | | }, |
| | | ids() { |
| | | let ids = []; |
| | | ids () { |
| | | let ids = [] |
| | | this.selectionList.forEach(ele => { |
| | | ids.push(ele.id); |
| | | }); |
| | | return ids.join(','); |
| | | ids.push(ele.id) |
| | | }) |
| | | return ids.join(',') |
| | | }, |
| | | }, |
| | | destroyed() { |
| | | destroyed () { |
| | | this.websocketMap.forEach((k, v) => { |
| | | if (null != v) { |
| | | v.close(); |
| | | this.websocketMap.delete(k); |
| | | this.webSocketIdSet.delete(k); |
| | | v.close() |
| | | this.websocketMap.delete(k) |
| | | this.webSocketIdSet.delete(k) |
| | | } |
| | | }); |
| | | }) |
| | | }, |
| | | methods: { |
| | | getDockModeText(value) { |
| | | return EDockModeText[value] || ''; |
| | | getDockModeText (value) { |
| | | return EDockModeText[value] || '' |
| | | }, |
| | | // 关闭所有的webscoket |
| | | closeAllWebsoket() { |
| | | closeAllWebsoket () { |
| | | this.websocketMap.forEach((k, v) => { |
| | | if (null != v) { |
| | | v.close(); |
| | | this.websocketMap.delete(k); |
| | | this.webSocketIdSet.delete(k); |
| | | v.close() |
| | | this.websocketMap.delete(k) |
| | | this.webSocketIdSet.delete(k) |
| | | } |
| | | }); |
| | | }) |
| | | }, |
| | | // 连接webSocket |
| | | connectWebSocket(data) { |
| | | const webSocketId = data.workspace_id; |
| | | connectWebSocket (data) { |
| | | const webSocketId = data.workspace_id |
| | | if (!this.webSocketIdSet.has(webSocketId)) { |
| | | // 防止重复连接 |
| | | this.webSocketIdSet.add(webSocketId); |
| | | const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + data.workspace_id; |
| | | this.webSocketIdSet.add(webSocketId) |
| | | const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + data.workspace_id |
| | | // 监听ws 消息 |
| | | this.useConnectWebSocket(webSocketId, webSorketUrl); |
| | | this.useConnectWebSocket(webSocketId, webSorketUrl) |
| | | } |
| | | }, |
| | | useConnectWebSocket(webSocketId, url) { |
| | | const websocket = new ConnectWebSocket(url); |
| | | useConnectWebSocket (webSocketId, url) { |
| | | const websocket = new ConnectWebSocket(url) |
| | | // 加入 webscoket map |
| | | this.websocketMap.set(webSocketId, websocket); |
| | | websocket?.registerMessageHandler(this.messageHandler); |
| | | websocket?.initSocket(); |
| | | this.websocketMap.set(webSocketId, websocket) |
| | | websocket?.registerMessageHandler(this.messageHandler) |
| | | websocket?.initSocket() |
| | | }, |
| | | async messageHandler(payload) { |
| | | async messageHandler (payload) { |
| | | if (!payload) { |
| | | return; |
| | | return |
| | | } |
| | | if (payload.biz_code != 'ota_progress') { |
| | | return; |
| | | return |
| | | } |
| | | var data = payload.data; |
| | | var data = payload.data |
| | | this.data.forEach(e => { |
| | | if (e.device_sn == data.sn && data.output.status == 'in_progress') { |
| | | e.firmware_progress = data.output.progress.percent; |
| | | e.firmware_progress = data.output.progress.percent |
| | | } |
| | | |
| | | if (e.device_sn == data.sn && data.output.status == 'ok') { |
| | | // 升级完成修改状态 |
| | | e.firmware_status = 1; |
| | | const webscoket = this.websocketMap.get(e.workspace_id); |
| | | e.firmware_status = 1 |
| | | const webscoket = this.websocketMap.get(e.workspace_id) |
| | | // 关闭 |
| | | webscoket?.close(); |
| | | webscoket?.close() |
| | | //删除对应的webscoket |
| | | this.websocketMap.delete(e.workspace_id); |
| | | this.webSocketIdSet.delete(e.workspace_id); |
| | | this.websocketMap.delete(e.workspace_id) |
| | | this.webSocketIdSet.delete(e.workspace_id) |
| | | } |
| | | }); |
| | | }) |
| | | }, |
| | | init() { |
| | | init () { |
| | | (this.page = { |
| | | pageSize: 10, |
| | | currentPage: 1, |
| | | total: 0, |
| | | }), |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | }, |
| | | // 打开权限分享页面 |
| | | handleOpenDevicePerShare(row) { |
| | | var that = this; |
| | | this.devicePerShareVisible = true; |
| | | handleOpenDevicePerShare (row) { |
| | | var that = this |
| | | this.devicePerShareVisible = true |
| | | this.$nextTick(() => { |
| | | that.$refs.devicePerShare.init(row); |
| | | }); |
| | | that.$refs.devicePerShare.init(row) |
| | | }) |
| | | }, |
| | | // 打开远程调试 |
| | | handleOpenRemoteDebugging(row) { |
| | | this.curDeviceInfo = row; |
| | | this.remoteDebuggingShow = true; |
| | | this.operateTitle = row.nickname + ' - ' + row.device_sn; |
| | | handleOpenRemoteDebugging (row) { |
| | | this.curDeviceInfo = row |
| | | this.remoteDebuggingShow = true |
| | | this.operateTitle = row.nickname + ' - ' + row.device_sn |
| | | }, |
| | | |
| | | // 设备下线 |
| | | handleDeviceOffline (row) { |
| | | ElMessageBox.confirm('确定下线该设备吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | | }) |
| | | .then(() => { |
| | | deviceOffline(row.device_sn) |
| | | .then(res => { |
| | | ElMessage.success('下线成功') |
| | | |
| | | this.init() |
| | | }) |
| | | .catch(error => { |
| | | ElMessage.error('下线失败') |
| | | }) |
| | | }) |
| | | .catch(() => { }) |
| | | }, |
| | | |
| | | // 打开存储对象配置页面 |
| | | handleOpenOssSet(row) { |
| | | var that = this; |
| | | this.deviceSn = row.device_sn; |
| | | this.ossSetBox = true; |
| | | this.bindOssId = row.oss_id; |
| | | this.ossEnd = row.oss_id; |
| | | handleOpenOssSet (row) { |
| | | var that = this |
| | | this.deviceSn = row.device_sn |
| | | this.ossSetBox = true |
| | | this.bindOssId = row.oss_id |
| | | this.ossEnd = row.oss_id |
| | | // 查询下拉列表 |
| | | const param = { |
| | | categoryKeys: '1,3,6', |
| | | }; |
| | | } |
| | | getOssList(1, 50, param).then(res => { |
| | | const data = res.data.data; |
| | | that.ossList = data.records; |
| | | }); |
| | | const data = res.data.data |
| | | that.ossList = data.records |
| | | }) |
| | | }, |
| | | // 打开固件管理页面 |
| | | handleOpenFirmwarm(row) { |
| | | var that = this; |
| | | this.firmwareManageVisible = true; |
| | | handleOpenFirmwarm (row) { |
| | | var that = this |
| | | this.firmwareManageVisible = true |
| | | this.$nextTick(() => { |
| | | that.$refs.firmwareManage.init(row); |
| | | }); |
| | | that.$refs.firmwareManage.init(row) |
| | | }) |
| | | }, |
| | | // 版本回退点击事件 |
| | | rollFirmware(row) { |
| | | rollFirmware (row) { |
| | | if (row.status == 0) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '设备不在线!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 如果是机场,查询飞机对应的信息 |
| | | if (row.domain === 3) { |
| | | // 查询子设备名称 |
| | | getDevices(row.workspace_id, row.child_sn).then(res => { |
| | | const data = res.data.data; |
| | | that.firmwareInfo['child_device_name'] = data.device_name; |
| | | that.firmwareInfo['child_version'] = data.firmware_version; |
| | | that.firmwareInfo['child_status'] = data.status; |
| | | }); |
| | | const data = res.data.data |
| | | that.firmwareInfo['child_device_name'] = data.device_name |
| | | that.firmwareInfo['child_version'] = data.firmware_version |
| | | that.firmwareInfo['child_status'] = data.status |
| | | }) |
| | | } |
| | | var that = this; |
| | | this.rollFirmwareBox = true; |
| | | var that = this |
| | | this.rollFirmwareBox = true |
| | | const param = { |
| | | device_name: row.device_name, |
| | | }; |
| | | } |
| | | // 获取固件最新版本信息 |
| | | getDeviceFirmwareList(1, 20, param).then(res => { |
| | | that.firmList = []; |
| | | const data = res.data.data.records; |
| | | that.firmList = [] |
| | | const data = res.data.data.records |
| | | if (data.length === 0) { |
| | | return; |
| | | return |
| | | } |
| | | data.forEach(e => { |
| | | if (e.firmware_version != row.firmware_version) { |
| | | that.firmList.push(e); |
| | | that.firmList.push(e) |
| | | } |
| | | }); |
| | | that.firmwareInfo['device_name'] = row.device_name; |
| | | that.firmwareInfo['sn'] = row.device_sn; |
| | | }) |
| | | that.firmwareInfo['device_name'] = row.device_name |
| | | that.firmwareInfo['sn'] = row.device_sn |
| | | // 3-普通升级,2-一致性升级; |
| | | that.firmwareInfo['firmware_upgrade_type'] = 3; |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id; |
| | | that.firmwareInfo['firmware_version'] = row.firmware_version; |
| | | that.firmwareInfo['status'] = row.status; |
| | | that.firmwareInfo['firmware_upgrade_type'] = 3 |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id |
| | | that.firmwareInfo['firmware_version'] = row.firmware_version |
| | | that.firmwareInfo['status'] = row.status |
| | | // domain 0:飞机 3:机场 |
| | | that.firmwareInfo['domain'] = row.domain; |
| | | that.firmwareInfo['child_sn'] = row.child_sn; |
| | | that.firmwareInfo['now_version'] = row.firmware_version; |
| | | }); |
| | | that.firmwareInfo['domain'] = row.domain |
| | | that.firmwareInfo['child_sn'] = row.child_sn |
| | | that.firmwareInfo['now_version'] = row.firmware_version |
| | | }) |
| | | }, |
| | | // 比较版本大小 |
| | | compare(version1, version2) { |
| | | let arr1 = version1.split('.'); |
| | | let arr2 = version2.split('.'); |
| | | let length = Math.max(arr1.length, arr2.length); |
| | | compare (version1, version2) { |
| | | let arr1 = version1.split('.') |
| | | let arr2 = version2.split('.') |
| | | let length = Math.max(arr1.length, arr2.length) |
| | | for (let i = 0; i < length; i++) { |
| | | const n1 = Number(arr1[i] || 0); |
| | | const n2 = Number(arr2[i] || 0); |
| | | const n1 = Number(arr1[i] || 0) |
| | | const n2 = Number(arr2[i] || 0) |
| | | // version1 > version2 返回1,如果 version1 < version2 返回-1,不然返回0 |
| | | if (n1 > n2) return 1; |
| | | if (n1 < n2) return -1; |
| | | if (n1 > n2) return 1 |
| | | if (n1 < n2) return -1 |
| | | } |
| | | return 0; |
| | | return 0 |
| | | }, |
| | | // 版本回退 |
| | | rollFirmwareConfirm() { |
| | | rollFirmwareConfirm () { |
| | | // 无人机升级/回滚 |
| | | if (this.rollFirmVersion == '') { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请先选择回退的固件版本!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | var that = this; |
| | | var arr = []; |
| | | that.firmwareInfo['product_version'] = this.rollFirmVersion; |
| | | var that = this |
| | | var arr = [] |
| | | that.firmwareInfo['product_version'] = this.rollFirmVersion |
| | | // 机场信息设置 |
| | | arr.push(that.firmwareInfo); |
| | | arr.push(that.firmwareInfo) |
| | | // 判断是机场升级还是飞机升级,如果是机场升级则把无人机信息一起带过去 |
| | | if (that.firmwareInfo.domain === 3) { |
| | | // 判断当前机场和无人机固件版本是否一致 |
| | | var dockFlag = this.compare( |
| | | this.firmwareInfo.firmware_version, |
| | | this.firmwareInfo.child_version |
| | | ); |
| | | ) |
| | | // 机场和无人机版本一致情况 |
| | | if (dockFlag == 0) { |
| | | if (!this.firmwareInfo.status || !this.firmwareInfo.child_status) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请先更新无人机固件版本后再进行机场固件版本更新!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 只有同时在线并且是升级才能操作 |
| | | // 判断升级还是降级,不能降级 |
| | | var upDownFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.firmware_version); |
| | | var upDownFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.firmware_version) |
| | | if (upDownFlag == -1) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请先回滚无人机固件版本后再进行机场固件版本更新!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 同时升级操作 |
| | | // 无人机信息设置 |
| | |
| | | sn: that.firmwareInfo.child_sn, |
| | | product_version: this.rollFirmVersion, |
| | | firmware_upgrade_type: 3, |
| | | }); |
| | | }) |
| | | } else { |
| | | // 版本不一致情况 |
| | | // 比对需要改变的版本号和无人机版本号是否一致,不一致提示不能操作 |
| | | var verFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.child_version); |
| | | var verFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.child_version) |
| | | if (verFlag == -1 || verFlag == 1) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '更新版本与当前无人机固件版本不一致,请更新和无人机一致的版本!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 需要改变的版本号和无人机版本号如果是一致,进行单独的机场升级/回滚操作 |
| | | } |
| | | } |
| | | ota(that.firmwareInfo.workspaceId, arr).then(res => { |
| | | that.firmwareVersion = ''; |
| | | that.firmwareInfo = {}; |
| | | that.rollFirmwareBox = false; |
| | | that.firmwareVersion = '' |
| | | that.firmwareInfo = {} |
| | | that.rollFirmwareBox = false |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | this.onLoad(this.page); |
| | | done(); |
| | | }); |
| | | }) |
| | | this.onLoad(this.page) |
| | | done() |
| | | }) |
| | | }, |
| | | // 升级固件按钮事件 |
| | | updateFirmware(row) { |
| | | updateFirmware (row) { |
| | | if (row.status == 0) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '设备不在线!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | var that = this; |
| | | this.firmwareBox = true; |
| | | var that = this |
| | | this.firmwareBox = true |
| | | const param = { |
| | | device_name: row.device_name, |
| | | }; |
| | | } |
| | | // 获取固件最新版本信息 |
| | | getDeviceUpgradeInfo(param).then(res => { |
| | | const data = res.data.data; |
| | | const data = res.data.data |
| | | if (data.length === 0) { |
| | | return; |
| | | return |
| | | } |
| | | that.firmwareVersion = data[0].product_version; |
| | | that.firmwareInfo['device_name'] = row.device_name; |
| | | that.firmwareInfo['sn'] = row.device_sn; |
| | | that.firmwareInfo['product_version'] = data[0].product_version; |
| | | that.firmwareVersion = data[0].product_version |
| | | that.firmwareInfo['device_name'] = row.device_name |
| | | that.firmwareInfo['sn'] = row.device_sn |
| | | that.firmwareInfo['product_version'] = data[0].product_version |
| | | // 3-普通升级,2-一致性升级; |
| | | that.firmwareInfo['firmware_upgrade_type'] = |
| | | row.firmware_status === that.DeviceFirmwareStatusEnum.ToUpgraded |
| | | ? that.DeviceFirmwareTypeEnum.ToUpgraded |
| | | : that.DeviceFirmwareTypeEnum.ConsistencyUpgrade; |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id; |
| | | }); |
| | | : that.DeviceFirmwareTypeEnum.ConsistencyUpgrade |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id |
| | | }) |
| | | }, |
| | | // 确定升级固件版本 |
| | | updateFirmwareConfirm() { |
| | | var that = this; |
| | | var arr = []; |
| | | arr.push(that.firmwareInfo); |
| | | updateFirmwareConfirm () { |
| | | var that = this |
| | | var arr = [] |
| | | arr.push(that.firmwareInfo) |
| | | ota(that.firmwareInfo.workspaceId, arr).then(res => { |
| | | that.firmwareVersion = ''; |
| | | that.firmwareInfo = {}; |
| | | that.firmwareBox = false; |
| | | that.firmwareVersion = '' |
| | | that.firmwareInfo = {} |
| | | that.firmwareBox = false |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | this.onLoad(this.page); |
| | | done(); |
| | | }); |
| | | }) |
| | | this.onLoad(this.page) |
| | | done() |
| | | }) |
| | | }, |
| | | handleOssSetClose() { |
| | | this.ossEnd = ''; |
| | | this.deviceSn = ''; |
| | | this.bindOssId = null; |
| | | handleOssSetClose () { |
| | | this.ossEnd = '' |
| | | this.deviceSn = '' |
| | | this.bindOssId = null |
| | | }, |
| | | // 存储对象配置确定 |
| | | ossSetConfirm() { |
| | | var that = this; |
| | | ossSetConfirm () { |
| | | var that = this |
| | | // 判断是否已选择 |
| | | if (!this.ossEnd) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请选择对于的存储对象!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 新增或者修改操作 |
| | | const data = { |
| | | oss_id: this.ossEnd, |
| | | sn: this.deviceSn, |
| | | }; |
| | | } |
| | | // 新增 |
| | | addOrUpdateOssBind(data).then(res => { |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | that.ossSetBox = false; |
| | | this.onLoad(this.page); |
| | | done(); |
| | | }); |
| | | }) |
| | | that.ossSetBox = false |
| | | this.onLoad(this.page) |
| | | done() |
| | | }) |
| | | }, |
| | | // 取消配置 |
| | | cancelOssSetConfirm() { |
| | | var that = this; |
| | | cancelOssSetConfirm () { |
| | | var that = this |
| | | // 判断是否已有配置 |
| | | if (!this.bindOssId) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '当前机场尚未配置存储对象信息!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | deleteByOssId(this.bindOssId).then(() => { |
| | | that.ossSetBox = false; |
| | | this.onLoad(this.page); |
| | | that.ossSetBox = false |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | }); |
| | | }) |
| | | }) |
| | | }, |
| | | // 取消按钮 oss |
| | | cancelOssSet() { |
| | | this.ossEnd = ''; |
| | | this.deviceSn = ''; |
| | | this.bindOssId = null; |
| | | this.ossSetBox = false; |
| | | cancelOssSet () { |
| | | this.ossEnd = '' |
| | | this.deviceSn = '' |
| | | this.bindOssId = null |
| | | this.ossSetBox = false |
| | | }, |
| | | rowSave(row, done, loading) { |
| | | let areaCode = row.area_code; |
| | | rowSave (row, done, loading) { |
| | | let areaCode = row.area_code |
| | | if (Array.isArray(areaCode)) { |
| | | areaCode = areaCode[areaCode.length - 1]; |
| | | areaCode = areaCode[areaCode.length - 1] |
| | | } else if (typeof areaCode === 'string' && areaCode.includes(',')) { |
| | | const codes = areaCode.split(','); |
| | | areaCode = codes[codes.length - 1]; |
| | | const codes = areaCode.split(',') |
| | | areaCode = codes[codes.length - 1] |
| | | } |
| | | row.area_code = areaCode || null; |
| | | row.area_code = areaCode || null |
| | | |
| | | add(row).then( |
| | | () => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | done(); |
| | | }) |
| | | done() |
| | | }, |
| | | error => { |
| | | window.console.log(error); |
| | | loading(); |
| | | window.console.log(error) |
| | | loading() |
| | | } |
| | | ); |
| | | ) |
| | | }, |
| | | rowUpdate(row, index, done, loading) { |
| | | rowUpdate (row, index, done, loading) { |
| | | const submitData = { |
| | | ...row, |
| | | area_code: row.area_code.split(',').pop(), |
| | | insure_start_time: row.duration_of_insurance[0], |
| | | insure_expired_time: row.duration_of_insurance[1], |
| | | }; |
| | | } |
| | | |
| | | delete submitData.duration_of_insurance; |
| | | delete submitData.duration_of_insurance |
| | | |
| | | update(submitData).then( |
| | | () => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | done(); |
| | | }) |
| | | done() |
| | | }, |
| | | error => { |
| | | window.console.log(error); |
| | | loading(); |
| | | window.console.log(error) |
| | | loading() |
| | | } |
| | | ); |
| | | ) |
| | | }, |
| | | rowDel(row) { |
| | | rowDel (row) { |
| | | this.$confirm('确定将选择数据删除?', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | | }) |
| | | .then(() => { |
| | | return remove(row.id); |
| | | return remove(row.id) |
| | | }) |
| | | .then(() => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | }); |
| | | }) |
| | | }) |
| | | }, |
| | | searchReset() { |
| | | this.query = {}; |
| | | this.onLoad(this.page); |
| | | searchReset () { |
| | | this.query = {} |
| | | this.onLoad(this.page) |
| | | }, |
| | | searchChange(params, done) { |
| | | this.query = params; |
| | | this.page.currentPage = 1; |
| | | this.onLoad(this.page, params); |
| | | done(); |
| | | searchChange (params, done) { |
| | | this.query = params |
| | | this.page.currentPage = 1 |
| | | this.onLoad(this.page, params) |
| | | done() |
| | | }, |
| | | selectionChange(list) { |
| | | this.selectionList = list; |
| | | selectionChange (list) { |
| | | this.selectionList = list |
| | | }, |
| | | selectionClear() { |
| | | this.selectionList = []; |
| | | this.$refs.crud.toggleSelection(); |
| | | selectionClear () { |
| | | this.selectionList = [] |
| | | this.$refs.crud.toggleSelection() |
| | | }, |
| | | handleDelete() { |
| | | handleDelete () { |
| | | if (this.selectionList.length === 0) { |
| | | this.$message.warning('请选择至少一条数据'); |
| | | return; |
| | | this.$message.warning('请选择至少一条数据') |
| | | return |
| | | } |
| | | this.$confirm('确定将选择数据删除?', { |
| | | confirmButtonText: '确定', |
| | |
| | | type: 'warning', |
| | | }) |
| | | .then(() => { |
| | | return remove(this.ids); |
| | | return remove(this.ids) |
| | | }) |
| | | .then(() => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | this.$refs.crud.toggleSelection(); |
| | | }); |
| | | }) |
| | | this.$refs.crud.toggleSelection() |
| | | }) |
| | | }, |
| | | getFullAreaCode(areaCode) { |
| | | if (!areaCode) return ''; |
| | | async getFullAreaCode (areaCode) { |
| | | if (!areaCode) return '' |
| | | |
| | | const code = areaCode.toString(); |
| | | const code = areaCode.toString() |
| | | |
| | | if (code.includes(',')) return code; |
| | | if (code.includes(',')) return code |
| | | |
| | | if (code.endsWith('0000000000')) { |
| | | return code; |
| | | return code |
| | | } else if (code.endsWith('00000000') && !code.endsWith('0000000000')) { |
| | | const provinceCode = code.substring(0, 2) + '0000000000'; |
| | | return `${provinceCode},${code}`; |
| | | const provinceCode = code.substring(0, 2) + '0000000000' |
| | | return `${provinceCode},${code}` |
| | | } else { |
| | | const provinceCode = code.substring(0, 2) + '0000000000'; |
| | | const cityCode = code.substring(0, 4) + '00000000'; |
| | | return `${provinceCode},${cityCode},${code}`; |
| | | const provinceCode = code.substring(0, 2) + '0000000000' |
| | | const cityCode = code.substring(0, 4) + '00000000' |
| | | return `${provinceCode},${cityCode},${code}` |
| | | } |
| | | }, |
| | | beforeOpen(done, type) { |
| | | beforeOpen (done, type) { |
| | | if (['edit', 'view'].includes(type)) { |
| | | getDetail(this.form.id).then(res => { |
| | | const data = res.data.data; |
| | | getDetail(this.form.id).then(async res => { |
| | | const data = res.data.data |
| | | |
| | | this.form = { |
| | | ...data, |
| | | area_code: this.getFullAreaCode(data.area_code), |
| | | area_code: await this.getFullAreaCode(data.area_code), |
| | | area_name: this.form.area_name, |
| | | cnmode_code: this.getDockModeText(this.form.mode_code), |
| | | cnstatus:this.form.status === false ? '离线':'在线', |
| | | cnstatus: this.form.status === false ? '离线' : '在线', |
| | | duration_of_insurance: [data?.insure_start_time || '', data?.insure_expired_time || ''], |
| | | }; |
| | | console.log('this.form', this.form); |
| | | } |
| | | |
| | | done(); |
| | | }); |
| | | done() |
| | | }) |
| | | } |
| | | }, |
| | | currentChange(currentPage) { |
| | | this.page.currentPage = currentPage; |
| | | currentChange (currentPage) { |
| | | this.page.currentPage = currentPage |
| | | }, |
| | | sizeChange(pageSize) { |
| | | this.page.pageSize = pageSize; |
| | | sizeChange (pageSize) { |
| | | this.page.pageSize = pageSize |
| | | }, |
| | | refreshChange() { |
| | | this.onLoad(this.page, this.query); |
| | | refreshChange () { |
| | | this.onLoad(this.page, this.query) |
| | | }, |
| | | onLoad(page, params = {}) { |
| | | onLoad (page, params = {}) { |
| | | // this.closeAllWebsoket(); |
| | | const { releaseTimeRange } = this.query; |
| | | const { releaseTimeRange } = this.query |
| | | let values = { |
| | | ...params, |
| | | ...this.query, |
| | | }; |
| | | } |
| | | if (releaseTimeRange) { |
| | | values = { |
| | | ...values, |
| | | releaseTime_datege: releaseTimeRange[0], |
| | | releaseTime_datelt: releaseTimeRange[1], |
| | | }; |
| | | values.releaseTimeRange = null; |
| | | } |
| | | values.releaseTimeRange = null |
| | | } |
| | | values['type'] = 1; |
| | | this.loading = true; |
| | | values['type'] = 1 |
| | | this.loading = true |
| | | getList(page.currentPage, page.pageSize, values).then(res => { |
| | | const data = res.data.data; |
| | | this.page.total = data.total; |
| | | const data = res.data.data |
| | | this.page.total = data.total |
| | | data.records.forEach(e => { |
| | | e['hasChildren'] = e.has_children; |
| | | e['hasChildren'] = e.has_children |
| | | if (e.firmware_status == 4) { |
| | | this.connectWebSocket(e); |
| | | this.connectWebSocket(e) |
| | | } |
| | | }); |
| | | this.data = data.records; |
| | | this.loading = false; |
| | | this.selectionClear(); |
| | | }); |
| | | }) |
| | | this.data = data.records |
| | | this.loading = false |
| | | this.selectionClear() |
| | | }) |
| | | }, |
| | | treeLoad(tree, treeNode, resolve) { |
| | | treeLoad (tree, treeNode, resolve) { |
| | | var params = { |
| | | childSn: tree.child_sn, |
| | | }; |
| | | } |
| | | getList(1, 10, params).then(res => { |
| | | const data = res.data.data.records; |
| | | resolve(data); |
| | | }); |
| | | const data = res.data.data.records |
| | | resolve(data) |
| | | }) |
| | | }, |
| | | }, |
| | | }; |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | import DeviceJobDetails from '../DeviceJobDetails.vue' |
| | | import CancelTaskDialog from '../CancelTaskDialog.vue' |
| | | import { useStore } from 'vuex' |
| | | import { cloneDeep } from 'lodash'; |
| | | const store = useStore() |
| | | const singleUavHome = computed(() => store.state.home.singleUavHome) |
| | | const jobListParams = reactive({ |
| | |
| | | |
| | | // 获取任务列表 |
| | | const getJobList = () => { |
| | | jobList(jobListParams).then(res => { |
| | | const apiParams = { |
| | | ...cloneDeep(jobListParams), |
| | | searchParams: { |
| | | ...cloneDeep(jobListParams.searchParams), |
| | | status_list: jobListParams.searchParams.status || undefined, |
| | | status: undefined, |
| | | }, |
| | | } |
| | | jobList(apiParams).then(res => { |
| | | if (res.data.code !== 0) return |
| | | jobListData.value = res.data.data.records |
| | | total.value = res.data.data.total |
| | |
| | | |
| | | // 获取行业统计数据 |
| | | const getJobEventBar = value => { |
| | | jobEventBar(value).then(res => { |
| | | const params = { |
| | | ...value, |
| | | status_list:value.status, |
| | | status:undefined |
| | | } |
| | | jobEventBar(params).then(res => { |
| | | if (res.data.code !== 0) return |
| | | option.xAxis.data = res.data.data.map(item => item.name) |
| | | option.series[0].data = res.data?.data.map(item => item.data[0].value) |
| | |
| | | left: 'center', |
| | | bottom: '2%', |
| | | textStyle: { |
| | | color: '#6B90B5', |
| | | color: '#363636', |
| | | fontSize: 12, |
| | | }, |
| | | itemWidth: 2, // 减小图例标记的宽度 |
| | |
| | | </div> |
| | | |
| | | <!-- 表格部分 --> |
| | | <avue-crud :data="tableData" :option="option" v-model:page="page" ref="crud" :table-loading="loading" |
| | | @current-change="currentChange" @refresh-change="refreshChange" @on-load="onLoad" |
| | | @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name"> |
| | | <avue-crud class="ztzf-public-general-avue-crud" :data="tableData" :option="option" v-model:page="page" |
| | | ref="crud" :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange" |
| | | @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name"> |
| | | <template #menu-left> |
| | | <el-button v-if="hasAddBtnPermission() && activeTab != 'WAIT_AUDIT'" type="primary" icon="el-icon-plus" |
| | | @click="handleAdd">新建工单</el-button> |
| | |
| | | |
| | | this.page.currentPage = 1 |
| | | this.onLoad(this.page, this.query) |
| | | this.handleViewDetail() |
| | | }, |
| | | /** |
| | | * 驳回 |
| | |
| | | </div> |
| | | |
| | | <!-- 表格部分 --> |
| | | <avue-crud ref="avueCrud" v-model="tableData" :option="option" :data="tableData" v-model:page="page" |
| | | @size-change="sizeChange" @current-change="handleCurrentChange" :table-loading="loading" |
| | | @selection-change="handleSelectionChange" :permission="permissionList" v-if="activeTab === tab.name"> |
| | | <avue-crud class="ztzf-public-general-avue-crud" ref="avueCrud" v-model="tableData" :option="option" |
| | | :data="tableData" v-model:page="page" @size-change="sizeChange" @current-change="handleCurrentChange" |
| | | @refresh-change="refreshChange" :table-loading="loading" @selection-change="handleSelectionChange" |
| | | :permission="permissionList" v-if="activeTab === tab.name"> |
| | | <template #menu-left> |
| | | <el-button v-if="(activeTab === 'all' || activeTab === 'myTickets') && permissionList.addBtn" |
| | | type="primary" icon="el-icon-plus" @click="handleAdd">新建工单</el-button> |
| | |
| | | |
| | | <template v-if="permission.tickets_repeat_review"> |
| | | <el-button v-if="row.status === 4 && row.isReview !== 1" type="text" icon="el-icon-check" |
| | | @click="reCheck(row)">复核</el-button> |
| | | @click="reCheck(row)">复核</el-button> |
| | | </template> |
| | | |
| | | </template> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="选择位置" prop="location" required> |
| | | <el-form-item label="选择位置" prop="location"> |
| | | <div class="location-wrapper"> |
| | | <avue-input-map v-model="form.location" :params="mapParams" @change="handleLocationChange" |
| | | type="button"> |
| | | <avue-input-map v-model="form.location" :clearable="false" :params="mapParams" |
| | | @change="handleLocationChange" type="button"> |
| | | <el-button type="primary" plain class="map-button"> |
| | | <i class="el-icon-map-location"></i> 地图选点 |
| | | </el-button> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="附件图片" prop="photos" required class="upload-wrapper"> |
| | | <el-upload v-if="createoredit === 1" ref="upload" :action="'#'" :auto-upload="false" list-type="picture-card" |
| | | :on-change="handleFileChange" :on-remove="handleUploadRemove" :before-upload="beforeUpload" |
| | | :file-list="form.photos" :limit="1" accept="image/*" class="create-upload"> |
| | | <el-upload v-if="createoredit === 1" ref="upload" :action="'#'" :auto-upload="false" |
| | | list-type="picture-card" :on-change="handleFileChange" :on-remove="handleUploadRemove" |
| | | :before-upload="beforeUpload" :file-list="form.photos" :limit="1" accept="image/*" |
| | | class="create-upload"> |
| | | <template v-if="form.photos.length < 1"> |
| | | <!-- <i class="el-icon-plus">+</i> --> |
| | | <div class="el-icon-plus"> |
| | | <div class="el-icon-plus"> |
| | | <span>+</span> |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <el-upload v-else ref="upload" :action="'#'" :auto-upload="false" list-type="picture-card" |
| | | <el-upload v-else ref="upload" :action="'#'" :auto-upload="false" list-type="picture-card" |
| | | :on-change="handleFileChange" :on-remove="handleUploadRemove" :before-upload="beforeUpload" |
| | | :file-list="popupShowImage(form.photos)" :limit="1" accept="image/*" class="create-upload"> |
| | | <template v-if="form.photos.length < 1"> |
| | | |
| | | <div class="el-icon-plus"> |
| | | |
| | | <div class="el-icon-plus"> |
| | | <span>+</span> |
| | | </div> |
| | | </template> |
| | |
| | | <!-- 工单详情对话框 --> |
| | | <el-dialog v-model="detailVisible" title="工单详情" width="80%" append-to-body> |
| | | <div class="detail-container"> |
| | | <div class="detail-top-title"> <div class="event-title-center event-orderNumber">{{ currentDetail.orderNumber || '工单编号' }}</div> |
| | | <div class="event-title-center">{{ currentDetail.orderName || '事件名称' }}</div></div> |
| | | <div class="detail-top-title"> |
| | | <div class="event-title-center event-orderNumber">{{ currentDetail.orderNumber || '工单编号' }}</div> |
| | | <div class="event-title-center">{{ currentDetail.orderName || '事件名称' }}</div> |
| | | </div> |
| | | <div v-if="totalTime" class="event-total-time"> |
| | | 总耗时:{{ totalTime }} |
| | | </div> |
| | |
| | | <template v-if="!currentDetail.photos || currentDetail.photos.length < 1"> |
| | | <!-- <i class="el-icon-plus">+</i> --> |
| | | <div class="el-icon-plus"> |
| | | <span>+</span> |
| | | </div> |
| | | <span>+</span> |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <div class="el-upload__tip" v-if="hasProcessedAndOverBtnPermission()"> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { getSmallImg,getShowImg } from '@/utils/util'; |
| | | import { getSmallImg, getShowImg } from '@/utils/util' |
| | | import { ElMessageBox, ElLoading } from 'element-plus' |
| | | import { calculateDefaultRange } from '@/utils/util' |
| | | import { gcj02ToWgs84, wgs84ToGcj02 } from '@/utils/coordinateTransformation' |
| | |
| | | dispatchLoading: false, |
| | | completeLoading: false, |
| | | finalizeLoading: false, |
| | | createoredit:'', |
| | | createoredit: '', |
| | | activeTab: 'all', |
| | | // tabs 只保留静态结构,不做权限判断 |
| | | tabs: [ |
| | |
| | | algorithm: [{ required: true, message: '请选择关联算法', trigger: 'change' }], |
| | | location: [ |
| | | { |
| | | required: true, |
| | | validator: (rule, value, callback) => { |
| | | if (!value || value.length < 2) { |
| | | callback(new Error('请选择位置信息')) |
| | | } else if (!value[0] || !value[1]) { |
| | | callback(new Error('请选择位置信息')) |
| | | } else { |
| | | callback() |
| | | } |
| | | }, |
| | | trigger: 'blur', |
| | | trigger: 'change' |
| | | }, |
| | | ], |
| | | photos: [ |
| | |
| | | } |
| | | }, |
| | | created () { |
| | | this.inputMapShowDefaultCenter = null |
| | | |
| | | this.loadAMapScripts() |
| | | this.fetchDropdownData() |
| | | console.log('permission.tickets_processing_btn', this.permission.tickets_processing_btn) |
| | |
| | | |
| | | mounted () { |
| | | const href = this.$route.href |
| | | if (this.$route?.query?.status !== undefined && this.$route?.query?.status !== null){ |
| | | if (this.$route?.query?.status !== undefined && this.$route?.query?.status !== null) { |
| | | this.filters.status = this.$route?.query?.status + '' |
| | | this.$router.replace({}) |
| | | } |
| | |
| | | |
| | | // 直接从返回对象中获取正确的路径 |
| | | const center = adcodeObj?.payload?.objects?.collection?.geometries?.[0]?.properties?.center |
| | | |
| | | if (Array.isArray(center) && center.length === 2) { |
| | | this.mapParams.center = center |
| | | this.inputMapShowDefaultCenter = center |
| | | } else { |
| | | // 如果找不到中心点,尝试使用 bbox 的中心点 |
| | | const bbox = adcodeObj?.payload?.bbox |
| | | if (Array.isArray(bbox) && bbox.length === 4) { |
| | | const centerX = (bbox[0] + bbox[2]) / 2 |
| | | const centerY = (bbox[1] + bbox[3]) / 2 |
| | | this.mapParams.center = [centerX, centerY] |
| | | this.inputMapShowDefaultCenter = [centerX, centerY] |
| | | } else { |
| | | this.mapParams.center = [115.861365, 28.621311] |
| | | this.inputMapShowDefaultCenter = [115.861365, 28.621311] |
| | | } |
| | | } |
| | | this.mapParams.center = [...this.inputMapShowDefaultCenter] |
| | | |
| | | this.mapLoaded = true |
| | | } catch (error) { |
| | |
| | | handlerValue = undefined |
| | | } |
| | | |
| | | // 验证位置信息 |
| | | if (!this.form.location || this.form.location.length < 2 || !this.form.location[0] || !this.form.location[1]) { |
| | | this.$message.warning('请在地图上选择位置') |
| | | return |
| | | } |
| | | |
| | | |
| | | let [lng, lat] = this.disposeLocation(true, this.form) |
| | | |
| | | const submitData = { |
| | |
| | | |
| | | // 草稿时也至少需要工单名称 |
| | | if (!submitData.eventName) { |
| | | this.$message.warning('请至少输入工单名称') |
| | | this.$message.warning('请输入工单名称') |
| | | return |
| | | } |
| | | |
| | |
| | | if (response.data.code === 0) { |
| | | this.$message.success('草稿保存成功') |
| | | this.dialogVisible = false |
| | | this.form={ |
| | | name: '', |
| | | type: '', |
| | | department: '', |
| | | handler: '', |
| | | algorithm: [], // 关联算法改为数组 |
| | | location: [], // 将存储为[经度, 纬度, 地址]格式 |
| | | address: '', |
| | | photos: [], |
| | | content: '', // 新增字段,用于存储后端返回的 content |
| | | }, |
| | | this.fetchTableData() |
| | | this.form = { |
| | | name: '', |
| | | type: '', |
| | | department: '', |
| | | handler: '', |
| | | algorithm: [], // 关联算法改为数组 |
| | | location: [], // 将存储为[经度, 纬度, 地址]格式 |
| | | address: '', |
| | | photos: [], |
| | | content: '', // 新增字段,用于存储后端返回的 content |
| | | }, |
| | | this.fetchTableData() |
| | | } else { |
| | | throw new Error(response.data.msg || '保存失败') |
| | | } |
| | |
| | | }, |
| | | |
| | | async handleCurrentChange (val) { |
| | | |
| | | |
| | | // 先更新页码 |
| | | this.page.currentPage = val |
| | | // 等待 DOM 更新后再请求数据 |
| | |
| | | }, |
| | | |
| | | async sizeChange (val) { |
| | | |
| | | this.page.pageSize = val |
| | | this.page.currentPage = 1 // 重置到第一页 |
| | | await this.$nextTick() |
| | |
| | | }, |
| | | |
| | | handleAdd () { |
| | | |
| | | this.createoredit =1 |
| | | this.createoredit = 1 |
| | | this.dialogVisible = true |
| | | this.mapParams.center = null |
| | | this.mapParams.center = [...this.inputMapShowDefaultCenter] |
| | | this.form.location = [] |
| | | }, |
| | | |
| | |
| | | this.stepInfos = [] |
| | | } |
| | | } |
| | | console.log(detailData,'detailDatadetailDatadetailData'); |
| | | console.log(detailData, 'detailDatadetailDatadetailData') |
| | | this.currentDetail = { |
| | | ...detailData, |
| | | address:null, |
| | | latAndLon: _.round(detailData.location[0],6) + ',' + _.round(detailData.location[1],6) |
| | | address: null, |
| | | latAndLon: _.round(detailData.location[0], 6) + ',' + _.round(detailData.location[1], 6) |
| | | } |
| | | |
| | | console.log('this.currentDetail',this.currentDetail); |
| | | console.log('this.currentDetail', this.currentDetail) |
| | | this.detailVisible = true |
| | | this.$nextTick(() => { |
| | | if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { |
| | |
| | | |
| | | // 添加编辑方法 |
| | | handleEdit (row) { |
| | | this.createoredit = 2 |
| | | this.createoredit = 2 |
| | | console.log('编辑原始数据:', row) |
| | | |
| | | // 尝试从row.dept_id或通过部门名称查找对应的部门ID |
| | |
| | | address: row.address || '', |
| | | content: row.content, |
| | | photos: [], |
| | | |
| | | |
| | | } |
| | | |
| | | let curLocation = [] |
| | |
| | | if (Array.isArray(this.form.location) && this.form.location.length >= 2) { |
| | | this.mapParams.center = [Number(this.form.location[0]), Number(this.form.location[1])] |
| | | } else { |
| | | this.mapParams.center = null // 没有经纬度时用默认 |
| | | this.mapParams.center = [...this.inputMapShowDefaultCenter] |
| | | } |
| | | // 如果有图片,添加到表单中 |
| | | if (row.photo_url) { |
| | |
| | | |
| | | // 添加选择变化处理方法 |
| | | handleSelectionChange (selection) { |
| | | |
| | | this.selections = selection |
| | | console.log('已选择的行:', selection) |
| | | }, |
| | |
| | | * @param goWgs84 是否由国测转换到84,默认false----由84转换到国测 |
| | | */ |
| | | disposeLocation (goWgs84 = false, data) { |
| | | let lng, lat |
| | | if (goWgs84) { |
| | | lng = data.location?.[0] ? String(data.location[0]) : undefined |
| | | lat = data.location?.[1] ? String(data.location[1]) : undefined |
| | | let lng = '', lat = '' |
| | | |
| | | if (lng && lat) { |
| | | [lng, lat] = gcj02ToWgs84(Number(lng), Number(lat)) |
| | | } |
| | | } else { |
| | | lng = Number(data.location[0]) |
| | | lat = Number(data.location[1]) |
| | | if (Array.isArray(data.location) && data.location.length > 0) { |
| | | if (goWgs84) { |
| | | lng = data.location?.[0] ? String(data.location[0]) : undefined |
| | | lat = data.location?.[1] ? String(data.location[1]) : undefined |
| | | |
| | | if (lng && lat) { |
| | | [lng, lat] = wgs84ToGcj02(Number(lng), Number(lat)) |
| | | if (lng && lat) { |
| | | [lng, lat] = gcj02ToWgs84(Number(lng), Number(lat)) |
| | | } |
| | | } else { |
| | | lng = Number(data.location[0]) |
| | | lat = Number(data.location[1]) |
| | | |
| | | if (lng && lat) { |
| | | [lng, lat] = wgs84ToGcj02(Number(lng), Number(lat)) |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | .detail-container { |
| | | padding: 20px; |
| | | |
| | | .detail-top-title { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | .event-orderNumber{ |
| | | margin-right: 10px;} |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | .event-orderNumber { |
| | | margin-right: 10px; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | checked == '智飞工单' ? zfstatusMap[item.status] : statusMap[item.status] |
| | | }}</span> |
| | | <span class="reads" :class="item.is_read === 0 ? 'unread' : ''"></span> |
| | | <span class="todo-text">{{ item.name }}</span> |
| | | <span class="todo-text">{{ item.event_num }}</span> |
| | | </div> |
| | | |
| | | <div class="action-area"> |
| | | <img :src="st7" alt="" /> |
| | | <span class="todo-date">{{ item.date?.slice(0, 10).replace(/-/g, '.') }}</span> |
| | | <!-- <img :src="st7" alt="" /> --> |
| | | <!-- <span class="todo-date">{{ item.date?.slice(0, 10).replace(/-/g, '.') }}</span> --> |
| | | <span class="todo-date">{{ item.name }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | loading.value = true |
| | | const type = 1 |
| | | const res = await addOrderRecordApi(type, ticketId) |
| | | |
| | | |
| | | } else if (checked.value === '智飞工单') { |
| | | loading.value = true |
| | | const type = 2 |
| | |
| | | const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode) |
| | | todos.value = res.data.data || [] |
| | | loading.value = false |
| | | // console.log('事件工单', todos.value); |
| | | } else if (checked.value === '智飞工单') { |
| | | loading.value = true |
| | | const type = 2 |
| | | const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode) |
| | | todos.value = res.data.data || [] |
| | | loading.value = false |
| | | // console.log('智飞工单', todos.value); |
| | | } |
| | | } |
| | | |
| | |
| | | .todo-text { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #343434; |
| | | color: #5C6476; |
| | | } |
| | | |
| | | .unread { |
| | |
| | | |
| | | .todo-date { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #5c6476; |
| | | font-size: 14px; |
| | | color: #343434; |
| | | margin-left: 2px; |
| | | } |
| | | } |
| | |
| | | <div :class="data.isSelected ? 'is-selected' : ''"> |
| | | <div class="date-number">{{ data.day.slice(8, 10) }}</div> |
| | | <div class="events"> |
| | | <div v-for="(event, index) in getEvents(data.day)" :key="index" class="event-item" :class="event.type" |
| | | @click="jumpcalendar(event, data.day)"> |
| | | <img :src="getEventIcon(event.type)" alt="" /> |
| | | {{ event.name }}<span>{{ event.value }}</span> |
| | | <div |
| | | v-for="(event, index) in getEvents(data.day)" |
| | | :key="index" |
| | | class="event-item" |
| | | :class="event.type" |
| | | @click="jumpcalendar(event, data.day)" |
| | | > |
| | | <div class="imgBox"><img :src="getEventIcon(event.type)" alt="" /> {{ event.name }}</div> |
| | | <span>{{ event.value }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import dayjs from 'dayjs' |
| | | import { jobEventBar, getCalen } from '@/api/home/index' |
| | | import { useRouter } from 'vue-router' |
| | | import ev1 from '@/assets/images/workbench/ev1.svg' |
| | | import ev2 from '@/assets/images/workbench/ev2.svg' |
| | | import { ElMessage } from 'element-plus' |
| | | const router = useRouter() |
| | | const events = ref({}) |
| | | import dayjs from 'dayjs'; |
| | | import { jobEventBar, getCalen } from '@/api/home/index'; |
| | | import { useRouter } from 'vue-router'; |
| | | import ev1 from '@/assets/images/workbench/ev1.svg'; |
| | | import ev2 from '@/assets/images/workbench/ev2.svg'; |
| | | import { ElMessage } from 'element-plus'; |
| | | const router = useRouter(); |
| | | const events = ref({}); |
| | | const params = ref({ |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }) |
| | | }); |
| | | const eventIcons = ref({ |
| | | 'work-order': ev1, |
| | | task: ev2, |
| | | }) |
| | | }); |
| | | const getEventIcon = type => { |
| | | return eventIcons.value[type] || eventIcons.value.default |
| | | } |
| | | function getCurrentMonthRange () { |
| | | return eventIcons.value[type] || eventIcons.value.default; |
| | | }; |
| | | function getCurrentMonthRange() { |
| | | return { |
| | | start_date: dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | end_date: dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | } |
| | | }; |
| | | } |
| | | const leftValue = ref(new Date()) |
| | | const leftValue = ref(new Date()); |
| | | watch( |
| | | () => leftValue.value, |
| | | (newV, oldV) => { |
| | | if (newV && oldV) { |
| | | const newDate = dayjs(newV) |
| | | const newDate = dayjs(newV); |
| | | params.value = { |
| | | start_date: newDate.startOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | end_date: newDate.endOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | } |
| | | getJobEventBar() |
| | | }; |
| | | getJobEventBar(); |
| | | } |
| | | }, |
| | | { deep: true, immediate: true } |
| | | ) |
| | | ); |
| | | |
| | | // 获取日期数字 |
| | | const getDate = date => { |
| | | return date.getDate() |
| | | } |
| | | return date.getDate(); |
| | | }; |
| | | |
| | | // 获取对应日期的事件 |
| | | const getEvents = dateString => { |
| | | return events.value[dateString] || [] |
| | | } |
| | | const monthRange = getCurrentMonthRange() |
| | | params.value = monthRange |
| | | return events.value[dateString] || []; |
| | | }; |
| | | const monthRange = getCurrentMonthRange(); |
| | | params.value = monthRange; |
| | | |
| | | const getJobEventBar = () => { |
| | | getCalen(params.value).then(res => { |
| | | if (res.data.code !== 0) return |
| | | events.value = res.data.data |
| | | }) |
| | | } |
| | | if (res.data.code !== 0) return; |
| | | const a = res.data.data; |
| | | const filteredData = {}; |
| | | for (let date in a) { |
| | | filteredData[date] = a[date].filter(item => item.type !== 'work-order'); |
| | | } |
| | | events.value = filteredData; |
| | | // events.value = res.data.data |
| | | }); |
| | | }; |
| | | const jumpcalendar = (event, day) => { |
| | | if (event.name === '工单') { |
| | | router.push({ |
| | |
| | | query: { |
| | | day: day, |
| | | }, |
| | | }) |
| | | }); |
| | | } else { |
| | | router.push({ |
| | | path: '/job/jobstatistics', |
| | | query: { |
| | | day: day, |
| | | }, |
| | | }) |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | onMounted(() => { |
| | | getJobEventBar() |
| | | }) |
| | | getJobEventBar(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .calenBox { |
| | | // height: 630px; |
| | | height: pxToVh(660); |
| | | |
| | | |
| | | |
| | | // 隐藏按钮组中间按钮 |
| | | .el-button-group>.el-button:not(:first-child):not(:last-child) { |
| | | .el-button-group > .el-button:not(:first-child):not(:last-child) { |
| | | display: none; |
| | | } |
| | | .date-number { |
| | | font-size: 13px !important;} |
| | | } |
| | | </style> |
| | | |
| | |
| | | background-color: #fff; |
| | | |
| | | ::v-deep(.el-calendar) { |
| | | |
| | | // height: 80%; // 日历填充容器 |
| | | // 标题样式 |
| | | .el-calendar__title { |
| | |
| | | // } |
| | | // } |
| | | .el-calendar-table .el-calendar-day { |
| | | height: 78px !important; |
| | | |
| | | height: pxToVh(91) !important; |
| | | } |
| | | |
| | | |
| | | // 选中日期样式 |
| | | .el-calendar-table td.is-selected { |
| | | background-color: #f0f7ff; |
| | |
| | | .event-item { |
| | | font-size: 12px; |
| | | // padding: 2px; |
| | | |
| | | text-align: center; |
| | | border-radius: 3px; |
| | | white-space: nowrap; |
| | | |
| | |
| | | |
| | | span { |
| | | font-weight: 600; |
| | | font-size: 14px; |
| | | font-size: 18px; |
| | | color: #029d36; |
| | | margin-left: 2px; |
| | | } |
| | | } |
| | | .imgBox { |
| | | font-size: 12px;} |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="flyOrder"> |
| | | <div class="fytitle"> |
| | | <div class="cardtotal"> |
| | | <p>飞行统计</p> |
| | | <img @click="refresh" src="/src/assets/images/workbench/st1.svg" alt="" /> |
| | | </div> |
| | | <div class="time-card"> |
| | | <div |
| | | class="card-item" |
| | | :class="item === checked ? 'active' : ''" |
| | | v-for="(item, index) in timeListEnum" |
| | | :key="index" |
| | | @click="timeClick(item, index)" |
| | | > |
| | | {{ timeListStr[index] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="flycenter"> |
| | | <div class="centerBox"> |
| | | <div class="centerItem" v-for="(itemfly, index) in flyTypeList" :key="index"> |
| | | <div class="flyimg"><img :src="itemfly.img" alt="" />{{ itemfly.name }}</div> |
| | | <div class="flydata"> |
| | | <span>{{ itemfly.value }}</span |
| | | >{{ unitMap[itemfly.name] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="lineChart"> |
| | | <div class="lineBox" ref="chartRef"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getFly, getFlyTime, tongjiNestApi } from '@/api/home/index'; |
| | | import flyImg1 from '@/assets/images/workbench/fy2.svg'; |
| | | import flyImg2 from '@/assets/images/workbench/fy3.svg'; |
| | | import flyImg3 from '@/assets/images/workbench/fy4.svg'; |
| | | import * as echarts from 'echarts'; |
| | | import useEchartsResize from '@/hooks/useEchartsResize'; |
| | | let checked = ref('CURRENT_YEAR'); |
| | | let timeListStr = ['本周', '本月', '本年']; |
| | | let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR']; |
| | | const dateSelect = ref('CURRENT_YEAR'); |
| | | const params = ref({ |
| | | date_enum: 'CURRENT_YEAR', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | const flyparams = ref({ |
| | | date_enum: 'YEAR', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | const chartRef = ref(null); |
| | | let { chart: lineChart } = useEchartsResize(chartRef); |
| | | const keyMapping = { |
| | | 飞行时长: 'total_flight_time', |
| | | 飞行里程: 'total_flight_distance', |
| | | // 任务成果: 'event_num', |
| | | }; |
| | | const unitMap = { |
| | | 飞行时长: '小时', |
| | | 飞行里程: '公里', |
| | | // 任务成果: '个', |
| | | }; |
| | | const flyTypeList = ref([ |
| | | { name: '飞行时长', value: 0, img: flyImg1, key: 'hour_count' }, |
| | | { name: '飞行里程', value: 0, img: flyImg2, key: 'flight_mileage' }, |
| | | // { name: '任务成果', value: 0, img: flyImg3 }, |
| | | ]); |
| | | // 飞行统计 |
| | | const getFlyList = () => { |
| | | // getFly(params.value).then(res => { |
| | | // flyTypeList.value = flyTypeList.value.map(item => ({ |
| | | // ...item, |
| | | // value: res.data.data[keyMapping[item.name]] || 0, |
| | | // })); |
| | | // }); |
| | | |
| | | tongjiNestApi(flyparams.value).then(res => { |
| | | flyTypeList.value = flyTypeList.value.map(item => ({ |
| | | ...item, |
| | | value: res.data.data[item.key] || 0, |
| | | })); |
| | | |
| | | }); |
| | | }; |
| | | const getFlyTimeList = () => { |
| | | getFlyTime(params.value).then(res => { |
| | | const resList = res?.data?.data || []; |
| | | lineCharts(resList); |
| | | }); |
| | | }; |
| | | const refresh = () => { |
| | | params.value.date_enum = 'CURRENT_YEAR'; |
| | | checked.value = 'CURRENT_YEAR'; |
| | | dateSelect.value = 'CURRENT_YEAR'; |
| | | getFlyList(); |
| | | getFlyTimeList(); |
| | | }; |
| | | let timeClick = (item, index) => { |
| | | |
| | | checked.value = item; |
| | | params.value.date_enum = item; |
| | | dateSelect.value = item; |
| | | if (item === 'CURRENT_MONTH') { |
| | | flyparams.value.date_enum = 'MONTH'; |
| | | } else if (item === 'CURRENT_WEEK') { |
| | | flyparams.value.date_enum = 'WEEK'; |
| | | } else { |
| | | flyparams.value.date_enum = 'YEAR'; |
| | | } |
| | | getFlyList(); |
| | | getFlyTimeList(); |
| | | }; |
| | | // 折线/柱状图 |
| | | const lineCharts = bardata => { |
| | | const categories = bardata?.map(item => item.name); // x轴类别 |
| | | const flight_distance = []; |
| | | const flight_time = []; |
| | | const event_num = []; |
| | | // /遍历数据填充各系列 |
| | | bardata?.forEach(period => { |
| | | let hasDuration = false, |
| | | hasDistance = false, |
| | | hasResult = false; |
| | | |
| | | period.data?.forEach(item => { |
| | | switch (item.name) { |
| | | case '飞行时长': |
| | | flight_time.push(parseFloat(item.value) || 0); |
| | | hasDuration = true; |
| | | break; |
| | | case '飞行里程': |
| | | flight_distance.push(parseFloat(item.value) || 0); |
| | | hasDistance = true; |
| | | break; |
| | | } |
| | | }); |
| | | |
| | | // 处理可能缺失的数据项 |
| | | if (!hasDuration) flight_time.push(0); |
| | | if (!hasDistance) flight_distance.push(0); |
| | | }); |
| | | var option = { |
| | | tooltip: { |
| | | trigger: 'item', |
| | | axisPointer: { |
| | | type: 'shadow', |
| | | }, |
| | | }, |
| | | grid: { |
| | | left: '2%', |
| | | right: '4%', |
| | | bottom: '14%', |
| | | top: '16%', |
| | | containLabel: true, |
| | | }, |
| | | legend: { |
| | | data: ['飞行时长', '飞行里程'], |
| | | left: 'center', |
| | | top: '5%', |
| | | color: '#383838', // 直接配置颜色 |
| | | itemWidth: 15, |
| | | itemHeight: 10, |
| | | itemGap: 25, |
| | | fontSize: '1.2rem', |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: categories, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | interval: 0, |
| | | color: '#383838', |
| | | }, |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: 'log', |
| | | name: '单位:小时', |
| | | nameTextStyle: { |
| | | color: '#383838', |
| | | fontSize: '1.2rem', |
| | | }, |
| | | min: 1, |
| | | logBase: 3, |
| | | axisLine: { |
| | | show: false, |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | show: true, |
| | | lineStyle: { |
| | | type: 'dashed', |
| | | color: '#cdd5e2', |
| | | width: 1, |
| | | opacity: 0.5, |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | color: '#666666', // 直接配置颜色 |
| | | }, |
| | | }, |
| | | { |
| | | type: 'log', |
| | | name: '单位:公里', |
| | | nameTextStyle: { |
| | | color: '#383838', |
| | | }, |
| | | min: 1, |
| | | logBase: 3, |
| | | axisLine: { |
| | | show: false, |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | show: false, |
| | | lineStyle: { |
| | | type: 'dashed', |
| | | color: '#cdd5e2', |
| | | width: 1, |
| | | opacity: 0.5, |
| | | }, |
| | | }, |
| | | |
| | | axisLabel: { |
| | | color: '#383838', // 直接配置颜色 |
| | | }, |
| | | }, |
| | | ], |
| | | series: [ |
| | | { |
| | | name: '飞行时长', |
| | | type: 'bar', |
| | | barWidth: '8px', |
| | | itemStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: '#91C1FF' }, |
| | | { offset: 1, color: '#2970FF' }, |
| | | ]), |
| | | borderRadius: 4, |
| | | }, |
| | | data: flight_time, |
| | | }, |
| | | |
| | | { |
| | | name: '飞行里程', |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | smooth: true, |
| | | itemStyle: { |
| | | color: '#01D0A7', |
| | | }, |
| | | lineStyle: { |
| | | width: 2, |
| | | type: 'solid', |
| | | }, |
| | | symbol: 'circle', |
| | | symbolSize: 6, |
| | | lineStyle: { |
| | | color: '#01D0A7', |
| | | }, |
| | | data: flight_distance, |
| | | }, |
| | | ], |
| | | }; |
| | | lineChart.value.setOption(option); |
| | | }; |
| | | onMounted(() => { |
| | | lineCharts(); |
| | | getFlyList(); |
| | | getFlyTimeList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | // 飞行统计 |
| | | .flyOrder { |
| | | border-radius: 8px 8px 8px 8px; |
| | | padding: 4px 14px 0 15px; |
| | | background: #ffffff !important; |
| | | margin-top: 5px; |
| | | height: pxToVh(400); |
| | | |
| | | .fytitle { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | .cardtotal { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 9px; |
| | | img { |
| | | width: 17px; |
| | | height: 17px; |
| | | margin-left: 6px; |
| | | cursor: pointer; |
| | | } |
| | | p { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .total-number { |
| | | font-family: 'Source Han Sans CN'; |
| | | |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | img { |
| | | width: 39px; |
| | | height: 38px; |
| | | } |
| | | |
| | | span { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | } |
| | | |
| | | .flycenter { |
| | | margin-top: 13px; |
| | | |
| | | .centerBox { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | |
| | | .centerItem { |
| | | padding: 7px 7px 0 15px; |
| | | width: 196px; |
| | | flex: 1; |
| | | // height: 80px; |
| | | height: pxToVh(80); |
| | | background: #f6f8fe; |
| | | box-shadow: 0px 5px 4px 0px #ebf1ff; |
| | | border-radius: 8px 8px 8px 8px; |
| | | border: 1px solid #ffffff; |
| | | margin-right: 16px; |
| | | |
| | | img { |
| | | width: 15px; |
| | | height: 15px; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .flyimg { |
| | | display: flex; |
| | | align-items: center; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .flydata { |
| | | margin-top: 10px; |
| | | text-align: right; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | |
| | | span { |
| | | font-weight: bold; |
| | | font-size: 24px; |
| | | color: #343434; |
| | | margin-right: 6px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .lineChart { |
| | | height: pxToVh(233) ; |
| | | width: 100%; |
| | | |
| | | .lineBox { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .time-card { |
| | | text-align: center; |
| | | // height: 30px; |
| | | height: pxToVh(30); |
| | | background: #ffffff; |
| | | |
| | | border: 1px solid #e5e5e5; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | display: flex; |
| | | width: 282px; |
| | | margin-left: 15px; |
| | | border-radius: 4px; |
| | | |
| | | .card-item { |
| | | width: 94px; |
| | | height: 100%; |
| | | line-height: 28px; |
| | | cursor: pointer; |
| | | font-family: 'Source Han Sans CN'; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .card-item:first-child { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item:nth-child(2) { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item.active { |
| | | color: #1441ff; |
| | | border: 1px solid #1c5cff; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <div class="machineNest"> |
| | | <div class="nestTop"> |
| | | <div class="card-title"> |
| | | <img :src="jc1" alt="" /> |
| | | |
| | | <div class="cardtotal"> |
| | | <p>机巢工单数量排名</p> |
| | | <img @click="refresh" src="/src/assets/images/workbench/st1.svg" alt="" /> |
| | | </div> |
| | | <div class="time-card"> |
| | | <div |
| | | class="card-item" |
| | | :class="item === checked ? 'active' : ''" |
| | | v-for="(item, index) in timeListEnum" |
| | | :key="index" |
| | | @click="timeClick(item, index)" |
| | | > |
| | | {{ timeListStr[index] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="nestCenter"> |
| | |
| | | import jc2 from '@/assets/images/workbench/jc2.png'; |
| | | import { industryJobNumPieChart } from '@/api/home/index'; |
| | | import { nextTick } from 'vue'; |
| | | |
| | | let checked = ref('CURRENT_YEAR'); |
| | | let timeListStr = ['本周', '本月', '本年']; |
| | | let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR']; |
| | | const params = ref({ |
| | | date_enum: 'CURRENT_YEAR', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | const dateSelect = ref('CURRENT_YEAR'); |
| | | const echartsRef = ref(null); |
| | | let { chart: jcchart } = useEchartsResize(echartsRef); |
| | | const jcOrder = ref([]); |
| | |
| | | type: String, |
| | | }, |
| | | }); |
| | | const params = reactive({ |
| | | date_enum: props.dateSelect, |
| | | }); |
| | | const refresh = () => { |
| | | params.value.date_enum = 'CURRENT_YEAR'; |
| | | checked.value = 'CURRENT_YEAR'; |
| | | dateSelect.value = 'CURRENT_YEAR'; |
| | | getIndustryJobNumPieChart(); |
| | | }; |
| | | let timeClick = (item, index) => { |
| | | checked.value = item; |
| | | params.value.date_enum = item; |
| | | dateSelect.value = item; |
| | | getIndustryJobNumPieChart(); |
| | | }; |
| | | // 空数据计算 |
| | | const isEmptyData = computed(() => { |
| | | return jcOrder.value.length === 0 || |
| | |
| | | } |
| | | // 获取机巢事件数据 |
| | | const getIndustryJobNumPieChart = () => { |
| | | industryJobNumPieChart(params).then(res => { |
| | | industryJobNumPieChart(params.value).then(res => { |
| | | const resList = res?.data?.data || []; |
| | | jcOrder.value = resList; |
| | | nextTick(() => { |
| | |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | right: '3%', |
| | | bottom: '3%', |
| | | top: '-3%', // 添加顶部间距 |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: 'value', |
| | | splitLine: { lineStyle: { color: '#E5E5E5' } }, |
| | | axisLabel: { color: '#7C8091',fontSize:'1.2rem' }, |
| | | splitLine: { lineStyle: { color: '#E5E5E5' },show:false }, |
| | | axisLabel: { color: '#7C8091',fontSize:'1.2rem',show:false }, |
| | | boundaryGap: [0, 0.01], |
| | | }, |
| | | yAxis: { |
| | | type: 'category', |
| | | data: optionData.yAxisData, |
| | | axisLabel: { color: '#7C8091' ,fontSize:'1.2rem'}, |
| | | axisLine: { lineStyle: { color: '#D1D1D1' } }, |
| | | axisLabel: { color: '#7C8091' ,fontSize:'1.2rem',}, |
| | | axisLine: { lineStyle: { color: '#D1D1D1' } ,show:false}, |
| | | axisTick: { show: false }, |
| | | // 确保排序后的数据从上到下显示(最大值在顶部) |
| | | inverse: true, |
| | |
| | | type: 'bar', |
| | | data: optionData.seriesData, |
| | | itemStyle: { |
| | | barBorderRadius: [0, 20, 20, 0], |
| | | color: params => { |
| | | // 前6项使用固定颜色循环,之后使用渐变 |
| | | return params.dataIndex < colors.length |
| | |
| | | show: true, |
| | | position: 'right', |
| | | formatter: '{c}', |
| | | itemStyle:{ |
| | | fontSize: '1.4rem', |
| | | color: '#414243', |
| | | |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | |
| | | |
| | | <style scoped lang="scss"> |
| | | .machineNest { |
| | | width: 100%; |
| | | width: 93%; |
| | | margin-left: 10px; |
| | | height: pxToVh(355); |
| | | border-radius: 8px 8px 8px 8px; |
| | | padding: 4px 14px 0 15px; |
| | | background: #ffffff !important; |
| | | .custom-text { |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | .nestTop { |
| | | .card-title { |
| | | .card-title { |
| | | display: flex; |
| | | margin-bottom: 10px; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | .cardtotal { |
| | | display: flex; |
| | | margin-left: 57px; |
| | | margin-bottom: 15px; |
| | | align-items: center; |
| | | |
| | | img { |
| | | width: 36px; |
| | | height: 40px; |
| | | width: 17px; |
| | | height: 17px; |
| | | margin-left: 6px; |
| | | cursor: pointer; |
| | | } |
| | | p { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .total-number { |
| | | font-family: 'Source Han Sans CN'; |
| | | |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | |
| | | img { |
| | | width: 36px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | .cardtotal { |
| | | display: flex; |
| | | align-items: center; |
| | |
| | | .nestCenter { |
| | | width: 100%; |
| | | // height: 600px; |
| | | height: pxToVh(600); |
| | | height: pxToVh(266); |
| | | .chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | .time-card { |
| | | text-align: center; |
| | | // height: 30px; |
| | | height: pxToVh(30); |
| | | background: #ffffff; |
| | | |
| | | border: 1px solid #e5e5e5; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | display: flex; |
| | | width: 282px; |
| | | margin-left: 15px; |
| | | border-radius: 4px; |
| | | |
| | | .card-item { |
| | | width: 94px; |
| | | height: 100%; |
| | | line-height: 28px; |
| | | cursor: pointer; |
| | | font-family: 'Source Han Sans CN'; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .card-item:first-child { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item:nth-child(2) { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item.active { |
| | | color: #1441ff; |
| | | border: 1px solid #1c5cff; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="workOrder"> |
| | | <div class="card-title"> |
| | | <div class="cardtotal"> |
| | | <p>工单统计占比</p> |
| | | <img @click="refresh" src="/src/assets/images/workbench/st1.svg" alt="" /> |
| | | </div> |
| | | <div class="time-card"> |
| | | <div |
| | | class="card-item" |
| | | :class="item === checked ? 'active' : ''" |
| | | v-for="(item, index) in timeListEnum" |
| | | :key="index" |
| | | @click="timeClick(item, index)" |
| | | > |
| | | {{ timeListStr[index] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="workorderbox"> |
| | | <div class="card-group"> |
| | | <div class="main-card"> |
| | | <div class="status-grid"> |
| | | <div class="status-item" v-for="(item, index) in eventTypeList" :key="index"> |
| | | <div class="statusCon"> |
| | | <div class="status-label">{{ item.name }}</div> |
| | | <div class="status-value">{{ item.value }}<span>个</span></div> |
| | | <div class="ratio"> |
| | | 占比 |
| | | <span :style="{ color: item.color }" |
| | | >{{ ((item.rate * 100) / 100).toFixed(2) }}%</span |
| | | > |
| | | </div> |
| | | </div> |
| | | <img :src="item.img" alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="charts"> |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import useEchartsResize from '@/hooks/useEchartsResize'; |
| | | import { getJobEventByStatus } from '@/api/home/index'; |
| | | import overviewImg2 from '@/assets/images/workbench/tc2.svg'; |
| | | import overviewImg3 from '@/assets/images/workbench/tc3.svg'; |
| | | import overviewImg4 from '@/assets/images/workbench/tc4.svg'; |
| | | import overviewImg5 from '@/assets/images/workbench/tc5.svg'; |
| | | let checked = ref('CURRENT_YEAR'); |
| | | let timeListStr = ['本周', '本月', '本年']; |
| | | let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR']; |
| | | const params = ref({ |
| | | date_enum: 'CURRENT_YEAR', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | const dateSelect = ref('CURRENT_YEAR'); |
| | | const eventTypeList = ref([ |
| | | { name: '待审核', value: 0, img: overviewImg2, color: '#FF472F', status: '2', rate: 0 }, |
| | | { name: '待处理', value: 0, img: overviewImg3, color: '#FF7411', status: '0', rate: 0 }, |
| | | { name: '处理中', value: 0, img: overviewImg4, color: '#FFC300', status: '3', rate: 0 }, |
| | | { name: '已完成', value: 0, img: overviewImg5, color: '#0291A1', status: '4', rate: 0 }, |
| | | ]); |
| | | // 工单统计 |
| | | const getTypeData = () => { |
| | | getJobEventByStatus(params.value).then(res => { |
| | | const resList = res?.data?.data || []; |
| | | |
| | | resList.forEach(item => { |
| | | eventTypeList.value.forEach(item1 => { |
| | | if (item1.name === item.name) { |
| | | item1.value = item.num; |
| | | item1.rate = item.rate; |
| | | } |
| | | }); |
| | | }); |
| | | initChart(resList); |
| | | }); |
| | | }; |
| | | const refresh = () => { |
| | | params.value.date_enum = 'CURRENT_YEAR'; |
| | | checked.value = 'CURRENT_YEAR'; |
| | | dateSelect.value = 'CURRENT_YEAR'; |
| | | getTypeData(); |
| | | }; |
| | | |
| | | let timeClick = (item, index) => { |
| | | checked.value = item; |
| | | params.value.date_enum = item; |
| | | dateSelect.value = item; |
| | | getTypeData(); |
| | | }; |
| | | // 图表 |
| | | const echartsRef = ref(null); |
| | | let { chart } = useEchartsResize(echartsRef); |
| | | const initChart = val => { |
| | | const totalNum = val.reduce((sum, item) => sum + item.num, 0); |
| | | const data = { |
| | | total: { |
| | | title: '总计', |
| | | figure: totalNum.toString(), // 动态计算总数 |
| | | }, |
| | | data: val.map(item => ({ |
| | | value: item.num, |
| | | name: item.name, |
| | | rate: item.rate, |
| | | })), |
| | | }; |
| | | const containerWidth = chart.value.clientWidth; |
| | | const isSmallScreen = containerWidth < 768; // 移动端判断 |
| | | const echartsOption = { |
| | | color: ['#FF472F', '#FF7411', '#FFC300', '#0291A1'], |
| | | tooltip: { |
| | | trigger: 'item', |
| | | padding: 0, |
| | | borderWidth: 0, |
| | | formatter: params => { |
| | | return `<div style="background-color: rgba($color: #FFFFFF, $alpha: 0.95); |
| | | box-shadow: 0 0.4rem 0.9rem 0 rgba($color: #000000, $alpha: 0.1); |
| | | padding:0 1.2rem; |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 0.4rem; |
| | | font-size: 1.2rem;" class="tooltip-area"> |
| | | <p>${params.marker}${params.name}</p> |
| | | <h4 style="margin-left: 1rem;">${params.data.rate || params.percent}%</h4> |
| | | </div>`; |
| | | }, |
| | | }, |
| | | legend: { |
| | | show: false, |
| | | }, |
| | | title: { |
| | | text: data.total.title, |
| | | textStyle: { |
| | | color: 'rgba(28, 31, 35, 0.80)', |
| | | fontSize: '1.2rem', |
| | | fontWeight: 'bold', |
| | | }, |
| | | subtext: data.total.figure, |
| | | subtextStyle: { |
| | | color: '#1C1F23', |
| | | fontSize: '2rem', |
| | | fontWeight: '600', |
| | | }, |
| | | top: '40%', |
| | | left: '48%', |
| | | textAlign: 'center', // 文本对齐 |
| | | }, |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'pie', |
| | | radius: ['55%', '83%'], |
| | | |
| | | label: { |
| | | show: false, |
| | | }, |
| | | labelLine: { |
| | | show: false, |
| | | length: 3, // 调整引导线长度 |
| | | length2: 5, |
| | | lineStyle: { |
| | | cap: 'round', |
| | | }, |
| | | minTurnAngle: 45, // 防止小角度重叠 |
| | | }, |
| | | data: data.data, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | chart.value.setOption(echartsOption); |
| | | }; |
| | | onMounted(() => { |
| | | getTypeData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .workOrder { |
| | | border-radius: 8px 8px 8px 8px; |
| | | padding: 4px 14px 0 15px; |
| | | background: #ffffff !important; |
| | | // height: 315px; |
| | | height: pxToVh(355); |
| | | margin-bottom: 10px; |
| | | .card-title { |
| | | display: flex; |
| | | margin-bottom: 10px; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | .cardtotal { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 9px; |
| | | img { |
| | | width: 17px; |
| | | height: 17px; |
| | | margin-left: 6px; |
| | | cursor: pointer; |
| | | } |
| | | p { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .total-number { |
| | | font-family: 'Source Han Sans CN'; |
| | | |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | |
| | | img { |
| | | width: 36px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | .workorderbox { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | .card-group { |
| | | width: 60%; |
| | | |
| | | .status-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | // row-gap: 19px; |
| | | // gap: 10px; |
| | | |
| | | // padding-bottom: 5px; |
| | | |
| | | .status-item { |
| | | display: flex; |
| | | text-align: center; |
| | | justify-content: space-between; |
| | | // height: 97px; |
| | | height: pxToVh(107); |
| | | // max-width: 158px; |
| | | margin-bottom: 19px; |
| | | margin-right: 14px; |
| | | width: 144px; |
| | | background: #f6f8fe; |
| | | border-radius: 8px 8px 8px 8px; |
| | | |
| | | img { |
| | | width: 26px; |
| | | height: 26px; |
| | | padding: 9px 10px 9px 2px; |
| | | } |
| | | |
| | | .statusCon { |
| | | display: flex; |
| | | flex-direction: column; |
| | | // align-items: center; |
| | | padding: 5px 4px 9px 10px; |
| | | text-align: left; |
| | | |
| | | .status-label { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | text-align: left; |
| | | color: #383838; |
| | | } |
| | | |
| | | .ratio { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #363636; |
| | | white-space: nowrap; |
| | | text-align: left; |
| | | } |
| | | |
| | | .status-value { |
| | | font-family: 'Source Han Sans CN'; |
| | | font-weight: bold; |
| | | font-size: 30px; |
| | | color: #363636; |
| | | // margin: 5px 0; |
| | | // font-style: italic; |
| | | display: inline-block; |
| | | transform: skewX(-5deg); |
| | | |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .charts { |
| | | margin-top: 20px; |
| | | width: 40%; |
| | | height: auto; |
| | | |
| | | .chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .time-card { |
| | | text-align: center; |
| | | // height: 30px; |
| | | height: pxToVh(30); |
| | | background: #ffffff; |
| | | |
| | | border: 1px solid #e5e5e5; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | display: flex; |
| | | width: 282px; |
| | | margin-left: 15px; |
| | | border-radius: 4px; |
| | | |
| | | .card-item { |
| | | width: 94px; |
| | | height: 100%; |
| | | line-height: 28px; |
| | | cursor: pointer; |
| | | font-family: 'Source Han Sans CN'; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .card-item:first-child { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item:nth-child(2) { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item.active { |
| | | color: #1441ff; |
| | | border: 1px solid #1c5cff; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | </template> |
| | | <template v-else-if="test[item]?.name == '机巢保险'"> |
| | | <div |
| | | v-for="statusKey in [1, 0]" |
| | | v-for="statusKey in [1, 0,2]" |
| | | :key="statusKey" |
| | | class="status-item" |
| | | :class="getStatusStyle(test[item]?.name, statusKey)" |
| | |
| | | <span class="count"> |
| | | {{ newtitleData[item].status_map[statusKey] }} |
| | | {{ test[item]?.name === '无人机' ? '架' : '个' }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="test[item]?.name == '无人机流量'"> |
| | | <div |
| | | v-for="statusKey in [ 0,2,1]" |
| | | :key="statusKey" |
| | | class="status-item" |
| | | :class="getStatusStyle(test[item]?.name, statusKey)" |
| | | :style="{ color: getStatusColor(test[item]?.name, statusKey) }" |
| | | > |
| | | <span |
| | | class="indicator" |
| | | :style="{ |
| | | backgroundColor: getStatusBackground(test[item]?.name, statusKey), |
| | | color: getStatusColor(test[item]?.name, statusKey), |
| | | }" |
| | | ></span> |
| | | <span class="label">{{ getStatusLabel(test[item]?.name, statusKey) }}</span> |
| | | <span class="count"> |
| | | {{ newtitleData[item].status_map[statusKey] }} |
| | | {{ test[item]?.name === '无人机流量' ? '架' : '个' }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | |
| | | const insureStatus = { |
| | | 0: '临近到期', |
| | | 1: '正常期限', |
| | | 2:'保险过期' |
| | | }; |
| | | const moveListStatus = { |
| | | '-1': '离线中', |
| | |
| | | const statusStyles = { |
| | | 机巢保险: { |
| | | 0: { class: 'expired', color: '#FFA600', background: '#FFA600' }, |
| | | 1: { class: 'normal', color: 'rgba(27, 148, 255, 1)', background: 'rgba(27, 148, 255, 1)' }, |
| | | 1: { class: 'normal', color: 'rgba(0, 180, 69, 1)', background: 'rgba(0, 180, 69, 1)' }, |
| | | 2: { class: 'flying',color: 'rgba(255, 36, 36, 1)', background: 'rgba(255, 36, 36, 1)' }, |
| | | }, |
| | | |
| | | 无人机流量: { |
| | | 0: { class: 'offline', color: 'rgba(0, 180, 69, 1)', background: 'rgba(0, 180, 69, 1)' }, |
| | | 1: { class: 'flying', color: 'rgba(186, 186, 186, 1)', background: 'rgba(186, 186, 186, 1)' }, |
| | | 2: { class: 'flying', color: 'rgba(255, 36, 36, 1)', background: 'rgba(255, 36, 36, 1)' }, |
| | | 1: { class: 'flying', color: 'rgba(255, 36, 36, 1)', background: 'rgba(255, 36, 36, 1)' }, |
| | | 2: { class: 'flying', color: '#FFA600', background: '#FFA600' }, |
| | | }, |
| | | 监控设备: { |
| | | 0: { class: 'offline', color: 'rgba(186, 186, 186, 1)', background: 'rgba(186, 186, 186, 1)' }, |
| New file |
| | |
| | | <template> |
| | | <div class="taskOutcome"> |
| | | <div class="card-title"> |
| | | <div class="cardtotal"> |
| | | <p>任务成果</p> |
| | | <span class="total-number">{{totalNum}}</span> |
| | | <p>个</p> |
| | | <img @click="refresh" src="/src/assets/images/workbench/st1.svg" alt="" /> |
| | | </div> |
| | | <div class="time-card"> |
| | | <div |
| | | class="card-item" |
| | | :class="item === checked ? 'active' : ''" |
| | | v-for="(item, index) in timeListEnum" |
| | | :key="index" |
| | | @click="timeClick(item, index)" |
| | | > |
| | | {{ timeListStr[index] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getdisposeApi, gettaskOutcomApi } from '@/api/home/index'; |
| | | import * as echarts from 'echarts'; |
| | | import dayjs from 'dayjs'; |
| | | import useEchartsResize from '@/hooks/useEchartsResize'; |
| | | let checked = ref('CURRENT_YEAR'); |
| | | let timeListStr = ['本周', '本月', '本年']; |
| | | let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR']; |
| | | const params = ref({ |
| | | dateEnum: 'CURRENT_YEAR', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | const totalNum = ref(0) |
| | | const dateSelect = ref('CURRENT_YEAR'); |
| | | const refresh = () => { |
| | | params.value.dateEnum = 'CURRENT_YEAR'; |
| | | checked.value = 'CURRENT_YEAR'; |
| | | dateSelect.value = 'CURRENT_YEAR'; |
| | | getBarChartData(params.value); |
| | | }; |
| | | let timeClick = (item, index) => { |
| | | checked.value = item; |
| | | params.value.dateEnum = item; |
| | | dateSelect.value = item; |
| | | getBarChartData(params.value); |
| | | }; |
| | | const seriesObj = {}; |
| | | const echartsOption = { |
| | | title: { |
| | | text: seriesObj ? '' :'暂无数据', |
| | | left: 'center', |
| | | top: 'center', |
| | | textStyle: { |
| | | color: '#999', |
| | | fontSize: '1.6rem', |
| | | fontWeight: 'normal' |
| | | } |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow', |
| | | }, |
| | | }, |
| | | legend: { |
| | | itemWidth: 14, |
| | | itemHeight: 8, |
| | | data: ['照片总数', '视频总数', 'AI识别', '全景', '三维', '正射'], // 确保包含所有系列名称 |
| | | top: '-1%', |
| | | textStyle: { |
| | | color: '#383838', |
| | | fontSize: 12, |
| | | }, |
| | | }, |
| | | grid: { |
| | | // top: '5%', |
| | | left: 0, |
| | | right: 0, |
| | | bottom: 0, |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | axisLine: { |
| | | show: true, // 隐藏轴线 |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | color: '#383838', |
| | | // interval: 0, // 显示所有标签 |
| | | }, |
| | | data: [], |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: 'value', |
| | | name: '', |
| | | nameTextStyle: { |
| | | color: '#E6F7FF', |
| | | fontSize: '1rem', |
| | | padding: [-8, 0, 0, 0], // 调整单位文字位置,向左偏移 |
| | | verticalAlign: 'top', |
| | | lineHeight: 12, |
| | | }, |
| | | axisLabel: { |
| | | interval: 0, // 显示所有标签 |
| | | color: '#383838', |
| | | fontFamily: 'Source Han Sans CN, Source Han Sans CN', |
| | | fontWeight: 400, |
| | | fontSize: 10, |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#383838', |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | type: 'dashed', // 设置为虚线 |
| | | }, |
| | | }, |
| | | }, |
| | | ], |
| | | series: [], |
| | | }; |
| | | const eventTypeList = ref([ |
| | | {name: '照片总数', value: 0, type: '0', color: '#ED672D' }, |
| | | { name: '视频总数', value: 0, type: '1', color: '#F5B763' }, |
| | | { name: 'AI识别', value: 0, type: '2', color: '#6851C3' }, |
| | | { name: '全景', value: 0, type: '5', color: '#FFE100' }, |
| | | { name: '三维', value: 0, type: '3', color: '#04C598' }, |
| | | { name: '正射', value: 0, type: '4', color: '#3673E8' }, |
| | | ]); |
| | | |
| | | eventTypeList.value.forEach(item => { |
| | | seriesObj[item.name] = { |
| | | type: 'bar', |
| | | stack: 'Ad', |
| | | barWidth: 8, |
| | | emphasis: { |
| | | focus: 'series', |
| | | }, |
| | | name: item.name, |
| | | itemStyle: { color: item.color }, |
| | | data: [], |
| | | }; |
| | | }); |
| | | const echartsRef = ref(null); |
| | | let { chart } = useEchartsResize(echartsRef); |
| | | // 获取柱状图数据 |
| | | const getBarChartData = value => { |
| | | gettaskOutcomApi(value).then(res => { |
| | | const list = res?.data?.data || []; |
| | | |
| | | |
| | | eventTypeList.value.forEach(item => { |
| | | item.value = 0; |
| | | }); |
| | | const typeMap = new Map(); |
| | | eventTypeList.value.forEach(item => { |
| | | typeMap.set(item.type, item); |
| | | }); |
| | | echartsOption.xAxis.data = list.map(item => item.name); |
| | | |
| | | Object.keys(seriesObj).forEach(key => { |
| | | seriesObj[key].data = []; |
| | | }); |
| | | // 遍历所有月份 |
| | | list.forEach(month => { |
| | | totalNum.value = month.total |
| | | const monthData = new Map(); |
| | | month.data.forEach(item => { |
| | | monthData.set(item.name, item.value); |
| | | }); |
| | | eventTypeList.value.forEach(typeItem => { |
| | | const value = monthData.get(typeItem.type) || 0; |
| | | if (seriesObj[typeItem.name]) { |
| | | seriesObj[typeItem.name].data.push(value); |
| | | } |
| | | }); |
| | | }); |
| | | if (list.length > 0) { |
| | | const lastMonth = list[list.length - 1]; |
| | | const lastMonthData = new Map(); |
| | | lastMonth.data.forEach(item => { |
| | | lastMonthData.set(item.name, item.value); |
| | | }); |
| | | |
| | | eventTypeList.value.forEach(item => { |
| | | const value = lastMonthData.get(item.type); |
| | | if (value !== undefined) { |
| | | item.value = value; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 更新图表 |
| | | echartsOption.series = Object.values(seriesObj); |
| | | chart.value.setOption(echartsOption); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getBarChartData(params.value); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .taskOutcome { |
| | | border-radius: 8px 8px 8px 8px; |
| | | padding: 4px 14px 0 15px; |
| | | background: #ffffff !important; |
| | | margin-top: 10px; |
| | | margin-left: 10px; |
| | | height: pxToVh(400); |
| | | .card-title { |
| | | display: flex; |
| | | margin-bottom: 10px; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | .cardtotal { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 9px; |
| | | img { |
| | | width: 17px; |
| | | height: 17px; |
| | | margin-left: 6px; |
| | | cursor: pointer; |
| | | } |
| | | p { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .total-number { |
| | | font-family: 'Source Han Sans CN'; |
| | | |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | |
| | | img { |
| | | width: 36px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .time-card { |
| | | text-align: center; |
| | | // height: 30px; |
| | | height: pxToVh(30); |
| | | background: #ffffff; |
| | | |
| | | border: 1px solid #e5e5e5; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | display: flex; |
| | | width: 282px; |
| | | margin-left: 15px; |
| | | border-radius: 4px; |
| | | |
| | | .card-item { |
| | | width: 94px; |
| | | height: 100%; |
| | | line-height: 28px; |
| | | cursor: pointer; |
| | | font-family: 'Source Han Sans CN'; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .card-item:first-child { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item:nth-child(2) { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .card-item.active { |
| | | color: #1441ff; |
| | | border: 1px solid #1c5cff; |
| | | } |
| | | } |
| | | .chart { |
| | | width: 98%; |
| | | // height: 265px; |
| | | height: pxToVh(285); |
| | | padding-left: 10px; |
| | | margin-top: 27px; |
| | | } |
| | | </style> |
| | |
| | | <!-- 综合统计分析 --> |
| | | <div class="comprehensiveCon"> |
| | | <div class="comprehensive"> |
| | | <div class="title"> |
| | | <div class="name"> |
| | | <span> 综合统计分析</span> |
| | | <img @click="refresh" src="/src/assets/images/workbench/st1.png" alt="" /> |
| | | <div class="time-card"> |
| | | <div class="card-item" :class="item === checked ? 'active' : ''" v-for="(item, index) in timeListEnum" |
| | | :key="index" @click="timeClick(item, index)"> |
| | | {{ timeListStr[index] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="arrow" @click="jumpshebei"> |
| | | <img src="/src/assets/images/workbench/st2.png" alt="" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="center"> |
| | | <div class="centerLeft"> |
| | | <!-- 工单统计 --> |
| | | <div class="workOrder"> |
| | | <div class="card-group"> |
| | | <div class="main-card"> |
| | | <div class="card-title"> |
| | | <img :src="overviewImg1" alt="" /> |
| | | <div class="cardtotal"> |
| | | <p>工单统计占比</p> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="status-grid"> |
| | | <div class="status-item" v-for="(item, index) in eventTypeList" :key="index"> |
| | | <div class="statusCon"> |
| | | <div class="status-label">{{ item.name }}</div> |
| | | <div class="status-value">{{ item.value }}<span>个</span></div> |
| | | <div class="ratio"> |
| | | 占比 |
| | | <span :style="{ color: item.color }">{{ ((item.rate * 100) / 100).toFixed(2) }}%</span> |
| | | </div> |
| | | </div> |
| | | <img :src="item.img" alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="charts"> |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </div> |
| | | <proportionStatic></proportionStatic> |
| | | <!-- 飞行统计 --> |
| | | <div class="flyOrder"> |
| | | <div class="fytitle"> |
| | | <img :src="fy1" alt="" /> |
| | | <span>飞行统计</span> |
| | | </div> |
| | | <div class="flycenter"> |
| | | <div class="centerBox"> |
| | | <div class="centerItem" v-for="(itemfly, index) in flyTypeList" :key="index"> |
| | | <div class="flyimg"><img :src="itemfly.img" alt="" />{{ itemfly.name }}</div> |
| | | <div class="flydata"> |
| | | <span>{{ itemfly.value }}</span>{{ unitMap[itemfly.name] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="lineChart"> |
| | | <div class="lineBox" ref="chartRef"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <flightStatistics></flightStatistics> |
| | | |
| | | </div> |
| | | <div class="centerRight"> |
| | | <flyratio :dateSelect="dateSelect"></flyratio> |
| | | <!-- 机巢工单数量排名(件) --> |
| | | <flyratio></flyratio> |
| | | <!-- 任务成果 --> |
| | | <taskOutcome></taskOutcome> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import taskOutcome from './components/taskOutcome.vue' |
| | | import flightStatistics from './components/flightStatistics.vue' |
| | | import proportionStatic from './components/proportionStatic.vue' |
| | | import flyratio from './components/flyratio.vue' |
| | | import Bocklog from './components/backlog.vue' |
| | | import CalenBox from './components/calendarBox.vue' |
| | |
| | | import { mapGetters } from 'vuex' |
| | | import { getJobEventByStatus, getJobEventTotal, getFly, getFlyTime } from '@/api/home/index' |
| | | import overviewImg1 from '@/assets/images/workbench/tc1.png' |
| | | import overviewImg2 from '@/assets/images/workbench/tc2.svg' |
| | | import overviewImg3 from '@/assets/images/workbench/tc3.svg' |
| | | import overviewImg4 from '@/assets/images/workbench/tc4.svg' |
| | | import overviewImg5 from '@/assets/images/workbench/tc5.svg' |
| | | |
| | | import fy1 from '@/assets/images/workbench/fy1.png' |
| | | import flyImg1 from '@/assets/images/workbench/fy2.svg' |
| | | import flyImg2 from '@/assets/images/workbench/fy3.svg' |
| | | import flyImg3 from '@/assets/images/workbench/fy4.svg' |
| | | |
| | | import statistics from './components/statistics.vue' |
| | | import { ElMessage } from 'element-plus' |
| | | let checked = ref('CURRENT_YEAR') |
| | |
| | | checked.value = item |
| | | params.value.date_enum = item |
| | | dateSelect.value = item |
| | | getTypeData() |
| | | getFlyList() |
| | | getFlyTimeList() |
| | | |
| | | |
| | | |
| | | } |
| | | const refresh = () => { |
| | | params.value.date_enum = 'CURRENT_YEAR' |
| | | checked.value = 'CURRENT_YEAR' |
| | | dateSelect.value = 'CURRENT_YEAR' |
| | | getTypeData() |
| | | getFlyList() |
| | | getFlyTimeList() |
| | | |
| | | |
| | | } |
| | | // 跳转 |
| | | const jumpshebei = () => { |
| | | ElMessage.warning('加急开发中...') |
| | | } |
| | | |
| | | const eventTypeList = ref([ |
| | | { name: '待审核', value: 0, img: overviewImg2, color: '#FF472F', status: '2', rate: 0 }, |
| | | { name: '待处理', value: 0, img: overviewImg3, color: '#FF7411', status: '0', rate: 0 }, |
| | | { name: '处理中', value: 0, img: overviewImg4, color: '#FFC300', status: '3', rate: 0 }, |
| | | { name: '已完成', value: 0, img: overviewImg5, color: '#0291A1', status: '4', rate: 0 }, |
| | | ]) |
| | | const keyMapping = { |
| | | 飞行时长: 'total_flight_time', |
| | | 飞行里程: 'total_flight_distance', |
| | | 任务成果: 'event_num', |
| | | } |
| | | const unitMap = { |
| | | 飞行时长: '时', |
| | | 飞行里程: '千米', |
| | | 任务成果: '个', |
| | | } |
| | | const flyTypeList = ref([ |
| | | { name: '飞行时长', value: 0, img: flyImg1 }, |
| | | { name: '飞行里程', value: 0, img: flyImg2 }, |
| | | { name: '任务成果', value: 0, img: flyImg3 }, |
| | | ]) |
| | | |
| | | const eventTotal = ref(0) |
| | | const data = ref([]) |
| | | // 工单统计 |
| | | const getTypeData = () => { |
| | | getJobEventByStatus(params.value).then(res => { |
| | | const resList = res?.data?.data || [] |
| | | |
| | | resList.forEach(item => { |
| | | eventTypeList.value.forEach(item1 => { |
| | | if (item1.name === item.name) { |
| | | item1.value = item.num |
| | | item1.rate = item.rate |
| | | } |
| | | }) |
| | | }) |
| | | initChart(resList) |
| | | }) |
| | | } |
| | | // 飞行统计 |
| | | const getFlyList = () => { |
| | | getFly(params.value).then(res => { |
| | | flyTypeList.value = flyTypeList.value.map(item => ({ |
| | | ...item, |
| | | value: res.data.data[keyMapping[item.name]] || 0, |
| | | })) |
| | | }) |
| | | } |
| | | const getFlyTimeList = () => { |
| | | getFlyTime(params.value).then(res => { |
| | | const resList = res?.data?.data || [] |
| | | lineCharts(resList) |
| | | }) |
| | | } |
| | | // 图表 |
| | | const echartsRef = ref(null) |
| | | let { chart } = useEchartsResize(echartsRef) |
| | | const chartRef = ref(null) |
| | | let { chart: lineChart } = useEchartsResize(chartRef) |
| | | const initChart = val => { |
| | | const totalNum = val.reduce((sum, item) => sum + item.num, 0) |
| | | const data = { |
| | | total: { |
| | | title: '总计', |
| | | figure: totalNum.toString(), // 动态计算总数 |
| | | }, |
| | | data: val.map(item => ({ |
| | | value: item.num, |
| | | name: item.name, |
| | | rate: item.rate, |
| | | })), |
| | | } |
| | | const containerWidth = chart.value.clientWidth |
| | | const isSmallScreen = containerWidth < 768 // 移动端判断 |
| | | const echartsOption = { |
| | | color: ['#FF472F', '#FF7411', '#FFC300', '#0291A1'], |
| | | tooltip: { |
| | | trigger: 'item', |
| | | padding: 0, |
| | | borderWidth: 0, |
| | | formatter: params => { |
| | | return `<div style="background-color: rgba($color: #FFFFFF, $alpha: 0.95); |
| | | box-shadow: 0 0.4rem 0.9rem 0 rgba($color: #000000, $alpha: 0.1); |
| | | padding:0 1.2rem; |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 0.4rem; |
| | | font-size: 1.2rem;" class="tooltip-area"> |
| | | <p>${params.marker}${params.name}</p> |
| | | <h4 style="margin-left: 1rem;">${params.data.rate || params.percent}%</h4> |
| | | </div>` |
| | | }, |
| | | }, |
| | | legend: { |
| | | show: false, |
| | | }, |
| | | title: { |
| | | text: data.total.title, |
| | | textStyle: { |
| | | color: 'rgba(28, 31, 35, 0.80)', |
| | | fontSize: '1.2rem', |
| | | fontWeight: 'bold', |
| | | }, |
| | | subtext: data.total.figure, |
| | | subtextStyle: { |
| | | color: '#1C1F23', |
| | | fontSize: '2rem', |
| | | fontWeight: '600', |
| | | }, |
| | | top: '40%', |
| | | left: '48%', |
| | | textAlign: 'center', // 文本对齐 |
| | | }, |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'pie', |
| | | radius: ['43%', '63%'], |
| | | avoidLabelOverlap: true, |
| | | left: '-2%', |
| | | label: { |
| | | formatter: params => { |
| | | // 使用 b 样式标记包裹百分比数值 |
| | | return `{a|${params.name}}: {b|${params.data.rate}%}` |
| | | }, |
| | | fontSize: isSmallScreen ? '0.8rem' : '1.2rem', |
| | | position: isSmallScreen ? 'outer' : 'outer', |
| | | alignTo: 'labelLine', |
| | | overflow: 'truncate', |
| | | overflow: 'none', // 禁用省略号 |
| | | bleedMargin: 30, // 防止标签被截断 |
| | | rich: { |
| | | a: { |
| | | color: 'rgba(28, 31, 35, 0.80)', |
| | | fontSize: isSmallScreen ? '1rem' : '1.2rem', |
| | | }, |
| | | b: { |
| | | color: '#1C1F23', |
| | | fontSize: isSmallScreen ? '1rem' : '1.3rem', |
| | | fontWeight: 'bold', |
| | | }, |
| | | }, |
| | | }, |
| | | labelLine: { |
| | | show: true, |
| | | length: 3, // 调整引导线长度 |
| | | length2: 5, |
| | | lineStyle: { |
| | | cap: 'round', |
| | | }, |
| | | minTurnAngle: 45, // 防止小角度重叠 |
| | | }, |
| | | data: data.data, |
| | | }, |
| | | ], |
| | | } |
| | | |
| | | chart.value.setOption(echartsOption) |
| | | } |
| | | |
| | | // 折线/柱状图 |
| | | const lineCharts = bardata => { |
| | | const categories = bardata?.map(item => item.name) // x轴类别 |
| | | const flight_distance = [] |
| | | const flight_time = [] |
| | | const event_num = [] |
| | | // /遍历数据填充各系列 |
| | | bardata?.forEach(period => { |
| | | let hasDuration = false, |
| | | hasDistance = false, |
| | | hasResult = false |
| | | |
| | | period.data?.forEach(item => { |
| | | switch (item.name) { |
| | | case '飞行时长': |
| | | flight_time.push(parseFloat(item.value) || 0) |
| | | hasDuration = true |
| | | break |
| | | case '飞行里程': |
| | | // 转换为万公 (假设原始单位是米) |
| | | // flight_distance.push((parseFloat(item.value) / 10 || 0)); |
| | | flight_distance.push(parseFloat(item.value) || 0) |
| | | hasDistance = true |
| | | break |
| | | case '任务成果': |
| | | event_num.push(Number(item.value) || 0) |
| | | hasResult = true |
| | | break |
| | | } |
| | | }) |
| | | |
| | | // 处理可能缺失的数据项 |
| | | if (!hasDuration) flight_time.push(0) |
| | | if (!hasDistance) flight_distance.push(0) |
| | | if (!hasResult) event_num.push(0) |
| | | }) |
| | | var option = { |
| | | tooltip: { |
| | | trigger: 'item', |
| | | axisPointer: { |
| | | type: 'shadow', |
| | | }, |
| | | }, |
| | | grid: { |
| | | left: '2%', |
| | | right: '4%', |
| | | bottom: '14%', |
| | | top: '16%', |
| | | containLabel: true, |
| | | }, |
| | | legend: { |
| | | data: ['飞行时长', '飞行里程', '任务成果'], |
| | | left: 'center', |
| | | top: '5%', |
| | | color: '#383838', // 直接配置颜色 |
| | | itemWidth: 15, |
| | | itemHeight: 10, |
| | | itemGap: 25, |
| | | fontSize: '1.2rem' |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: categories, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | interval: 0, |
| | | color: '#383838', |
| | | }, |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: 'log', |
| | | name: '单位:万', |
| | | nameTextStyle: { |
| | | color: '#383838', |
| | | fontSize: '1.2rem' |
| | | }, |
| | | min: 1, |
| | | logBase: 3, |
| | | axisLine: { |
| | | show: false, |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | show: true, |
| | | lineStyle: { |
| | | type: 'dashed', |
| | | color: '#cdd5e2', |
| | | width: 1, |
| | | opacity: 0.5, |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | color: '#666666', // 直接配置颜色 |
| | | }, |
| | | }, |
| | | { |
| | | type: 'log', |
| | | name: '', |
| | | nameTextStyle: { |
| | | color: '#383838', |
| | | }, |
| | | min: 1, |
| | | logBase: 3, |
| | | axisLine: { |
| | | show: false, |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | show: true, |
| | | lineStyle: { |
| | | type: 'dashed', |
| | | color: '#cdd5e2', |
| | | width: 1, |
| | | opacity: 0.5, |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | color: '#383838', // 直接配置颜色 |
| | | }, |
| | | }, |
| | | ], |
| | | series: [ |
| | | { |
| | | name: '飞行时长', |
| | | type: 'bar', |
| | | barWidth: '8px', |
| | | itemStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: '#29acff' }, |
| | | { offset: 1, color: '#4bdfff' }, |
| | | ]), |
| | | borderRadius: 6, |
| | | }, |
| | | data: flight_time, |
| | | }, |
| | | { |
| | | name: '飞行里程', |
| | | type: 'bar', |
| | | barWidth: '8px', |
| | | itemStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: '#01c871' }, |
| | | { offset: 1, color: '#55f49c' }, |
| | | ]), |
| | | borderRadius: 6, |
| | | }, |
| | | data: flight_distance, |
| | | }, |
| | | |
| | | { |
| | | name: '任务成果', |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | smooth: true, |
| | | // itemStyle: { |
| | | // color: '#ffa43a', // 默认状态颜色 |
| | | // borderColor: 'rgba(255, 234, 0, 0.5)', |
| | | // borderWidth: 5, |
| | | // }, |
| | | // emphasis: { |
| | | // // 悬停状态 |
| | | // itemStyle: { |
| | | // color: '#ff8c00', // 悬停时颜色变深 |
| | | // borderWidth: 6, |
| | | // }, |
| | | // }, |
| | | lineStyle: { |
| | | color: 'rgba(52, 146, 242, 1)', |
| | | }, |
| | | data: event_num, |
| | | }, |
| | | ], |
| | | } |
| | | lineChart.value.setOption(option) |
| | | } |
| | | onMounted(() => { |
| | | getJobEventTotal().then(res => { |
| | | eventTotal.value = res?.data?.data || 0 |
| | | }) |
| | | |
| | | |
| | | getTypeData() |
| | | lineCharts() |
| | | getFlyList() |
| | | getFlyTimeList() |
| | | }) |
| | | </script> |
| | | |
| | |
| | | height: 100%; |
| | | |
| | | .comprehensiveCon { |
| | | background: #ffffff !important; |
| | | // background: #ffffff !important; |
| | | height: pxToVh(776); |
| | | border-radius: 8px 8px 8px 8px; |
| | | |
| | | .comprehensive { |
| | | |
| | | padding: 14px 14px 0 21px; |
| | | // padding: 14px 14px 0 21px; |
| | | |
| | | .title { |
| | | display: flex; |
| | |
| | | // 工、单 |
| | | .center { |
| | | display: flex; |
| | | |
| | | .centerLeft { |
| | | width: 60%; |
| | | border-right: 1px solid #dfdfdf; |
| | | width: 50%; |
| | | |
| | | .workOrder { |
| | | margin-top: 11px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | border-bottom: 1px solid #dfdfdf; |
| | | |
| | | .card-group { |
| | | width: 40%; |
| | | |
| | | .card-title { |
| | | display: flex; |
| | | margin-bottom: 10px; |
| | | align-items: center; |
| | | |
| | | img { |
| | | width: 36px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | |
| | | .cardtotal { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 9px; |
| | | |
| | | p { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .total-number { |
| | | font-family: 'Source Han Sans CN'; |
| | | |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | |
| | | .status-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | row-gap: 14px; |
| | | gap: 10px; |
| | | padding-bottom: 5px; |
| | | |
| | | .status-item { |
| | | display: flex; |
| | | text-align: center; |
| | | justify-content: space-between; |
| | | height: 97px; |
| | | // height: pxToVh(107); |
| | | max-width: 158px; |
| | | background: #f6f8fe; |
| | | border-radius: 8px 8px 8px 8px; |
| | | |
| | | img { |
| | | width: 26px; |
| | | height: 26px; |
| | | padding: 9px 10px 9px 2px; |
| | | } |
| | | |
| | | .statusCon { |
| | | display: flex; |
| | | flex-direction: column; |
| | | // align-items: center; |
| | | padding: 5px 4px 9px 10px; |
| | | text-align: left; |
| | | |
| | | .status-label { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | text-align: left; |
| | | color: #383838; |
| | | } |
| | | |
| | | .ratio { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #363636; |
| | | white-space: nowrap; |
| | | text-align: left; |
| | | } |
| | | |
| | | .status-value { |
| | | font-family: 'Source Han Sans CN'; |
| | | font-weight: bold; |
| | | font-size: 30px; |
| | | color: #363636; |
| | | // margin: 5px 0; |
| | | // font-style: italic; |
| | | display: inline-block; |
| | | transform: skewX(-5deg); |
| | | |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .charts { |
| | | margin-top: 30px; |
| | | width: 100%; |
| | | height: auto; |
| | | |
| | | .chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 飞行统计 |
| | | .flyOrder { |
| | | margin-top: 5px; |
| | | |
| | | .fytitle { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | img { |
| | | width: 39px; |
| | | height: 38px; |
| | | } |
| | | |
| | | span { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | } |
| | | |
| | | .flycenter { |
| | | margin-top: 13px; |
| | | |
| | | .centerBox { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | |
| | | .centerItem { |
| | | padding: 7px 7px 0 15px; |
| | | // width: 196px; |
| | | flex: 1; |
| | | // height: 80px; |
| | | height: pxToVh(80); |
| | | background: #f6f8fe; |
| | | box-shadow: 0px 5px 4px 0px #ebf1ff; |
| | | border-radius: 8px 8px 8px 8px; |
| | | border: 1px solid #ffffff; |
| | | margin-right: 16px; |
| | | |
| | | img { |
| | | width: 15px; |
| | | height: 15px; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .flyimg { |
| | | display: flex; |
| | | align-items: center; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | .flydata { |
| | | margin-top: 10px; |
| | | text-align: right; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | |
| | | span { |
| | | font-weight: bold; |
| | | font-size: 24px; |
| | | color: #343434; |
| | | margin-right: 6px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .lineChart { |
| | | height: 209px; |
| | | width: 100%; |
| | | |
| | | .lineBox { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | .centerRight { |
| | | width: 40%; |
| | | width: 50%; |
| | | } |
| | | } |
| | | } |