fix: 工作台、工单统计看板接口对接
This commit is contained in:
@@ -200,13 +200,22 @@ export interface DashboardStatsResp {
|
||||
};
|
||||
funnelData: Array<{ name: string; value: number }>;
|
||||
heatmapData: { days: string[]; hours: string[]; data: number[][] };
|
||||
areaRanking: Array<{
|
||||
area: string;
|
||||
functionTypeRanking: Array<{
|
||||
functionType: string;
|
||||
count: number;
|
||||
completed: number;
|
||||
rate: number;
|
||||
}>;
|
||||
durationStats: Array<{ type: string; avgDuration: number }>;
|
||||
}
|
||||
|
||||
/** 近7天客流趋势响应 */
|
||||
export interface TrafficTrendResp {
|
||||
dates: string[];
|
||||
inData: number[];
|
||||
outData: number[];
|
||||
netData: number[];
|
||||
totalIn: number;
|
||||
totalOut: number;
|
||||
}
|
||||
|
||||
/** 实时客流响应 */
|
||||
@@ -268,6 +277,13 @@ export function getWorkspaceStats() {
|
||||
);
|
||||
}
|
||||
|
||||
/** 获取近7天客流趋势统计 */
|
||||
export function getTrafficTrend() {
|
||||
return requestClient.get<TrafficTrendResp>(
|
||||
'/ops/order-center/traffic-trend',
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 工单操作接口 ====================
|
||||
|
||||
/** 重新分配/派单 */
|
||||
|
||||
@@ -58,28 +58,20 @@ interface DashboardStats {
|
||||
// 工单处理漏斗
|
||||
funnelData: Array<{ name: string; value: number }>;
|
||||
|
||||
// 时段热力图数据(7天 x 24小时)
|
||||
// 时段热力图数据(近7天 x 24小时)
|
||||
heatmapData: {
|
||||
data: number[][]; // 7x24的二维数组
|
||||
days: string[]; // 周一到周日
|
||||
hours: string[]; // 0-23小时
|
||||
};
|
||||
|
||||
// 区域工单排行
|
||||
areaRanking: Array<{
|
||||
area: string;
|
||||
// 功能类型工单排行
|
||||
functionTypeRanking: Array<{
|
||||
functionType: string;
|
||||
completed: number;
|
||||
count: number;
|
||||
rate: number;
|
||||
}>;
|
||||
|
||||
// 工单作业时长统计
|
||||
durationStats: Array<{
|
||||
avgDuration: number; // 分钟
|
||||
color: string;
|
||||
icon: string;
|
||||
type: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// ========== 响应式数据 ==========
|
||||
@@ -92,17 +84,15 @@ const hourlyChartRef = ref();
|
||||
const timeTrendChartRef = ref();
|
||||
const funnelChartRef = ref();
|
||||
const heatmapChartRef = ref();
|
||||
const areaRankingChartRef = ref();
|
||||
const durationChartRef = ref();
|
||||
const functionTypeRankingChartRef = ref();
|
||||
|
||||
const { renderEcharts: renderTrendChart } = useEcharts(trendChartRef);
|
||||
const { renderEcharts: renderHourlyChart } = useEcharts(hourlyChartRef);
|
||||
const { renderEcharts: renderTimeTrendChart } = useEcharts(timeTrendChartRef);
|
||||
const { renderEcharts: renderFunnelChart } = useEcharts(funnelChartRef);
|
||||
const { renderEcharts: renderHeatmapChart } = useEcharts(heatmapChartRef);
|
||||
const { renderEcharts: renderAreaRankingChart } =
|
||||
useEcharts(areaRankingChartRef);
|
||||
const { renderEcharts: renderDurationChart } = useEcharts(durationChartRef);
|
||||
const { renderEcharts: renderFunctionTypeRankingChart } =
|
||||
useEcharts(functionTypeRankingChartRef);
|
||||
|
||||
const statsData = ref<DashboardStats>({
|
||||
pendingCount: 0,
|
||||
@@ -129,8 +119,7 @@ const statsData = ref<DashboardStats>({
|
||||
hours: [],
|
||||
data: [],
|
||||
},
|
||||
areaRanking: [],
|
||||
durationStats: [],
|
||||
functionTypeRanking: [],
|
||||
});
|
||||
|
||||
// ========== 空数据(API失败时的fallback) ==========
|
||||
@@ -144,8 +133,7 @@ const EMPTY_STATS: DashboardStats = {
|
||||
timeTrendData: { dates: [], responseTimeData: [], completionTimeData: [] },
|
||||
funnelData: [],
|
||||
heatmapData: { days: [], hours: [], data: [] },
|
||||
areaRanking: [],
|
||||
durationStats: [],
|
||||
functionTypeRanking: [],
|
||||
};
|
||||
|
||||
// ========== 图表配置 ==========
|
||||
@@ -674,10 +662,10 @@ function getHeatmapChartOptions(): ECOption {
|
||||
}
|
||||
|
||||
/**
|
||||
* 区域工单排行图表配置
|
||||
* 功能类型排行图表配置
|
||||
*/
|
||||
function getAreaRankingChartOptions(): ECOption {
|
||||
const { areaRanking } = statsData.value;
|
||||
function getFunctionTypeRankingChartOptions(): ECOption {
|
||||
const { functionTypeRanking } = statsData.value;
|
||||
return {
|
||||
grid: {
|
||||
left: '3%',
|
||||
@@ -698,7 +686,7 @@ function getAreaRankingChartOptions(): ECOption {
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: areaRanking.map((item) => item.area),
|
||||
data: functionTypeRanking.map((item) => item.functionType),
|
||||
axisLabel: {
|
||||
fontSize: 13,
|
||||
color: '#595959',
|
||||
@@ -713,7 +701,7 @@ function getAreaRankingChartOptions(): ECOption {
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: areaRanking.map((item) => ({
|
||||
data: functionTypeRanking.map((item) => ({
|
||||
value: item.count,
|
||||
itemStyle: {
|
||||
color: getCompletionRateColor(item.rate),
|
||||
@@ -743,67 +731,6 @@ function getAreaRankingChartOptions(): ECOption {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 工单作业时长统计图表配置
|
||||
*/
|
||||
function getDurationChartOptions(): ECOption {
|
||||
const { durationStats } = statsData.value;
|
||||
return {
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '18%',
|
||||
top: '5%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
max: 'dataMax',
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: durationStats.map((item) => item.type),
|
||||
axisLabel: {
|
||||
fontSize: 13,
|
||||
color: '#595959',
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: durationStats.map((item) => ({
|
||||
value: item.avgDuration,
|
||||
itemStyle: {
|
||||
color: item.color,
|
||||
borderRadius: [0, 6, 6, 0],
|
||||
},
|
||||
})),
|
||||
barWidth: 18,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
fontSize: 13,
|
||||
color: '#262626',
|
||||
fontWeight: 500,
|
||||
formatter: (params: any) => `${params.value}分钟`,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ========== 数据加载 ==========
|
||||
|
||||
/** 加载统计数据 */
|
||||
@@ -811,20 +738,6 @@ async function loadStats() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const resp = await getDashboardStats();
|
||||
// 映射 durationStats: 后端返回 type(枚举key) + avgDuration,前端补充显示名、图标、颜色
|
||||
const mappedDurationStats = (resp.durationStats || []).map((item) => {
|
||||
const mapping = CLEANING_TYPE_MAP[item.type] || {
|
||||
type: item.type,
|
||||
icon: 'solar:broom-bold',
|
||||
color: '#8c8c8c',
|
||||
};
|
||||
return {
|
||||
type: mapping.type,
|
||||
icon: mapping.icon,
|
||||
color: mapping.color,
|
||||
avgDuration: item.avgDuration,
|
||||
};
|
||||
});
|
||||
|
||||
statsData.value = {
|
||||
pendingCount: resp.pendingCount,
|
||||
@@ -836,8 +749,7 @@ async function loadStats() {
|
||||
timeTrendData: resp.timeTrendData,
|
||||
funnelData: resp.funnelData,
|
||||
heatmapData: resp.heatmapData,
|
||||
areaRanking: resp.areaRanking,
|
||||
durationStats: mappedDurationStats,
|
||||
functionTypeRanking: resp.functionTypeRanking,
|
||||
};
|
||||
|
||||
// 渲染图表
|
||||
@@ -848,8 +760,7 @@ async function loadStats() {
|
||||
renderTimeTrendChart(getTimeTrendChartOptions());
|
||||
renderFunnelChart(getFunnelChartOptions());
|
||||
renderHeatmapChart(getHeatmapChartOptions());
|
||||
renderAreaRankingChart(getAreaRankingChartOptions());
|
||||
renderDurationChart(getDurationChartOptions());
|
||||
renderFunctionTypeRankingChart(getFunctionTypeRankingChartOptions());
|
||||
} catch {
|
||||
// API失败时使用空数据作为fallback
|
||||
statsData.value = { ...EMPTY_STATS };
|
||||
@@ -983,9 +894,9 @@ onUnmounted(() => {
|
||||
</Col>
|
||||
|
||||
<Col :xs="24" :lg="8">
|
||||
<Card class="chart-card" title="工单时段分布">
|
||||
<Card class="chart-card" title="今日工单时段分布">
|
||||
<template #extra>
|
||||
<Tooltip title="展示一天24小时各时段的工单数量分布">
|
||||
<Tooltip title="展示今日24小时各时段的工单数量分布">
|
||||
<IconifyIcon
|
||||
icon="solar:info-circle-bold-duotone"
|
||||
class="info-icon"
|
||||
@@ -1040,13 +951,13 @@ onUnmounted(() => {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 第四行:时段热力图 + 区域工单排行 + 工单作业时长 -->
|
||||
<!-- 第四行:时段热力图(近7天) + 功能类型排行 -->
|
||||
<Row :gutter="[12, 12]">
|
||||
<!-- 时<EFBFBD><EFBFBD><EFBFBD>热力图 -->
|
||||
<Col :xs="24" :md="8" :lg="8">
|
||||
<!-- 时段热力图(近7天) -->
|
||||
<Col :xs="24" :md="12" :lg="12">
|
||||
<Card class="modern-card modern-card--heatmap">
|
||||
<div class="modern-header">
|
||||
<span class="modern-title">时段热力图</span>
|
||||
<span class="modern-title">时段热力图(近7天)</span>
|
||||
</div>
|
||||
<Spin :spinning="chartLoading">
|
||||
<EchartsUI ref="heatmapChartRef" class="modern-chart" />
|
||||
@@ -1054,26 +965,17 @@ onUnmounted(() => {
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<!-- 区域工单排行 -->
|
||||
<Col :xs="24" :md="8" :lg="8">
|
||||
<!-- 功能类型排行 -->
|
||||
<Col :xs="24" :md="12" :lg="12">
|
||||
<Card class="modern-card modern-card--ranking">
|
||||
<div class="modern-header">
|
||||
<span class="modern-title">区域工单排行</span>
|
||||
<span class="modern-title">功能类型排行</span>
|
||||
</div>
|
||||
<Spin :spinning="chartLoading">
|
||||
<EchartsUI ref="areaRankingChartRef" class="modern-chart" />
|
||||
</Spin>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<!-- 工单作业时长统计 -->
|
||||
<Col :xs="24" :md="8" :lg="8">
|
||||
<Card class="modern-card modern-card--duration">
|
||||
<div class="modern-header">
|
||||
<span class="modern-title">作业时长统计</span>
|
||||
</div>
|
||||
<Spin :spinning="chartLoading">
|
||||
<EchartsUI ref="durationChartRef" class="modern-chart" />
|
||||
<EchartsUI
|
||||
ref="functionTypeRankingChartRef"
|
||||
class="modern-chart"
|
||||
/>
|
||||
</Spin>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
Reference in New Issue
Block a user