Merge branch 'master' of http://139.196.74.78:10010/r/drone/drone-web-manage
7 files modified
35 files added
| | |
| | | "axios": "^0.21.1", |
| | | "crypto-js": "^4.1.1", |
| | | "dayjs": "^1.11.13", |
| | | "echarts": "^5.6.0", |
| | | "element-plus": "^2.9.7", |
| | | "js-base64": "^3.7.4", |
| | | "js-cookie": "^3.0.0", |
| | |
| | | "vite-plugin-compression": "^0.5.1", |
| | | "vite-plugin-vue-setup-extend": "^0.4.0" |
| | | } |
| | | } |
| | | } |
| | |
| | | dayjs: |
| | | specifier: ^1.11.13 |
| | | version: 1.11.13 |
| | | echarts: |
| | | specifier: ^5.6.0 |
| | | version: 5.6.0 |
| | | element-plus: |
| | | specifier: ^2.9.7 |
| | | version: 2.9.7(vue@3.4.30) |
| | |
| | | resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} |
| | | dev: false |
| | | |
| | | /echarts@5.6.0: |
| | | resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} |
| | | dependencies: |
| | | tslib: 2.3.0 |
| | | zrender: 5.6.1 |
| | | dev: false |
| | | |
| | | /ee-first@1.1.1: |
| | | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} |
| | | dev: false |
| | |
| | | commander: 2.20.3 |
| | | dev: false |
| | | |
| | | /tslib@2.3.0: |
| | | resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} |
| | | dev: false |
| | | |
| | | /turf-jsts@1.2.3: |
| | | resolution: {integrity: sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA==} |
| | | dev: false |
| | |
| | | wmf: 1.0.2 |
| | | word: 0.3.0 |
| | | dev: false |
| | | |
| | | /zrender@5.6.1: |
| | | resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} |
| | | dependencies: |
| | | tslib: 2.3.0 |
| | | dev: false |
| New file |
| | |
| | | @font-face { |
| | | font-family: "Source Han Sans CN"; |
| | | src: url("SourceHanSansCN-Regular.otf") format("opentype"); |
| | | font-weight: 400; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Source Han Sans CN"; |
| | | src: url("SourceHanSansCN-Medium.otf") format("opentype"); |
| | | font-weight: 500; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Source Han Sans CN"; |
| | | src: url("SourceHanSansCN-Bold.otf") format("opentype") ; |
| | | font-weight: 700; |
| | | font-style: normal; |
| | | } |
| | | |
| | | |
| | | @font-face { |
| | | font-family: "Segoe UI"; |
| | | src: url("SourceHanSansCN-Regular.otf") format("opentype"); |
| | | font-weight: 400; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Segoe UI"; |
| | | src: url("SourceHanSansCN-Medium.otf") format("opentype"); |
| | | font-weight: 500; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Segoe UI"; |
| | | src: url("SourceHanSansCN-Bold.otf") format("opentype") ; |
| | | font-weight: 700; |
| | | font-style: normal; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /* 额外的标题字体 */ |
| | | @font-face { |
| | | font-family: "YouSheBiaoTiHei"; |
| | | src: url("YouSheBiaoTiHei.TTF") format("truetype"); |
| | | font-weight: normal; |
| | | font-style: normal; |
| | | } |
| New file |
| | |
| | | import request from '@/axios' |
| | | // 事件概况总数 |
| | | export const getJobEventTotal = () => { |
| | | return request({ |
| | | url: '/drone-device-core/jobEvent/total', |
| | | method: 'get', |
| | | }) |
| | | } |
| | | // 事件概况分类数 |
| | | export const getJobEventByStatus = data => { |
| | | return request({ |
| | | url: '/drone-device-core/jobEvent/eventByStatus', |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | // 行业任务统计 |
| | | export const industryJobNumPieChart = data => { |
| | | return request({ |
| | | url: '/drone-device-core/jobEvent/deviceEventList', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | // 事件任务统计 |
| | | export const jobEventBar = data => { |
| | | return request({ |
| | | url: '/drone-device-core/wayline/waylineJobInfo/jobEventBar', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | // 设备统计 |
| | | export const getStatics = (areaCode) => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/deviceStatistics', |
| | | method: 'post', |
| | | params: { |
| | | areaCode |
| | | }, |
| | | }); |
| | | }; |
| | | // 日历 |
| | | export const getCalen = data => { |
| | | return request({ |
| | | url: '/drone-device-core/wayline/waylineJobInfo/jobEventBar', |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | |
| | | }, |
| | | fistPage: { |
| | | name: '事件工单', |
| | | // path: '/wel/index', |
| | | path: '/tickets/ticket', |
| | | path: '/wel/index', |
| | | // path: '/tickets/ticket', |
| | | |
| | | }, |
| | | //配置菜单的属性 |
| New file |
| | |
| | | import * as echarts from 'echarts' |
| | | |
| | | |
| | | /** |
| | | * ECharts自适应容器大小Hook |
| | | * |
| | | * @param {Object} domRef - DOM元素引用对象,需包含value属性指向要挂载的DOM节点 |
| | | * @returns {Object} chart - 包含value属性的对象,value保存ECharts实例引用 |
| | | */ |
| | | const useEchartsResize = (domRef) => { |
| | | // 图表实例容器,用对象包装便于保持引用地址 |
| | | const chart = {value: null} |
| | | |
| | | // 窗口大小变化处理函数 |
| | | const resizeChart = () => { |
| | | chart.value?.resize() |
| | | } |
| | | |
| | | // 组件挂载后初始化逻辑 |
| | | onMounted(() => { |
| | | if (!domRef.value) return |
| | | // 初始化ECharts实例并注册resize监听 |
| | | chart.value = echarts.init(domRef.value) |
| | | window.addEventListener('resize', resizeChart) |
| | | }) |
| | | |
| | | // 组件卸载前清理逻辑 |
| | | onBeforeUnmount(() => { |
| | | // 移除监听并清理图表实例 |
| | | window.removeEventListener('resize', resizeChart) |
| | | chart.value?.dispose() |
| | | chart.value = null |
| | | }) |
| | | |
| | | return {chart} |
| | | } |
| | | |
| | | export default useEchartsResize |
| | |
| | | isAuth: false, |
| | | }, |
| | | }, |
| | | // { |
| | | // path: '/', |
| | | // name: '主页', |
| | | // // redirect: '/wel', |
| | | // redirect:'/tickets/ticket', |
| | | // }, |
| | | { |
| | | path: '/', |
| | | name: '主页', |
| | | redirect: '/tickets/ticket', |
| | | }, |
| | | path: '/', |
| | | name: '主页', |
| | | redirect: '/wel', |
| | | |
| | | }, |
| | | // { |
| | | // path: '/', |
| | | // name: '主页', |
| | | // redirect: '/tickets/ticket', |
| | | // }, |
| | | ]; |
| | |
| | | ], |
| | | }, |
| | | // 事件工单 |
| | | { |
| | | path: '/tickets', |
| | | component: () => |
| | | Store.getters.isMacOs ? import('@/mac/index.vue') : import('@/page/index/index.vue'), |
| | | redirect: '/tickets/ticket', |
| | | children: [ |
| | | { |
| | | path: 'ticket', |
| | | name: '事件工单', |
| | | meta: { |
| | | i18n: 'dashboard', |
| | | }, |
| | | component: () => import(/* webpackChunkName: "views" */ '@/views/tickets/ticket.vue'), |
| | | }, |
| | | ], |
| | | }, |
| | | // { |
| | | // path: '/tickets', |
| | | // component: () => |
| | | // Store.getters.isMacOs ? import('@/mac/index.vue') : import('@/page/index/index.vue'), |
| | | // redirect: '/tickets/ticket', |
| | | // children: [ |
| | | // { |
| | | // path: 'ticket', |
| | | // name: '事件工单', |
| | | // meta: { |
| | | // i18n: 'dashboard', |
| | | // }, |
| | | // component: () => import(/* webpackChunkName: "views" */ '@/views/tickets/ticket.vue'), |
| | | // }, |
| | | // ], |
| | | // }, |
| | | |
| | | { |
| | | path: '/test', |
| | |
| | | flex: 1; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | background: #f0f2f5; |
| | | // background: #f0f2f5; |
| | | background: linear-gradient( 180deg, #EEF3FE 0%, #F8F9FB 100%); |
| | | } |
| | | |
| | | #avue-view { |
| New file |
| | |
| | | <template> |
| | | <div class="bocklogBox"> |
| | | <div class="block"> |
| | | <div class="title">待办事项</div> |
| | | |
| | | <div class="todo-items"> |
| | | <div |
| | | v-for="(item, index) in todos" |
| | | :key="index" |
| | | class="todo-item" |
| | | :class="`status-${item.status}`" |
| | | > |
| | | <div class="status-indicator"></div> |
| | | |
| | | <div class="content-wrapper"> |
| | | <div class="main-content"> |
| | | <span class="status-tag">{{ statusMap[item.status] }}</span> |
| | | <span class="todo-text">{{ item.title }}</span> |
| | | </div> |
| | | |
| | | <div class="action-area"> |
| | | <img :src="st7" alt=""> |
| | | <span class="todo-date">{{ item.date }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import st7 from '@/assets/images/workbench/st7.png' |
| | | // 状态显示映射 |
| | | const statusMap = { |
| | | pending: '待审核', |
| | | processing: '处理中', |
| | | todo: '待处理', |
| | | }; |
| | | |
| | | // 待办事项数据 |
| | | const todos = ref([ |
| | | { |
| | | status: 'pending', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'todo', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'processing', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'pending', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'todo', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | ]); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .bocklogBox { |
| | | width: 100%; |
| | | height: 306px; |
| | | background: rgba(255, 255, 255, 0.41); |
| | | box-shadow: 0px 3px 4px -1px rgba(125, 125, 125, 0.25); |
| | | border-radius: 8px 8px 8px 8px; |
| | | border: 2px solid #ffffff; |
| | | .block { |
| | | margin: 11px 21px 13px 22px; |
| | | .title { |
| | | font-weight: bold; |
| | | font-size: 16px; |
| | | color: #363636; |
| | | } |
| | | |
| | | .todo-items { |
| | | display: grid; |
| | | gap: 0.8rem; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .todo-item { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | background: #FFFFFF; |
| | | border-radius: 8px; |
| | | box-shadow: 0px 2px 4px 0px rgba(173, 173, 173, 0.18); |
| | | transition: all 0.2s ease; |
| | | border-left: 4px solid transparent; |
| | | height: 42px; |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .status-indicator { |
| | | margin-left: 18px; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .content-wrapper { |
| | | flex: 1; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | gap: 1rem; |
| | | } |
| | | |
| | | .main-content { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 0.8rem; |
| | | |
| | | .status-tag { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | |
| | | padding: 0.3rem 0.5rem; |
| | | border-radius: 12px; |
| | | } |
| | | |
| | | .todo-text { |
| | | font-size: 14px; |
| | | color: #343434; |
| | | } |
| | | } |
| | | |
| | | .action-area { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-right: 42px; |
| | | |
| | | .todo-date { |
| | | color: #666; |
| | | font-size: 0.9rem; |
| | | } |
| | | |
| | | |
| | | } |
| | | } |
| | | |
| | | // 状态颜色方案 |
| | | .status-pending { |
| | | border-left-color: #FF6560; |
| | | color: #FF6560; |
| | | .status-indicator { |
| | | background: #FF6560; |
| | | } |
| | | } |
| | | |
| | | .status-todo { |
| | | border-left-color: #5D77FB; |
| | | color: #5D77FB; |
| | | .status-indicator { |
| | | background: #5D77FB; |
| | | } |
| | | } |
| | | |
| | | .status-processing { |
| | | border-left-color: #FF8B26; |
| | | color: #FF8B26; |
| | | |
| | | .status-indicator { |
| | | background: #FF8B26; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // @media (max-width: 768px) { |
| | | // .todo-list-container { |
| | | // padding: 1rem; |
| | | |
| | | // .content-wrapper { |
| | | // flex-direction: column; |
| | | // align-items: flex-start !important; |
| | | // gap: 0.5rem !important; |
| | | // } |
| | | |
| | | // .action-area { |
| | | // width: 100%; |
| | | // justify-content: space-between; |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | // @media (max-width: 480px) { |
| | | // .main-content { |
| | | // flex-direction: column; |
| | | // align-items: flex-start !important; |
| | | // } |
| | | |
| | | // .status-tag { |
| | | // margin-bottom: 0.3rem; |
| | | // } |
| | | // } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="calenBox"> |
| | | <el-calendar ref="calendar" v-model="leftValue" @change="handleMonthChange(date)"> |
| | | <template #date-cell="{ data }"> |
| | | <div> |
| | | <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" |
| | | > |
| | | <span></span> |
| | | {{ event.content }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-calendar> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import dayjs from 'dayjs'; |
| | | import { jobEventBar, getCalen } from '@/api/home/index'; |
| | | // 日历事件数据 |
| | | // 格式: 'YYYY-MM-DD': [...events] |
| | | const events = ref({ |
| | | '2025-05-01': [ |
| | | { type: 'work-order', content: '工单26' }, |
| | | { type: 'task', content: '任务26' }, |
| | | ], |
| | | '2025-05-04': [ |
| | | { type: 'task', content: '任务26' }, |
| | | { type: 'work-order', content: '工单26' }, |
| | | ], |
| | | }); |
| | | const params = ref({ |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | |
| | | 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()); |
| | | watch( |
| | | () => leftValue.value, |
| | | (newV, oldV) => { |
| | | if (newV && dayjs(newV).isSame(dayjs(), 'day')) { |
| | | console.log('点击了今天'); |
| | | } |
| | | |
| | | if (newV && oldV) { |
| | | const newDate = dayjs(newV); |
| | | const oldDate = dayjs(oldV); |
| | | |
| | | if (newDate.isBefore(oldDate, 'month')) { |
| | | console.log('点击了上个月'); |
| | | } else if (newDate.isAfter(oldDate, 'month')) { |
| | | console.log('点击了下个月'); |
| | | } |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ); |
| | | const handleMonthChange = date => { |
| | | console.log('date', date); |
| | | |
| | | // 获取当前视图月份的第一天 |
| | | const firstDay = new Date(date.getFullYear(), date.getMonth(), 1); |
| | | |
| | | // 获取当前视图月份的最后一天 |
| | | const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0); |
| | | |
| | | // 格式化为YYYY-MM-DD格式(可选) |
| | | const formatDate = d => d.toISOString().slice(0, 10); |
| | | |
| | | console.log('首日:', formatDate(firstDay)); |
| | | console.log('末日:', formatDate(lastDay)); |
| | | }; |
| | | // 获取日期数字 |
| | | const getDate = date => { |
| | | return date.getDate(); |
| | | }; |
| | | // 获取对应日期的事件 |
| | | const getEvents = dateString => { |
| | | // console.log('000',dateString); |
| | | |
| | | return events.value[dateString] || []; |
| | | }; |
| | | const getJobEventBar = () => { |
| | | const monthRange = getCurrentMonthRange(); |
| | | params.value = monthRange; |
| | | console.log(monthRange); |
| | | getCalen(params.value).then(res => { |
| | | if (res.data.code !== 0) return; |
| | | // events.value = res.data.data |
| | | console.log('日历', res.data.data); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getJobEventBar(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .calenBox { |
| | | margin-top: 10px; |
| | | height: 546px; |
| | | overflow: hidden; |
| | | .event-item { |
| | | font-size: 12px; |
| | | padding: 2px; |
| | | margin: 2px 0; |
| | | border-radius: 3px; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | |
| | | &.work-order { |
| | | // background: #ecf5ff; |
| | | color: #409eff; |
| | | span { |
| | | display: inline-block; |
| | | width: 7px; |
| | | height: 7px; |
| | | background: #1c5cff; |
| | | border-radius: 50%; |
| | | margin-right: 2px; |
| | | } |
| | | } |
| | | |
| | | &.task { |
| | | // background: #f0f9eb; |
| | | color: #67c23a; |
| | | span { |
| | | display: inline-block; |
| | | width: 7px; |
| | | height: 7px; |
| | | background: #029d36; |
| | | border-radius: 50%; |
| | | margin-right: 2px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="machineNest"> |
| | | <div class="nestTop"> |
| | | <div class="card-title"> |
| | | <img :src="jc1" alt="" /> |
| | | <div class="cardtotal"> |
| | | <p>机巢事件数量排名</p> |
| | | <!-- <div class="total-number">111</div> |
| | | <span>个</span> --> |
| | | </div> |
| | | </div> |
| | | <!-- <div class="status-grid"> |
| | | <div v-for="(item, index) in jcOrder" :key="index" class="status-item"> |
| | | <img :src="jc2" alt="" /> |
| | | <div> |
| | | <div class="status-label">{{ item.name }}</div> |
| | | <div :style="{ color: '#387FC8' }" class="status-value"> |
| | | {{ item.value }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | </div> |
| | | <div class="nestCenter"> |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import * as echarts from 'echarts'; |
| | | import useEchartsResize from '@/hooks/useEchartsResize'; |
| | | import jc1 from '@/assets/images/workbench/jc1.png'; |
| | | import jc2 from '@/assets/images/workbench/jc2.png'; |
| | | import { industryJobNumPieChart } from '@/api/home/index'; |
| | | const echartsRef = ref(null); |
| | | let { chart: jcchart } = useEchartsResize(echartsRef); |
| | | const jcOrder = ref([]); |
| | | // 获取机巢事件数据 |
| | | const getIndustryJobNumPieChart = value => { |
| | | industryJobNumPieChart(value).then(res => { |
| | | console.log('ppp', res); |
| | | |
| | | const resList = res?.data?.data || []; |
| | | jcOrder.value = resList; |
| | | pieInit(resList); |
| | | }); |
| | | }; |
| | | const pieInit = resList => { |
| | | // 处理数据,过滤掉没有name的项 |
| | | const validData = resList.filter(item => item.name); |
| | | |
| | | // 准备图表数据 |
| | | const optionData = { |
| | | yAxisData: validData.map(item => item.name), |
| | | seriesData: validData.map(item => item.value), |
| | | }; |
| | | |
| | | const option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow', |
| | | }, |
| | | formatter: '{b}: {c}', |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: 'value', |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: '#E5E5E5', |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | color: '#35455aa6', |
| | | }, |
| | | boundaryGap: [0, 0.01], |
| | | }, |
| | | yAxis: { |
| | | type: 'category', |
| | | data: optionData.yAxisData, // 使用处理后的分类数据 |
| | | axisLabel: { |
| | | color: '#35455aa6', |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#D1D1D1', |
| | | }, |
| | | }, |
| | | axisTick: { |
| | | show: false, |
| | | }, |
| | | }, |
| | | series: [ |
| | | { |
| | | type: 'bar', |
| | | data: optionData.seriesData, // 使用处理后的数值数据 |
| | | itemStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ |
| | | { offset: 0, color: '#93BAFF' }, |
| | | { offset: 1, color: '#C5DEFF' }, |
| | | ]), |
| | | }, |
| | | barWidth: '30%', |
| | | label: { |
| | | show: true, |
| | | position: 'right', |
| | | formatter: '{c}', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | jcchart.value.setOption(option); |
| | | }; |
| | | onMounted(() => { |
| | | getIndustryJobNumPieChart({ date_enum: 'CURRENT_WEEK' }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .machineNest { |
| | | .nestTop { |
| | | .card-title { |
| | | display: flex; |
| | | margin-left: 57px; |
| | | margin-bottom: 15px; |
| | | 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: 'YouSheBiaoTiHei'; |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | .status-grid { |
| | | margin-left: 30px; |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | row-gap: 17px; |
| | | .status-item { |
| | | display: flex; |
| | | text-align: center; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: #f6f8fe; |
| | | margin-right: 10px; |
| | | padding: 10px; |
| | | img { |
| | | width: 40px; |
| | | height: 40px; |
| | | margin-right: 13px; |
| | | } |
| | | .status-label { |
| | | font-size: 14px; |
| | | color: #383838; |
| | | } |
| | | .status-value { |
| | | font-family: 'YouSheBiaoTiHei'; |
| | | font-weight: bold; |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .nestCenter { |
| | | width: 100%; |
| | | height: 600px; |
| | | .chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="statistics"> |
| | | <div class="title"> |
| | | <div class="name"> |
| | | <span> 设备统计</span> |
| | | <img src="/src/assets/images/workbench/st1.png" alt="" /> |
| | | </div> |
| | | <div class="arrow"> |
| | | <img src="/src/assets/images/workbench/st2.png" alt="" /> |
| | | </div> |
| | | </div> |
| | | <!-- <div class="grid-container"> |
| | | <div v-for="(item, index) in Object.keys(newtitleData)" :key="index" class="device-card"> |
| | | <div class="device-title"> |
| | | <img :src="test[item].img" :alt="newtitleData[item].name" /> |
| | | <div class="itemcenter"> |
| | | <div>{{ test[item]?.name || '--' }}</div> |
| | | <span class="shu">{{ newtitleData[item].total_num }}</span> |
| | | <span>个</span> |
| | | </div> |
| | | </div> |
| | | <div class="status-list"> |
| | | |
| | | <div |
| | | v-for="(status, statusIndex) in newtitleData[item].status_map" |
| | | :key="statusIndex" |
| | | class="status-item" |
| | | :class="getStatusStyle(test[item]?.name, statusIndex)" |
| | | :style="{ color: getStatusColor(test[item]?.name, statusIndex) }" |
| | | > |
| | | <span class="indicator"></span> |
| | | <span class="label">{{ getStatusLabel(test[item]?.name, statusIndex) }}</span> |
| | | <span class="count" |
| | | >{{ status }} {{ test[item]?.name === '无人机' ? '架' : '个' }}</span |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | <div class="grid-container1"> |
| | | <div v-for="(item, index) in Object.keys(newtitleData)" :key="index" class="device-card"> |
| | | <div class="device-title"> |
| | | <img :src="test[item].img" :alt="newtitleData[item].name" /> |
| | | <div class="itemcenter"> |
| | | <div>{{ test[item]?.name || '--' }}</div> |
| | | <span class="shu">{{ newtitleData[item].total_num }}</span> |
| | | <span>个</span> |
| | | </div> |
| | | </div> |
| | | <div class="status-list"> |
| | | <!-- :class="`status-${statusIndex}`" --> |
| | | <div |
| | | v-for="(status, statusIndex) in newtitleData[item].status_map" |
| | | :key="statusIndex" |
| | | class="status-item" |
| | | :class="getStatusStyle(test[item]?.name, statusIndex)" |
| | | :style="{ color: getStatusColor(test[item]?.name, statusIndex) }" |
| | | > |
| | | <span |
| | | class="indicator" |
| | | :style="{ |
| | | backgroundColor: getStatusBackground(test[item]?.name, statusIndex), |
| | | color: getStatusColor(test[item]?.name, statusIndex), |
| | | }" |
| | | ></span> |
| | | <span class="label">{{ getStatusLabel(test[item]?.name, statusIndex) }}</span> |
| | | <span class="count" |
| | | >{{ status }} {{ test[item]?.name === '无人机' ? '架' : '个' }}</span |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { useStore } from 'vuex'; |
| | | import { computed } from 'vue'; |
| | | import titleImg1 from '@/assets/images/workbench/st3.png'; |
| | | import titleImg2 from '@/assets/images/workbench/st4.png'; |
| | | import titleImg3 from '@/assets/images/workbench/st5.png'; |
| | | import titleImg4 from '@/assets/images/workbench/st6.png'; |
| | | import titleImg5 from '@/assets/images/workbench/st8.png'; |
| | | import titleImg6 from '@/assets/images/workbench/st9.png'; |
| | | import { getStatics } from '@/api/home/index'; |
| | | const store = useStore(); |
| | | Object.key; |
| | | const userInfo = computed(() => store.getters.userInfo); |
| | | const newtitleData = ref({}); |
| | | const test = { |
| | | no_move_list: { |
| | | name: '机巢', |
| | | img: titleImg1, |
| | | }, |
| | | plant_list: { |
| | | name: '无人机', |
| | | img: titleImg2, |
| | | }, |
| | | flow_type_list: { |
| | | name: '机巢流量', |
| | | img: titleImg3, |
| | | }, |
| | | monitor_list: { |
| | | name: '监控设备', |
| | | img: titleImg4, |
| | | }, |
| | | move_list: { |
| | | name: '移动机巢', |
| | | img: titleImg5, |
| | | }, |
| | | |
| | | insure_list: { |
| | | name: '机巢保险', |
| | | img: titleImg6, |
| | | }, |
| | | }; |
| | | const statusSelect = { |
| | | 0: '空闲中', |
| | | 4: '作业中', |
| | | '-1': '离线中', |
| | | }; |
| | | // 流量 状态 0充足,1=流量到期,2=不足 |
| | | const flowStatus = { |
| | | 0: '流量无忧', |
| | | 1: '流量到期', |
| | | 2: '流量不足', |
| | | }; |
| | | // "监控状态 1=在线,0=离线" |
| | | const monitorStatus = { |
| | | 0: '离线中', |
| | | 1: '在线中', |
| | | }; |
| | | // 机巢保险 1=保险,0=未保险 |
| | | const insureStatus = { |
| | | 0: '临近到期', |
| | | 1: '正常期限', |
| | | }; |
| | | // 样式配置对象 |
| | | const statusStyles = { |
| | | 机巢保险: { |
| | | 0: { class: 'expired', color: '#7C8091', background: '#7C8091' }, |
| | | 1: { class: 'normal', color: '#1B94FF', background: '#1B94FF' }, |
| | | }, |
| | | |
| | | 机巢流量: { |
| | | 0: { class: 'offline', color: '#11CE3E', background: '#11CE3E' }, |
| | | 1: { class: 'flying', color: '#1B94FF', background: '#1B94FF' }, |
| | | 2: { class: 'flying', color: '#7C8091', background: '#7C8091' }, |
| | | }, |
| | | 监控设备: { |
| | | 0: { class: 'offline', color: '#bababa', background: '#bababa' }, |
| | | 1: { class: 'flying', color: '#1B94FF', background: '#1B94FF' }, |
| | | }, |
| | | // 默认样式配置 |
| | | default: { |
| | | 0: { class: 'warning', color: '#1b94ff', background: '#1b94ff' }, |
| | | 4: { class: 'success', color: '#11ce3e', background: '#11ce3e' }, |
| | | '-1': { class: 'success', color: '#bababa', background: '#bababa' }, |
| | | }, |
| | | }; |
| | | const getStatusStyle = (name, statusIndex) => { |
| | | // 获取样式配置,优先使用名称专属配置 |
| | | const styleConfig = statusStyles[name] || statusStyles.default; |
| | | return styleConfig[statusIndex]?.class || ''; |
| | | }; |
| | | |
| | | const getStatusColor = (name, statusIndex) => { |
| | | // 获取颜色配置,优先使用名称专属配置 |
| | | const styleConfig = statusStyles[name] || statusStyles.default; |
| | | return styleConfig[statusIndex]?.color || '#333'; |
| | | }; |
| | | // 新增背景颜色获取方法 |
| | | const getStatusBackground = (name, statusIndex) => { |
| | | const styleConfig = statusStyles[name] || statusStyles.default; |
| | | return ( |
| | | styleConfig[statusIndex]?.background || |
| | | styleConfig[statusIndex]?.color || // 降级使用字体颜色 |
| | | '#F0F0F0' |
| | | ); // 最终默认颜色 |
| | | }; |
| | | const getStatusLabel = (itemName, statusCode) => { |
| | | switch (itemName) { |
| | | case '机巢流量': |
| | | return flowStatus[statusCode] || statusSelect[statusCode]; |
| | | case '监控设备': |
| | | return monitorStatus[statusCode] || statusSelect[statusCode]; |
| | | case '机巢保险': |
| | | return insureStatus[statusCode] || statusSelect[statusCode]; |
| | | default: |
| | | return statusSelect[statusCode] || `未知状态(${statusCode})`; |
| | | } |
| | | }; |
| | | const getStaticsList = () => { |
| | | getStatics(userInfo.value.detail.areaCode).then(res => { |
| | | newtitleData.value = res.data.data; |
| | | console.log('设备', newtitleData.value); |
| | | }); |
| | | }; |
| | | |
| | | const unitMap = { |
| | | drone: '架', |
| | | nest: '个', |
| | | monitor: '个', |
| | | mobile: '个', |
| | | }; |
| | | const titleData = ref([ |
| | | { |
| | | img: titleImg1, |
| | | name: '机巢', |
| | | type: 'nest', |
| | | data: 52, |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | { |
| | | img: titleImg2, |
| | | name: '无人机', |
| | | data: 52, |
| | | type: 'drone', |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | { |
| | | img: titleImg3, |
| | | name: '监控设备', |
| | | type: 'monitor', |
| | | data: 52, |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | { |
| | | img: titleImg4, |
| | | name: '移动机巢', |
| | | type: 'mobile', |
| | | data: 52, |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | ]); |
| | | onMounted(() => { |
| | | getStaticsList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .statistics { |
| | | // height: 174px; |
| | | background: #ffffff; |
| | | border-radius: 8px 8px 8px 8px; |
| | | margin-bottom: 10px; |
| | | .title { |
| | | padding: 14px 14px 0 21px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | .name { |
| | | display: flex; |
| | | align-items: center; |
| | | span { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | .arrow { |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | .grid-container { |
| | | padding: 0 14px 0 21px; |
| | | display: grid; |
| | | grid-template-columns: repeat(6, 1fr); |
| | | .device-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .device-title { |
| | | display: flex; |
| | | align-items: center; |
| | | img { |
| | | width: 43px; |
| | | height: 44px; |
| | | } |
| | | } |
| | | .itemcenter { |
| | | margin-left: 14px; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #343434; |
| | | .shu { |
| | | font-weight: bold; |
| | | font-size: 36px; |
| | | color: #363636; |
| | | } |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #7c8091; |
| | | } |
| | | } |
| | | } |
| | | .status-list { |
| | | display: grid; |
| | | |
| | | .status-item { |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 6px; |
| | | background: #fff; |
| | | font-size: 12px; |
| | | gap: 10px; |
| | | margin-bottom: 5px; |
| | | .indicator { |
| | | width: 6px; |
| | | height: 6px; |
| | | border-radius: 50%; |
| | | } |
| | | .label { |
| | | flex: 1; |
| | | // font-size: clamp(12px, 1vw, 14px); |
| | | } |
| | | |
| | | .count { |
| | | // font-size: clamp(14px, 1.1vw, 16px); |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | &.status-4 .indicator { |
| | | background: #11ce3e; |
| | | } |
| | | &.status-4 { |
| | | color: #11ce3e; |
| | | } |
| | | &.status-0 .indicator { |
| | | background: #1b94ff; |
| | | } |
| | | &.status-0 { |
| | | color: #1b94ff; |
| | | } |
| | | |
| | | &.status--1 .indicator { |
| | | background: #bababa; |
| | | } |
| | | &.status--1 { |
| | | color: #bababa; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .grid-container1 { |
| | | padding: 0 14px 0 21px; |
| | | display: grid; |
| | | grid-template-columns: repeat(6, 1fr); |
| | | .device-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .device-title { |
| | | display: flex; |
| | | align-items: center; |
| | | img { |
| | | width: 43px; |
| | | height: 44px; |
| | | } |
| | | } |
| | | .itemcenter { |
| | | margin-left: 14px; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #343434; |
| | | .shu { |
| | | font-weight: bold; |
| | | font-size: 36px; |
| | | color: #363636; |
| | | } |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #7c8091; |
| | | } |
| | | } |
| | | } |
| | | .status-list { |
| | | display: grid; |
| | | |
| | | .status-item { |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 6px; |
| | | background: #fff; |
| | | font-size: 12px; |
| | | gap: 10px; |
| | | margin-bottom: 5px; |
| | | .indicator { |
| | | width: 6px; |
| | | height: 6px; |
| | | border-radius: 50%; |
| | | } |
| | | .label { |
| | | flex: 1; |
| | | // font-size: clamp(12px, 1vw, 14px); |
| | | } |
| | | |
| | | .count { |
| | | // font-size: clamp(14px, 1.1vw, 16px); |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | &.status-4 .indicator { |
| | | background: #11ce3e; |
| | | } |
| | | &.status-4 { |
| | | color: #11ce3e; |
| | | } |
| | | &.status-0 .indicator { |
| | | background: #1b94ff; |
| | | } |
| | | &.status-0 { |
| | | color: #1b94ff; |
| | | } |
| | | |
| | | &.status--1 .indicator { |
| | | background: #bababa; |
| | | } |
| | | &.status--1 { |
| | | color: #bababa; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div> |
| | | 个人工作台 |
| | | <div class="workbench"> |
| | | <div class="workleft"> |
| | | <!-- 设备统计 --> |
| | | <statistics></statistics> |
| | | <!-- 综合统计分析 --> |
| | | <div class="comprehensiveCon"> |
| | | <div class="comprehensive"> |
| | | <div class="title"> |
| | | <div class="name"> |
| | | <span> 综合统计分析</span> |
| | | <img 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"> |
| | | <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 class="total-number">{{ eventTotal }}</div> |
| | | <span>个</span> --> |
| | | </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 :style="{ color: item.color }" class="status-value"> |
| | | {{ item.value }}<span>个</span> |
| | | </div> |
| | | <div class="ratio"> |
| | | 占比 |
| | | <span :style="{ color: item.color }" |
| | | >{{ (item.rate / 100) * 100 }}%</span |
| | | > |
| | | </div> |
| | | </div> |
| | | <img :src="item.img" alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="charts"> |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </div> |
| | | <!-- 飞行统计 --> |
| | | <div class="flyOrder"> |
| | | <div class="fytitle"> |
| | | <img src="@/assets/images/workbench/fy1.png" alt="" /> |
| | | <span>飞行统计</span> |
| | | </div> |
| | | <div class="flycenter"> |
| | | <div class="centerBox"> |
| | | <div class="centerItem" v-for="(item, index) in 3" :key="index"> |
| | | <div><img src="@/assets/images/workbench/fy2.png" alt="" />飞行时长</div> |
| | | <div class="flydata"><span>5421.56</span>时</div> |
| | | </div> |
| | | </div> |
| | | <div class="lineChart"> |
| | | <div class="lineBox" ref="chartRef"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="centerRight"> |
| | | <flyratio></flyratio> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="workright"> |
| | | <Bocklog></Bocklog> |
| | | <CalenBox></CalenBox> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | <script setup> |
| | | import flyratio from './components/flyratio.vue'; |
| | | import Bocklog from './components/backlog.vue'; |
| | | import CalenBox from './components/calendarBox.vue'; |
| | | import * as echarts from 'echarts'; |
| | | import useEchartsResize from '@/hooks/useEchartsResize'; |
| | | import { mapGetters } from 'vuex'; |
| | | |
| | | export default { |
| | | name: 'wel', |
| | | data() { |
| | | return { |
| | | activeNames: ['1', '2', '3', '5'], |
| | | logActiveNames: ['37'], |
| | | }; |
| | | }, |
| | | computed: { |
| | | ...mapGetters(['userInfo']), |
| | | }, |
| | | methods: { |
| | | handleChange(val) { |
| | | window.console.log(val); |
| | | }, |
| | | }, |
| | | import { ref } from 'vue'; |
| | | import overviewImg1 from '@/assets/images/workbench/tc1.png'; |
| | | import overviewImg2 from '@/assets/images/workbench/tc2.png'; |
| | | import overviewImg3 from '@/assets/images/workbench/tc3.png'; |
| | | import overviewImg4 from '@/assets/images/workbench/tc4.png'; |
| | | import overviewImg5 from '@/assets/images/workbench/tc5.png'; |
| | | import overviewImg6 from '@/assets/images/workbench/tc6.png'; |
| | | import flyImg1 from '@/assets/images/workbench/fy2.png'; |
| | | import statistics from './components/statistics.vue'; |
| | | import { getJobEventByStatus, getJobEventTotal } from '@/api/home/index'; |
| | | // const value = ref(new Date()); |
| | | let checked = ref('CURRENT_WEEK'); |
| | | let timeListStr = ['本周', '本月', '本年']; |
| | | let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR']; |
| | | let timeClick = (item, index) => { |
| | | checked.value = item; |
| | | params.value.date_enum = item; |
| | | console.log('日期选择', params.value.date_enum); |
| | | getTypeData(); |
| | | }; |
| | | const eventTypeList = ref([ |
| | | { name: '待审核', value: 0, img: overviewImg2, color: '#FF6560', status: '2', rate: 0 }, |
| | | { name: '待处理', value: 0, img: overviewImg3, color: '#5D77FB', status: '0', rate: 0 }, |
| | | { name: '处理中', value: 0, img: overviewImg4, color: '#FF8B26', status: '3', rate: 0 }, |
| | | { name: '已完成', value: 0, img: overviewImg5, color: '#0291A1', status: '4', rate: 0 }, |
| | | ]); |
| | | const flyTypeList = ref([ |
| | | { name: '飞行时长', value: 0, img: overviewImg2 }, |
| | | { name: '飞行里程', value: 0, img: overviewImg3 }, |
| | | { name: '任务成功', value: 0, img: overviewImg4 }, |
| | | ]); |
| | | const params = ref({ |
| | | date_enum: 'CURRENT_WEEK', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | const eventTotal = ref(0); |
| | | const data = ref([]); |
| | | // 工单统计 |
| | | const getTypeData = () => { |
| | | getJobEventByStatus(params.value).then(res => { |
| | | const resList = res?.data?.data || []; |
| | | console.log('工单统计', 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 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 echartsOption = { |
| | | color: ['#FF6560', '#5D77FB', '#FF8B26', '#0291A1'], |
| | | tooltip: { |
| | | trigger: 'item', |
| | | padding: 0, |
| | | borderWidth: 0, |
| | | formatter: params => { |
| | | return `<div style="background-color: rgba($color: #FFFFFF, $alpha: 0.95); |
| | | box-shadow: 0 4px 9px 0 rgba($color: #000000, $alpha: 0.1); |
| | | padding:0 12px; |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 4px; |
| | | font-size: 12px;" class="tooltip-area"> |
| | | <p>${params.marker}${params.name}</p> |
| | | <h4 style="margin-left: 30px;">${params.data.rate || params.percent}%</h4> |
| | | </div>`; |
| | | }, |
| | | }, |
| | | legend: { |
| | | show: false, |
| | | }, |
| | | title: { |
| | | text: data.total.title, |
| | | textStyle: { |
| | | color: 'rgba(28, 31, 35, 0.80)', |
| | | fontSize: '12px', |
| | | fontWeight: 'normal', |
| | | }, |
| | | subtext: data.total.figure, |
| | | subtextStyle: { |
| | | color: '#1C1F23', |
| | | fontSize: '20px', |
| | | fontWeight: '600', |
| | | }, |
| | | top: '40%', |
| | | left: 'center', |
| | | }, |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'pie', |
| | | radius: ['30%', '45%'], |
| | | avoidLabelOverlap: true, |
| | | // avoidLabelOverlap: false, |
| | | label: { |
| | | formatter: `{a|{b}}: {b|{d}%}`, |
| | | alignTo: 'labelLine', |
| | | rich: { |
| | | a: { |
| | | color: 'rgba(28, 31, 35, 0.80)', |
| | | fontSize: '12px', |
| | | }, |
| | | b: { |
| | | color: '#1C1F23', |
| | | fontSize: '14px', |
| | | fontWeight: '600', |
| | | }, |
| | | }, |
| | | }, |
| | | labelLine: { |
| | | show: true, |
| | | lineStyle: { |
| | | cap: 'round', |
| | | }, |
| | | }, |
| | | data: data.data, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | chart.value.setOption(echartsOption); |
| | | }; |
| | | |
| | | // 柱状图 |
| | | const lineCharts = () => { |
| | | 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%', |
| | | textStyle: { |
| | | color: '#666666', |
| | | }, |
| | | itemWidth: 15, |
| | | itemHeight: 10, |
| | | itemGap: 25, |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | |
| | | axisLabel: { |
| | | interval: 0, |
| | | textStyle: { |
| | | color: '#666666', |
| | | }, |
| | | }, |
| | | }, |
| | | yAxis: [ |
| | | // 只保留第一个y轴 |
| | | { |
| | | type: 'log', |
| | | name: '单位:万套', |
| | | nameTextStyle: { |
| | | color: '#666666', |
| | | }, |
| | | min: 1, |
| | | logBase: 3, |
| | | axisLine: { |
| | | show: false, |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | |
| | | splitLine: { |
| | | show: true, // 显示分割线 |
| | | lineStyle: { |
| | | type: 'dashed', // 设置为虚线 |
| | | color: '#cdd5e2', // 颜色与轴线一致 |
| | | width: 1, // 线宽 |
| | | opacity: 0.5, // 透明度 |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | textStyle: { |
| | | color: '#666666', |
| | | }, |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#cdd5e2', |
| | | }, |
| | | }, |
| | | }, |
| | | // 移除第二个y轴配置 |
| | | ], |
| | | series: [ |
| | | { |
| | | name: '飞行时长', |
| | | type: 'bar', |
| | | barWidth: '12px', |
| | | itemStyle: { |
| | | normal: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: '#29acff', |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: '#4bdfff', |
| | | }, |
| | | ]), |
| | | barBorderRadius: 6, |
| | | }, |
| | | }, |
| | | data: [11, 14, 133, 4, 10, 14, 116, 12, 12, 58, 15, 12], |
| | | }, |
| | | { |
| | | name: '飞行里程', |
| | | type: 'bar', |
| | | barWidth: '12px', |
| | | itemStyle: { |
| | | normal: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: '#01c871', |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: '#55f49c', |
| | | }, |
| | | ]), |
| | | barBorderRadius: 6, |
| | | }, |
| | | }, |
| | | data: [0, 1, 9, 0, 5, 3, 0, 2, 0, 0, 1, 0], |
| | | }, |
| | | { |
| | | name: '任务成果', |
| | | type: 'line', |
| | | yAxisIndex: 0, // 改为使用第一个y轴 |
| | | smooth: true, |
| | | // symbol: 'circle', |
| | | // symbolSize: 8, |
| | | // itemStyle: { |
| | | // normal: { |
| | | // color: '#ffa43a', |
| | | // borderColor: 'rgba(255, 234, 0, 0.5)', |
| | | // borderWidth: 5, |
| | | // }, |
| | | // }, |
| | | lineStyle: { |
| | | color: 'rgba(52, 146, 242, 1)', |
| | | }, |
| | | data: [1, 7, 6, 30, 20, 21.43, 11, 16.67, 22, 88, 6.67, 10], |
| | | }, |
| | | ], |
| | | }; |
| | | lineChart.value.setOption(option); |
| | | }; |
| | | onMounted(() => { |
| | | getJobEventTotal().then(res => { |
| | | eventTotal.value = res?.data?.data || 0; |
| | | }); |
| | | |
| | | getTypeData(); |
| | | lineCharts(); |
| | | }); |
| | | </script> |
| | | |
| | | <style> |
| | |
| | | font-size: 14px; |
| | | } |
| | | </style> |
| | | <style scoped lang="scss"> |
| | | // 开始 |
| | | .workbench { |
| | | padding: 0px 20px 0 20px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | .workleft { |
| | | width: 68%; |
| | | margin-right: 10px; |
| | | // 综合 |
| | | .comprehensiveCon { |
| | | // height: 736px; |
| | | background: #ffffff; |
| | | border-radius: 8px 8px 8px 8px; |
| | | .comprehensive { |
| | | padding: 14px 14px 0 21px; |
| | | .title { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | .name { |
| | | display: flex; |
| | | align-items: center; |
| | | span { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | .arrow { |
| | | cursor: pointer; |
| | | } |
| | | .time-card { |
| | | text-align: center; |
| | | height: 30px; |
| | | background: #ffffff; |
| | | border-radius: 4px 0px 0px 4px; |
| | | border: 1px solid #e5e5e5; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | display: flex; |
| | | width: 282px; |
| | | margin-left: 15px; |
| | | .card-item { |
| | | width: 94px; |
| | | height: 100%; |
| | | line-height: 32px; |
| | | cursor: pointer; |
| | | } |
| | | .card-item:first-child { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | .card-item:nth-child(2) { |
| | | border-right: 1px solid #e5e5e5; |
| | | } |
| | | .active { |
| | | color: #1441ff; |
| | | } |
| | | } |
| | | } |
| | | // 工、单 |
| | | .center { |
| | | display: flex; |
| | | .centerLeft { |
| | | width: 60%; |
| | | border-right: 1px solid #dfdfdf; |
| | | .workOrder { |
| | | margin-top: 21px; |
| | | display: flex; |
| | | 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: 'YouSheBiaoTiHei'; |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | .status-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | row-gap: 14px; |
| | | gap: 10px; |
| | | .status-item { |
| | | display: flex; |
| | | text-align: center; |
| | | height: 97px; |
| | | background: #f6f8fe; |
| | | border-radius: 8px 8px 8px 8px; |
| | | img { |
| | | width: 26px; |
| | | height: 26px; |
| | | padding: 9px 2px; |
| | | } |
| | | .statusCon { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | padding: 9px 4px 0 4px; |
| | | .status-label { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #383838; |
| | | } |
| | | .ratio { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #363636; |
| | | white-space: nowrap; |
| | | } |
| | | .status-value { |
| | | font-family: 'YouSheBiaoTiHei'; |
| | | font-weight: bold; |
| | | font-size: 24px; |
| | | margin: 5px 0; |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .charts { |
| | | margin-top: 30px; |
| | | width: 348px; |
| | | height: 242px; |
| | | .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; |
| | | .centerItem { |
| | | padding: 7px 7px 0 15px; |
| | | // width: 196px; |
| | | flex: 1; |
| | | height: 80px; |
| | | background: #f6f8fe; |
| | | box-shadow: 0px 5px 4px 0px #ebf1ff; |
| | | border-radius: 8px 8px 8px 8px; |
| | | border: 1px solid #ffffff; |
| | | margin-right: 16px; |
| | | .flydata { |
| | | margin-top: 15px; |
| | | 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%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .workright { |
| | | width: 32%; |
| | | } |
| | | </style> |