zhangwencui
2026-04-01 5bd6b1f66c29b099c3dca5855607a16e9e18f0ea
src/views/index.vue
@@ -16,9 +16,9 @@
                   type="primary"
                   plain
                   @click="refreshDashboardData">刷新数据</el-button>
        <el-button size="small"
        <!-- <el-button size="small"
                   plain
                   @click="configDialogVisible = true">首页配置</el-button>
                   @click="configDialogVisible = true">首页配置</el-button> -->
      </div>
    </div>
    <div class="content-grid">
@@ -40,7 +40,7 @@
            </el-button>
          </div>
        </section>
        <section class="section-card">
        <!-- <section class="section-card">
          <div class="section-title">重点待办</div>
          <div class="todo-row"
               v-for="todo in todos"
@@ -49,7 +49,7 @@
                    :type="todo.type">{{ todo.level }}</el-tag>
            <span>{{ todo.title }}</span>
          </div>
        </section>
        </section> -->
        <section class="section-card">
          <div class="section-title">经营关注</div>
          <div class="focus-row"
@@ -157,11 +157,10 @@
          </section>
          <section class="section-card"
                   v-if="isSectionVisible('costChart')">
            <div class="section-title">能耗与成本结构</div>
            <div class="section-title">能耗类型占比</div>
            <Echarts :chartStyle="chartStyle"
                     :legend="costLegend"
                     :tooltip="pieTooltip"
                     :series="costSeries"
                     :series="energyTypeSeries"
                     style="height: 260px" />
          </section>
        </div>
