| src/components/basic-container/main.vue | ●●●●● patch | view | raw | blame | history | |
| src/components/wel-container/main.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/wel/components/backlog.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/wel/components/calendarBox.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/wel/index.vue | ●●●●● patch | view | raw | blame | history |
src/components/basic-container/main.vue
@@ -2,7 +2,7 @@ * @Author : yuan * @Date : 2025-06-14 15:19:16 * @LastEditors : yuan * @LastEditTime : 2025-06-25 16:56:30 * @LastEditTime : 2025-06-27 14:19:22 * @FilePath : \src\components\basic-container\main.vue * @Description : * Copyright 2025 OBKoro1, All Rights Reserved. @@ -45,21 +45,27 @@ <style lang="scss" scoped> .basic-container { padding: 10px; height: 0; flex: 1; padding: 10px; display: flex; flex-direction: column; // box-sizing: border-box; // height: 100%; .basic-container__card { height: 100% !important; height: 0; flex: 1; display: flex; flex-direction: column; overflow: hidden; .el-card__body { ::v-deep(.el-card__body) { height: 0 !important; flex: 1 !important; display: flex; flex-direction: column; height: 100% !important; overflow: hidden; box-sizing: border-box; src/components/wel-container/main.vue
@@ -1,6 +1,6 @@ <template> <div class="basic-container" :style="styleName" :class="{ 'basic-container--block': block }"> <el-card class="basic-container__card"> <div class="wel-container" :style="styleName" :class="{ 'wel-container--block': block }"> <el-card class="wel-container__card"> <slot></slot> </el-card> </div> @@ -8,7 +8,7 @@ <script> export default { name: 'basicContainer', name: 'welContainer', props: { radius: { type: [String, Number], @@ -23,27 +23,29 @@ }, }, computed: { styleName() { styleName () { return { borderRadius: `${this.radius}px`, background: this.background, }; } }, }, }; } </script> <style lang="scss" scoped> .basic-container { .wel-container { height: 0; flex: 1; padding-top: 0 !important; padding-top: 0 !important; // box-sizing: border-box; // height: 100%; .el-card { background-color: transparent !important; } .basic-container__card { .wel-container__card { height: 100% !important; overflow: hidden; @@ -53,8 +55,10 @@ height: 100% !important; overflow: hidden; box-sizing: border-box; padding: 10px !important; padding-top: 0 !important; & > div { &>div { height: 0; flex: 1; } @@ -64,7 +68,7 @@ &--block { height: 100%; .basic-container__card { .wel-container__card { height: 100%; } } src/views/wel/components/backlog.vue
@@ -4,33 +4,21 @@ <div class="titleBox"> <div class="title">待办事项</div> <div class="rightTab"> <div class="titleItem" :class="item.name === checked ? 'activeItem' : ''" v-for="(item, index) in titleList" :key="index" @click="titleClick(item, index)" > <div class="titleItem" :class="item.name === checked ? 'activeItem' : ''" v-for="(item, index) in titleList" :key="index" @click="titleClick(item, index)"> <div> {{ item.name }} <div class="dots" >{{ item.number >= 100 ? '99+' : item.number }}</div> <!-- <div class="dots">99+</div> --> <div class="dots">{{ item.number >= 100 ? '99+' : item.number }}</div> <!-- <div class="dots">99+</div> --> </div> </div> </div> </div> <div class="todo-items" v-if="todos.length > 0" v-loading="loading"> <div v-for="(item, index) in todos" :key="index" class="todo-item" :style="getStatusStyle(item.status)" @click="jumporder(item)" > <div class="status-indicator" ><img :src="getStatus(item.status)" alt=""></div> <div v-for="(item, index) in todos" :key="index" class="todo-item" :style="getStatusStyle(item.status)" @click="jumporder(item)"> <div class="status-indicator"><img :src="getStatus(item.status)" alt=""></div> <div class="content-wrapper"> <div class="main-content"> @@ -60,22 +48,22 @@ </template> <script setup> import st7 from '@/assets/images/workbench/st7.svg'; import db1 from '@/assets/images/workbench/db1.svg'; import db2 from '@/assets/images/workbench/db2.svg'; import db3 from '@/assets/images/workbench/db3.svg'; import db4 from '@/assets/images/workbench/db4.svg'; import db5 from '@/assets/images/workbench/db5.svg'; import { getOrderCountApi, addOrderRecordApi, getOrderOrEventApi } from '@/api/home/index'; import { onMounted } from 'vue'; import { useRouter } from 'vue-router'; import { useStore } from 'vuex'; const zfTotal = ref(0); const sjTotal = ref(0); const router = useRouter(); const store = useStore(); const permission = computed(() => store.getters.permission); const checked = ref('事件工单'); import st7 from '@/assets/images/workbench/st7.svg' import db1 from '@/assets/images/workbench/db1.svg' import db2 from '@/assets/images/workbench/db2.svg' import db3 from '@/assets/images/workbench/db3.svg' import db4 from '@/assets/images/workbench/db4.svg' import db5 from '@/assets/images/workbench/db5.svg' import { getOrderCountApi, addOrderRecordApi, getOrderOrEventApi } from '@/api/home/index' import { onMounted } from 'vue' import { useRouter } from 'vue-router' import { useStore } from 'vuex' const zfTotal = ref(0) const sjTotal = ref(0) const router = useRouter() const store = useStore() const permission = computed(() => store.getters.permission) const checked = ref('事件工单') const titleList = ref([ { label: 'event_order_count', @@ -87,37 +75,37 @@ name: '智飞工单', number: 0, }, ]); ]) const titleClick = (item, index) => { checked.value = item.name; getListMatter(); }; checked.value = item.name getListMatter() } const userInfo = computed(() => store.getters.userInfo); const todos = ref([]); const loading = ref(false); const userInfo = computed(() => store.getters.userInfo) const todos = ref([]) const loading = ref(false) // * 事件工单中2=待审核,0=待处理,3=处理中 // * 智飞工单中1: 待审核,2=已驳回,3=已通过 const statusMap = { 0: '待处理', 2: '待审核', 3: '处理中', }; } const statusMapColor = { 0: { color: '#FF7411', backgroundImage: db3, borderLeftColor: '#FF7411' }, 2: { color: '#FF472F', backgroundImage: db1, borderLeftColor: '#FF472F' }, 3: { color: '#FFC300', backgroundImage: db4, borderLeftColor: '#FFC300' }, }; } const zfstatusMap = { 1: '待审核', 2: '已驳回', 3: '已通过', }; } const zfstatusMapColor = { 1: { color: '#FF472F', backgroundImage: db2, borderLeftColor: '#FF472F' }, 2: { color: '#4200DD', backgroundImage: db5, borderLeftColor: '#4200DD' }, 3: { color: '#FF472F', backgroundImage: db2, borderLeftColor: '#FF472F' }, }; } // 根据状态获取图标 // const getStatusBackground = statusIndex => { // if (checked.value === '智飞工单') { @@ -130,115 +118,115 @@ // }; const getStatus = statusIndex => { if (checked.value === '智飞工单') { const style = zfstatusMapColor[statusIndex]; return style ? style.backgroundImage : ''; const style = zfstatusMapColor[statusIndex] return style ? style.backgroundImage : '' } else { const style = statusMapColor[statusIndex]; return style ? style.backgroundImage : ''; const style = statusMapColor[statusIndex] return style ? style.backgroundImage : '' } }; } const getStatusStyle = statusIndex => { if (checked.value === '智飞工单') { const style = zfstatusMapColor[statusIndex] || { color: '#999', borderLeftColor: '#999', }; } return { color: style.color, borderLeft: `3px solid ${style.borderLeftColor}`, }; } } else { const style = statusMapColor[statusIndex] || { color: '#999', borderLeftColor: '#999', }; } return { color: style.color, borderLeft: `3px solid ${style.borderLeftColor}`, }; } } }; } // 获取状态对应的文字颜色 const getStatusColor = statusIndex => { if (checked.value === '智飞工单') { return zfstatusMapColor[statusIndex]?.color || '#999'; return zfstatusMapColor[statusIndex]?.color || '#999' } else { return statusMapColor[statusIndex]?.color || '#999'; return statusMapColor[statusIndex]?.color || '#999' } }; } // 跳转到对应详情页 const jumporder = val => { if (checked.value === '智飞工单') { const id = val.id; getaddOrderRecord(id); const id = val.id getaddOrderRecord(id) router.push({ path: `/tickets/orderLog`, query: { id, }, }); }) } else { getaddOrderRecord(val.id); const orderNumber = val.event_num; getaddOrderRecord(val.id) const orderNumber = val.event_num router.push({ path: `/tickets/ticket`, query: { orderNumber, }, }); }) } }; } // 标记事件工单或智飞工单已读 const getaddOrderRecord = async id => { const ticketId = id; const ticketId = id if (checked.value === '事件工单') { loading.value = true; const type = 1; const res = await addOrderRecordApi(type, ticketId); 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 addOrderRecordApi(type, ticketId); loading.value = true const type = 2 const res = await addOrderRecordApi(type, ticketId) } }; } // 获取事件工单和智飞工单的数量 const getOrderCount = async () => { const res = await getOrderCountApi(userInfo.value.detail.areaCode); const data = res.data.data; const res = await getOrderCountApi(userInfo.value.detail.areaCode) const data = res.data.data titleList.value.forEach(item => { if (data[item.label] !== undefined) { item.number = data[item.label]; item.number = data[item.label] } }); }; }) } // 获取事件工单或者智飞工单的信息 const getListMatter = async () => { if (checked.value === '事件工单') { loading.value = true; const type = 1; const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode); todos.value = res.data.data || []; loading.value = false; loading.value = true const type = 1 const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode) todos.value = res.data.data || [] loading.value = false } 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; loading.value = true const type = 2 const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode) todos.value = res.data.data || [] loading.value = false } }; } onMounted(() => { getListMatter(); getOrderCount(); }); getListMatter() getOrderCount() }) </script> <style> .el-empty__image { width: 100px !important; } width: 100px !important; } </style> <style lang="scss" scoped> .bocklogBox { width: 100%; // height: 306px; @@ -247,22 +235,28 @@ box-shadow: 0px 3px 4px -1px rgba(125, 125, 125, 0.25); border-radius: 8px 8px 8px 8px; border: 2px solid #ffffff; box-sizing: border-box; .block { margin: 11px 21px 13px 22px; .titleBox { display: flex; justify-content: space-between; } .title { font-family: 'Source Han Sans CN'; font-weight: bold; font-size: 16px; color: #363636; } .rightTab { display: flex; text-align: center; padding-right: 10px; .titleItem { position: relative; font-weight: 400; @@ -271,6 +265,7 @@ margin-left: 18px; cursor: pointer; } .activeItem { border-radius: 0px 0px 0px 0px; font-family: Source Han Sans CN, Source Han Sans CN; @@ -279,12 +274,13 @@ color: #1c5cff; border-bottom: 2px solid #1c5cff; } .dots { width: 17px; // height: 17px; height: pxToVh(17); border-radius: 50%; background: #366fff; background: #366fff; color: #fff; font-size: 9px; position: absolute; @@ -293,7 +289,8 @@ padding: 1px; line-height: 17px; } .dots1 { .dots1 { width: 17px; // height: 17px; height: pxToVh(17); @@ -308,6 +305,7 @@ line-height: 17px; } } .todo-items { margin-top: 10px; // height: 250px; @@ -327,6 +325,7 @@ border-left: 4px solid transparent; // height: 42px; height: pxToVh(42); &:hover { transform: translateY(-2px); box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05); @@ -336,9 +335,10 @@ margin-left: 3px; width: 1.6rem; height: 1.6rem; img{ width: 100%; height: 100%; img { width: 100%; height: 100%; } } @@ -368,6 +368,7 @@ font-size: 14px; color: #343434; } .unread { width: 5px; height: 5px; @@ -375,6 +376,7 @@ border-radius: 50%; margin: 0 4px; } .reads { width: 5px; height: 5px; @@ -388,10 +390,12 @@ display: flex; align-items: center; margin-right: 42px; img { width: 15px; height: 15px; } .todo-date { font-weight: 400; font-size: 14px; @@ -401,6 +405,7 @@ } } } .custom-text { font-size: 14px; color: #7c8091; src/views/wel/components/calendarBox.vue
@@ -5,13 +5,8 @@ <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)" > <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> @@ -23,65 +18,65 @@ </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 events.value = res.data.data }) } const jumpcalendar = (event, day) => { if (event.name === '工单') { router.push({ @@ -89,28 +84,49 @@ query: { day: day, }, }); }) } else { router.push({ path: '/job/jobstatistics', query: { day: day, }, }); }) } }; } onMounted(() => { getJobEventBar(); }); getJobEventBar() }) </script> <style lang="scss"> .calenBox { // height: 630px; height: pxToVh(610); .el-calendar { // height: 630px; height: pxToVh(660); // 隐藏按钮组中间按钮 .el-button-group>.el-button:not(:first-child):not(:last-child) { display: none; } } </style> <style lang="scss" scoped> .calenBox { margin-top: 10px; // height: 630px; height: pxToVh(622); border-radius: 10px; overflow: hidden; background-color: #fff; ::v-deep(.el-calendar) { // height: 80%; // 日历填充容器 // 标题样式 &__title { .el-calendar__title { font-weight: bold; font-size: 14px; color: #363636; @@ -119,19 +135,21 @@ // 日历主体 // &__body { // height: 98%; // 关键:继承父高度 // .el-calendar-table { // height: 90% !important; // 百分比生效 // } // } .el-calendar-table .el-calendar-day{ height: 78px !important;} .el-calendar-table .el-calendar-day { height: 78px !important; } // 选中日期样式 .el-calendar-table td.is-selected { background-color: #f0f7ff; border: 2px solid #409eff; border-radius: 4px; .date-number { font-weight: bold; color: #409eff; @@ -139,30 +157,18 @@ } } // 隐藏按钮组中间按钮 .el-button-group > .el-button:not(:first-child):not(:last-child) { display: none; } } </style> <style lang="scss" scoped> .calenBox { margin-top: 10px; // height: 630px; height: pxToVh(610); border-radius: 10px; overflow: hidden; background-color: #fff; .event-item { font-size: 12px; // padding: 2px; border-radius: 3px; white-space: nowrap; &.work-order { font-weight: 400; font-size: 12px; color: #7b7b7b; span { font-weight: 600; font-size: 14px; @@ -175,6 +181,7 @@ font-weight: 400; font-size: 12px; color: #7b7b7b; span { font-weight: 600; font-size: 14px; src/views/wel/index.vue
@@ -1,216 +1,208 @@ <template> <welContainer> <div class="workbench"> <div class="workleft"> <!-- 设备统计 --> <statistics></statistics> <!-- 综合统计分析 --> <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] }} <wel-container> <div class="workbench"> <div class="workleft"> <!-- 设备统计 --> <statistics></statistics> <!-- 综合统计分析 --> <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="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 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> <img :src="item.img" alt="" /> </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> <div class="charts"> <div class="chart" ref="echartsRef"></div> </div> </div> <!-- 飞行统计 --> <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 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> <div class="lineChart"> <div class="lineBox" ref="chartRef"></div> <div class="lineChart"> <div class="lineBox" ref="chartRef"></div> </div> </div> </div> </div> </div> <div class="centerRight"> <flyratio :dateSelect="dateSelect"></flyratio> <div class="centerRight"> <flyratio :dateSelect="dateSelect"></flyratio> </div> </div> </div> </div> </div> </div> <div class="workright"> <Bocklog></Bocklog> <CalenBox></CalenBox> <div class="workright"> <Bocklog></Bocklog> <CalenBox></CalenBox> </div> </div> </div> </welContainer> </wel-container> </template> <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'; 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'); let timeListStr = ['本周', '本月', '本年']; let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR']; 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' 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') 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 dateSelect = ref('CURRENT_YEAR') let timeClick = (item, index) => { checked.value = item; params.value.date_enum = item; dateSelect.value = item; getTypeData(); getFlyList(); getFlyTimeList(); }; 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(); }; params.value.date_enum = 'CURRENT_YEAR' checked.value = 'CURRENT_YEAR' dateSelect.value = 'CURRENT_YEAR' getTypeData() getFlyList() getFlyTimeList() } // 跳转 const jumpshebei = () => { ElMessage.warning('加急开发中...'); }; 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 eventTotal = ref(0) const data = ref([]) // 工单统计 const getTypeData = () => { getJobEventByStatus(params.value).then(res => { const resList = res?.data?.data || []; 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; item1.value = item.num item1.rate = item.rate } }); }); initChart(resList); }); }; }) }) 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 resList = res?.data?.data || [] lineCharts(resList) }) } // 图表 const echartsRef = ref(null); let { chart } = useEchartsResize(echartsRef); const chartRef = ref(null); let { chart: lineChart } = useEchartsResize(chartRef); 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 totalNum = val.reduce((sum, item) => sum + item.num, 0) const data = { total: { title: '总计', @@ -221,7 +213,7 @@ name: item.name, rate: item.rate, })), }; } const containerWidth = chart.value.clientWidth const isSmallScreen = containerWidth < 768 // 移动端判断 const echartsOption = { @@ -240,7 +232,7 @@ 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>`; </div>` }, }, legend: { @@ -273,22 +265,22 @@ label: { formatter: params => { // 使用 b 样式标记包裹百分比数值 return `{a|${params.name}}: {b|${params.data.rate}%}`; return `{a|${params.name}}: {b|${params.data.rate}%}` }, fontSize: isSmallScreen ? '0.8rem' : '1.2rem', position: isSmallScreen ? 'outer' : 'outer', fontSize: isSmallScreen ? '0.8rem' : '1.2rem', position: isSmallScreen ? 'outer' : 'outer', alignTo: 'labelLine', overflow: 'truncate', overflow: 'none', // 禁用省略号 overflow: 'none', // 禁用省略号 bleedMargin: 30, // 防止标签被截断 rich: { a: { color: 'rgba(28, 31, 35, 0.80)', fontSize: isSmallScreen ? '1rem' : '1.2rem', fontSize: isSmallScreen ? '1rem' : '1.2rem', }, b: { color: '#1C1F23', fontSize: isSmallScreen ? '1rem' : '1.3rem', color: '#1C1F23', fontSize: isSmallScreen ? '1rem' : '1.3rem', fontWeight: 'bold', }, }, @@ -305,47 +297,47 @@ data: data.data, }, ], }; } chart.value.setOption(echartsOption); }; chart.value.setOption(echartsOption) } // 折线/柱状图 const lineCharts = bardata => { const categories = bardata?.map(item => item.name); // x轴类别 const flight_distance = []; const flight_time = []; const event_num = []; 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; hasResult = false period.data?.forEach(item => { switch (item.name) { case '飞行时长': flight_time.push(parseFloat(item.value) || 0); hasDuration = true; break; 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; flight_distance.push(parseFloat(item.value) || 0) hasDistance = true break case '任务成果': event_num.push(Number(item.value) || 0); hasResult = true; break; 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); }); if (!hasDuration) flight_time.push(0) if (!hasDistance) flight_distance.push(0) if (!hasResult) event_num.push(0) }) var option = { tooltip: { trigger: 'item', @@ -368,7 +360,7 @@ itemWidth: 15, itemHeight: 10, itemGap: 25, fontSize:'1.2rem' fontSize: '1.2rem' }, xAxis: { type: 'category', @@ -380,7 +372,7 @@ }, axisLabel: { interval: 0, color: '#383838', color: '#383838', }, }, yAxis: [ @@ -389,7 +381,7 @@ name: '单位:万套', nameTextStyle: { color: '#383838', fontSize:'1.2rem' fontSize: '1.2rem' }, min: 1, logBase: 3, @@ -491,19 +483,19 @@ data: event_num, }, ], }; lineChart.value.setOption(option); }; } lineChart.value.setOption(option) } onMounted(() => { getJobEventTotal().then(res => { eventTotal.value = res?.data?.data || 0; }); eventTotal.value = res?.data?.data || 0 }) getTypeData(); lineCharts(); getFlyList(); getFlyTimeList(); }); getTypeData() lineCharts() getFlyList() getFlyTimeList() }) </script> <style> @@ -513,27 +505,32 @@ </style> <style scoped lang="scss"> .workbench { height: 100%; height: 100%; flex: 1; // padding: 0px 20px 0 10px; display: flex; justify-content: space-between; } .workleft { width: 68%; margin-right: 10px; height: 100%; .comprehensiveCon { background: #ffffff !important; height: pxToVh(766); background: #ffffff !important; height: pxToVh(776); 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; @@ -541,16 +538,20 @@ font-size: 16px; color: #363636; font-family: 'Source Han Sans CN'; span { margin-right: 4px; } img { cursor: pointer; } } .arrow { cursor: pointer; } .time-card { text-align: center; // height: 30px; @@ -564,6 +565,7 @@ display: flex; width: 282px; margin-left: 15px; .card-item { width: 94px; height: 100%; @@ -574,54 +576,67 @@ 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; } } } // 工、单 .center { display: flex; .centerLeft { width: 60%; border-right: 1px solid #dfdfdf; .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'; @@ -630,12 +645,14 @@ 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; @@ -645,23 +662,27 @@ 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; @@ -669,6 +690,7 @@ white-space: nowrap; text-align: left; } .status-value { font-family: 'Source Han Sans CN'; font-weight: bold; @@ -689,10 +711,12 @@ } } } .charts { margin-top: 30px; width: 100%; height: auto; .chart { width: 100%; height: 100%; @@ -703,19 +727,23 @@ // 飞行统计 .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; @@ -723,6 +751,7 @@ display: flex; justify-content: space-between; margin-bottom: 10px; .centerItem { padding: 7px 7px 0 15px; // width: 196px; @@ -734,11 +763,13 @@ 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; @@ -746,11 +777,13 @@ font-size: 14px; color: #7c8091; } .flydata { margin-top: 10px; text-align: right; font-size: 14px; color: #7c8091; span { font-weight: bold; font-size: 24px; @@ -764,6 +797,7 @@ .lineChart { height: 209px; width: 100%; .lineBox { width: 100%; height: 100%; @@ -772,6 +806,7 @@ } } } .centerRight { width: 40%; } @@ -781,8 +816,9 @@ } .workright { height: 100%; width: 32%; width: 0; flex: 1; height: 100%; // display: flex; // flex-direction: column; }