|
@@ -8,6 +8,7 @@
|
|
|
<script setup>
|
|
|
import * as echarts from "echarts";
|
|
|
import ScreenComHeader from "@/views/screens/configs/screenComHeader.vue";
|
|
|
+import { qualityConsistentInspection } from "@/api/screens";
|
|
|
|
|
|
const props = defineProps({
|
|
|
moduleId: {
|
|
@@ -15,68 +16,13 @@ const props = defineProps({
|
|
|
required: true,
|
|
|
},
|
|
|
});
|
|
|
+
|
|
|
const chartRef = ref(null);
|
|
|
+const chartData = ref([]);
|
|
|
let chartInstance = null;
|
|
|
let timer = null;
|
|
|
const currentProductIndex = ref(0);
|
|
|
|
|
|
-// 假数据结构(含ISO时间字符串)
|
|
|
-const mockData = [
|
|
|
- {
|
|
|
- productName: "智能控制器",
|
|
|
- inspectionItems: [
|
|
|
- {
|
|
|
- name: "外观检查",
|
|
|
- startTime: "2023-05-10T09:00:00Z",
|
|
|
- endTime: "2023-05-10T09:25:00Z",
|
|
|
- color: "#5470C6",
|
|
|
- },
|
|
|
- {
|
|
|
- name: "功能测试",
|
|
|
- startTime: "2023-05-10T09:25:00Z",
|
|
|
- endTime: "2023-05-10T10:25:00Z",
|
|
|
- color: "#91CC75",
|
|
|
- },
|
|
|
- {
|
|
|
- name: "老化试验",
|
|
|
- startTime: "2023-05-10T10:25:00Z",
|
|
|
- endTime: "2023-05-10T12:25:00Z",
|
|
|
- color: "#EE6666",
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- productName: "温度传感器",
|
|
|
- inspectionItems: [
|
|
|
- {
|
|
|
- name: "外观检查",
|
|
|
- startTime: "2023-05-10T08:00:00Z",
|
|
|
- endTime: "2023-05-10T08:30:00Z",
|
|
|
- color: "#5470C6",
|
|
|
- },
|
|
|
- {
|
|
|
- name: "精度校准",
|
|
|
- startTime: "2023-05-10T08:30:00Z",
|
|
|
- endTime: "2023-05-10T10:30:00Z",
|
|
|
- color: "#73C0DE",
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
-];
|
|
|
-
|
|
|
-// 计算时间差(分钟)
|
|
|
-const getDuration = (start, end) => {
|
|
|
- return (new Date(end) - new Date(start)) / (1000 * 60);
|
|
|
-};
|
|
|
-
|
|
|
-// 获取当前检验阶段
|
|
|
-const getCurrentPhase = (product) => {
|
|
|
- const now = new Date(); // 真实当前时间
|
|
|
- return product.inspectionItems.findIndex((item) => {
|
|
|
- return now >= new Date(item.startTime) && now < new Date(item.endTime);
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
// 初始化图表
|
|
|
const initChart = () => {
|
|
|
if (!chartRef.value) return;
|
|
@@ -85,51 +31,58 @@ const initChart = () => {
|
|
|
updateChart();
|
|
|
window.addEventListener("resize", handleResize);
|
|
|
|
|
|
- // 3秒切换产品
|
|
|
+ // 5秒切换产品批次
|
|
|
timer = setInterval(() => {
|
|
|
- currentProductIndex.value =
|
|
|
- (currentProductIndex.value + 1) % mockData.length;
|
|
|
- updateChart();
|
|
|
+ if (chartData.value.length > 0) {
|
|
|
+ currentProductIndex.value =
|
|
|
+ (currentProductIndex.value + 1) % chartData.value.length;
|
|
|
+ updateChart();
|
|
|
+ }
|
|
|
}, 5000);
|
|
|
};
|
|
|
|
|
|
// 更新图表
|
|
|
const updateChart = () => {
|
|
|
- if (!chartInstance || mockData.length === 0) return;
|
|
|
+ if (!chartInstance || chartData.value.length === 0) return;
|
|
|
|
|
|
- const currentProduct = mockData[currentProductIndex.value];
|
|
|
- const currentPhaseIndex = getCurrentPhase(currentProduct);
|
|
|
+ const currentItem = chartData.value[currentProductIndex.value];
|
|
|
|
|
|
- // 预处理数据
|
|
|
- const processedData = currentProduct.inspectionItems.map((item) => {
|
|
|
- const duration = getDuration(item.startTime, item.endTime);
|
|
|
- return {
|
|
|
- ...item,
|
|
|
- duration,
|
|
|
- timeLabel: `${formatTime(item.startTime)}~${formatTime(item.endTime)}`,
|
|
|
- };
|
|
|
- });
|
|
|
+ // 计算总时长用于比例
|
|
|
+ const totalDuration = currentItem.optionList.reduce(
|
|
|
+ (sum, item) => sum + item.duration,
|
|
|
+ 0
|
|
|
+ );
|
|
|
|
|
|
const option = {
|
|
|
backgroundColor: "transparent",
|
|
|
title: [
|
|
|
{
|
|
|
- text: `当前产品: ${currentProduct.productName}`,
|
|
|
+ text: `产品:${currentItem.productName} 批号:${currentItem.batchCode}`,
|
|
|
+ subtext: `检验类型:${currentItem.inspectType}`,
|
|
|
left: "center",
|
|
|
top: 10,
|
|
|
textStyle: {
|
|
|
color: "#fff",
|
|
|
- fontSize: 14,
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: "bold",
|
|
|
+ },
|
|
|
+ subtextStyle: {
|
|
|
+ color: "#aaa",
|
|
|
+ fontSize: 12,
|
|
|
+ marginTop: 5,
|
|
|
},
|
|
|
},
|
|
|
],
|
|
|
tooltip: {
|
|
|
trigger: "item",
|
|
|
formatter: (params) => {
|
|
|
+ const item = currentItem.optionList[params.dataIndex];
|
|
|
+ const percentage = ((item.duration / totalDuration) * 100).toFixed(1);
|
|
|
return `
|
|
|
<div style="font-weight:bold;margin-bottom:5px">${params.name}</div>
|
|
|
- <div style="margin:3px 0">时段: ${params.data.timeLabel}</div>
|
|
|
- <div style="margin:3px 0">耗时: ${params.data.duration}分钟</div>
|
|
|
+ <div style="margin:3px 0">检验时长: ${item.duration}小时</div>
|
|
|
+ <div style="margin:3px 0">占比: ${percentage}%</div>
|
|
|
+ <div style="margin:3px 0">状态: ${item.status === 1 ? "通过" : "未通过"}</div>
|
|
|
`;
|
|
|
},
|
|
|
backgroundColor: "rgba(0,0,0,0.8)",
|
|
@@ -141,35 +94,48 @@ const updateChart = () => {
|
|
|
},
|
|
|
series: [
|
|
|
{
|
|
|
- name: "检验进度",
|
|
|
+ name: "检验项目",
|
|
|
type: "pie",
|
|
|
- radius: ["45%", "35%"],
|
|
|
+ radius: ["40%", "60%"],
|
|
|
center: ["50%", "50%"],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ selectedMode: "multiple", // 允许选中单个扇区
|
|
|
+ selectedOffset: 20, // 选中扇区的偏移量
|
|
|
itemStyle: {
|
|
|
- borderRadius: 6,
|
|
|
+ borderRadius: 4,
|
|
|
borderColor: "#0f1325",
|
|
|
borderWidth: 2,
|
|
|
},
|
|
|
label: {
|
|
|
show: true,
|
|
|
- fontSize: 14,
|
|
|
+ formatter: "{b}",
|
|
|
+ fontSize: 12,
|
|
|
color: "#fff",
|
|
|
- weight: "bold",
|
|
|
+ fontWeight: "normal",
|
|
|
},
|
|
|
labelLine: {
|
|
|
show: true,
|
|
|
- length: 60,
|
|
|
- smooth: 0.1, // 引导线轻微弯曲
|
|
|
+ length: 20,
|
|
|
+ length2: 30,
|
|
|
+ smooth: 0.2,
|
|
|
},
|
|
|
- data: processedData.map((item, index) => ({
|
|
|
- name: item.name,
|
|
|
+ data: currentItem.optionList.map((item, index) => ({
|
|
|
+ name: item.optionName,
|
|
|
value: item.duration,
|
|
|
- timeLabel: item.timeLabel,
|
|
|
- duration: item.duration,
|
|
|
+ // 状态为1的项目默认选中(凸出)
|
|
|
+ selected: item.status === 1,
|
|
|
itemStyle: {
|
|
|
- color: item.color,
|
|
|
- shadowBlur: index === currentPhaseIndex ? 10 : 0,
|
|
|
- shadowColor: item.color,
|
|
|
+ color: getColorByIndex(index),
|
|
|
+ // 状态为1的项目增加发光效果
|
|
|
+ shadowBlur: item.status === 1 ? 10 : 0,
|
|
|
+ shadowColor:
|
|
|
+ item.status === 1 ? getColorByIndex(index) : "transparent",
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ itemStyle: {
|
|
|
+ shadowBlur: 15,
|
|
|
+ shadowColor: getColorByIndex(index),
|
|
|
+ },
|
|
|
},
|
|
|
})),
|
|
|
},
|
|
@@ -179,18 +145,41 @@ const updateChart = () => {
|
|
|
chartInstance.setOption(option, true);
|
|
|
};
|
|
|
|
|
|
-// 时间格式化 (HH:mm)
|
|
|
-const formatTime = (isoString) => {
|
|
|
- const date = new Date(isoString);
|
|
|
- return date.toTimeString().slice(0, 5);
|
|
|
+// 根据索引获取颜色
|
|
|
+const getColorByIndex = (index) => {
|
|
|
+ const colors = [
|
|
|
+ "#5470C6",
|
|
|
+ "#91CC75",
|
|
|
+ "#EE6666",
|
|
|
+ "#73C0DE",
|
|
|
+ "#FAC858",
|
|
|
+ "#3BA272",
|
|
|
+ "#FC8452",
|
|
|
+ "#9A60B4",
|
|
|
+ ];
|
|
|
+ return colors[index % colors.length];
|
|
|
};
|
|
|
|
|
|
const handleResize = () => {
|
|
|
chartInstance?.resize();
|
|
|
};
|
|
|
|
|
|
+// 加载数据
|
|
|
+const loadData = async () => {
|
|
|
+ try {
|
|
|
+ const res = await qualityConsistentInspection(2);
|
|
|
+ chartData.value = res.data;
|
|
|
+ if (chartData.value.length > 0) {
|
|
|
+ updateChart();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("加载数据失败:", error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
initChart();
|
|
|
+ loadData();
|
|
|
});
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
@@ -206,7 +195,7 @@ onBeforeUnmount(() => {
|
|
|
.multi-product-ring-container {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
- min-height: 450px; /* 增加高度适应新元素 */
|
|
|
+ min-height: 450px;
|
|
|
position: relative;
|
|
|
background-color: transparent;
|
|
|
border-radius: 8px;
|