@@ -543,6 +542,61 @@
    },
  ]);
  // 能耗类型占比数据
  const energyTypeSeries = reactive([
    {
      type: "pie",
      radius: ["40%", "70%"],
      center: ["50%", "50%"],
      avoidLabelOverlap: false,
      itemStyle: {
        borderRadius: 10,
        borderColor: "#fff",
        borderWidth: 2,
      },
      label: {
        show: true,
        formatter: "{b}: {d}%",
      },
      data: [
        { value: 0, name: "水", itemStyle: { color: "#409EFF" } },
        { value: 0, name: "电", itemStyle: { color: "#E6A23C" } },
        { value: 0, name: "气", itemStyle: { color: "#F56C6C" } },
      ],
    },
  ]);
  // 模拟能耗数据
  const energyData = reactive({
    water: 120,
    electricity: 350,
    gas: 80,
  });
  // 更新能耗类型占比图表
  const updateEnergyTypeChart = () => {
    const { water, electricity, gas } = energyData;
    const total = water + electricity + gas;
    energyTypeSeries[0].data = [
      {
        value: total > 0 ? ((water / total) * 100).toFixed(2) : 0,
        name: "水",
        itemStyle: { color: "#409EFF" },
      },
      {
        value: total > 0 ? ((electricity / total) * 100).toFixed(2) : 0,
        name: "电",
        itemStyle: { color: "#E6A23C" },
      },
      {
        value: total > 0 ? ((gas / total) * 100).toFixed(2) : 0,
        name: "气",
        itemStyle: { color: "#F56C6C" },
      },
    ];
  };
  const planTable = reactive([]);
  const recentTrendCards = reactive([
    {
@@ -715,102 +769,100 @@
  };
  const loadHomeTodos = async () => {
    try {
      const res = await homeTodos();
      const list = Array.isArray(res?.data) ? res.data : [];
      const mapped = list.slice(0, 4).map((item, idx) => {
        const text =
          item?.approveReason || item?.approveTypeName || `待处理事项 ${idx + 1}`;
        const levelType = idx === 0 ? "danger" : idx <= 2 ? "warning" : "success";
        const level = idx === 0 ? "高" : idx <= 2 ? "中" : "低";
        return { level, title: text, type: levelType };
      });
      updateArray(todos, mapped);
      const pendingMapped = list.slice(0, 4).map((item, idx) => {
        const title =
          item?.approveReason || item?.approveTypeName || `待处理事项 ${idx + 1}`;
        const path = inferTodoPath(item);
        return {
          id: item?.id || `${idx}-${title}`,
          title,
          level: idx === 0 ? "高" : idx <= 2 ? "中" : "低",
          type: idx === 0 ? "danger" : idx <= 2 ? "warning" : "success",
          path,
          ownerId: item?.approveUserId || item?.userId || "",
          ownerName: item?.approveUserName || item?.userName || "",
        };
      });
      updateArray(pendingTasks, pendingMapped);
    } catch (error) {
      console.error("homeTodos接口获取失败:", error);
    }
    // try {
    //   const res = await homeTodos();
    //   const list = Array.isArray(res?.data) ? res.data : [];
    //   const mapped = list.slice(0, 4).map((item, idx) => {
    //     const text =
    //       item?.approveReason || item?.approveTypeName || `待处理事项 ${idx + 1}`;
    //     const levelType = idx === 0 ? "danger" : idx <= 2 ? "warning" : "success";
    //     const level = idx === 0 ? "高" : idx <= 2 ? "中" : "低";
    //     return { level, title: text, type: levelType };
    //   });
    //   updateArray(todos, mapped);
    //   const pendingMapped = list.slice(0, 4).map((item, idx) => {
    //     const title =
    //       item?.approveReason || item?.approveTypeName || `待处理事项 ${idx + 1}`;
    //     const path = inferTodoPath(item);
    //     return {
    //       id: item?.id || `${idx}-${title}`,
    //       title,
    //       level: idx === 0 ? "高" : idx <= 2 ? "中" : "低",
    //       type: idx === 0 ? "danger" : idx <= 2 ? "warning" : "success",
    //       path,
    //       ownerId: item?.approveUserId || item?.userId || "",
    //       ownerName: item?.approveUserName || item?.userName || "",
    //     };
    //   });
    //   updateArray(pendingTasks, pendingMapped);
    // } catch (error) {
    //   console.error("homeTodos接口获取失败:", error);
    // }
  };
  const loadOrderAndProgress = async () => {
    try {
      const [orderRes, progressRes] = await Promise.allSettled([
        orderCount(),
        getProgressStatistics(),
      ]);
      if (orderRes.status === "fulfilled") {
        const items = Array.isArray(orderRes.value?.data)
          ? orderRes.value.data
          : [];
        const byName = Object.fromEntries(
          items.map(i => [String(i?.name || "").replace(/\s/g, ""), i?.value])
        );
        businessFocus[0].value = `${
          pickFirstNumber(byName, ["生产订单数", "生产订单总数", "总订单数"]) || 0
        } 单`;
        businessFocus[1].value = `${
          pickFirstNumber(byName, ["已完成订单数"]) || 0
        } 单`;
        businessFocus[2].value = `${
          pickFirstNumber(byName, ["待生产订单数", "未完成订单数"]) || 0
        } 单`;
        businessFocus[3].value = `${
          pickFirstNumber(byName, ["部分完成订单数"]) || 0
        } 单`;
      }
      if (progressRes.status === "fulfilled") {
        const p = progressRes.value?.data || {};
        const detail = Array.isArray(p.completedOrderDetails)
          ? p.completedOrderDetails
          : [];
        const rows = detail.slice(0, 6).map((item, index) => {
          const qty = pickFirstNumber(item, ["quantity", "planQuantity"]);
          const done = pickFirstNumber(item, [
            "completeQuantity",
            "completedQuantity",
          ]);
          return {
            planNo: item.npsNo || item.productionPlanNo || `NO-${index + 1}`,
            product: item.productCategory || item.productName || "-",
            qty,
            issued: done,
            status:
              qty > 0 && done >= qty ? "已完成" : done > 0 ? "执行中" : "待下发",
          };
        });
        updateArray(planTable, rows);
        setTrendCard(
          "planIssued",
          detail
            .slice(-7)
            .map(i =>
              pickFirstNumber(i, [
                "completeQuantity",
                "completedQuantity",
                "issueNum",
              ])
            )
        );
      }
    } catch (error) {
      console.error("orderCount/getProgressStatistics接口获取失败:", error);
    }
    // try {
    //   const [orderRes, progressRes] = await Promise.allSettled([
    //     orderCount(),
    //     getProgressStatistics(),
    //   ]);
    //   if (orderRes.status === "fulfilled") {
    //     const items = Array.isArray(orderRes.value?.data)
    //       ? orderRes.value.data
    //       : [];
    //     const byName = Object.fromEntries(
    //       items.map(i => [String(i?.name || "").replace(/\s/g, ""), i?.value])
    //     );
    //     businessFocus[0].value = `${
    //       pickFirstNumber(byName, ["生产订单数", "生产订单总数", "总订单数"]) || 0
    //     } 单`;
    //     businessFocus[1].value = `${
    //       pickFirstNumber(byName, ["已完成订单数"]) || 0
    //     } 单`;
    //     businessFocus[2].value = `${
    //       pickFirstNumber(byName, ["待生产订单数", "未完成订单数"]) || 0
    //     } 单`;
    //     businessFocus[3].value = `${
    //       pickFirstNumber(byName, ["部分完成订单数"]) || 0
    //     } 单`;
    //   }
    //   if (progressRes.status === "fulfilled") {
    //     const p = progressRes.value?.data || {};
    //     const detail = Array.isArray(p.completedOrderDetails)
    //       ? p.completedOrderDetails
    //       : [];
    //     const rows = detail.slice(0, 6).map((item, index) => {
    //       const qty = pickFirstNumber(item, ["quantity", "planQuantity"]);
    //       const done = pickFirstNumber(item, [
    //         "completeQuantity",
    //         "completedQuantity",
    //       ]);
    //       return {
    //         planNo: item.npsNo || item.productionPlanNo || `NO-${index + 1}`,
    //         product: item.productCategory || item.productName || "-",
    //         qty,
    //         issued: done,
    //         status:
    //           qty > 0 && done >= qty ? "已完成" : done > 0 ? "执行中" : "待下发",
    //       };
    //     });
    //     updateArray(planTable, rows);
    //     setTrendCard(
    //       "planIssued",
    //       detail
    //         .slice(-7)
    //         .map(i =>
    //           pickFirstNumber(i, [
    //             "completeQuantity",
    //             "completedQuantity",
    //             "issueNum",
    //           ])
    //         )
    //     );
    //   }
    // } catch (error) {
    //   console.error("orderCount/getProgressStatistics接口获取失败:", error);
    // }
  };
  const inferTodoPath = todo => {
@@ -827,112 +879,112 @@
  };
  const loadPlanTrend = async () => {
    try {
      const res = await processDataProductionStatistics({
        type: chartRangePlan.value,
      });
      const list = Array.isArray(res?.data) ? res.data : [];
      planXAxis[0].data = list.map(
        (i, index) => i.processName || `工序${index + 1}`
      );
      planSeries[0].data = list.map(i =>
        pickFirstNumber(i, ["totalInput", "input", "planNum"])
      );
      planSeries[1].data = list.map(i =>
        pickFirstNumber(i, ["totalOutput", "output", "issueNum"])
      );
      planSeries[2].data = list.map(i =>
        pickFirstNumber(i, ["totalScrap", "scrap", "completeNum"])
      );
    } catch (error) {
      console.error("processDataProductionStatistics接口获取失败:", error);
    }
    // try {
    //   const res = await processDataProductionStatistics({
    //     type: chartRangePlan.value,
    //   });
    //   const list = Array.isArray(res?.data) ? res.data : [];
    //   planXAxis[0].data = list.map(
    //     (i, index) => i.processName || `工序${index + 1}`
    //   );
    //   planSeries[0].data = list.map(i =>
    //     pickFirstNumber(i, ["totalInput", "input", "planNum"])
    //   );
    //   planSeries[1].data = list.map(i =>
    //     pickFirstNumber(i, ["totalOutput", "output", "issueNum"])
    //   );
    //   planSeries[2].data = list.map(i =>
    //     pickFirstNumber(i, ["totalScrap", "scrap", "completeNum"])
    //   );
    // } catch (error) {
    //   console.error("processDataProductionStatistics接口获取失败:", error);
    // }
  };
  const loadQualityData = async () => {
    try {
      const res = await qualityInspectionStatistics({
        type: chartRangeQuality.value,
      });
      const data = res?.data || {};
      const items = Array.isArray(data.item) ? data.item : [];
      if (items.length > 0) {
        qualityXAxis[0].data = items.map(i => i.date || i.name || "-");
        qualitySeries[0].data = items.map(i =>
          pickFirstNumber(i, [
            "supplierNum",
            "processNum",
            "factoryNum",
            "totalNum",
          ])
        );
        setTrendCard(
          "qualityRaw",
          items.map(i => pickFirstNumber(i, ["supplierNum"]))
        );
        setTrendCard(
          "qualityProcess",
          items.map(i => pickFirstNumber(i, ["processNum"]))
        );
        setTrendCard(
          "qualityFactory",
          items.map(i => pickFirstNumber(i, ["factoryNum"]))
        );
      } else {
        qualityXAxis[0].data = ["来料检", "过程检", "成品检"];
        qualitySeries[0].data = [
          pickFirstNumber(data, ["supplierNum"]),
          pickFirstNumber(data, ["processNum"]),
          pickFirstNumber(data, ["factoryNum"]),
        ];
        setTrendCard("qualityRaw", [pickFirstNumber(data, ["supplierNum"])]);
        setTrendCard("qualityProcess", [pickFirstNumber(data, ["processNum"])]);
        setTrendCard("qualityFactory", [pickFirstNumber(data, ["factoryNum"])]);
      }
      businessFocus[4].value = `${pickFirstNumber(data, [
        "supplierNum",
        "totalNum",
      ])} 条`;
      businessFocus[5].value = `${pickFirstNumber(data, ["processNum"])} 条`;
    } catch (error) {
      console.error("qualityInspectionStatistics接口获取失败:", error);
    }
    // try {
    //   const res = await qualityInspectionStatistics({
    //     type: chartRangeQuality.value,
    //   });
    //   const data = res?.data || {};
    //   const items = Array.isArray(data.item) ? data.item : [];
    //   if (items.length > 0) {
    //     qualityXAxis[0].data = items.map(i => i.date || i.name || "-");
    //     qualitySeries[0].data = items.map(i =>
    //       pickFirstNumber(i, [
    //         "supplierNum",
    //         "processNum",
    //         "factoryNum",
    //         "totalNum",
    //       ])
    //     );
    //     setTrendCard(
    //       "qualityRaw",
    //       items.map(i => pickFirstNumber(i, ["supplierNum"]))
    //     );
    //     setTrendCard(
    //       "qualityProcess",
    //       items.map(i => pickFirstNumber(i, ["processNum"]))
    //     );
    //     setTrendCard(
    //       "qualityFactory",
    //       items.map(i => pickFirstNumber(i, ["factoryNum"]))
    //     );
    //   } else {
    //     qualityXAxis[0].data = ["来料检", "过程检", "成品检"];
    //     qualitySeries[0].data = [
    //       pickFirstNumber(data, ["supplierNum"]),
    //       pickFirstNumber(data, ["processNum"]),
    //       pickFirstNumber(data, ["factoryNum"]),
    //     ];
    //     setTrendCard("qualityRaw", [pickFirstNumber(data, ["supplierNum"])]);
    //     setTrendCard("qualityProcess", [pickFirstNumber(data, ["processNum"])]);
    //     setTrendCard("qualityFactory", [pickFirstNumber(data, ["factoryNum"])]);
    //   }
    //   businessFocus[4].value = `${pickFirstNumber(data, [
    //     "supplierNum",
    //     "totalNum",
    //   ])} 条`;
    //   businessFocus[5].value = `${pickFirstNumber(data, ["processNum"])} 条`;
    // } catch (error) {
    //   console.error("qualityInspectionStatistics接口获取失败:", error);
    // }
  };
  const loadWarningCenter = async () => {
    try {
      const res = await nonComplianceWarning();
      const list = Array.isArray(res?.data) ? res.data : [];
      const mapped = list.slice(0, 6).map((item, idx) => {
        const levelNum = toNumber(item.level ?? item.warningLevel ?? 2);
        const levelType =
          levelNum >= 3 ? "danger" : levelNum === 2 ? "warning" : "info";
        const levelText = levelNum >= 3 ? "高" : levelNum === 2 ? "中" : "低";
        const title =
          item.name || item.title || item.paramName || `异常预警 ${idx + 1}`;
        const text = `${title}${item.processName || ""}${
          item.orderNo || ""
        }`.toLowerCase();
        const path = text.includes("质检")
          ? routePathMap.processInspection
          : text.includes("订单")
          ? routePathMap.order
          : routePathMap.processInspection ||
            routePathMap.order ||
            routePathMap.plan;
        return {
          id: item.id || `${idx}-${title}`,
          levelType,
          levelText,
          title,
          path,
        };
      });
      updateArray(warningList, mapped);
    } catch (error) {
      console.error("nonComplianceWarning接口获取失败:", error);
      updateArray(warningList, []);
    }
    // try {
    //   const res = await nonComplianceWarning();
    //   const list = Array.isArray(res?.data) ? res.data : [];
    //   const mapped = list.slice(0, 6).map((item, idx) => {
    //     const levelNum = toNumber(item.level ?? item.warningLevel ?? 2);
    //     const levelType =
    //       levelNum >= 3 ? "danger" : levelNum === 2 ? "warning" : "info";
    //     const levelText = levelNum >= 3 ? "高" : levelNum === 2 ? "中" : "低";
    //     const title =
    //       item.name || item.title || item.paramName || `异常预警 ${idx + 1}`;
    //     const text = `${title}${item.processName || ""}${
    //       item.orderNo || ""
    //     }`.toLowerCase();
    //     const path = text.includes("质检")
    //       ? routePathMap.processInspection
    //       : text.includes("订单")
    //       ? routePathMap.order
    //       : routePathMap.processInspection ||
    //         routePathMap.order ||
    //         routePathMap.plan;
    //     return {
    //       id: item.id || `${idx}-${title}`,
    //       levelType,
    //       levelText,
    //       title,
    //       path,
    //     };
    //   });
    //   updateArray(warningList, mapped);
    // } catch (error) {
    //   console.error("nonComplianceWarning接口获取失败:", error);
    //   updateArray(warningList, []);
    // }
  };
  const initSectionConfig = () => {
@@ -964,17 +1016,17 @@
  };
  const loadCostComposition = async () => {
    try {
      const res = await expenseCompositionAnalysis({ type: 1 });
      const list = Array.isArray(res?.data) ? res.data : [];
      const mapped = list.map(i => ({
        name: i.name || "未命名",
        value: pickFirstNumber(i, ["value", "amount", "cost"]),
      }));
      costSeries[0].data = mapped;
    } catch (error) {
      console.error("expenseCompositionAnalysis接口获取失败:", error);
    }
    // try {
    //   const res = await expenseCompositionAnalysis({ type: 1 });
    //   const list = Array.isArray(res?.data) ? res.data : [];
    //   const mapped = list.map(i => ({
    //     name: i.name || "未命名",
    //     value: pickFirstNumber(i, ["value", "amount", "cost"]),
    //   }));
    //   costSeries[0].data = mapped;
    // } catch (error) {
    //   console.error("expenseCompositionAnalysis接口获取失败:", error);
    // }
  };
  const refreshDashboardData = () => {
@@ -984,6 +1036,7 @@
    loadQualityData();
    loadCostComposition();
    loadWarningCenter();
    updateEnergyTypeChart();
    lastUpdatedAt.value = new Date().toLocaleString();
  };