feat(mes): 迁移 home 首页
parent
b325db0450
commit
a237758516
|
|
@ -0,0 +1,89 @@
|
|||
import type { MesHomeApi } from '#/api/mes/home';
|
||||
|
||||
import { MesProWorkOrderStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
/** 首页汇总统计默认值 */
|
||||
export const defaultSummary: MesHomeApi.Summary = {
|
||||
andonActiveCount: 0,
|
||||
machineryMaintenance: 0,
|
||||
machineryProducing: 0,
|
||||
machineryStop: 0,
|
||||
machineryTotal: 0,
|
||||
repairActiveCount: 0,
|
||||
todayOutput: 0,
|
||||
todayQualifiedQuantity: 0,
|
||||
todayUnqualifiedQuantity: 0,
|
||||
workOrderActiveCount: 0,
|
||||
workOrderFinishedCount: 0,
|
||||
workOrderPrepareCount: 0,
|
||||
yesterdayOutput: 0,
|
||||
};
|
||||
|
||||
/** 工单状态对应的颜色映射 */
|
||||
export const WORK_ORDER_STATUS_COLOR_MAP: Record<number, string> = {
|
||||
[MesProWorkOrderStatusEnum.PREPARE]: '#909399', // 草稿
|
||||
[MesProWorkOrderStatusEnum.CONFIRMED]: '#409EFF', // 已确认
|
||||
[MesProWorkOrderStatusEnum.FINISHED]: '#67C23A', // 已完成
|
||||
[MesProWorkOrderStatusEnum.CANCELED]: '#F56C6C', // 已取消
|
||||
};
|
||||
|
||||
/** 生产趋势折线图配置 */
|
||||
export function getProductionTrendChartOptions(
|
||||
dates: string[],
|
||||
quantities: number[],
|
||||
qualified: number[],
|
||||
unqualified: number[],
|
||||
): any {
|
||||
return {
|
||||
grid: { bottom: 40, left: 50, right: 20, top: 20 },
|
||||
legend: { bottom: 0, data: ['产量', '合格品', '不良品'] },
|
||||
series: [
|
||||
{
|
||||
areaStyle: { color: 'rgba(64,158,255,0.15)' },
|
||||
data: quantities,
|
||||
itemStyle: { color: '#409EFF' },
|
||||
name: '产量',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
},
|
||||
{
|
||||
data: qualified,
|
||||
itemStyle: { color: '#67C23A' },
|
||||
name: '合格品',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
},
|
||||
{
|
||||
data: unqualified,
|
||||
itemStyle: { color: '#F56C6C' },
|
||||
name: '不良品',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
},
|
||||
],
|
||||
tooltip: { axisPointer: { type: 'cross' }, trigger: 'axis' },
|
||||
xAxis: { boundaryGap: false, data: dates, type: 'category' },
|
||||
yAxis: { minInterval: 1, type: 'value' },
|
||||
};
|
||||
}
|
||||
|
||||
/** 工单状态分布饼图配置 */
|
||||
export function getWorkOrderStatusChartOptions(
|
||||
data: Array<{ itemStyle: { color: string }; name: string; value: number }>,
|
||||
): any {
|
||||
return {
|
||||
legend: { bottom: 0, type: 'scroll' },
|
||||
series: [
|
||||
{
|
||||
avoidLabelOverlap: true,
|
||||
data,
|
||||
emphasis: { label: { fontSize: 14, fontWeight: 'bold', show: true } },
|
||||
itemStyle: { borderColor: '#fff', borderRadius: 6, borderWidth: 2 },
|
||||
label: { formatter: '{b}\n{c}', show: true },
|
||||
radius: ['40%', '70%'],
|
||||
type: 'pie',
|
||||
},
|
||||
],
|
||||
tooltip: { formatter: '{b}: {c} ({d}%)', trigger: 'item' },
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesHomeApi } from '#/api/mes/home';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { DocAlert, Page } from '@vben/common-ui';
|
||||
|
||||
import { ElCol, ElRow } from 'element-plus';
|
||||
|
||||
import { getHomeSummary } from '#/api/mes/home';
|
||||
|
||||
import { defaultSummary } from './data';
|
||||
import AlertPanel from './modules/alert-panel.vue';
|
||||
import KpiCards from './modules/kpi-cards.vue';
|
||||
import ProductionTrend from './modules/production-trend.vue';
|
||||
import Shortcuts from './modules/shortcuts.vue';
|
||||
import WorkOrderChart from './modules/work-order-chart.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const summary = ref<MesHomeApi.Summary>(defaultSummary); // 首页汇总统计
|
||||
|
||||
/** 跳转到目标页面(按路由 name) */
|
||||
function handleNavigate(name: string) {
|
||||
router.push({ name });
|
||||
}
|
||||
|
||||
/** 加载首页汇总统计 */
|
||||
async function loadSummary() {
|
||||
summary.value = await getHomeSummary();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSummary();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="MES 手册(功能开启)"
|
||||
url="https://doc.iocoder.cn/mes/build/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 第一行:核心 KPI 汇总卡片 -->
|
||||
<KpiCards :summary="summary" class="mb-4" @navigate="handleNavigate" />
|
||||
|
||||
<!-- 第二行:生产趋势 + 待办异常 -->
|
||||
<ElRow :gutter="16" class="mb-4">
|
||||
<ElCol :lg="16" :md="24" :sm="24" :xl="16" :xs="24" class="mb-4">
|
||||
<ProductionTrend />
|
||||
</ElCol>
|
||||
<ElCol :lg="8" :md="24" :sm="24" :xl="8" :xs="24" class="mb-4">
|
||||
<AlertPanel :summary="summary" @navigate="handleNavigate" />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<!-- 第三行:工单分布 + 快捷入口 -->
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :lg="12" :md="24" :sm="24" :xl="12" :xs="24" class="mb-4">
|
||||
<WorkOrderChart />
|
||||
</ElCol>
|
||||
<ElCol :lg="12" :md="24" :sm="24" :xl="12" :xs="24" class="mb-4">
|
||||
<Shortcuts @navigate="handleNavigate" />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesHomeApi } from '#/api/mes/home';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElBadge, ElCard } from 'element-plus';
|
||||
|
||||
defineOptions({ name: 'MesHomeAlertPanel' });
|
||||
|
||||
const props = defineProps<{
|
||||
summary: MesHomeApi.Summary;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
navigate: [name: string];
|
||||
}>();
|
||||
|
||||
/** 待办提醒列表:标签、描述、图标、目标路由名称、数量 */
|
||||
const alertItems = computed(() => [
|
||||
{
|
||||
count: props.summary.andonActiveCount,
|
||||
desc: '未处置的安灯呼叫',
|
||||
icon: 'lucide:bell-ring',
|
||||
iconClass: 'bg-red-50 text-red-500',
|
||||
label: '安灯报警',
|
||||
routeName: 'MesProAndonRecord',
|
||||
},
|
||||
{
|
||||
count: props.summary.repairActiveCount,
|
||||
desc: '待处理的维修工单',
|
||||
icon: 'lucide:wrench',
|
||||
iconClass: 'bg-orange-50 text-orange-500',
|
||||
label: '设备维修',
|
||||
routeName: 'MesDvRepair',
|
||||
},
|
||||
{
|
||||
count: props.summary.workOrderPrepareCount,
|
||||
desc: '草稿状态的生产工单',
|
||||
icon: 'lucide:clipboard-list',
|
||||
iconClass: 'bg-blue-50 text-blue-500',
|
||||
label: '待排产工单',
|
||||
routeName: 'MesProWorkOrder',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard header="待办与异常" class="h-full">
|
||||
<div class="flex flex-col">
|
||||
<div
|
||||
v-for="item in alertItems"
|
||||
:key="item.label"
|
||||
class="hover:bg-accent flex cursor-pointer items-center gap-3 border-b px-2 py-4 transition-colors last:border-b-0"
|
||||
@click="emit('navigate', item.routeName)"
|
||||
>
|
||||
<div
|
||||
class="flex size-10 flex-shrink-0 items-center justify-center rounded-lg"
|
||||
:class="item.iconClass"
|
||||
>
|
||||
<IconifyIcon class="size-5" :icon="item.icon" />
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-0.5">
|
||||
<span class="text-sm font-medium">{{ item.label }}</span>
|
||||
<span class="text-muted-foreground text-xs">{{ item.desc }}</span>
|
||||
</div>
|
||||
<ElBadge :value="item.count" :hidden="!item.count" />
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesHomeApi } from '#/api/mes/home';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { CountTo } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElCard, ElCol, ElDivider, ElRow } from 'element-plus';
|
||||
|
||||
defineOptions({ name: 'MesHomeKpiCards' });
|
||||
|
||||
const props = defineProps<{
|
||||
summary: MesHomeApi.Summary;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
navigate: [name: string];
|
||||
}>();
|
||||
|
||||
/** 是否有质量数据(合格品 + 不良品 > 0) */
|
||||
const hasQualityData = computed(
|
||||
() =>
|
||||
props.summary.todayQualifiedQuantity +
|
||||
props.summary.todayUnqualifiedQuantity >
|
||||
0,
|
||||
);
|
||||
|
||||
/** 质量合格率 = 合格品 / (合格品 + 不良品) * 100,无数据时为 0 */
|
||||
const qualityRate = computed(() => {
|
||||
const total =
|
||||
props.summary.todayQualifiedQuantity +
|
||||
props.summary.todayUnqualifiedQuantity;
|
||||
if (total === 0) {
|
||||
return 0;
|
||||
}
|
||||
return (props.summary.todayQualifiedQuantity / total) * 100;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
|
||||
<ElCard
|
||||
shadow="hover"
|
||||
class="kpi-card cursor-pointer transition-all hover:-translate-y-1"
|
||||
@click="emit('navigate', 'MesProWorkOrder')"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="flex size-14 flex-shrink-0 items-center justify-center rounded-xl text-white"
|
||||
style="background: linear-gradient(135deg, #409eff, #66b1ff)"
|
||||
>
|
||||
<IconifyIcon class="size-7" icon="lucide:file-text" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground mb-1 text-sm">生产工单</div>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<CountTo
|
||||
class="text-2xl font-bold leading-tight text-[#409eff]"
|
||||
:end-val="summary.workOrderActiveCount"
|
||||
:duration="1500"
|
||||
/>
|
||||
<span class="text-muted-foreground text-xs">进行中</span>
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
<span>待排产 {{ summary.workOrderPrepareCount }}</span>
|
||||
<ElDivider direction="vertical" />
|
||||
<span>已完成 {{ summary.workOrderFinishedCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
|
||||
<ElCard
|
||||
shadow="hover"
|
||||
class="kpi-card cursor-pointer transition-all hover:-translate-y-1"
|
||||
@click="emit('navigate', 'MesProFeedback')"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="flex size-14 flex-shrink-0 items-center justify-center rounded-xl text-white"
|
||||
style="background: linear-gradient(135deg, #67c23a, #85ce61)"
|
||||
>
|
||||
<IconifyIcon class="size-7" icon="lucide:bar-chart-3" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground mb-1 text-sm">今日产量</div>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<CountTo
|
||||
class="text-2xl font-bold leading-tight text-[#67c23a]"
|
||||
:end-val="summary.todayOutput"
|
||||
:duration="1500"
|
||||
/>
|
||||
<span class="text-muted-foreground text-xs">件</span>
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
<span>昨日 {{ summary.yesterdayOutput }} 件</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
|
||||
<ElCard
|
||||
shadow="hover"
|
||||
class="kpi-card cursor-pointer transition-all hover:-translate-y-1"
|
||||
@click="emit('navigate', 'MesProFeedback')"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="flex size-14 flex-shrink-0 items-center justify-center rounded-xl text-white"
|
||||
style="background: linear-gradient(135deg, #e6a23c, #ebb563)"
|
||||
>
|
||||
<IconifyIcon class="size-7" icon="lucide:circle-check" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground mb-1 text-sm">质量合格率</div>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<CountTo
|
||||
class="text-2xl font-bold leading-tight text-[#e6a23c]"
|
||||
:decimals="1"
|
||||
:end-val="qualityRate"
|
||||
:duration="1500"
|
||||
/>
|
||||
<span class="text-muted-foreground text-xs">%</span>
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
<template v-if="hasQualityData">
|
||||
<span>合格 {{ summary.todayQualifiedQuantity }}</span>
|
||||
<ElDivider direction="vertical" />
|
||||
<span>不良 {{ summary.todayUnqualifiedQuantity }}</span>
|
||||
</template>
|
||||
<span v-else>暂无数据</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4">
|
||||
<ElCard
|
||||
shadow="hover"
|
||||
class="kpi-card cursor-pointer transition-all hover:-translate-y-1"
|
||||
@click="emit('navigate', 'MesDvMachinery')"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="flex size-14 flex-shrink-0 items-center justify-center rounded-xl text-white"
|
||||
style="background: linear-gradient(135deg, #7c3aed, #9461f5)"
|
||||
>
|
||||
<IconifyIcon class="size-7" icon="lucide:cpu" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground mb-1 text-sm">设备状态</div>
|
||||
<div class="flex items-baseline gap-1">
|
||||
<CountTo
|
||||
class="text-2xl font-bold leading-tight text-[#7c3aed]"
|
||||
:end-val="summary.machineryProducing"
|
||||
:duration="1500"
|
||||
/>
|
||||
<span class="text-muted-foreground text-xs">
|
||||
/ {{ summary.machineryTotal }} 运行中
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
<span class="text-red-400">停机 {{ summary.machineryStop }}</span>
|
||||
<ElDivider direction="vertical" />
|
||||
<span class="text-orange-400">
|
||||
维护 {{ summary.machineryMaintenance }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<script lang="ts" setup>
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
|
||||
import { ElCard, ElRadioButton, ElRadioGroup } from 'element-plus';
|
||||
|
||||
import { getProductionTrend } from '#/api/mes/home';
|
||||
|
||||
import { getProductionTrendChartOptions } from '../data';
|
||||
|
||||
defineOptions({ name: 'MesHomeProductionTrend' });
|
||||
|
||||
const trendDays = ref(7); // 当前选中的天数范围
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(chartRef);
|
||||
|
||||
/** 加载生产趋势数据并渲染图表 */
|
||||
async function loadData() {
|
||||
const data = await getProductionTrend(trendDays.value);
|
||||
const dates = data.map((d) => d.date.slice(5));
|
||||
const quantities = data.map((d) => d.quantity);
|
||||
const qualified = data.map((d) => d.qualifiedQuantity);
|
||||
const unqualified = data.map((d) => d.unqualifiedQuantity);
|
||||
await renderEcharts(
|
||||
getProductionTrendChartOptions(dates, quantities, qualified, unqualified),
|
||||
);
|
||||
}
|
||||
|
||||
/** 切换天数范围 */
|
||||
function handleDaysChange() {
|
||||
loadData();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard class="h-full">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="font-medium">生产趋势</span>
|
||||
<ElRadioGroup
|
||||
v-model="trendDays"
|
||||
size="small"
|
||||
@change="handleDaysChange"
|
||||
>
|
||||
<ElRadioButton :value="7">近 7 天</ElRadioButton>
|
||||
<ElRadioButton :value="30">近 30 天</ElRadioButton>
|
||||
</ElRadioGroup>
|
||||
</div>
|
||||
</template>
|
||||
<EchartsUI ref="chartRef" class="h-[320px] w-full" />
|
||||
</ElCard>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<script lang="ts" setup>
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { ElCard, ElCol, ElRow } from 'element-plus';
|
||||
|
||||
defineOptions({ name: 'MesHomeShortcuts' });
|
||||
|
||||
const emit = defineEmits<{
|
||||
navigate: [name: string];
|
||||
}>();
|
||||
|
||||
/** 快捷入口列表,3×3 网格布局与工单状态分布面板等高 */
|
||||
const shortcuts = [
|
||||
{
|
||||
bgColor: '#409EFF',
|
||||
icon: 'lucide:file-text',
|
||||
name: '生产工单',
|
||||
routeName: 'MesProWorkOrder',
|
||||
},
|
||||
{
|
||||
bgColor: '#67C23A',
|
||||
icon: 'lucide:edit',
|
||||
name: '生产报工',
|
||||
routeName: 'MesProFeedback',
|
||||
},
|
||||
{
|
||||
bgColor: '#E6A23C',
|
||||
icon: 'lucide:search',
|
||||
name: '质量检验',
|
||||
routeName: 'MesQcIqc',
|
||||
},
|
||||
{
|
||||
bgColor: '#F56C6C',
|
||||
icon: 'lucide:box',
|
||||
name: '库存查询',
|
||||
routeName: 'MesWmMaterialStock',
|
||||
},
|
||||
{
|
||||
bgColor: '#7c3aed',
|
||||
icon: 'lucide:cpu',
|
||||
name: '设备管理',
|
||||
routeName: 'MesDvMachinery',
|
||||
},
|
||||
{
|
||||
bgColor: '#0ea5e9',
|
||||
icon: 'lucide:list',
|
||||
name: '生产任务',
|
||||
routeName: 'MesProTask',
|
||||
},
|
||||
{
|
||||
bgColor: '#14b8a6',
|
||||
icon: 'lucide:truck',
|
||||
name: '到货通知',
|
||||
routeName: 'MesWmArrivalNotice',
|
||||
},
|
||||
{
|
||||
bgColor: '#f59e0b',
|
||||
icon: 'lucide:settings-2',
|
||||
name: '设备维修',
|
||||
routeName: 'MesDvRepair',
|
||||
},
|
||||
{
|
||||
bgColor: '#ec4899',
|
||||
icon: 'lucide:tickets',
|
||||
name: '流转卡',
|
||||
routeName: 'MesProCard',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard header="快捷入口" class="h-full">
|
||||
<ElRow :gutter="16">
|
||||
<ElCol v-for="item in shortcuts" :key="item.name" :span="8" class="mb-4">
|
||||
<div
|
||||
class="hover:bg-accent flex cursor-pointer flex-col items-center gap-2 rounded-lg py-3 transition-all hover:-translate-y-0.5"
|
||||
@click="emit('navigate', item.routeName)"
|
||||
>
|
||||
<div
|
||||
class="flex size-12 items-center justify-center rounded-xl text-white"
|
||||
:style="{ background: item.bgColor }"
|
||||
>
|
||||
<IconifyIcon class="size-6" :icon="item.icon" />
|
||||
</div>
|
||||
<span class="text-sm">{{ item.name }}</span>
|
||||
</div>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElCard>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<script lang="ts" setup>
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
|
||||
import { ElCard } from 'element-plus';
|
||||
|
||||
import { getWorkOrderStatusDistribution } from '#/api/mes/home';
|
||||
|
||||
import { getWorkOrderStatusChartOptions, WORK_ORDER_STATUS_COLOR_MAP } from '../data';
|
||||
|
||||
defineOptions({ name: 'MesHomeWorkOrderChart' });
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(chartRef);
|
||||
|
||||
/** 加载工单状态分布数据并渲染饼图 */
|
||||
async function loadData() {
|
||||
const data = await getWorkOrderStatusDistribution();
|
||||
const chartData = data.map((d) => ({
|
||||
itemStyle: { color: WORK_ORDER_STATUS_COLOR_MAP[d.status] || '#409EFF' },
|
||||
name: d.statusName,
|
||||
value: d.count,
|
||||
}));
|
||||
await renderEcharts(getWorkOrderStatusChartOptions(chartData));
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard header="工单状态分布" class="h-full">
|
||||
<EchartsUI ref="chartRef" class="h-[280px] w-full" />
|
||||
</ElCard>
|
||||
</template>
|
||||
Loading…
Reference in New Issue