Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
349 lines
8.7 KiB
Vue
349 lines
8.7 KiB
Vue
<script setup lang="ts">
|
||
import { EditOutlined } from '@ant-design/icons-vue';
|
||
import {EchartsUI, type EchartsUIType, useEcharts} from "@vben/plugins/echarts";
|
||
import {onMounted, ref} from "vue";
|
||
import {countsList} from '#/api/property/customerService/centerConsole'
|
||
import { useRouter } from 'vue-router'
|
||
|
||
const router = useRouter()
|
||
const board = ref ({})
|
||
const getBoard = async () => {
|
||
board.value = await countsList();
|
||
}
|
||
const xAxisData = ref<any[]>([]);
|
||
const seriesData = ref<any[]>([]);
|
||
|
||
// 工单计数
|
||
const workOrderCount = ref<EchartsUIType>();
|
||
const { renderEcharts: renderWorkOrderCount } = useEcharts(workOrderCount);
|
||
async function fetchWorkOrderCount() {
|
||
xAxisData.value = board.value.recentWeekWorkOrders.map(item => {
|
||
const [year, month, day] = item.date.split('-');
|
||
const monthDay = `${month}-${day}`;
|
||
return `${monthDay}${item.dayOfWeek}`;
|
||
});
|
||
seriesData.value = board.value.recentWeekWorkOrders.map(item => {
|
||
return `${item.count}`;
|
||
});
|
||
renderWorkOrderCount({
|
||
tooltip: { trigger: 'axis' },
|
||
xAxis: {
|
||
type: 'category',
|
||
boundaryGap: false,
|
||
data: xAxisData.value
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
name: '近一周工单数',
|
||
},
|
||
series: [
|
||
{
|
||
data: seriesData.value,
|
||
type: 'line',
|
||
areaStyle: {},
|
||
smooth: true,
|
||
}
|
||
]
|
||
});
|
||
}
|
||
|
||
// 满意度指数
|
||
const satisfactionLevel = ref<EchartsUIType>();
|
||
const { renderEcharts: renderSatisfactionLevel } = useEcharts(satisfactionLevel);
|
||
async function fetchSatisfactionLevel() {
|
||
seriesData.value = board.value.satisfactionRateList.map(item => {
|
||
return `${item.value}`;
|
||
})
|
||
renderSatisfactionLevel({
|
||
title: {text: '满意度指数',left: '18px'},
|
||
tooltip: { trigger: 'item'},
|
||
radar: {
|
||
indicator: [
|
||
{name: '1星'},
|
||
{name: '2星'},
|
||
{name: '3星'},
|
||
{name: '4星'},
|
||
{name: '5星'},
|
||
]
|
||
},
|
||
series: [
|
||
{
|
||
type: 'radar',
|
||
name: '满意度指数',
|
||
data: [
|
||
{
|
||
value: seriesData.value,
|
||
}
|
||
]
|
||
}
|
||
]
|
||
})
|
||
}
|
||
|
||
// 近半年工单数
|
||
const orderNumber = ref<EchartsUIType>();
|
||
const { renderEcharts: renderOrderNumber } = useEcharts(orderNumber);
|
||
async function fetchOrderNumber() {
|
||
seriesData.value = board.value.recentSixMonthWorkOrders.map(item => [
|
||
item.month, // 月份(对应目标结构的第一个元素)
|
||
item.orderCount // 工单数量(对应目标结构的第二个元素)
|
||
])
|
||
seriesData.value.unshift(['product','入驻员工'])
|
||
renderOrderNumber({
|
||
title: {text: '近半年工单数',left: '18px'},
|
||
legend: {},
|
||
tooltip: { trigger: 'axis' },
|
||
dataset: {
|
||
source: seriesData.value
|
||
},
|
||
xAxis: { type: 'category' },
|
||
yAxis: {},
|
||
series: [{ type: 'bar' }]
|
||
})
|
||
}
|
||
|
||
// 工单类型分类占比
|
||
const orderTyper = ref<EchartsUIType>();
|
||
const { renderEcharts: renderOrderTyper } = useEcharts(orderTyper);
|
||
async function fetchOrderTyper() {
|
||
seriesData.value = board.value.satisfactionChartList.map(item => {
|
||
return {
|
||
value: item.quantity,
|
||
name: item.type,
|
||
};
|
||
})
|
||
const totalQuantity = board.value.satisfactionChartList.reduce((sum, item) => {
|
||
return sum + Number(item.quantity || 0);
|
||
}, 0);
|
||
console.log(seriesData.value)
|
||
renderOrderTyper({
|
||
title: {text: '工单类型分类占比',left: '18px',top: '18px'},
|
||
tooltip: {
|
||
trigger: 'item'
|
||
},
|
||
legend: {
|
||
top: '40%',
|
||
right: '10%',
|
||
orient: 'vertical',
|
||
icon: 'circle',
|
||
itemWidth: 8,
|
||
itemHeight: 8
|
||
},
|
||
series: [
|
||
{
|
||
type: 'pie',
|
||
center: ['40%', '55%'],
|
||
radius: ['40%', '70%'],
|
||
label: {
|
||
show: true,
|
||
position: 'center',
|
||
formatter: [
|
||
'员工单数量',
|
||
`{num|${totalQuantity}个}`
|
||
].join('\n'),
|
||
fontSize: 14,
|
||
fontWeight: 'bold',
|
||
color: 'gray',
|
||
rich: {
|
||
num: {
|
||
fontSize: 18,
|
||
fontWeight: 'bolder',
|
||
color:'black',
|
||
padding: [10, 0, 0, 0],
|
||
}
|
||
}
|
||
},
|
||
avoidLabelOverlap: false,
|
||
labelLine: {
|
||
show: false
|
||
},
|
||
data: seriesData.value
|
||
}
|
||
]
|
||
})
|
||
}
|
||
|
||
const goOrderPool = () => {
|
||
router.push('/property/business/workOrders')
|
||
}
|
||
|
||
const goToDo = () => {
|
||
router.push('/property/business/workOrderPending')
|
||
}
|
||
|
||
onMounted(async () => {
|
||
await getBoard()
|
||
await fetchWorkOrderCount()
|
||
await fetchSatisfactionLevel()
|
||
await fetchOrderNumber()
|
||
await fetchOrderTyper()
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="panel">
|
||
|
||
<div class="bulletin-board">
|
||
<div class="bulletin-board-title">工单看板</div>
|
||
<div class="bulletin-board-content">
|
||
<div>
|
||
<div>
|
||
<div>工单总数:</div>
|
||
<div>{{ board.workOrdersTotal }}</div>
|
||
<div style="color: #1890FF;cursor: pointer" @click="goOrderPool()">点击前往工单池</div>
|
||
</div>
|
||
<div class="icon-edit"><EditOutlined /></div>
|
||
</div>
|
||
<div>
|
||
<div>
|
||
<div>待派工单数:</div>
|
||
<div>{{ board.notWorkOrdersTotal }}</div>
|
||
<div style="color: #1890FF;cursor: pointer" @click="goToDo()">点击前往工单待办</div>
|
||
</div>
|
||
<div class="icon-edit"><EditOutlined /></div>
|
||
</div>
|
||
<div>
|
||
<div>
|
||
<div>未办结超时工单:</div>
|
||
<div>{{ board.novertimeOrdersTotal }}</div>
|
||
<div>处理中的工单数:<span style="color: green">{{ board.inHandOrdersTotal }}</span></div>
|
||
</div>
|
||
<div class="icon-edit"><EditOutlined /></div>
|
||
</div>
|
||
<div>
|
||
<div>
|
||
<div>当月工单超时率:</div>
|
||
<div>{{ board.novertimeOrdersRate }}%</div>
|
||
<div>超时工单数:<span style="color: red">{{ board.outTimeOrdersTotal }}</span></div>
|
||
</div>
|
||
<div class="icon-edit"><EditOutlined /></div>
|
||
</div>
|
||
<div>
|
||
<div>
|
||
<div>当月满意度:</div>
|
||
<div>{{ board.monthoSatisfaction }}%</div>
|
||
<div>满意度:{{ board.satisfaction }}</div>
|
||
</div>
|
||
<div class="icon-edit"><EditOutlined /></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="chart">
|
||
<div class="chart-one">
|
||
<div>
|
||
<div>
|
||
<div style="font-size: 20px;font-weight: bold;margin-bottom: 20px;color: #464646">工单计数</div>
|
||
<div style="margin-left: 20px;line-height: 30px">
|
||
<div>累计处理工单数</div>
|
||
<div>{{board.recentWeekWorkOrders?.[0]?.weekCount || '0'}}</div>
|
||
<!-- <div>累计回复数</div>-->
|
||
<!-- <div>2</div>-->
|
||
</div>
|
||
</div>
|
||
<EchartsUI
|
||
ref="workOrderCount"
|
||
height="400px"
|
||
width="100%"
|
||
style="background: #fff; border-radius: 8px;padding-top: 30px"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<EchartsUI
|
||
ref="satisfactionLevel"
|
||
height="400px"
|
||
width="100%"
|
||
style="background: #fff; border-radius: 8px;padding-top: 18px"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div class="chart-two">
|
||
<div>
|
||
<EchartsUI
|
||
ref="orderNumber"
|
||
height="400px"
|
||
width="100%"
|
||
style="background: #fff; border-radius: 8px;padding-top: 18px"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<EchartsUI
|
||
ref="orderTyper"
|
||
height="400px"
|
||
width="100%"
|
||
style="background: #fff; border-radius: 8px"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.panel{
|
||
width: 100%;
|
||
padding: 40px;
|
||
box-sizing: border-box;
|
||
|
||
.bulletin-board{
|
||
|
||
.bulletin-board-title{
|
||
font-size: 25px;
|
||
font-weight: bold;
|
||
margin-bottom: 20px;
|
||
}
|
||
.bulletin-board-content{
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 40px;
|
||
|
||
> div{
|
||
height: auto;
|
||
background-color: #FFFFFF;
|
||
padding: 18px;
|
||
border-radius: 10px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
> div:first-child {
|
||
line-height: 32px;
|
||
|
||
div:nth-child(2) {
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
.icon-edit{
|
||
font-size: 30px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.chart{
|
||
|
||
.chart-one{
|
||
margin-top:40px;
|
||
display: grid;
|
||
grid-template-columns: 2fr 1fr;
|
||
gap: 40px;
|
||
|
||
>div:first-child{
|
||
background-color: #FFFFFF;
|
||
border-radius: 8px;
|
||
display: grid;
|
||
grid-template-columns: 1fr 6fr;
|
||
|
||
>div:first-child{
|
||
padding: 18px 0 0 18px;
|
||
}
|
||
}
|
||
}
|
||
.chart-two{
|
||
margin-top:40px;
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 40px;
|
||
}
|
||
}
|
||
}
|
||
</style>
|