瀏覽代碼

Merge branch 'master(gitee)'

# Conflicts:
#	src/views/analysis/process/Xbar-R.vue
#	src/views/analysis/process/Xbar-S-2.vue
#	src/views/analysis/process/Xbar-S.vue
#	src/views/analysis/process/index.vue
#	src/views/analysis/target/index/index.vue
“luofeng” 3 月之前
父節點
當前提交
451572c66a

+ 3 - 3
.env.development

@@ -8,10 +8,10 @@ VITE_APP_PORT = 3005
 VITE_APP_BASE_API = '/dev-api'
 
 # 上传文件接口地址
-VITE_APP_UPLOAD_URL = 'http://192.168.101.4:7105'
+VITE_APP_UPLOAD_URL = 'http://127.0.0.1:7105'
 # 开发接口地址
- VITE_APP_API_URL = 'http://139.155.176.112:7105'
-#  VITE_APP_API_URL = 'http://192.168.1.111:7105'
+ VITE_APP_API_URL = 'http://127.0.0.1:7105'
+
 
 # 是否启用 Mock 服务
 VITE_MOCK_DEV_SERVER = false

+ 131 - 1
src/api/analysis/index.js

@@ -1,4 +1,134 @@
 import request from "@/utils/request";
+import axios from "axios";
+
+export function collectData(data) {
+  return request({
+    url: "/api/v1/spc/collectData",
+    method: "post",
+    data,
+  });
+}
+export function cpkCompute(params) {
+  return request({
+    url: "/api/v1/spc/cpkCompute",
+    method: "post",
+    params: params,
+  });
+}
+export function deleteQualityFile(data) {
+  return request({
+    url: "/api/v1/quality/file/del",
+    method: "post",
+    data,
+  });
+}
+export function getQualityFileList(data) {
+  return request({
+    url: "/api/v1/quality/file/list",
+    method: "post",
+    data,
+  });
+}
+export function EWMACompute(data) {
+  return request({
+    url: "/api/v1/spc/EWMACompute",
+    method: "post",
+    data,
+  });
+}
+export function EWMACompute2(params) {
+  return request({
+    url: "/api/v1/spc/EWMACompute2",
+    method: "get",
+    params: params,
+  });
+}
+export function IMRCompute(data) {
+  return request({
+    url: "/api/v1/spc/IMRCompute",
+    method: "post",
+    data,
+  });
+}
+export function IMRCompute2(params) {
+  return request({
+    url: "/api/v1/spc/IMRCompute2",
+    method: "get",
+    params: params,
+  });
+}
+export function UCompute(data) {
+  return request({
+    url: "/api/v1/spc/UCompute",
+    method: "post",
+    data,
+  });
+}
+export function UCompute2(params) {
+  return request({
+    url: "/api/v1/spc/UCompute2",
+    method: "get",
+    params: params,
+  });
+}
+export function CCompute(data) {
+  return request({
+    url: "/api/v1/spc/CCompute",
+    method: "post",
+    data,
+  });
+}
+export function CCompute2(params) {
+  return request({
+    url: "/api/v1/spc/CCompute2",
+    method: "get",
+    params: params,
+  });
+}
+export function NPCompute(data) {
+  return request({
+    url: "/api/v1/spc/NPCompute",
+    method: "post",
+    data,
+  });
+}
+export function NPCompute2(params) {
+  return request({
+    url: "/api/v1/spc/NPCompute2",
+    method: "get",
+    params: params,
+  });
+}
+
+export function PCompute(data) {
+  return request({
+    url: "/api/v1/spc/PCompute",
+    method: "post",
+    data,
+  });
+}
+export function PCompute2(params) {
+  return request({
+    url: "/api/v1/spc/PCompute2",
+    method: "get",
+    params: params,
+  });
+}
+
+export function XBarSCompute(data) {
+  return request({
+    url: "/api/v1/spc/XBarSCompute",
+    method: "post",
+    data,
+  });
+}
+export function XBarRCompute(data) {
+  return request({
+    url: "/api/v1/spc/XBarRCompute",
+    method: "post",
+    data,
+  });
+}
 
 export function XBarSCompute(data) {
   return request({
@@ -10,7 +140,7 @@ export function XBarSCompute(data) {
 
 export function getData(data) {
   return request({
-    url: "/api/v1/spc/page1",
+    url: "/api/v1/spc/page",
     method: "post",
     data,
   });

+ 22 - 2
src/api/file/index.ts

@@ -2,12 +2,32 @@ import request from "@/utils/request";
 import { AxiosPromise } from "axios";
 import { FileInfo } from "./types";
 
+export function uploadFileApi(
+  file: File,
+  generatePdf: boolean = false
+): AxiosPromise<FileInfo> {
+  const formData = new FormData();
+  console.log(file);
+  formData.append("file", file);
+  formData.append("fileName", file.name);
+  formData.append("generatePdf", generatePdf);
+  return request({
+    url: "/api/v1/base/upload",
+    method: "post",
+    data: formData,
+    headers: {
+      "Content-Type": "multipart/form-data",
+    },
+  });
+}
+
+
 /**
  * 上传文件
  *
  * @param file
  */
-export function uploadFileApi(file: File): AxiosPromise<FileInfo> {
+/*export function uploadFileApi(file: File): AxiosPromise<FileInfo> {
   const formData = new FormData();
   formData.append("file", file);
   return request({
@@ -18,7 +38,7 @@ export function uploadFileApi(file: File): AxiosPromise<FileInfo> {
       "Content-Type": "multipart/form-data",
     },
   });
-}
+}*/
 
 /**
  * 删除文件

+ 2 - 1
src/hooks/userCrud.ts

@@ -364,12 +364,13 @@ export const useCrud = (config?: UseCrudConfig) => {
     /**
      * 根据搜索项导出数据
      */
-    exportData: async (urlStr: string) => {
+    exportData: async (urlStr: string, filePath: string) => {
       handleSearchData();
       const response = await request({
         url: urlStr,
         method: "post",
         data: search.value,
+        params: { filePath: filePath },
         responseType: "arraybuffer",
       });
       Utils.downloadFile(response);

+ 9 - 18
src/router/modules/analysis.ts

@@ -57,23 +57,14 @@ export default {
         },
       ],
     },
-    {
-      path: "sales",
-      component: () => import("@/views/sales/index.vue"),
-      name: "Sales",
-      meta: {
-        title: "销售反馈",
-        icon: "Guide",
-      },
-    },
-    {
-      path: "charts",
-      component: () => import("@/views/charts/index.vue"),
-      name: "Charts",
-      meta: {
-        title: "图表分析",
-        icon: "Guide",
-      },
-    },
+    // {
+    //   path: "charts",
+    //   component: () => import("@/views/charts/index.vue"),
+    //   name: "Charts",
+    //   meta: {
+    //     title: "图表分析",
+    //     icon: "Guide",
+    //   },
+    // },
   ],
 };

+ 9 - 0
src/router/modules/statistic.ts

@@ -37,5 +37,14 @@ export default {
         icon: "CreditCard",
       },
     },
+    {
+      path: "sales",
+      component: () => import("@/views/sales/index.vue"),
+      name: "Sales",
+      meta: {
+        title: "销售反馈",
+        icon: "Guide",
+      },
+    },
   ],
 };

+ 349 - 0
src/views/analysis/process/C.vue

@@ -0,0 +1,349 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box" v-show="!addStatus">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+            "
+          >
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              控制图绘制
+            </div>
+
+            <el-button
+              type="primary"
+              v-print="'#print'"
+              style="margin-left: 10px; height: 25px"
+              >打 印</el-button
+            >
+          </div>
+          <div class="info">
+            <div id="print">
+              <div id="charts" :style="{ height: maxHeight / 2 + 'px' }"></div>
+            </div>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+import { useDictionaryStore } from "@/store";
+import { CCompute } from "@/api/analysis";
+
+const tableData = ref([]);
+const chartData = ref({});
+const param = ref([
+  7, 5, 6, 8, 4, 9, 5, 6, 7, 4, 5, 10, 3, 6, 5, 7, 12, 5, 6, 4,
+]);
+const getTableData = async (data) => {
+  // const { data } = await CCompute(data);
+  data.CL = data.CL.toFixed(4);
+  data.LCL = data.LCL.toFixed(4);
+  data.UCL = data.UCL.toFixed(4);
+  data.DataPoints = data.DataPoints.map((num) => {
+    return parseFloat(num.y.toFixed(4));
+  });
+  chartData.value = data;
+
+  chartsOption1.value.title[0].text = `C控制图`;
+  setChart1();
+};
+
+const { dicts } = useDictionaryStore();
+const opOptions = ref([...dicts.spc_operation]);
+const value = ref(opOptions.value[0].remark);
+const Y1value = ref([]);
+const X1array = ref([]);
+const setY1value = () => {
+  Y1value.value = [];
+  chartData.value.DataPoints.forEach((item) => {
+    Y1value.value.push(item);
+  });
+  Y1value.value.unshift("");
+  Y1value.value.push("");
+
+  chartsOption1.value.series[0].data = Y1value.value;
+};
+
+const setX1array = async () => {
+  X1array.value = await [];
+  chartData.value.DataPoints.forEach((item, index) => {
+    X1array.value.push(index + 1);
+  });
+  X1array.value.unshift("");
+  X1array.value.push("");
+  chartsOption1.value.xAxis[0].data = X1array.value;
+};
+
+const setChart1Info = () => {
+  // chartsOption1.value.title[0].text = `上限=${showData.value.avgMax ? showData.value.avgMax : "-"}`;
+  // chartsOption1.value.title[0].text = `x̄=${showData.value.avgAvg ? showData.value.avgAvg : "-"}`;
+  chartsOption1.value.series[0].markLine.data[0].yAxis = chartData.value.UCL;
+  chartsOption1.value.series[0].markLine.data[0].label.formatter = `上限=${
+    chartData.value.UCL
+  }`;
+  chartsOption1.value.series[0].markLine.data[1].yAxis = chartData.value.LCL;
+  chartsOption1.value.series[0].markLine.data[1].label.formatter = `下限=${chartData.value.LCL}`;
+  chartsOption1.value.series[0].markLine.data[2].yAxis = chartData.value.CL;
+  chartsOption1.value.series[0].markLine.data[2].label.formatter = `x̄=${
+    chartData.value.CL
+  }`;
+  // chartsOption1.value.title[2].text = `下限=${showData.value.avgMin ? showData.value.avgMin : "0"}`;
+};
+const setChart1 = () => {
+  setChart1Info();
+  setY1value();
+  setX1array();
+  charts1.value.setOption(chartsOption1.value, true);
+};
+
+const title = ref("调阻精度");
+const showLable = ref("调阻");
+const changeSelect = () => {
+  setTimeout(async () => {
+    // showLable.value = selectRef.value.currentPlaceholder;
+    // opOptions.value.forEach((item) => {
+    //   if (item.dictLabel == showLable.value) {
+    //     lableValue.value = item.dictValue;
+    //   }
+    // });
+    // switch (showLable.value) {
+    //   case "调阻":
+    //     title.value = "调阻精度";
+    //     break;
+    //   case "粘片":
+    //     title.value = "剪切强度";
+    //     break;
+    //   case "键合":
+    //     title.value = "键合强度";
+    //     break;
+    //   default:
+    //     title.value = "调阻精度";
+    //     break;
+    // }
+    // await getTableData();
+    // setChart2();
+  }, 0);
+};
+
+const maxHeight = ref(null);
+const maxWidth = ref(null);
+const charts1 = shallowRef(null);
+const chartsOption1 = ref({
+  title: [
+    {
+      text: `C控制图`,
+      left: "40%",
+    },
+  ],
+  grid: {
+    right: "15%",
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: {},
+    },
+  },
+  tooltip: {
+    show: true,
+  },
+  xAxis: [
+    {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+  ],
+  yAxis: [
+    {
+      type: "value",
+      // scale: true, // 开启自适应缩放
+    },
+  ],
+  series: [
+    {
+      type: "line",
+      lineStyle: {
+        color: "rgb(26, 122, 240)",
+      },
+      symbolSize: 13,
+      symbol: "circle",
+      itemStyle: {
+        color: (params) => {
+          const paramValue = Number(params.value);
+          if (
+            paramValue <= Number(chartData.value.UCL) &&
+            paramValue >= Number(chartData.value.LCL)
+          ) {
+            return "rgb(26, 122, 240)";
+          } else {
+            return "red";
+          }
+        },
+      },
+      markLine: {
+        silent: true,
+        data: [
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `上限=${chartData.value.UCL}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `下限=${chartData.value.LCL}`,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+          {
+            yAxis: 0,
+            silent: false,
+            label: {
+              position: "end",
+              formatter: ``,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+        ],
+      },
+    },
+  ],
+});
+const Height = ref(0);
+const setHeight = () => {
+  Height.value = document.querySelector(".databox").clientHeight;
+  maxHeight.value = document.querySelector(".info").clientHeight;
+  maxWidth.value = document.querySelector(".info").clientWidth;
+};
+
+const setView = () => {
+  setHeight();
+  charts1.value = echarts.init(document.getElementById("charts"));
+  charts1.value.setOption(chartsOption1.value, true);
+};
+onMounted(() => {
+  // init();
+});
+const init = async (data) => {
+  tableData.value = data;
+
+  setHeight();
+
+  if (data) {
+    charts1.value = echarts.init(document.getElementById("charts"));
+    window.addEventListener("resize", setView);
+    await getTableData(data);
+    // changeSelect();
+    charts1.value.setOption(chartsOption1.value, true);
+  }
+};
+onBeforeUnmount(() => {
+  window.removeEventListener("resize", setView);
+});
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+@media print {
+  #print {
+    margin-left: -18%;
+  }
+}
+.formStyle {
+  width: 400px;
+  margin: 20px auto;
+}
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1044 - 0
src/views/analysis/process/CList.vue


+ 316 - 0
src/views/analysis/process/EWMA.vue

@@ -0,0 +1,316 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box" v-show="!addStatus">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+            "
+          >
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              控制图绘制
+            </div>
+
+            <el-button
+              type="primary"
+              v-print="'#print'"
+              style="margin-left: 10px; height: 25px"
+              >打 印</el-button
+            >
+          </div>
+          <div class="info">
+            <div id="print">
+              <div ref="chartRef" style="width: 100%; height: 400px"></div>
+            </div>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, watch } from "vue";
+import * as echarts from "echarts";
+import { EWMACompute } from "@/api/analysis";
+
+// 假设的数据
+var chartData = ref([]);
+
+const param = {
+  ProcessA: [50.1, 50.3, 49.8],
+};
+
+const getTableData = (resultData) => {
+  // 转换函数
+  const transformData = (data) => {
+    const result = [];
+    let sampleId = 1;
+
+    // 遍历原始数据,排除 total 属性
+    for (const key in data) {
+      const arr = data[key];
+      for (const item in arr) {
+        result.push({
+          sampleId: sampleId.toString(),
+          value: arr[item].EWMA,
+          ewma: arr[item].CL,
+          ucl: arr[item].UCL,
+          lcl: arr[item].LCL,
+        });
+        sampleId++;
+      }
+    }
+
+    return result;
+  };
+
+  // 转换后的数据
+  const formattedData = ref(transformData(resultData));
+
+  chartData.value = formattedData.value;
+};
+
+const chartRef = ref(null);
+const chartInstance = ref(null);
+
+const prepareData = () => {
+  let sampleIds = chartData.value.map((item) => item.sampleId);
+  let values = chartData.value.map((item) => item.value);
+  let ewmaValues = chartData.value.map((item) => item.ewma);
+  let uclValues = chartData.value.map((item) => item.ucl);
+  let lclValues = chartData.value.map((item) => item.lcl);
+
+  // 保留四位小数
+  values = values.map((num) => parseFloat(num.toFixed(4)));
+  ewmaValues = ewmaValues.map((num) => parseFloat(num.toFixed(4)));
+  uclValues = uclValues.map((num) => parseFloat(num.toFixed(4)));
+  lclValues = lclValues.map((num) => parseFloat(num.toFixed(4)));
+
+  return {
+    sampleIds,
+    values,
+    ewmaValues,
+    uclValues,
+    lclValues,
+  };
+};
+
+const initChart = (chartDom) => {
+  const myChart = echarts.init(chartDom);
+  const { sampleIds, values, ewmaValues, uclValues, lclValues } = prepareData();
+
+  const option = {
+    title: {
+      text: "EWMA控制图",
+      left: "center",
+    },
+    tooltip: {
+      trigger: "axis",
+    },
+    xAxis: {
+      type: "category",
+      boundaryGap: true, // 不留白,从原点开始
+      data: sampleIds,
+      axisLine: {
+        show: true,
+      },
+    },
+    yAxis: {
+      type: "value",
+      scale: true,
+      // name: "EWMA值",
+    },
+    series: [
+      {
+        name: "数值",
+        type: "line",
+        // step: "start",
+        symbol: "circle", //将小圆点改成实心 不写symbol默认空心
+        symbolSize: 8,
+        data: values.map((value, index) => ({
+          value,
+          itemStyle: {
+            color:
+              value > uclValues[index] || value < lclValues[index]
+                ? "red"
+                : "blue",
+          },
+        })),
+      },
+      {
+        name: "均值",
+        type: "line",
+        showSymbol: false,
+        data: ewmaValues,
+        lineStyle: {
+          color: "green",
+        },
+      },
+      {
+        name: "UCL",
+        type: "line",
+        data: uclValues,
+        showSymbol: false,
+        lineStyle: {
+          color: "red",
+          type: "dashed",
+        },
+      },
+      {
+        name: "LCL",
+        type: "line",
+        data: lclValues,
+        showSymbol: false,
+        lineStyle: {
+          color: "red",
+          type: "dashed",
+        },
+      },
+    ],
+    color: ["blue", "green", "red", "red", "black"],
+  };
+
+  myChart.setOption(option);
+  chartInstance.value = myChart;
+};
+
+const resizeChart = () => {
+  if (chartInstance.value) {
+    chartInstance.value.resize();
+  }
+};
+
+onMounted(() => {
+  window.addEventListener("resize", resizeChart);
+});
+
+// watch(chartData, () => {
+//   if (chartInstance.value) {
+//     const { sampleIds, values, ewmaValues, uclValues, lclValues, meanValue } =
+//       prepareData();
+//     const option = {
+//       title: [
+//         {
+//           text: `EWMA控制图`,
+//           left: "40%",
+//         },
+//       ],
+//       xAxis: {
+//         data: sampleIds,
+//       },
+//       series: [
+//         {
+//           data: values.map((value, index) => ({
+//             value,
+//             itemStyle: {
+//               color:
+//                 value > uclValues[index] || value < lclValues[index]
+//                   ? "red"
+//                   : "blue",
+//             },
+//           })),
+//         },
+//         {
+//           data: ewmaValues,
+//         },
+//         {
+//           data: uclValues,
+//         },
+//         {
+//           data: lclValues,
+//         },
+//         {
+//           data: Array(sampleIds.length).fill(meanValue),
+//         },
+//       ],
+//     };
+//     chartInstance.value.setOption(option);
+//   }
+// });
+
+const init = (data) => {
+  if (data) {
+    nextTick(async () => {
+      await getTableData(data);
+      await initChart(chartRef.value);
+    });
+  }
+};
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+/* 样式可以根据需要进行调整 */
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1044 - 0
src/views/analysis/process/EWMAList.vue


+ 566 - 0
src/views/analysis/process/I-MR.vue

@@ -0,0 +1,566 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box" v-show="!addStatus">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+            "
+          >
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              控制图绘制
+            </div>
+
+            <el-button
+              type="primary"
+              v-print="'#print'"
+              style="margin-left: 10px; height: 25px"
+              >打 印</el-button
+            >
+          </div>
+          <div class="info">
+            <div id="print">
+              <div id="charts" :style="{ height: maxHeight / 2 + 'px' }"></div>
+              <div id="charts1" :style="{ height: maxHeight / 2 + 'px' }"></div>
+            </div>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+import { useDictionaryStore } from "@/store";
+import { IMRCompute } from "@/api/analysis";
+
+const tableData = ref([]);
+const chartData = ref({
+  MR_CHART: {
+    LCL: 0,
+    CL: 0,
+    points: [],
+    UCL: 0,
+  },
+  I_CHART: {
+    LCL: 0,
+    CL: 0,
+    points: [],
+    UCL: 0,
+  },
+});
+const param = ref({
+  dataList: [7, 10.02, 9, 8.04, 11, 9.23, 10.1, 10.07],
+  subgroupSize: 2,
+});
+const getTableData = async (data) => {
+  data.MR_CHART.LCL = data.MR_CHART.LCL.toFixed(4);
+  data.MR_CHART.UCL = data.MR_CHART.UCL.toFixed(4);
+  data.MR_CHART.CL = data.MR_CHART.CL.toFixed(4);
+  data.MR_CHART.points = data.MR_CHART.points.map((num) =>
+    parseFloat(num.y.toFixed(4))
+  );
+  data.I_CHART.LCL = data.I_CHART.LCL.toFixed(4);
+  data.I_CHART.UCL = data.I_CHART.UCL.toFixed(4);
+  data.I_CHART.CL = data.I_CHART.CL.toFixed(4);
+  data.I_CHART.points = data.I_CHART.points.map((num) =>
+    parseFloat(num.y.toFixed(4))
+  );
+  chartData.value = data;
+
+  chartsOption1.value.title[0].text = `I-MR控制图`;
+  setChart1();
+  setChart2();
+};
+
+const { dicts } = useDictionaryStore();
+const opOptions = ref([...dicts.spc_operation]);
+const value = ref(opOptions.value[0].remark);
+const Y1value = ref([]);
+const X1array = ref([]);
+const setY1value = () => {
+  Y1value.value = [];
+  chartData.value.I_CHART.points.forEach((item) => {
+    Y1value.value.push(item);
+  });
+  Y1value.value.unshift("");
+  Y1value.value.push("");
+
+  chartsOption1.value.series[0].data = Y1value.value;
+};
+
+const setX1array = async () => {
+  X1array.value = await [];
+  chartData.value.I_CHART.points.forEach((item, index) => {
+    X1array.value.push(index + 1);
+  });
+  X1array.value.unshift("");
+  X1array.value.push("");
+  chartsOption1.value.xAxis[0].data = X1array.value;
+};
+
+const setChart1Info = () => {
+  // chartsOption1.value.title[0].text = `上限=${showData.value.avgMax ? showData.value.avgMax : "-"}`;
+  // chartsOption1.value.title[0].text = `x̄=${showData.value.avgAvg ? showData.value.avgAvg : "-"}`;
+  chartsOption1.value.series[0].markLine.data[0].yAxis =
+    chartData.value.I_CHART.UCL;
+  chartsOption1.value.series[0].markLine.data[0].label.formatter = `上限=${
+    chartData.value.I_CHART.UCL
+  }`;
+  chartsOption1.value.series[0].markLine.data[1].yAxis =
+    chartData.value.I_CHART.LCL;
+  chartsOption1.value.series[0].markLine.data[1].label.formatter = `下限=${
+    chartData.value.I_CHART.LCL
+  }`;
+  chartsOption1.value.series[0].markLine.data[2].yAxis =
+    chartData.value.I_CHART.CL;
+  chartsOption1.value.series[0].markLine.data[2].label.formatter = `x̄=${
+    chartData.value.I_CHART.CL
+  }`;
+  // chartsOption1.value.title[2].text = `下限=${showData.value.avgMin ? showData.value.avgMin : "0"}`;
+};
+const setChart2Info = () => {
+  // chartsOption2.value.title[0].text = `上限=${showData.value.rangeMax ? showData.value.rangeMax : "-"}`;
+  // chartsOption2.value.title[0].text = `R=${showData.value.rangeAvg ? showData.value.rangeAvg : "-"}`;
+  chartsOption2.value.series[0].markLine.data[0].yAxis =
+    chartData.value.MR_CHART.UCL;
+  chartsOption2.value.series[0].markLine.data[0].label.formatter = `上限=${
+    chartData.value.MR_CHART.UCL
+  }`;
+  chartsOption2.value.series[0].markLine.data[1].yAxis =
+    chartData.value.MR_CHART.LCL;
+  chartsOption2.value.series[0].markLine.data[1].label.formatter = `下限=${
+    chartData.value.MR_CHART.LCL
+  }`;
+  chartsOption2.value.series[0].markLine.data[2].yAxis =
+    chartData.value.MR_CHART.CL;
+  chartsOption2.value.series[0].markLine.data[2].label.formatter = `x̄=${
+    chartData.value.MR_CHART.CL
+  }`;
+  // chartsOption2.value.title[2].text = `下限=${showData.value.rangeMin ? showData.value.rangeMin : "0"}`;
+};
+const setChart1 = () => {
+  setChart1Info();
+  setY1value();
+  setX1array();
+  charts1.value.setOption(chartsOption1.value, true);
+};
+const setChart2 = () => {
+  setChart2Info();
+  setY2value();
+  setX2array();
+  charts2.value.setOption(chartsOption2.value, true);
+};
+
+const title = ref("调阻精度");
+const showLable = ref("调阻");
+const changeSelect = () => {
+  setTimeout(async () => {
+    // showLable.value = selectRef.value.currentPlaceholder;
+    // opOptions.value.forEach((item) => {
+    //   if (item.dictLabel == showLable.value) {
+    //     lableValue.value = item.dictValue;
+    //   }
+    // });
+    // switch (showLable.value) {
+    //   case "调阻":
+    //     title.value = "调阻精度";
+    //     break;
+    //   case "粘片":
+    //     title.value = "剪切强度";
+    //     break;
+    //   case "键合":
+    //     title.value = "键合强度";
+    //     break;
+    //   default:
+    //     title.value = "调阻精度";
+    //     break;
+    // }
+    // await getTableData();
+
+  }, 0);
+};
+
+const maxHeight = ref(null);
+const maxWidth = ref(null);
+const charts1 = shallowRef(null);
+const charts2 = shallowRef(null);
+const chartsOption1 = ref({
+  title: [
+    // {
+    //   text: `x̄=${showData.value.avgAvg ? showData.value.avgAvg : "-"}`,
+    //   right: "5%",
+    //   top: "42%",
+    //   textStyle: {
+    //     fontSize: 15,
+    //     color: "#333",
+    //     fontWeight: 100,
+    //   },
+    // },
+    {
+      text: `I-MR控制图`,
+      left: "40%",
+    },
+    {
+      text: "单",
+      left: "4%",
+      top: "42%",
+    },
+    {
+      text: "值",
+      left: "4%",
+      top: "49%",
+    },
+  ],
+  grid: {
+    right: "15%",
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: {},
+    },
+  },
+  tooltip: {
+    show: true,
+  },
+  xAxis: [
+    {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+  ],
+  yAxis: [
+    {
+      type: "value",
+      scale: true, // 开启自适应缩放
+    },
+  ],
+  series: [
+    {
+      type: "line",
+      lineStyle: {
+        color: "rgb(26, 122, 240)",
+      },
+      symbolSize: 13,
+      symbol: "circle",
+      itemStyle: {
+        color: (params) => {
+          const paramValue = Number(params.value);
+          if (
+            paramValue <= Number(chartData.value.I_CHART.UCL) &&
+            paramValue >= Number(chartData.value.I_CHART.LCL)
+          ) {
+            return "rgb(26, 122, 240)";
+          } else {
+            return "red";
+          }
+        },
+      },
+      markLine: {
+        silent: true,
+        data: [
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `上限=${chartData.value.I_CHART.UCL}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `下限=${chartData.value.I_CHART.LCL}`,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+          {
+            yAxis: 0,
+            silent: false,
+            label: {
+              position: "end",
+              formatter: ``,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+        ],
+      },
+    },
+  ],
+});
+const chartsOption2 = ref({
+  title: [
+    // {
+    //   text: `R=${showData.value.rangeAvg ? showData.value.rangeAvg : "-"}`,
+    //   right: "5%",
+    //   top: "42%",
+    //   textStyle: {
+    //     fontSize: 15,
+    //     color: "#333",
+    //     fontWeight: 100,
+    //   },
+    // },
+    {
+      text: "移",
+      left: "4%",
+      top: "28%",
+    },
+    {
+      text: "动",
+      left: "4%",
+      top: "35%",
+    },
+    {
+      text: "极",
+      left: "4%",
+      top: "42%",
+    },
+    {
+      text: "差",
+      left: "4%",
+      top: "49%",
+    },
+  ],
+  toolbox: {
+    feature: {
+      saveAsImage: {},
+    },
+  },
+  grid: {
+    right: "15%",
+  },
+  xAxis: [
+    {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+  ],
+  yAxis: [
+    {
+      type: "value",
+      scale: true, // 开启自适应缩放
+    },
+  ],
+  tooltip: {
+    show: true,
+  },
+  series: [
+    {
+      type: "line",
+      lineStyle: {
+        color: "rgb(26, 122, 240)",
+      },
+      symbolSize: 13,
+      symbol: "circle",
+      itemStyle: {
+        color: (params) => {
+          const paramValue = Number(params.value);
+          if (
+            paramValue <= Number(chartData.value.MR_CHART.UCL) &&
+            paramValue >= Number(chartData.value.MR_CHART.LCL)
+          ) {
+            return "rgb(26, 122, 240)";
+          } else {
+            return "red";
+          }
+        },
+      },
+      markLine: {
+        silent: true,
+        data: [
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `上限=${chartData.value.MR_CHART.UCL}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `下限=${chartData.value.MR_CHART.LCL}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            yAxis: 0,
+            silent: false,
+            label: {
+              position: "end",
+              formatter: ``,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+        ],
+      },
+    },
+  ],
+});
+const Height = ref(0);
+const setHeight = () => {
+  Height.value = document.querySelector(".databox").clientHeight;
+  maxHeight.value = document.querySelector(".info").clientHeight;
+  maxWidth.value = document.querySelector(".info").clientWidth;
+};
+const Y2value = ref([]);
+const X2array = ref([]);
+const setY2value = () => {
+  Y2value.value = [];
+  chartData.value.MR_CHART.points.forEach((item) => {
+    Y2value.value.push(item);
+  });
+  Y2value.value.unshift("");
+  Y2value.value.push("");
+  chartsOption2.value.series[0].data = Y2value.value;
+};
+const setX2array = () => {
+  X2array.value = [];
+  chartData.value.MR_CHART.points.forEach((item, index) => {
+    X2array.value.push(index + 1);
+  });
+  X2array.value.unshift("");
+  X2array.value.push("");
+  chartsOption2.value.xAxis[0].data = X2array.value;
+};
+
+const setView = () => {
+  setHeight();
+  charts1.value = echarts.init(document.getElementById("charts"));
+  charts2.value = echarts.init(document.getElementById("charts1"));
+  charts1.value.setOption(chartsOption1.value, true);
+  charts2.value.setOption(chartsOption2.value, true);
+};
+onMounted(() => {
+  // init();
+});
+const init = async (data) => {
+  tableData.value = data;
+
+  setHeight();
+
+  if (data) {
+    charts1.value = echarts.init(document.getElementById("charts"));
+    charts2.value = echarts.init(document.getElementById("charts1"));
+    window.addEventListener("resize", setView);
+    await getTableData(data);
+    // changeSelect();
+    charts1.value.setOption(chartsOption1.value, true);
+    charts2.value.setOption(chartsOption2.value, true);
+  }
+};
+onBeforeUnmount(() => {
+  window.removeEventListener("resize", setView);
+});
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+@media print {
+  #print {
+    margin-left: -18%;
+  }
+}
+.formStyle {
+  width: 400px;
+  margin: 20px auto;
+}
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1044 - 0
src/views/analysis/process/I-MRList.vue


+ 350 - 0
src/views/analysis/process/NP.vue

@@ -0,0 +1,350 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box" v-show="!addStatus">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+            "
+          >
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              控制图绘制
+            </div>
+
+            <el-button
+              type="primary"
+              v-print="'#print'"
+              style="margin-left: 10px; height: 25px"
+              >打 印</el-button
+            >
+          </div>
+          <div class="info">
+            <div id="print">
+              <div id="charts" :style="{ height: maxHeight / 2 + 'px' }"></div>
+            </div>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+import { useDictionaryStore } from "@/store";
+import { NPCompute } from "@/api/analysis";
+
+const tableData = ref([]);
+const chartData = ref({});
+const param = ref({
+  dataList: [3, 7, 5],
+  subgroupSize: 150,
+  scale: 4,
+});
+const getTableData = async (data) => {
+  data.cl = data.cl.toFixed(4);
+  data.lcl = data.lcl.toFixed(4);
+  data.ucl = data.ucl.toFixed(4);
+  data.DataPoints = data.DataPoints.map((num) => {
+    return parseFloat(num.y.toFixed(4));
+  });
+  chartData.value = data;
+
+  chartsOption1.value.title[0].text = `NP控制图`;
+  setChart1();
+};
+
+const { dicts } = useDictionaryStore();
+const opOptions = ref([...dicts.spc_operation]);
+const value = ref(opOptions.value[0].remark);
+const Y1value = ref([]);
+const X1array = ref([]);
+const setY1value = () => {
+  Y1value.value = [];
+  chartData.value.DataPoints.forEach((item) => {
+    Y1value.value.push(item);
+  });
+  Y1value.value.unshift("");
+  Y1value.value.push("");
+
+  chartsOption1.value.series[0].data = Y1value.value;
+};
+
+const setX1array = async () => {
+  X1array.value = await [];
+  chartData.value.DataPoints.forEach((item, index) => {
+    X1array.value.push(index + 1);
+  });
+  X1array.value.unshift("");
+  X1array.value.push("");
+  chartsOption1.value.xAxis[0].data = X1array.value;
+};
+
+const setChart1Info = () => {
+  // chartsOption1.value.title[0].text = `上限=${showData.value.avgMax ? showData.value.avgMax : "-"}`;
+  // chartsOption1.value.title[0].text = `x̄=${showData.value.avgAvg ? showData.value.avgAvg : "-"}`;
+  chartsOption1.value.series[0].markLine.data[0].yAxis = chartData.value.ucl;
+  chartsOption1.value.series[0].markLine.data[0].label.formatter = `上限=${
+    chartData.value.ucl
+  }`;
+  chartsOption1.value.series[0].markLine.data[1].yAxis = chartData.value.lcl;
+  chartsOption1.value.series[0].markLine.data[1].label.formatter = `下限=${chartData.value.lcl}`;
+  chartsOption1.value.series[0].markLine.data[2].yAxis = chartData.value.cl;
+  chartsOption1.value.series[0].markLine.data[2].label.formatter = `x̄=${
+    chartData.value.cl
+  }`;
+  // chartsOption1.value.title[2].text = `下限=${showData.value.avgMin ? showData.value.avgMin : "0"}`;
+};
+const setChart1 = () => {
+  setChart1Info();
+  setY1value();
+  setX1array();
+  charts1.value.setOption(chartsOption1.value, true);
+};
+
+const title = ref("调阻精度");
+const showLable = ref("调阻");
+const changeSelect = () => {
+  setTimeout(async () => {
+    // showLable.value = selectRef.value.currentPlaceholder;
+    // opOptions.value.forEach((item) => {
+    //   if (item.dictLabel == showLable.value) {
+    //     lableValue.value = item.dictValue;
+    //   }
+    // });
+    // switch (showLable.value) {
+    //   case "调阻":
+    //     title.value = "调阻精度";
+    //     break;
+    //   case "粘片":
+    //     title.value = "剪切强度";
+    //     break;
+    //   case "键合":
+    //     title.value = "键合强度";
+    //     break;
+    //   default:
+    //     title.value = "调阻精度";
+    //     break;
+    // }
+    // await getTableData();
+    // setChart2();
+  }, 0);
+};
+
+const maxHeight = ref(null);
+const maxWidth = ref(null);
+const charts1 = shallowRef(null);
+const chartsOption1 = ref({
+  title: [
+    {
+      text: `NP控制图`,
+      left: "40%",
+    },
+  ],
+  grid: {
+    right: "15%",
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: {},
+    },
+  },
+  tooltip: {
+    show: true,
+  },
+  xAxis: [
+    {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+  ],
+  yAxis: [
+    {
+      type: "value",
+      scale: true, // 开启自适应缩放
+    },
+  ],
+  series: [
+    {
+      type: "line",
+      lineStyle: {
+        color: "rgb(26, 122, 240)",
+      },
+      symbolSize: 13,
+      symbol: "circle",
+      itemStyle: {
+        color: (params) => {
+          const paramValue = Number(params.value);
+          if (
+            paramValue <= Number(chartData.value.ucl) &&
+            paramValue >= Number(chartData.value.lcl)
+          ) {
+            return "rgb(26, 122, 240)";
+          } else {
+            return "red";
+          }
+        },
+      },
+      markLine: {
+        silent: true,
+        data: [
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `上限=${chartData.value.ucl}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `下限=${chartData.value.lcl}`,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+          {
+            yAxis: 0,
+            silent: false,
+            label: {
+              position: "end",
+              formatter: ``,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+        ],
+      },
+    },
+  ],
+});
+const Height = ref(0);
+const setHeight = () => {
+  Height.value = document.querySelector(".databox").clientHeight;
+  maxHeight.value = document.querySelector(".info").clientHeight;
+  maxWidth.value = document.querySelector(".info").clientWidth;
+};
+
+const setView = () => {
+  setHeight();
+  charts1.value = echarts.init(document.getElementById("charts"));
+  charts1.value.setOption(chartsOption1.value, true);
+};
+onMounted(() => {
+  // init();
+});
+const init = async (data) => {
+  tableData.value = data;
+
+  setHeight();
+
+  if (data) {
+    charts1.value = echarts.init(document.getElementById("charts"));
+    window.addEventListener("resize", setView);
+    await getTableData(data);
+    // changeSelect();
+    charts1.value.setOption(chartsOption1.value, true);
+  }
+};
+onBeforeUnmount(() => {
+  window.removeEventListener("resize", setView);
+});
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+@media print {
+  #print {
+    margin-left: -18%;
+  }
+}
+.formStyle {
+  width: 400px;
+  margin: 20px auto;
+}
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1044 - 0
src/views/analysis/process/NPList.vue


+ 328 - 0
src/views/analysis/process/P.vue

@@ -0,0 +1,328 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box" v-show="!addStatus">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+            "
+          >
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              控制图绘制
+            </div>
+
+            <el-button
+              type="primary"
+              v-print="'#print'"
+              style="margin-left: 10px; height: 25px"
+              >打 印</el-button
+            >
+          </div>
+          <div class="info">
+            <div id="print">
+              <div ref="chartRef" style="width: 100%; height: 400px"></div>
+            </div>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, watch } from "vue";
+import * as echarts from "echarts";
+import { PCompute } from "@/api/analysis";
+
+// 假设的数据
+var chartData = ref([]);
+
+const param = {
+  x: {
+    defectCount: 30,
+    sampleSize: 500,
+  },
+  x2: {
+    defectCount: 50,
+    sampleSize: 600,
+  },
+};
+
+const getTableData = async (resultData) => {
+  // 转换函数
+  const transformData = (data) => {
+    const result = [];
+    let sampleId = 1;
+
+    // 遍历原始数据,排除 total 属性
+    for (const key in data) {
+      if (key !== "total") {
+        const item = data[key];
+        result.push({
+          sampleId: sampleId.toString(),
+          value: item.p,
+          ewma: item.cl,
+          ucl: item.ucl,
+          lcl: item.lcl,
+        });
+        sampleId++;
+      }
+    }
+
+    return result;
+  };
+
+  // 转换后的数据
+  const formattedData = ref(transformData(resultData));
+  chartData.value = formattedData.value;
+};
+
+const chartRef = ref(null);
+const chartInstance = ref(null);
+
+const prepareData = () => {
+  let sampleIds = chartData.value.map((item) => item.sampleId);
+  let values = chartData.value.map((item) => item.value);
+  let ewmaValues = chartData.value.map((item) => item.ewma);
+  let uclValues = chartData.value.map((item) => item.ucl);
+  let lclValues = chartData.value.map((item) => item.lcl);
+
+  // 保留四位小数
+  values = values.map((num) => parseFloat(num.toFixed(4)));
+  ewmaValues = ewmaValues.map((num) => parseFloat(num.toFixed(4)));
+  uclValues = uclValues.map((num) => parseFloat(num.toFixed(4)));
+  lclValues = lclValues.map((num) => parseFloat(num.toFixed(4)));
+
+  return {
+    sampleIds,
+    values,
+    ewmaValues,
+    uclValues,
+    lclValues,
+  };
+};
+
+const initChart = (chartDom) => {
+  const myChart = echarts.init(chartDom);
+  const { sampleIds, values, ewmaValues, uclValues, lclValues } = prepareData();
+
+  const option = {
+    title: {
+      text: "P控制图",
+      left: "center",
+    },
+    tooltip: {
+      trigger: "axis",
+    },
+    xAxis: {
+      type: "category",
+      boundaryGap: true, // 不留白,从原点开始
+      data: sampleIds,
+    },
+    yAxis: {
+      type: "value",
+      // name: "EWMA值",
+    },
+    series: [
+      {
+        name: "数值",
+        type: "line",
+        // step: "start",
+        symbol: "circle", //将小圆点改成实心 不写symbol默认空心
+        symbolSize: 8,
+        data: values.map((value, index) => ({
+          value,
+          itemStyle: {
+            color:
+              value > uclValues[index] || value < lclValues[index]
+                ? "red"
+                : "blue",
+          },
+        })),
+      },
+      {
+        name: "均值",
+        type: "line",
+        showSymbol: false,
+        data: ewmaValues,
+        lineStyle: {
+          color: "green",
+        },
+      },
+      // {
+      //   name: "均值",
+      //   type: "line",
+      //   data: Array(sampleIds.length).fill(meanValue),
+      //   showSymbol: false,
+      //   lineStyle: {
+      //     color: "green",
+      //     type: "solid",
+      //   },
+      // },
+      {
+        name: "UCL",
+        type: "line",
+        data: uclValues,
+        showSymbol: false,
+        lineStyle: {
+          color: "red",
+          type: "dashed",
+        },
+      },
+      {
+        name: "LCL",
+        type: "line",
+        data: lclValues,
+        showSymbol: false,
+        lineStyle: {
+          color: "red",
+          type: "dashed",
+        },
+      },
+    ],
+    color: ["blue", "green", "red", "red", "black"],
+  };
+
+  myChart.setOption(option);
+  chartInstance.value = myChart;
+};
+
+const resizeChart = () => {
+  if (chartInstance.value) {
+    chartInstance.value.resize();
+  }
+};
+
+onMounted(() => {
+  window.addEventListener("resize", resizeChart);
+});
+
+// watch(chartData, () => {
+//   if (chartInstance.value) {
+//     const { sampleIds, values, ewmaValues, uclValues, lclValues, meanValue } =
+//       prepareData();
+//     const option = {
+//       title: [
+//         {
+//           text: `P控制图`,
+//           left: "40%",
+//         },
+//       ],
+//       xAxis: {
+//         data: sampleIds,
+//       },
+//       series: [
+//         {
+//           data: values.map((value, index) => ({
+//             value,
+//             itemStyle: {
+//               color:
+//                 value > uclValues[index] || value < lclValues[index]
+//                   ? "red"
+//                   : "blue",
+//             },
+//           })),
+//         },
+//         {
+//           data: ewmaValues,
+//         },
+//         {
+//           data: uclValues,
+//         },
+//         {
+//           data: lclValues,
+//         },
+//         {
+//           data: Array(sampleIds.length).fill(meanValue),
+//         },
+//       ],
+//     };
+//     chartInstance.value.setOption(option);
+//   }
+// });
+
+const init = (data) => {
+  if (data) {
+    nextTick(async () => {
+      await getTableData(data);
+      await initChart(chartRef.value);
+    });
+  }
+};
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+/* 样式可以根据需要进行调整 */
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1044 - 0
src/views/analysis/process/PList.vue


+ 319 - 0
src/views/analysis/process/U.vue

@@ -0,0 +1,319 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box" v-show="!addStatus">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+            "
+          >
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              控制图绘制
+            </div>
+
+            <el-button
+              type="primary"
+              v-print="'#print'"
+              style="margin-left: 10px; height: 25px"
+              >打 印</el-button
+            >
+          </div>
+          <div class="info">
+            <div id="print">
+              <div ref="chartRef" style="width: 100%; height: 400px"></div>
+            </div>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, watch } from "vue";
+import * as echarts from "echarts";
+import { UCompute } from "@/api/analysis";
+
+// 假设的数据
+var chartData = ref([]);
+
+const param = {
+  x: {
+    defectCount: 30,
+    sampleSize: 100,
+  },
+  x2: {
+    defectCount: 50,
+    sampleSize: 200,
+  },
+};
+
+const getTableData = async (resultData) => {
+  // 转换函数
+  const transformData = (data) => {
+    const result = [];
+    let sampleId = 1;
+
+    // 遍历原始数据,排除 total 属性
+    for (const key in data) {
+      if (key !== "total") {
+        const item = data[key];
+        result.push({
+          sampleId: sampleId.toString(),
+          value: item.u,
+          ewma: item.cl,
+          ucl: item.ucl,
+          lcl: item.lcl,
+        });
+        sampleId++;
+      }
+    }
+
+    return result;
+  };
+
+  // 转换后的数据
+  const formattedData = ref(transformData(resultData));
+
+  chartData.value = formattedData.value;
+};
+
+const chartRef = ref(null);
+const chartInstance = ref(null);
+
+const prepareData = () => {
+  let sampleIds = chartData.value.map((item) => item.sampleId);
+  let values = chartData.value.map((item) => item.value);
+  let ewmaValues = chartData.value.map((item) => item.ewma);
+  let uclValues = chartData.value.map((item) => item.ucl);
+  let lclValues = chartData.value.map((item) => item.lcl);
+
+  // 保留四位小数
+  values = values.map((num) => parseFloat(num.toFixed(4)));
+  ewmaValues = ewmaValues.map((num) => parseFloat(num.toFixed(4)));
+  uclValues = uclValues.map((num) => parseFloat(num.toFixed(4)));
+  lclValues = lclValues.map((num) => parseFloat(num.toFixed(4)));
+
+  return {
+    sampleIds,
+    values,
+    ewmaValues,
+    uclValues,
+    lclValues,
+  };
+};
+
+const initChart = (chartDom) => {
+  const myChart = echarts.init(chartDom);
+  const { sampleIds, values, ewmaValues, uclValues, lclValues } = prepareData();
+
+  const option = {
+    title: {
+      text: "U控制图",
+      left: "center",
+    },
+    tooltip: {
+      trigger: "axis",
+    },
+    xAxis: {
+      type: "category",
+      boundaryGap: true, // 不留白,从原点开始
+      data: sampleIds,
+    },
+    yAxis: {
+      type: "value",
+      // name: "EWMA值",
+    },
+    series: [
+      {
+        name: "数值",
+        type: "line",
+        // step: "start",
+        symbol: "circle", //将小圆点改成实心 不写symbol默认空心
+        symbolSize: 8,
+        data: values.map((value, index) => ({
+          value,
+          itemStyle: {
+            color:
+              value > uclValues[index] || value < lclValues[index]
+                ? "red"
+                : "blue",
+          },
+        })),
+      },
+      {
+        name: "均值",
+        type: "line",
+        showSymbol: false,
+        data: ewmaValues,
+        lineStyle: {
+          color: "green",
+        },
+      },
+      {
+        name: "UCL",
+        type: "line",
+        data: uclValues,
+        showSymbol: false,
+        lineStyle: {
+          color: "red",
+          type: "dashed",
+        },
+      },
+      {
+        name: "LCL",
+        type: "line",
+        data: lclValues,
+        showSymbol: false,
+        lineStyle: {
+          color: "red",
+          type: "dashed",
+        },
+      },
+    ],
+    color: ["blue", "green", "red", "red", "black"],
+  };
+
+  myChart.setOption(option);
+  chartInstance.value = myChart;
+};
+
+const resizeChart = () => {
+  if (chartInstance.value) {
+    chartInstance.value.resize();
+  }
+};
+
+onMounted(() => {
+  window.addEventListener("resize", resizeChart);
+});
+
+// watch(chartData, () => {
+//   if (chartInstance.value) {
+//     const { sampleIds, values, ewmaValues, uclValues, lclValues, meanValue } =
+//       prepareData();
+//     const option = {
+//       title: [
+//         {
+//           text: `U控制图`,
+//           left: "40%",
+//         },
+//       ],
+//       xAxis: {
+//         data: sampleIds,
+//       },
+//       series: [
+//         {
+//           data: values.map((value, index) => ({
+//             value,
+//             itemStyle: {
+//               color:
+//                 value > uclValues[index] || value < lclValues[index]
+//                   ? "red"
+//                   : "blue",
+//             },
+//           })),
+//         },
+//         {
+//           data: ewmaValues,
+//         },
+//         {
+//           data: uclValues,
+//         },
+//         {
+//           data: lclValues,
+//         },
+//         {
+//           data: Array(sampleIds.length).fill(meanValue),
+//         },
+//       ],
+//     };
+//     chartInstance.value.setOption(option);
+//   }
+// });
+
+const init = (data) => {
+  if (data) {
+    nextTick(async () => {
+      await getTableData(data);
+      await initChart(chartRef.value);
+    });
+  }
+};
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+/* 样式可以根据需要进行调整 */
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1044 - 0
src/views/analysis/process/UList.vue


+ 546 - 0
src/views/analysis/process/Xbar-R-2.vue

@@ -0,0 +1,546 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box" v-show="!addStatus">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+            "
+          >
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              控制图绘制
+            </div>
+
+            <el-button
+              type="primary"
+              v-print="'#print'"
+              style="margin-left: 10px; height: 25px"
+              >打 印</el-button
+            >
+          </div>
+          <div class="info">
+            <div id="print">
+              <div id="charts" :style="{ height: maxHeight / 2 + 'px' }"></div>
+              <div id="charts1" :style="{ height: maxHeight / 2 + 'px' }"></div>
+            </div>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+import { useDictionaryStore } from "@/store";
+
+const tableData = ref([]);
+const chartData = ref([]);
+const getTableData = async (data) => {
+  data.lclX = data.lclX.toFixed(4);
+  data.uclR = data.uclR.toFixed(4);
+  data.rBar = data.rBar.toFixed(4);
+  data.xDoubleBar = data.xDoubleBar.toFixed(4);
+  data.uclX = data.uclX.toFixed(4);
+  data.lclR = data.lclR.toFixed(4);
+  data.subgroupRanges = data.subgroupRanges.map((num) =>
+    parseFloat(num.toFixed(4))
+  );
+  data.subgroupMeans = data.subgroupMeans.map((num) =>
+    parseFloat(num.toFixed(4))
+  );
+  chartData.value = data;
+
+  chartsOption1.value.title[0].text = `${title.value}的Xbar-R控制图`;
+  setChart1();
+  setChart2();
+};
+
+const { dicts } = useDictionaryStore();
+const opOptions = ref([...dicts.spc_operation]);
+const value = ref(opOptions.value[0].remark);
+const Y1value = ref([]);
+const X1array = ref([]);
+const setY1value = () => {
+  Y1value.value = [];
+  chartData.value.subgroupMeans.forEach((item) => {
+    Y1value.value.push(item);
+  });
+  Y1value.value.unshift("");
+  Y1value.value.push("");
+
+  chartsOption1.value.series[0].data = Y1value.value;
+};
+
+const setX1array = async () => {
+  X1array.value = await [];
+  chartData.value.subgroupMeans.forEach((item, index) => {
+    X1array.value.push(index + 1);
+  });
+  X1array.value.unshift("");
+  X1array.value.push("");
+  chartsOption1.value.xAxis[0].data = X1array.value;
+};
+
+const setChart1Info = () => {
+  // chartsOption1.value.title[0].text = `上限=${showData.value.avgMax ? showData.value.avgMax : "-"}`;
+  // chartsOption1.value.title[0].text = `x̄=${showData.value.avgAvg ? showData.value.avgAvg : "-"}`;
+  chartsOption1.value.series[0].markLine.data[0].yAxis = chartData.value.uclX;
+  chartsOption1.value.series[0].markLine.data[0].label.formatter = `上限=${
+    chartData.value.uclX
+  }`;
+  chartsOption1.value.series[0].markLine.data[1].yAxis = chartData.value.lclX;
+  chartsOption1.value.series[0].markLine.data[1].label.formatter = `下限=${chartData.value.lclX}`;
+  chartsOption1.value.series[0].markLine.data[2].yAxis =
+    chartData.value.xDoubleBar;
+  chartsOption1.value.series[0].markLine.data[2].label.formatter = `x̄=${
+    chartData.value.xDoubleBar
+  }`;
+  // chartsOption1.value.title[2].text = `下限=${showData.value.avgMin ? showData.value.avgMin : "0"}`;
+};
+const setChart2Info = () => {
+  // chartsOption2.value.title[0].text = `上限=${showData.value.rangeMax ? showData.value.rangeMax : "-"}`;
+  // chartsOption2.value.title[0].text = `R=${showData.value.rangeAvg ? showData.value.rangeAvg : "-"}`;
+  chartsOption2.value.series[0].markLine.data[0].yAxis = chartData.value.uclR;
+  chartsOption2.value.series[0].markLine.data[0].label.formatter = `上限=${
+    chartData.value.uclR
+  }`;
+  chartsOption2.value.series[0].markLine.data[1].yAxis = chartData.value.lclR;
+  chartsOption2.value.series[0].markLine.data[1].label.formatter = `下限=${
+    chartData.value.lclR
+  }`;
+  chartsOption2.value.series[0].markLine.data[2].yAxis = chartData.value.rBar;
+  chartsOption2.value.series[0].markLine.data[2].label.formatter = `x̄=${
+    chartData.value.rBar
+  }`;
+  // chartsOption2.value.title[2].text = `下限=${showData.value.rangeMin ? showData.value.rangeMin : "0"}`;
+};
+const setChart1 = () => {
+  setChart1Info();
+  setY1value();
+  setX1array();
+  charts1.value.setOption(chartsOption1.value, true);
+};
+const setChart2 = () => {
+  setChart2Info();
+  setY2value();
+  setX2array();
+  charts2.value.setOption(chartsOption2.value, true);
+};
+
+const title = ref("调阻精度");
+const showLable = ref("调阻");
+const changeSelect = () => {
+  setTimeout(() => {
+    // showLable.value = selectRef.value.currentPlaceholder;
+    // opOptions.value.forEach((item) => {
+    //   if (item.dictLabel == showLable.value) {
+    //     lableValue.value = item.dictValue;
+    //   }
+    // });
+    // switch (showLable.value) {
+    //   case "调阻":
+    //     title.value = "调阻精度";
+    //     break;
+    //   case "粘片":
+    //     title.value = "剪切强度";
+    //     break;
+    //   case "键合":
+    //     title.value = "键合强度";
+    //     break;
+    //   default:
+    //     title.value = "调阻精度";
+    //     break;
+    // }
+    // await getTableData();
+    chartsOption1.value.title[0].text = `${title.value}的Xbar-R控制图`;
+    setChart1();
+    setChart2();
+  }, 0);
+};
+
+const maxHeight = ref(null);
+const maxWidth = ref(null);
+const charts1 = shallowRef(null);
+const charts2 = shallowRef(null);
+const chartsOption1 = ref({
+  title: [
+    // {
+    //   text: `x̄=${showData.value.avgAvg ? showData.value.avgAvg : "-"}`,
+    //   right: "5%",
+    //   top: "42%",
+    //   textStyle: {
+    //     fontSize: 15,
+    //     color: "#333",
+    //     fontWeight: 100,
+    //   },
+    // },
+    {
+      text: `${title.value}的Xbar-R控制图`,
+      left: "40%",
+    },
+    {
+      text: "均",
+      left: "4%",
+      top: "42%",
+    },
+    {
+      text: "值",
+      left: "4%",
+      top: "49%",
+    },
+  ],
+  grid: {
+    right: "15%",
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: {},
+    },
+  },
+  tooltip: {
+    show: true,
+  },
+  xAxis: [
+    {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+  ],
+  yAxis: [
+    {
+      type: "value",
+      scale: true, // 开启自适应缩放
+    },
+  ],
+  series: [
+    {
+      type: "line",
+      lineStyle: {
+        color: "rgb(26, 122, 240)",
+      },
+      symbolSize: 13,
+      symbol: "circle",
+      itemStyle: {
+        color: (params) => {
+          const paramValue = Number(params.value);
+          if (
+            paramValue <= Number(chartData.value.uclX) &&
+            paramValue >= Number(chartData.value.lclX)
+          ) {
+            return "rgb(26, 122, 240)";
+          } else {
+            return "red";
+          }
+        },
+      },
+      markLine: {
+        silent: true,
+        data: [
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `上限=${chartData.value.uclX}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `下限=${chartData.value.lclX}`,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+          {
+            yAxis: 0,
+            silent: false,
+            label: {
+              position: "end",
+              formatter: ``,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+        ],
+      },
+    },
+  ],
+});
+const chartsOption2 = ref({
+  title: [
+    // {
+    //   text: `R=${showData.value.rangeAvg ? showData.value.rangeAvg : "-"}`,
+    //   right: "5%",
+    //   top: "42%",
+    //   textStyle: {
+    //     fontSize: 15,
+    //     color: "#333",
+    //     fontWeight: 100,
+    //   },
+    // },
+    {
+      text: "极",
+      left: "4%",
+      top: "35%",
+    },
+    {
+      text: "差",
+      left: "4%",
+      top: "42%",
+    },
+  ],
+  toolbox: {
+    feature: {
+      saveAsImage: {},
+    },
+  },
+  grid: {
+    right: "15%",
+  },
+  xAxis: [
+    {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+  ],
+  yAxis: [
+    {
+      type: "value",
+      scale: true, // 开启自适应缩放
+    },
+  ],
+  tooltip: {
+    show: true,
+  },
+  series: [
+    {
+      type: "line",
+      lineStyle: {
+        color: "rgb(26, 122, 240)",
+      },
+      symbolSize: 13,
+      symbol: "circle",
+      itemStyle: {
+        color: (params) => {
+          const paramValue = Number(params.value);
+          if (
+            paramValue <= Number(chartData.value.uclR) &&
+            paramValue >= Number(chartData.value.lclR)
+          ) {
+            return "rgb(26, 122, 240)";
+          } else {
+            return "red";
+          }
+        },
+      },
+      markLine: {
+        silent: true,
+        data: [
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `上限=${chartData.value.uclR}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            silent: false,
+            yAxis: 0,
+            label: {
+              position: "end",
+              formatter: `下限=${chartData.value.lclR}`,
+              color: "#333",
+            },
+            lineStyle: { type: "solid", color: "#333", width: 2 },
+          },
+          {
+            yAxis: 0,
+            silent: false,
+            label: {
+              position: "end",
+              formatter: ``,
+              color: "#333",
+            },
+            lineStyle: {
+              type: "solid",
+              color: "#333",
+              width: 2,
+            },
+          },
+        ],
+      },
+    },
+  ],
+});
+const Height = ref(0);
+const setHeight = () => {
+  Height.value = document.querySelector(".databox").clientHeight;
+  maxHeight.value = document.querySelector(".info").clientHeight;
+  maxWidth.value = document.querySelector(".info").clientWidth;
+};
+const Y2value = ref([]);
+const X2array = ref([]);
+const setY2value = () => {
+  Y2value.value = [];
+  chartData.value.subgroupRanges.forEach((item) => {
+    Y2value.value.push(item);
+  });
+  Y2value.value.unshift("");
+  Y2value.value.push("");
+  chartsOption2.value.series[0].data = Y2value.value;
+};
+const setX2array = () => {
+  X2array.value = [];
+  chartData.value.subgroupRanges.forEach((item, index) => {
+    X2array.value.push(index + 1);
+  });
+  X2array.value.unshift("");
+  X2array.value.push("");
+  chartsOption2.value.xAxis[0].data = X2array.value;
+};
+
+const setView = () => {
+  setHeight();
+  charts1.value = echarts.init(document.getElementById("charts"));
+  charts2.value = echarts.init(document.getElementById("charts1"));
+  charts1.value.setOption(chartsOption1.value, true);
+  charts2.value.setOption(chartsOption2.value, true);
+};
+onMounted(() => {
+  init();
+});
+const init = async (data) => {
+  tableData.value = data;
+
+  setHeight();
+
+  if (data) {
+    charts1.value = echarts.init(document.getElementById("charts"));
+    charts2.value = echarts.init(document.getElementById("charts1"));
+    window.addEventListener("resize", setView);
+    await getTableData(data);
+    // changeSelect();
+    charts1.value.setOption(chartsOption1.value, true);
+    charts2.value.setOption(chartsOption2.value, true);
+  }
+
+  // nextTick(() => {
+  //   if (tableData.value != undefined && tableData.value.length > 0) {
+  //     getTableData();
+  //     // changeSelect();
+  //   }
+  //
+  //   charts1.value = echarts.init(document.getElementById("charts"));
+  //   charts2.value = echarts.init(document.getElementById("charts1"));
+  //   charts1.value.setOption(chartsOption1.value, true);
+  //   charts2.value.setOption(chartsOption2.value, true);
+  // });
+  // window.addEventListener("resize", setView);
+};
+onBeforeUnmount(() => {
+  window.removeEventListener("resize", setView);
+});
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+@media print {
+  #print {
+    margin-left: -18%;
+  }
+}
+.formStyle {
+  width: 400px;
+  margin: 20px auto;
+}
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

+ 6 - 27
src/views/analysis/process/Xbar-R.vue

@@ -76,14 +76,14 @@ const setChart1Info = () => {
   chartsOption1.value.series[0].markLine.data[0].yAxis = JSON.parse(
     value.value
   ).avgMax;
-  chartsOption1.value.series[0].markLine.data[0].label.formatter = `                          上限=${
+  chartsOption1.value.series[0].markLine.data[0].label.formatter = `上限=${
     JSON.parse(value.value).avgMax ? JSON.parse(value.value).avgMax : "0"
   }`;
   chartsOption1.value.series[0].markLine.data[1].yAxis = JSON.parse(value.value)
     .avgMin
     ? JSON.parse(value.value).avgMin
     : 0;
-  chartsOption1.value.series[0].markLine.data[1].label.formatter = `           下限=${JSON.parse(value.value).avgMin ? JSON.parse(value.value).avgMin : "0"}`;
+  chartsOption1.value.series[0].markLine.data[1].label.formatter = `下限=${JSON.parse(value.value).avgMin ? JSON.parse(value.value).avgMin : "0"}`;
   chartsOption1.value.series[0].markLine.data[2].yAxis = JSON.parse(
     value.value
   ).avgMid;
@@ -98,14 +98,14 @@ const setChart2Info = () => {
   chartsOption2.value.series[0].markLine.data[0].yAxis = JSON.parse(
     value.value
   ).rangeMax;
-  chartsOption2.value.series[0].markLine.data[0].label.formatter = `                          上限=${
+  chartsOption2.value.series[0].markLine.data[0].label.formatter = `上限=${
     JSON.parse(value.value).rangeMax ? JSON.parse(value.value).rangeMax : "0"
   }`;
   chartsOption2.value.series[0].markLine.data[1].yAxis = JSON.parse(value.value)
     .rangeMin
     ? JSON.parse(value.value).rangeMin
     : 0;
-  chartsOption2.value.series[0].markLine.data[1].label.formatter = `           下限=${
+  chartsOption2.value.series[0].markLine.data[1].label.formatter = `下限=${
     JSON.parse(value.value).rangeMin ? JSON.parse(value.value).rangeMin : "0"
   }`;
   chartsOption2.value.series[0].markLine.data[2].yAxis = JSON.parse(
@@ -181,16 +181,6 @@ const chartsOption1 = ref({
       left: "40%",
     },
     {
-      text: "样",
-      left: "4%",
-      top: "28%",
-    },
-    {
-      text: "本",
-      left: "4%",
-      top: "35%",
-    },
-    {
       text: "均",
       left: "4%",
       top: "42%",
@@ -305,16 +295,6 @@ const chartsOption2 = ref({
     //   },
     // },
     {
-      text: "样",
-      left: "4%",
-      top: "28%",
-    },
-    {
-      text: "本",
-      left: "4%",
-      top: "35%",
-    },
-    {
       text: "极",
       left: "4%",
       top: "42%",
@@ -449,9 +429,8 @@ const setView = () => {
 onMounted(() => {
   init();
 });
-const init = (data, str) => {
-  console.log(data, "data");
-  console.log(str, "str");
+const init = (data) => {
+
   tableData.value = data;
   changeSelect();
   setHeight();

+ 816 - 0
src/views/analysis/process/Xbar-RList.vue

@@ -0,0 +1,816 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box">
+          <div class="title">
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              样本数据录入
+            </div>
+            <div class="header" v-show="!addStatus && !editStatus">
+              <Search
+                :searchOptions="searchForm"
+                ref="searchRef"
+                @data-list="getTableData"
+                @reset-list="reset"
+              />
+            </div>
+            <div class="btns">
+              <el-upload
+                v-if="!addStatus && !editStatus"
+                style="float: left"
+                :action="uploadUrl"
+                :on-success="handleSuccess"
+                :before-upload="beforeUpload"
+                :limit="1"
+                accept=".xlsx, .xls"
+                :show-file-list="false"
+                ref="uploadRef"
+              >
+                <el-button size="small" type="primary">Excel导入</el-button>
+              </el-upload>
+              <el-button
+                v-if="!addStatus && !editStatus"
+                style="margin-left: 15px"
+                type="primary"
+                size="small"
+                class="btn"
+                @click="
+                  exportData(
+                    '/api/v1/spc/downloadTemplate',
+                    '/spc/template/XBarR控制图数据导入模版.xlsx'
+                  )
+                "
+                >模版下载</el-button
+              >
+              <el-button
+                style="margin-left: 15px"
+                v-if="!addStatus && !editStatus"
+                type="primary"
+                size="small"
+                class="btn"
+                @click="changeaddstatus"
+                >新增</el-button
+              >
+              <el-button
+                style="margin-left: 15px"
+                v-if="!addStatus && !editStatus"
+                type="primary"
+                size="small"
+                class="btn"
+                @click="compute"
+                >计算</el-button
+              >
+
+              <el-button
+                v-if="editStatus || addStatus"
+                type="success"
+                size="small"
+                class="btn"
+                @click="submit"
+                >确定</el-button
+              >
+              <el-button
+                v-if="editStatus || addStatus"
+                type="info"
+                size="small"
+                class="btn"
+                @click="canceleOp"
+                >取消</el-button
+              >
+            </div>
+          </div>
+          <div class="info" v-if="!addStatus">
+            <el-table
+              :data="tableData"
+              border
+              :style="{
+                height: maxHeight - 150 + 'px',
+                width: maxWidth + 'px',
+              }"
+              :show-overflow-tooltip="true"
+              :row-class-name="tableRowClassName"
+            >
+              <el-table-column
+                align="center"
+                prop="dateStr"
+                sortable
+                label="日期"
+              >
+                <template #default="{ row }"
+                  ><span>{{ row.dateStr }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" prop="model" label="产品型号">
+                <template #default="{ row }"
+                  ><span>{{ row.model }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" prop="batchNo" label="生产批号">
+                <template #default="{ row }"
+                  ><span>{{ row.batchNo }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" prop="source" label="数据来源">
+                <template #default="{ row }"
+                  ><span>{{ row.source }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column
+                align="center"
+                prop="accuracys"
+                label="数据"
+                :show-overflow-tooltip="false"
+              >
+                <template #default="{ row }">
+                  <el-tooltip placement="top">
+                    <template #content>
+                      <div v-for="(item, index) in row.accuracys" :key="index">
+                        <span>数值{{ index + 1 }}: {{ item }}</span>
+                      </div>
+                    </template>
+                    <div class="ellipsis-text">
+                      {{ row.accuracys.join(", ") }}
+                    </div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!--              <el-table-column
+                align="center"
+                prop="avg"
+                label="平均值"
+                width="60"
+              /><el-table-column
+                align="center"
+                prop="range"
+                label="极差"
+                width="60"
+              />--><!--<el-table-column
+                align="center"
+                prop="checkUser"
+                label="检测人"
+              >
+                <template #default="{ row }"
+                  ><span>{{ row.checkUser }}</span>
+                </template> </el-table-column
+              ><el-table-column
+                align="center"
+                prop="checkDeviceNo"
+                label="检查设备编号"
+              >
+                <template #default="{ row }"
+                  ><span>{{ row.checkDeviceNo }}</span>
+                </template>
+              </el-table-column>-->
+              <el-table-column align="center" prop="abnormal" label="是否异常">
+                <template #default="{ row }"
+                  ><span>{{ row.abnormal }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column
+                align="center"
+                prop="abnormal1"
+                label="异常原因"
+                :show-overflow-tooltip="false"
+              >
+                <template #default="{ row }">
+                  <el-tooltip placement="top">
+                    <template #content>
+                      <div
+                        v-for="(item, index) in row.abnormal1.split(',')"
+                        :key="index"
+                      >
+                        <span>{{ item }}</span>
+                      </div>
+                    </template>
+                    <div class="ellipsis-text">
+                      {{ row.abnormal1 }}
+                    </div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!--              <el-table-column align="center" prop="analyseUser" label="分析人">
+                <template #default="{ row }"
+                  ><span>{{ row.analyseUser }}</span>
+                </template>
+              </el-table-column>-->
+              <el-table-column align="center" prop="measure" label="处置措施">
+                <template #default="{ row }"
+                  ><span>{{ row.measure }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" width="240" prop="" label="操作">
+                <template #default="{ row }">
+                  <el-button
+                    v-if="row.source === '自动'"
+                    type="primary"
+                    size="small"
+                    class="btn"
+                    @click="dataAcquisition(row)"
+                    style="height: 25px"
+                    >采集数据</el-button
+                  >
+                  <el-button
+                    type="primary"
+                    size="small"
+                    class="btn"
+                    @click="updataItem(row)"
+                    style="height: 25px"
+                    >修改</el-button
+                  >
+                  <el-button
+                    type="info"
+                    size="small"
+                    class="btn"
+                    style="height: 25px"
+                    @click="deleteSubmit(row.id)"
+                    >删除</el-button
+                  >
+                </template>
+              </el-table-column>
+            </el-table>
+            <Pagination
+              :total="currentOption.total"
+              :page="currentOption.page"
+              :limit="currentOption.limit"
+              :pageSizes="currentOption.pageSizes"
+              v-model:page="currentOption.page"
+              v-model:limit="currentOption.limit"
+              @pagination="getTableData"
+            />
+          </div>
+          <div class="info" v-else>
+            <el-form
+              ref="ruleFormRef"
+              :model="addData"
+              :rules="rules"
+              label-width="auto"
+              class="formStyle"
+            >
+              <el-form-item label="日期" prop="dateStr">
+                <el-date-picker
+                  v-model="addData.dateStr"
+                  type="date"
+                  aria-label="Pick a date"
+                  placeholder="Pick a date"
+                  style="width: 100%"
+                  format="YYYY-MM-DD"
+                  value-format="YYYY-MM-DD"
+                />
+              </el-form-item>
+              <el-form-item label="任务编号" prop="qualityTaskId">
+                <el-select
+                  v-model="addData.qualityTaskId"
+                  @change="
+                    (value) => {
+                      taskChange(value);
+                    }
+                  "
+                >
+                  <el-option
+                    v-for="(item, index) in taskOption"
+                    :key="index"
+                    :label="item.taskCode"
+                    :value="Number(item.id)"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="产品型号" prop="model">
+                <el-input :disabled="true" v-model="addData.model" />
+              </el-form-item>
+              <el-form-item label="数据来源" prop="source">
+                <el-input :disabled="true" v-model="addData.source" />
+              </el-form-item>
+              <el-form-item label="生产批号" prop="batchNo">
+                <el-input v-model="addData.batchNo" />
+              </el-form-item>
+              <el-form-item
+                v-for="(item, index) in addData.accuracys"
+                :label="'数值' + (index + 1)"
+                :key="index"
+                :rules="[
+                  {
+                    required: true,
+                    trigger: 'change',
+                  },
+                ]"
+              >
+                <el-input-number
+                  :precision="2"
+                  :step="0.01"
+                  style="width: 100%"
+                  v-model="addData.accuracys[index]"
+                />
+              </el-form-item>
+              <!-- <el-form-item label="平均值" prop="avg">
+                <el-input v-model="addData.avg" />
+              </el-form-item>
+              <el-form-item label="极差" prop="range">
+                <el-input v-model="addData.range" />
+              </el-form-item> -->
+              <!--              <el-form-item label="检测人" prop="checkUser">
+                <el-input v-model="addData.checkUser" />
+              </el-form-item>
+              <el-form-item label="检查设备编号" prop="checkDeviceNo">
+                <el-input v-model="addData.checkDeviceNo" />
+              </el-form-item>-->
+              <!-- <el-form-item label="是否异常" prop="abnormal">
+                <el-input v-model="addData.abnormal" />
+              </el-form-item> -->
+              <!--              <el-form-item label="分析人" prop="analyseUser">
+                <el-input v-model="addData.analyseUser" />
+              </el-form-item>-->
+              <el-form-item label="处置措施" prop="measure">
+                <el-input v-model="addData.measure" />
+              </el-form-item>
+            </el-form>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import * as echarts from "echarts";
+import { useDictionaryStore } from "@/store";
+import {
+  getData,
+  addDatas,
+  deleteData,
+  updateData,
+  getTaskCode,
+} from "@/api/analysis";
+import Search from "@/components/Search/index.vue";
+import { XBarRCompute, collectData } from "@/api/analysis";
+import { useCrud } from "@/hooks/userCrud";
+
+const { Utils } = useCrud({
+  src: "/api/v1/spc/pDownloadTemplate",
+});
+const { exportData } = Utils;
+const emit = defineEmits(["tableData"]);
+
+const dataAcquisition = async (row) => {
+  const { data } = await collectData({
+    qualitySpcRecordId: row.id,
+    model: row.model,
+    batchNo: row.batchNo,
+    operation: lableValue.value,
+  });
+  emit("tableData", data);
+  getTableData();
+};
+
+const compute = async () => {
+  const accuracysList = ref([]);
+  for (const item of tableData.value) {
+    if (item.accuracys?.length > 0) {
+      accuracysList.value.push(item.accuracys);
+    } else {
+      ElMessage.error(`产品 "${item.model}" 的 "${item.batchNo}" 批次数据为空`);
+      return;
+    }
+  }
+  const { data } = await XBarRCompute({
+    dataList: accuracysList.value,
+    scale: 4,
+  });
+  emit("tableData", data);
+};
+
+const tableRowClassName = ({ row }) => {
+  if (row.abnormal === "是") {
+    return "warning-row";
+  }
+  return "";
+};
+
+const { dicts } = useDictionaryStore();
+
+const year = ref("0");
+const currentOption = reactive({
+  total: 0,
+  page: 1,
+  limit: 10,
+  pageSizes: [10, 20, 30, 40, 50],
+});
+const lableValue = ref("");
+const searchRef = ref(null);
+const getTaskOption = async () => {
+  const { data } = await getTaskCode({
+    operationCode: lableValue.value,
+  });
+  taskOption.value = data;
+};
+const getTableData = async () => {
+  const { data, code, msg } = await getData({
+    ...searchRef.value.searchForm,
+    pageNo: currentOption.page,
+    pageSize: currentOption.limit,
+    // yearStr: year.value,
+    operation: lableValue.value,
+  });
+  if (code == "200") {
+    tableData.value = data.records;
+    showData.value = { ...data, list: null };
+    currentOption.total = data.totalCount;
+    currentOption.page = data.pageNo;
+    oldDataJSON.value = JSON.stringify(data.records);
+  }
+  disabled.value = false;
+};
+const searchForm = [
+  {
+    label: "日期",
+    prop: "createds",
+    type: "daterange",
+  },
+  {
+    label: "产品型号",
+    prop: "model",
+    type: "input",
+  },
+  {
+    label: "生产批号",
+    prop: "batchNo",
+    type: "input",
+  },
+];
+//编辑状态
+const editStatus = ref(false);
+const addStatus = ref(false);
+const changeEditstatus = () => {
+  editStatus.value = !changeEditstatus.value;
+  addStatus.value = false;
+};
+const changeaddstatus = () => {
+  addStatus.value = !addStatus.value;
+  editStatus.value = false;
+};
+const canceleOp = () => {
+  addStatus.value = false;
+  editStatus.value = false;
+  reset();
+};
+
+const disabled = ref(false);
+const tableData = ref([]);
+//Form
+const ruleFormRef = ref(null);
+const rules = {
+  qualityTaskId: [
+    {
+      required: true,
+      trigger: "change",
+    },
+  ],
+  dateStr: [
+    {
+      required: true,
+      trigger: "change",
+    },
+  ],
+  model: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  batchNo: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy1: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy2: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy3: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy4: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy5: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  checkUser: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  checkDeviceNo: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  // abnormal: [
+  //   {
+  //     required: true,
+
+  //     trigger: "change",
+  //   },
+  // ],
+  analyseUser: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  measure: [
+    {
+      required: false,
+
+      trigger: "change",
+    },
+  ],
+};
+const resItem = {
+  // abnormal: "",
+  analyseUser: "",
+  batchNo: "",
+  checkDeviceNo: "",
+  checkUser: "",
+  dateStr: "",
+  measure: "",
+  model: "",
+};
+const addData = ref({
+  // abnormal: "",
+  analyseUser: "",
+  batchNo: "",
+  checkDeviceNo: "",
+  checkUser: "",
+  dateStr: "",
+  measure: "",
+  model: "",
+});
+const accuracysSum = ref(0);
+const taskChange = (value) => {
+  taskOption.value.forEach((item) => {
+    if (item.id == value) {
+      addData.value.model = item.prodtModel;
+      addData.value.source = item.source;
+      if (item.source === "手动") {
+        accuracysSum.value = Number(item.processCount);
+        addData.value.accuracys = [];
+        let array = [];
+        for (let i = 0; i < accuracysSum.value; i++) {
+          array.push(0);
+        }
+        addData.value.accuracys = array;
+      } else {
+        addData.value.accuracys = [];
+      }
+    }
+  });
+};
+const oldDataJSON = ref("");
+const showData = ref({});
+const opOptions = ref([...dicts.spc_operation]);
+
+//修改
+const updataItem = (row) => {
+  editStatus.value = true;
+  addData.value = row;
+  addStatus.value = true;
+};
+const taskOption = ref([]);
+const value = ref(opOptions.value[0].remark);
+const showLable = ref("调阻");
+const maxHeight = ref(null);
+const maxWidth = ref(null);
+const Height = ref(0);
+const setHeight = () => {
+  Height.value = document.querySelector(".databox").clientHeight;
+  maxHeight.value = document.querySelector(".info").clientHeight;
+  maxWidth.value = document.querySelector(".info").clientWidth;
+};
+
+//当新增或者编辑的确定操作
+const submit = () => {
+  if (editStatus.value == true) {
+    updateSubmit();
+  } else {
+    addSubmit();
+  }
+};
+const addSubmit = async () => {
+  await ruleFormRef.value.validate(async (valid, fields) => {
+    if (valid) {
+      const { data, code } = await addDatas({
+        ...addData.value,
+        yearStr: year.value,
+        operation: lableValue.value,
+      });
+      if (code == "200") {
+        ElMessage.success("添加成功!");
+        reset();
+        getTableData();
+      }
+    } else {
+      ElMessage.error("请检查表单信息");
+    }
+  });
+};
+const deleteSubmit = async (id) => {
+  const { data, code } = await deleteData({
+    id,
+  });
+  if (code == "200") {
+    ElMessage.success("删除成功!");
+    getTableData();
+  }
+};
+const updateSubmit = async () => {
+  const { data, code } = await updateData({
+    ...addData.value,
+  });
+  if (code == "200") {
+    ElMessage.success("更新成功!");
+    reset();
+    getTableData();
+  }
+};
+const reset = () => {
+  addStatus.value = false;
+  editStatus.value = false;
+  addData.value = { ...resItem };
+  searchRef.value.searchForm = {};
+  currentOption.value = {
+    total: 0,
+    page: 0,
+    limit: 12,
+    pageSizes: [12],
+    operation: value.value,
+  };
+  getTableData();
+};
+
+const uploadUrl = ref("");
+const beforeUpload = (file) => {
+  const isExcel =
+    file.type === "application/vnd.ms-excel" ||
+    file.type ===
+      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+  if (!isExcel) {
+    ElMessage.error("只能上传Excel文件!");
+  }
+  return isExcel;
+};
+const uploadRef = ref("uploadRef");
+const handleSuccess = (response) => {
+  if (response.code === "200") {
+    ElMessage.success("Excel导入成功!");
+    uploadRef.value.clearFiles();
+    getTableData();
+  } else {
+    ElMessage.error("Excel导入失败!");
+  }
+};
+
+onMounted(async () => {
+  setHeight();
+  year.value = new Date().getFullYear() + "";
+  opOptions.value.forEach((item) => {
+    if (item.dictLabel == showLable.value) {
+      lableValue.value = item.dictValue;
+    }
+  });
+
+  await getTableData();
+  uploadUrl.value =
+    import.meta.env.VITE_APP_BASE_API +
+    "/api/v1/spc/xBarRUpload?operation=" +
+    lableValue.value;
+});
+onBeforeUnmount(() => {});
+
+const init = (data) => {
+  lableValue.value = data;
+  getTaskOption();
+  getTableData();
+};
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+@media print {
+  #print {
+    margin-left: -18%;
+  }
+}
+:deep(.el-table .warning-row) {
+  background-color: rgb(241, 142, 142) !important;
+}
+.ellipsis-text {
+  white-space: nowrap; /* 禁止换行 */
+  overflow: hidden; /* 隐藏超出部分 */
+  text-overflow: ellipsis; /* 显示省略号 */
+  width: 100%; /* 宽度占满单元格 */
+}
+.formStyle {
+  width: 400px;
+  margin: 20px auto;
+}
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

+ 34 - 52
src/views/analysis/process/Xbar-S-2.vue

@@ -41,22 +41,7 @@ import { XBarSCompute } from "@/api/analysis";
 
 const tableData = ref([]);
 const chartData = ref([]);
-const getTableData = async () => {
-  // 存储提取的 accuracys 数据
-  const accuracysList = ref([]);
-
-  // 提取 accuracys 数据
-  console.log(JSON.stringify(tableData.value), "tableData.value");
-  tableData.value.forEach((v) => {
-    accuracysList.value.push(v.accuracys);
-  });
-
-  console.log(accuracysList.value, "dataList");
-  const { data } = await XBarSCompute({
-    dataList: accuracysList.value,
-    scale: accuracysList.value[0].length,
-  });
-  console.log(JSON.stringify(data), "resultDataList");
+const getTableData = async (data) => {
   data.lclX = data.lclX.toFixed(4);
   data.uclS = data.uclS.toFixed(4);
   data.sBar = data.sBar.toFixed(4);
@@ -70,6 +55,10 @@ const getTableData = async () => {
     parseFloat(num.toFixed(4))
   );
   chartData.value = data;
+
+  chartsOption1.value.title[0].text = `${title.value}的Xbar-S控制图`;
+  setChart1();
+  setChart2();
 };
 
 const { dicts } = useDictionaryStore();
@@ -79,8 +68,6 @@ const Y1value = ref([]);
 const X1array = ref([]);
 const setY1value = () => {
   Y1value.value = [];
-  console.log(chartData.value, "chartData.value");
-  console.log(chartData.value.subgroupMeans, "chartData.value.subgroupMeans");
   chartData.value.subgroupMeans.forEach((item) => {
     Y1value.value.push(item);
   });
@@ -104,11 +91,11 @@ const setChart1Info = () => {
   // chartsOption1.value.title[0].text = `上限=${showData.value.avgMax ? showData.value.avgMax : "-"}`;
   // chartsOption1.value.title[0].text = `x̄=${showData.value.avgAvg ? showData.value.avgAvg : "-"}`;
   chartsOption1.value.series[0].markLine.data[0].yAxis = chartData.value.uclX;
-  chartsOption1.value.series[0].markLine.data[0].label.formatter = `           上限=${
+  chartsOption1.value.series[0].markLine.data[0].label.formatter = `上限=${
     chartData.value.uclX
   }`;
   chartsOption1.value.series[0].markLine.data[1].yAxis = chartData.value.lclX;
-  chartsOption1.value.series[0].markLine.data[1].label.formatter = `           下限=${chartData.value.lclX}`;
+  chartsOption1.value.series[0].markLine.data[1].label.formatter = `下限=${chartData.value.lclX}`;
   chartsOption1.value.series[0].markLine.data[2].yAxis =
     chartData.value.xDoubleBar;
   chartsOption1.value.series[0].markLine.data[2].label.formatter = `x̄=${
@@ -120,15 +107,15 @@ const setChart2Info = () => {
   // chartsOption2.value.title[0].text = `上限=${showData.value.rangeMax ? showData.value.rangeMax : "-"}`;
   // chartsOption2.value.title[0].text = `R=${showData.value.rangeAvg ? showData.value.rangeAvg : "-"}`;
   chartsOption2.value.series[0].markLine.data[0].yAxis = chartData.value.uclS;
-  chartsOption2.value.series[0].markLine.data[0].label.formatter = `   上限=${
+  chartsOption2.value.series[0].markLine.data[0].label.formatter = `上限=${
     chartData.value.uclS
   }`;
   chartsOption2.value.series[0].markLine.data[1].yAxis = chartData.value.lclS;
-  chartsOption2.value.series[0].markLine.data[1].label.formatter = `   下限=${
+  chartsOption2.value.series[0].markLine.data[1].label.formatter = `下限=${
     chartData.value.lclS
   }`;
   chartsOption2.value.series[0].markLine.data[2].yAxis = chartData.value.sBar;
-  chartsOption2.value.series[0].markLine.data[2].label.formatter = `   x̄=${
+  chartsOption2.value.series[0].markLine.data[2].label.formatter = `x̄=${
     chartData.value.sBar
   }`;
   // chartsOption2.value.title[2].text = `下限=${showData.value.rangeMin ? showData.value.rangeMin : "0"}`;
@@ -198,16 +185,6 @@ const chartsOption1 = ref({
       left: "40%",
     },
     {
-      text: "样",
-      left: "4%",
-      top: "28%",
-    },
-    {
-      text: "本",
-      left: "4%",
-      top: "35%",
-    },
-    {
       text: "均",
       left: "4%",
       top: "42%",
@@ -239,6 +216,7 @@ const chartsOption1 = ref({
   yAxis: [
     {
       type: "value",
+      scale: true, // 开启自适应缩放
     },
   ],
   series: [
@@ -321,29 +299,19 @@ const chartsOption2 = ref({
     //   },
     // },
     {
-      text: "样",
-      left: "4%",
-      top: "28%",
-    },
-    {
-      text: "本",
-      left: "4%",
-      top: "35%",
-    },
-    {
       text: "标",
       left: "4%",
-      top: "42%",
+      top: "35%",
     },
     {
       text: "准",
       left: "4%",
-      top: "49%",
+      top: "42%",
     },
     {
       text: "差",
       left: "4%",
-      top: "56%",
+      top: "49%",
     },
   ],
   toolbox: {
@@ -364,6 +332,7 @@ const chartsOption2 = ref({
   yAxis: [
     {
       type: "value",
+      scale: true, // 开启自适应缩放
     },
   ],
   tooltip: {
@@ -469,20 +438,33 @@ const setView = () => {
 onMounted(() => {
   init();
 });
-const init = (data, str) => {
+const init = async (data) => {
   tableData.value = data;
 
   setHeight();
 
-  nextTick(async () => {
-    await getTableData();
-    await changeSelect();
+  if (data) {
     charts1.value = echarts.init(document.getElementById("charts"));
     charts2.value = echarts.init(document.getElementById("charts1"));
+    window.addEventListener("resize", setView);
+    await getTableData(data);
+    // changeSelect();
     charts1.value.setOption(chartsOption1.value, true);
     charts2.value.setOption(chartsOption2.value, true);
-  });
-  window.addEventListener("resize", setView);
+  }
+
+  // nextTick(() => {
+  //   if (tableData.value != undefined && tableData.value.length > 0) {
+  //     getTableData();
+  //     // changeSelect();
+  //   }
+  //
+  //   charts1.value = echarts.init(document.getElementById("charts"));
+  //   charts2.value = echarts.init(document.getElementById("charts1"));
+  //   charts1.value.setOption(chartsOption1.value, true);
+  //   charts2.value.setOption(chartsOption2.value, true);
+  // });
+  // window.addEventListener("resize", setView);
 };
 onBeforeUnmount(() => {
   window.removeEventListener("resize", setView);

+ 0 - 1
src/views/analysis/process/Xbar-S.vue

@@ -450,7 +450,6 @@ onMounted(() => {
 });
 const init = (data, str) => {
   tableData.value = data;
-  console.log(JSON.stringify(data), "data");
   changeSelect();
   setHeight();
   nextTick(() => {

+ 780 - 0
src/views/analysis/process/Xbar-SList.vue

@@ -0,0 +1,780 @@
+<template>
+  <div class="container1">
+    <div class="databox">
+      <el-scrollbar :style="{ height: Height + 'px' }">
+        <div class="box">
+          <div class="title">
+            <div style="display: flex; align-items: center">
+              <div class="bg"></div>
+              样本数据录入
+            </div>
+            <div class="header" v-show="!addStatus && !editStatus">
+              <Search
+                :searchOptions="searchForm"
+                ref="searchRef"
+                @data-list="getTableData"
+                @reset-list="reset"
+              />
+            </div>
+            <div class="btns">
+              <el-upload
+                v-if="!addStatus && !editStatus"
+                style="float: left"
+                :action="uploadUrl"
+                :on-success="handleSuccess"
+                :before-upload="beforeUpload"
+                :limit="1"
+                accept=".xlsx, .xls"
+                :show-file-list="false"
+                ref="uploadRef"
+              >
+                <el-button size="small" type="primary">Excel导入</el-button>
+              </el-upload>
+              <el-button
+                v-if="!addStatus && !editStatus"
+                style="margin-left: 15px"
+                type="primary"
+                size="small"
+                class="btn"
+                @click="
+                  exportData(
+                    '/api/v1/spc/downloadTemplate',
+                    '/spc/template/XBarS控制图数据导入模版.xlsx'
+                  )
+                "
+                >模版下载</el-button
+              >
+              <el-button
+                style="margin-left: 15px"
+                v-if="!addStatus && !editStatus"
+                type="primary"
+                size="small"
+                class="btn"
+                @click="changeaddstatus"
+                >新增</el-button
+              >
+              <el-button
+                style="margin-left: 15px"
+                v-if="!addStatus && !editStatus"
+                type="primary"
+                size="small"
+                class="btn"
+                @click="compute"
+                >计算</el-button
+              >
+              <el-button
+                v-if="editStatus || addStatus"
+                type="success"
+                size="small"
+                class="btn"
+                @click="submit"
+                >确定</el-button
+              >
+              <el-button
+                v-if="editStatus || addStatus"
+                type="info"
+                size="small"
+                class="btn"
+                @click="canceleOp"
+                >取消</el-button
+              >
+            </div>
+          </div>
+          <div class="info" v-if="!addStatus">
+            <el-table
+              :data="tableData"
+              border
+              :style="{
+                height: maxHeight - 150 + 'px',
+                width: maxWidth + 'px',
+              }"
+              :show-overflow-tooltip="true"
+              :row-class-name="tableRowClassName"
+            >
+              <el-table-column
+                align="center"
+                prop="dateStr"
+                sortable
+                label="日期"
+              >
+                <template #default="{ row }"
+                  ><span>{{ row.dateStr }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" prop="model" label="产品型号">
+                <template #default="{ row }"
+                  ><span>{{ row.model }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" prop="batchNo" label="生产批号">
+                <template #default="{ row }"
+                  ><span>{{ row.batchNo }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column
+                align="center"
+                prop="accuracys"
+                label="数据"
+                :show-overflow-tooltip="false"
+              >
+                <template #default="{ row }">
+                  <el-tooltip placement="top">
+                    <template #content>
+                      <div v-for="(item, index) in row.accuracys" :key="index">
+                        <span>数值{{ index + 1 }}: {{ item }}</span>
+                      </div>
+                    </template>
+                    <div class="ellipsis-text">
+                      {{ row.accuracys.join(", ") }}
+                    </div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!--              <el-table-column
+                align="center"
+                prop="avg"
+                label="平均值"
+                width="60"
+              /><el-table-column
+                align="center"
+                prop="range"
+                label="极差"
+                width="60"
+              />--><!--<el-table-column
+                align="center"
+                prop="checkUser"
+                label="检测人"
+              >
+                <template #default="{ row }"
+                  ><span>{{ row.checkUser }}</span>
+                </template> </el-table-column
+              ><el-table-column
+                align="center"
+                prop="checkDeviceNo"
+                label="检查设备编号"
+              >
+                <template #default="{ row }"
+                  ><span>{{ row.checkDeviceNo }}</span>
+                </template>
+              </el-table-column>-->
+              <el-table-column align="center" prop="abnormal" label="是否异常">
+                <template #default="{ row }"
+                  ><span>{{ row.abnormal }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column
+                align="center"
+                prop="abnormal1"
+                label="异常原因"
+                :show-overflow-tooltip="false"
+              >
+                <template #default="{ row }">
+                  <el-tooltip placement="top">
+                    <template #content>
+                      <div
+                        v-for="(item, index) in row.abnormal1.split(',')"
+                        :key="index"
+                      >
+                        <span>{{ item }}</span>
+                      </div>
+                    </template>
+                    <div class="ellipsis-text">
+                      {{ row.abnormal1 }}
+                    </div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!--              <el-table-column align="center" prop="analyseUser" label="分析人">
+                <template #default="{ row }"
+                  ><span>{{ row.analyseUser }}</span>
+                </template>
+              </el-table-column>-->
+              <el-table-column align="center" prop="measure" label="处置措施">
+                <template #default="{ row }"
+                  ><span>{{ row.measure }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" width="160" prop="" label="操作">
+                <template #default="{ row }">
+                  <el-button
+                    type="primary"
+                    size="small"
+                    class="btn"
+                    @click="updataItem(row)"
+                    style="height: 25px"
+                    >修改</el-button
+                  >
+                  <el-button
+                    type="info"
+                    size="small"
+                    class="btn"
+                    style="height: 25px"
+                    @click="deleteSubmit(row.id)"
+                    >删除</el-button
+                  >
+                </template>
+              </el-table-column>
+            </el-table>
+            <Pagination
+              :total="currentOption.total"
+              :page="currentOption.page"
+              :limit="currentOption.limit"
+              :pageSizes="currentOption.pageSizes"
+              v-model:page="currentOption.page"
+              v-model:limit="currentOption.limit"
+              @pagination="getTableData"
+            />
+          </div>
+          <div class="info" v-else>
+            <el-form
+              ref="ruleFormRef"
+              :model="addData"
+              :rules="rules"
+              label-width="auto"
+              class="formStyle"
+            >
+              <el-form-item label="日期" prop="dateStr">
+                <el-date-picker
+                  v-model="addData.dateStr"
+                  type="date"
+                  aria-label="Pick a date"
+                  placeholder="Pick a date"
+                  style="width: 100%"
+                  format="YYYY-MM-DD"
+                  value-format="YYYY-MM-DD"
+                />
+              </el-form-item>
+              <el-form-item label="任务编号" prop="qualityTaskId">
+                <el-select
+                  v-model="addData.qualityTaskId"
+                  @change="
+                    (value) => {
+                      taskChange(value);
+                    }
+                  "
+                >
+                  <el-option
+                    v-for="(item, index) in taskOption"
+                    :key="index"
+                    :label="item.taskCode"
+                    :value="item.id"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="产品型号" prop="model">
+                <el-input :disabled="true" v-model="addData.model" />
+              </el-form-item>
+              <el-form-item label="生产批号" prop="batchNo">
+                <el-input v-model="addData.batchNo" />
+              </el-form-item>
+              <el-form-item
+                v-for="(item, index) in addData.accuracys"
+                :label="'数值' + (index + 1)"
+                :key="index"
+                :rules="[
+                  {
+                    required: true,
+                    trigger: 'change',
+                  },
+                ]"
+              >
+                <el-input-number
+                  :precision="2"
+                  :step="0.01"
+                  style="width: 100%"
+                  v-model="addData.accuracys[index]"
+                />
+              </el-form-item>
+              <!-- <el-form-item label="平均值" prop="avg">
+                <el-input v-model="addData.avg" />
+              </el-form-item>
+              <el-form-item label="极差" prop="range">
+                <el-input v-model="addData.range" />
+              </el-form-item> -->
+              <!--              <el-form-item label="检测人" prop="checkUser">
+                <el-input v-model="addData.checkUser" />
+              </el-form-item>
+              <el-form-item label="检查设备编号" prop="checkDeviceNo">
+                <el-input v-model="addData.checkDeviceNo" />
+              </el-form-item>-->
+              <!-- <el-form-item label="是否异常" prop="abnormal">
+                <el-input v-model="addData.abnormal" />
+              </el-form-item> -->
+              <!--              <el-form-item label="分析人" prop="analyseUser">-->
+              <!--                <el-input v-model="addData.analyseUser" />-->
+              <!--              </el-form-item>-->
+              <el-form-item label="处置措施" prop="measure">
+                <el-input v-model="addData.measure" />
+              </el-form-item>
+            </el-form>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { useDictionaryStore } from "@/store";
+import {
+  getData,
+  addDatas,
+  deleteData,
+  updateData,
+  getTaskCode,
+} from "@/api/analysis";
+import Search from "@/components/Search/index.vue";
+import { XBarSCompute } from "@/api/analysis";
+import { useCrud } from "@/hooks/userCrud";
+
+const { Utils } = useCrud({
+  src: "/api/v1/spc/pDownloadTemplate",
+});
+const { exportData } = Utils;
+
+const emit = defineEmits(["tableData"]);
+const compute = async () => {
+  // 存储提取的 accuracys 数据
+  const accuracysList = ref([]);
+
+  // 提取 accuracys 数据
+  tableData.value.forEach((v) => {
+    accuracysList.value.push(v.accuracys);
+  });
+
+  const { data } = await XBarSCompute({
+    dataList: accuracysList.value,
+    scale: 4,
+  });
+  emit("tableData", data);
+};
+
+const tableRowClassName = ({ row }) => {
+  if (row.abnormal === "是") {
+    return "warning-row";
+  }
+  return "";
+};
+
+const { dicts } = useDictionaryStore();
+
+const year = ref("0");
+const currentOption = reactive({
+  total: 0,
+  page: 1,
+  limit: 10,
+  pageSizes: [10, 20, 30, 40, 50],
+});
+const lableValue = ref("");
+const searchRef = ref(null);
+const getTaskOption = async () => {
+  const { data } = await getTaskCode({
+    operationCode: lableValue.value,
+  });
+  taskOption.value = data;
+};
+const getTableData = async () => {
+  const { data, code, msg } = await getData({
+    ...searchRef.value.searchForm,
+    pageNo: currentOption.page,
+    pageSize: currentOption.limit,
+    // yearStr: year.value,
+    operation: lableValue.value,
+  });
+  if (code == "200") {
+    tableData.value = data.records;
+    showData.value = { ...data, list: null };
+    currentOption.total = data.totalCount;
+    currentOption.page = data.pageNo;
+    oldDataJSON.value = JSON.stringify(data.records);
+  }
+  disabled.value = false;
+};
+const searchForm = [
+  {
+    label: "日期",
+    prop: "createds",
+    type: "daterange",
+  },
+  {
+    label: "产品型号",
+    prop: "model",
+    type: "input",
+  },
+  {
+    label: "生产批号",
+    prop: "batchNo",
+    type: "input",
+  },
+];
+//编辑状态
+const editStatus = ref(false);
+const addStatus = ref(false);
+const changeEditstatus = () => {
+  editStatus.value = !changeEditstatus.value;
+  addStatus.value = false;
+};
+const changeaddstatus = () => {
+  addStatus.value = !addStatus.value;
+  editStatus.value = false;
+};
+const canceleOp = () => {
+  addStatus.value = false;
+  editStatus.value = false;
+  reset();
+};
+const disabled = ref(false);
+const tableData = ref([]);
+//Form
+const ruleFormRef = ref(null);
+
+const rules = {
+  qualityTaskId: [
+    {
+      required: true,
+      trigger: "change",
+    },
+  ],
+  dateStr: [
+    {
+      required: true,
+      trigger: "change",
+    },
+  ],
+  model: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  batchNo: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy1: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy2: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy3: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy4: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  accuracy5: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  checkUser: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  checkDeviceNo: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  // abnormal: [
+  //   {
+  //     required: true,
+
+  //     trigger: "change",
+  //   },
+  // ],
+  analyseUser: [
+    {
+      required: true,
+
+      trigger: "change",
+    },
+  ],
+  measure: [
+    {
+      required: false,
+
+      trigger: "change",
+    },
+  ],
+};
+const resItem = {
+  // abnormal: "",
+  analyseUser: "",
+  batchNo: "",
+  checkDeviceNo: "",
+  checkUser: "",
+  dateStr: "",
+  measure: "",
+  model: "",
+};
+const addData = ref({
+  // abnormal: "",
+  analyseUser: "",
+  batchNo: "",
+  checkDeviceNo: "",
+  checkUser: "",
+  dateStr: "",
+  measure: "",
+  model: "",
+});
+const accuracysSum = ref(0);
+const taskChange = (value) => {
+  taskOption.value.forEach((item) => {
+    if (item.id == value) {
+      addData.value.model = item.prodtModel;
+      accuracysSum.value = Number(item.processCount);
+      addData.value.accuracys = [];
+      let array = [];
+      for (let i = 0; i < accuracysSum.value; i++) {
+        array.push(0);
+      }
+      addData.value.accuracys = array;
+    }
+  });
+};
+const oldDataJSON = ref("");
+const showData = ref({});
+const opOptions = ref([...dicts.spc_operation]);
+
+//修改
+const updataItem = (row) => {
+  editStatus.value = true;
+  addData.value = row;
+  addStatus.value = true;
+};
+const taskOption = ref([]);
+const value = ref(opOptions.value[0].remark);
+const showLable = ref("调阻");
+const maxHeight = ref(null);
+const maxWidth = ref(null);
+const Height = ref(0);
+const setHeight = () => {
+  Height.value = document.querySelector(".databox").clientHeight;
+  maxHeight.value = document.querySelector(".info").clientHeight;
+  maxWidth.value = document.querySelector(".info").clientWidth;
+};
+
+//当新增或者编辑的确定操作
+const submit = () => {
+  if (editStatus.value == true) {
+    updateSubmit();
+  } else {
+    addSubmit();
+  }
+};
+const addSubmit = async () => {
+  await ruleFormRef.value.validate(async (valid, fields) => {
+    if (valid) {
+      const { data, code } = await addDatas({
+        ...addData.value,
+        yearStr: year.value,
+        operation: lableValue.value,
+      });
+      if (code == "200") {
+        ElMessage.success("添加成功!");
+        reset();
+        getTableData();
+      }
+    } else {
+      ElMessage.error("请检查表单信息");
+    }
+  });
+};
+const deleteSubmit = async (id) => {
+  const { data, code } = await deleteData({
+    id,
+  });
+  if (code == "200") {
+    ElMessage.success("删除成功!");
+    getTableData();
+  }
+};
+const updateSubmit = async () => {
+  const { data, code } = await updateData({
+    ...addData.value,
+  });
+  if (code == "200") {
+    ElMessage.success("更新成功!");
+    reset();
+    getTableData();
+  }
+};
+const reset = () => {
+  addStatus.value = false;
+  editStatus.value = false;
+  addData.value = { ...resItem };
+  searchRef.value.searchForm = {};
+  currentOption.value = {
+    total: 0,
+    page: 0,
+    limit: 12,
+    pageSizes: [12],
+    operation: value.value,
+  };
+  getTableData();
+};
+
+const uploadUrl = ref("");
+const beforeUpload = (file) => {
+  const isExcel =
+    file.type === "application/vnd.ms-excel" ||
+    file.type ===
+      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+  if (!isExcel) {
+    ElMessage.error("只能上传Excel文件!");
+  }
+  return isExcel;
+};
+const uploadRef = ref("uploadRef");
+const handleSuccess = (response) => {
+  if (response.code === "200") {
+    ElMessage.success("Excel导入成功!");
+    uploadRef.value.clearFiles();
+    getTableData();
+  } else {
+    ElMessage.error("Excel导入失败!");
+  }
+};
+
+onMounted(async () => {
+  setHeight();
+  year.value = new Date().getFullYear() + "";
+  opOptions.value.forEach((item) => {
+    if (item.dictLabel == showLable.value) {
+      lableValue.value = item.dictValue;
+    }
+  });
+
+  await getTableData();
+  uploadUrl.value =
+    import.meta.env.VITE_APP_BASE_API +
+    "/api/v1/spc/xBarRUpload?operation=" +
+    lableValue.value;
+});
+onBeforeUnmount(() => {});
+
+const init = (data) => {
+  lableValue.value = data;
+  getTaskOption();
+  getTableData();
+};
+
+// 暴露 init 方法
+defineExpose({
+  init,
+});
+</script>
+
+<style lang="scss" scoped>
+@media print {
+  #print {
+    margin-left: -18%;
+  }
+}
+:deep(.el-table .warning-row) {
+  background-color: rgb(241, 142, 142) !important;
+}
+.ellipsis-text {
+  white-space: nowrap; /* 禁止换行 */
+  overflow: hidden; /* 隐藏超出部分 */
+  text-overflow: ellipsis; /* 显示省略号 */
+  width: 100%; /* 宽度占满单元格 */
+}
+.formStyle {
+  width: 400px;
+  margin: 20px auto;
+}
+.container1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    .header {
+      height: 120px;
+      border-bottom: 2px solid #00000010;
+      padding: 20px;
+    }
+    .body {
+      padding: 20px;
+    }
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    .box {
+      height: 710px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .illustrate {
+        padding: 20px 60px;
+      }
+      .tableTitle {
+        text-align: center;
+        margin: 10px 0;
+        padding-right: 40px;
+      }
+      .header {
+        margin-top: 20px;
+        //margin-left: 100px;
+        display: flex;
+        width: 100%;
+        height: auto;
+      }
+      //.title {
+      //  height: 50px;
+      //  display: flex;
+      //  align-items: center;
+      //  margin-bottom: 10px;
+      //  justify-content: space-between;
+      //  .btns {
+      //    display: flex;
+      //    align-items: center;
+      //    .btn {
+      //      height: 24px;
+      //      font-size: 14px;
+      //      margin: 0 5px;
+      //    }
+      //  }
+      //}
+      .info {
+        margin-top: 20px;
+        flex: 1;
+        height: 300px;
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1304 - 0
src/views/analysis/process/index-1.vue


文件差異過大導致無法顯示
+ 264 - 1100
src/views/analysis/process/index.vue


+ 37 - 27
src/views/analysis/spc/index.vue

@@ -20,14 +20,14 @@
       <el-table
         :data="tableData"
         border
-        :style="{ height: maxHeight - 80 + 'px' }"
+
       >
         <el-table-column prop="taskCode" label="任务编号" />
         <el-table-column prop="prodtModel" label="产品型号" />
         <el-table-column prop="operationName" label="工序名称" />
         <el-table-column prop="source" label="采集数据源" />
-        <el-table-column prop="param" label="控制参数" />
-        <el-table-column prop="chart" label="控制图" />
+        <!--        <el-table-column prop="param" label="控制参数" />-->
+        <!--        <el-table-column prop="chart" label="控制图" />-->
         <el-table-column prop="address" label="预警规则">
           <template #default="{ row }">
             <el-button
@@ -41,9 +41,9 @@
           </template>
         </el-table-column>
         <el-table-column prop="processCount" label="工序采集点数" />
-        <el-table-column prop="centralValue" label="中心值" />
-        <el-table-column prop="upperLimit" label="上限" />
-        <el-table-column prop="floor" label="下限" />
+        <!--        <el-table-column prop="centralValue" label="中心值" />-->
+        <!--        <el-table-column prop="upperLimit" label="上限" />-->
+        <!--        <el-table-column prop="floor" label="下限" />-->
         <el-table-column prop="created" label="创建时间" />
         <el-table-column
           align="center"
@@ -123,12 +123,12 @@
               <el-option label="手动" value="手动" />
             </el-select>
           </el-form-item>
-          <el-form-item label="控制参数" prop="param">
-            <el-input v-model="formData.param" />
-          </el-form-item>
-          <el-form-item label="控制图" prop="chart">
-            <el-input :disabled="true" v-model="formData.chart" />
-          </el-form-item>
+          <!--          <el-form-item label="控制参数" prop="param">-->
+          <!--            <el-input v-model="formData.param" />-->
+          <!--          </el-form-item>-->
+          <!--          <el-form-item label="控制图" prop="chart">-->
+          <!--            <el-input :disabled="true" v-model="formData.chart" />-->
+          <!--          </el-form-item>-->
           <el-form-item label="工序采集点数" prop="processCount">
             <el-input-number
               v-model="formData.processCount"
@@ -136,19 +136,19 @@
               :step="1"
             />
           </el-form-item>
-          <el-form-item label="上限" prop="upperLimit">
-            <el-input-number v-model="formData.upperLimit" />
-          </el-form-item>
-          <el-form-item label="下限" prop="floor">
-            <el-input-number v-model="formData.floor" />
-          </el-form-item>
-          <el-form-item label="中心值" prop="centralValue">
-            <el-input-number
-              :min="formData.floor"
-              :max="formData.upperLimit"
-              v-model="formData.centralValue"
-            />
-          </el-form-item>
+          <!--          <el-form-item label="上限" prop="upperLimit">-->
+          <!--            <el-input-number v-model="formData.upperLimit" />-->
+          <!--          </el-form-item>-->
+          <!--          <el-form-item label="下限" prop="floor">-->
+          <!--            <el-input-number v-model="formData.floor" />-->
+          <!--          </el-form-item>-->
+          <!--          <el-form-item label="中心值" prop="centralValue">-->
+          <!--            <el-input-number-->
+          <!--              :min="formData.floor"-->
+          <!--              :max="formData.upperLimit"-->
+          <!--              v-model="formData.centralValue"-->
+          <!--            />-->
+          <!--          </el-form-item>-->
         </el-form>
         <div style="display: flex; justify-content: space-evenly">
           <el-button
@@ -198,6 +198,7 @@ const currentOption = reactive({
   total: 0,
   page: 1,
   limit: 10,
+  pageSizes: [10, 20, 30, 40, 50],
 });
 const showStatus = ref(true);
 const tableData = ref([]);
@@ -239,6 +240,7 @@ const toRuler = (row) => {
   router.push({ name: "SPCrules" });
 };
 const getData = async (obj) => {
+  console.log("222222222222222222")
   const { data, code } = await getTableData({
     ...searchRef.value.searchForm,
     pageNo: currentOption.page,
@@ -375,6 +377,14 @@ const reset = () => {
   };
   addStatus.value = false;
   editStatus.value = false;
+  currentOption.value = {
+    total: 0,
+    page: 1,
+    limit: 10,
+    pageSizes: [10, 20, 30, 40, 50],
+  };
+  console.log(currentOption.value, "currentOption.value")
+  console.log("11111111111111111")
 };
 
 const resetData = () => {
@@ -404,7 +414,7 @@ const toSubmit = async () => {
         });
         if (code == "200") {
           ElMessage.success("添加成功!");
-          reset();
+          await reset();
           getData();
         }
       } else {
@@ -425,7 +435,7 @@ const toSubmit = async () => {
         });
         if (code == "200") {
           ElMessage.success("修改成功!");
-          reset();
+          await reset();
           getData();
         }
       } else {

+ 108 - 74
src/views/analysis/target/index/index.vue

@@ -18,15 +18,11 @@
         </el-select>
       </div>
       <div class="body">
-        <div class="text">
-          规格上限:{{ value ? JSON.parse(value).up : "-" }}
-        </div>
-        <div class="text">
-          规格下限:{{ value ? JSON.parse(value).down : "-" }}
-        </div>
-        <div class="text">
-          计量单位:{{ value ? JSON.parse(value).unit : "-" }}
-        </div>
+        <div class="text">上限:{{ value ? JSON.parse(value).up : "-" }}</div>
+        <div class="text">下限:{{ value ? JSON.parse(value).down : "-" }}</div>
+        <!--        <div class="text">-->
+        <!--          计量单位:{{ value ? JSON.parse(value).unit : "-" }}-->
+        <!--        </div>-->
       </div>
     </div>
     <div class="databox">
@@ -53,23 +49,15 @@
                 @click="changeEditstatus"
                 >编辑</el-button
               > -->
-              <el-button
-                v-if="!addStatus && !editStatus"
-                type="primary"
-                class="btn"
-                @click="updataItem(row.Index)"
-                size="small"
-                >计算Cpk</el-button
-              >
-              <el-button
-                v-if="!addStatus && !editStatus"
-                type="primary"
-                class="btn"
-                v-print="'#print'"
-                @click="printFnc"
-                size="small"
-                >打印</el-button
-              >
+              <!--              <el-button-->
+              <!--                v-if="!addStatus && !editStatus"-->
+              <!--                type="primary"-->
+              <!--                class="btn"-->
+              <!--                v-print="'#print'"-->
+              <!--                @click="printFnc"-->
+              <!--                size="small"-->
+              <!--                >打印</el-button-->
+              <!--              >-->
               <el-button
                 v-if="!addStatus && !editStatus"
                 type="primary"
@@ -161,7 +149,6 @@
               >
                 <el-table-column
                   align="center"
-                  width="100"
                   prop="dateStr"
                   sortable
                   label="日期"
@@ -170,28 +157,17 @@
                     ><span>{{ row.dateStr }}</span>
                   </template>
                 </el-table-column>
-                <el-table-column
-                  align="center"
-                  width="110"
-                  prop="model"
-                  label="产品型号"
-                >
+                <el-table-column align="center" prop="model" label="产品型号">
                   <template #default="{ row }"
                     ><span>{{ row.model }}</span>
                   </template>
                 </el-table-column>
-                <el-table-column
-                  align="center"
-                  width="100"
-                  prop="batchNo"
-                  label="生产批号"
-                >
+                <el-table-column align="center" prop="batchNo" label="生产批号">
                   <template #default="{ row }"
                     ><span>{{ row.batchNo }}</span>
                   </template>
                 </el-table-column>
                 <el-table-column
-                  width="100"
                   align="center"
                   prop="accuracys"
                   label="数据"
@@ -213,7 +189,7 @@
                     </el-tooltip>
                   </template>
                 </el-table-column>
-<!--                <el-table-column
+                <!--                <el-table-column
                   width="100"
                   align="center"
                   prop="accuracys"
@@ -225,7 +201,7 @@
                     </div>
                   </template>
                 </el-table-column>-->
-                <!--                <el-table-column
+                <!--                                <el-table-column
                   align="center"
                   prop="Cpk"
                   label="Cpk值"
@@ -235,12 +211,7 @@
                     ><span>{{ Number(row.cpk).toFixed(2) }}</span>
                   </template>
                 </el-table-column>-->
-                <el-table-column
-                  align="center"
-                  width="70"
-                  prop="checkUser"
-                  label="检测人"
-                >
+                <!--                <el-table-column align="center" prop="checkUser" label="检测人">
                   <template #default="{ row }"
                     ><span>{{ row.checkUser }}</span>
                   </template> </el-table-column
@@ -252,8 +223,8 @@
                   <template #default="{ row }"
                     ><span>{{ row.checkDeviceNo }}</span>
                   </template>
-                </el-table-column>
-                <el-table-column
+                </el-table-column>-->
+                <!--                <el-table-column
                   align="center"
                   prop="abnormal"
                   width="60"
@@ -276,7 +247,7 @@
                   <template #default="{ row }"
                     ><span>{{ row.measure }}</span>
                   </template>
-                </el-table-column>
+                </el-table-column>-->
                 <el-table-column align="center" prop="remark" label="备注">
                   <template #default="{ row }"
                     ><span>{{ row.remark }}</span>
@@ -293,6 +264,13 @@
                     <el-button
                       type="primary"
                       class="btn"
+                      @click="computeCPK(row.id)"
+                      style="height: 25px"
+                      >计算Cpk</el-button
+                    >
+                    <el-button
+                      type="primary"
+                      class="btn"
                       @click="updataItem(row.Index)"
                       style="height: 25px"
                       >修改</el-button
@@ -462,7 +440,7 @@
                     ><span>{{ Number(row.cpk).toFixed(2) }}</span>
                   </template>
                 </el-table-column>
-                <el-table-column
+                <!--                <el-table-column
                   align="center"
                   width="60"
                   prop="checkUser"
@@ -480,7 +458,7 @@
                   <template #default="{ row }"
                     ><span>{{ row.checkDeviceNo }}</span>
                   </template>
-                </el-table-column>
+                </el-table-column>-->
                 <el-table-column
                   align="center"
                   prop="abnormal"
@@ -522,12 +500,12 @@
                   </template>
                 </el-table-column>
               </el-table>
-              <div class="illustrate">
-                <div>
-                  1)Cpx值为表征内引线键合强度一致性的参数,通过相应的软件计算得出,内引线键合强度一致性是否符合要求的判据:Cpk值≥1.33。
-                </div>
-                <div>2)“备注”栏填写被测内引线的材料和规格。</div>
-              </div>
+              <!--              <div class="illustrate">-->
+              <!--                <div>-->
+              <!--                  1)Cpk值为表征内引线键合强度一致性的参数,通过相应的软件计算得出,内引线键合强度一致性是否符合要求的判据:Cpk值≥1.33。-->
+              <!--                </div>-->
+              <!--                <div>2)“备注”栏填写被测内引线的材料和规格。</div>-->
+              <!--              </div>-->
             </div>
             <Pagination
               :total="currentOption.total"
@@ -535,6 +513,7 @@
               :limit="currentOption.limit"
               :pageSizes="currentOption.pageSizes"
               v-model:page="currentOption.page"
+              v-model:limit="currentOption.limit"
               @pagination="getTableData"
             />
           </div>
@@ -856,13 +835,13 @@
               <el-form-item label="极差" prop="range">
                 <el-input v-model="addData.range" />
               </el-form-item> -->
-                <el-form-item label="检测人" prop="checkUser">
+                <!--                <el-form-item label="检测人" prop="checkUser">
                   <el-input v-model="addData.checkUser" />
                 </el-form-item>
                 <el-form-item label="检查设备编号" prop="checkDeviceNo">
                   <el-input v-model="addData.checkDeviceNo" />
-                </el-form-item>
-                <el-form-item label="是否异常" prop="abnormal">
+                </el-form-item>-->
+                <!--                <el-form-item label="是否异常" prop="abnormal">
                   <el-input v-model="addData.abnormal" />
                 </el-form-item>
                 <el-form-item label="分析人" prop="analyseUser">
@@ -870,7 +849,7 @@
                 </el-form-item>
                 <el-form-item label="处置措施" prop="measure">
                   <el-input v-model="addData.measure" />
-                </el-form-item>
+                </el-form-item>-->
                 <el-form-item label="备注" prop="remark">
                   <el-input
                     maxlength="40"
@@ -921,8 +900,59 @@ import {
   deleteData,
   updateData,
   getTaskCode,
+  cpkCompute,
 } from "@/api/analysis";
 import Search from "@/components/Search/index.vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+// 模拟后台接口
+const cpkApi = async (id) => {
+  const { data } = await cpkCompute({
+    spcRecordId: id,
+  });
+
+  return data;
+};
+
+const computeCPK = async (id) => {
+  try {
+    // 显示加载中的提示
+    ElMessageBox.alert("计算中...", "提示", {
+      confirmButtonText: "确定",
+      showClose: false,
+      closeOnClickModal: false,
+      closeOnPressEscape: false,
+    });
+
+    // 调用后台接口
+    const response = await cpkApi(id);
+
+    // 关闭加载中的提示
+    ElMessageBox.close();
+
+    const result = ref("");
+    if ("error" in response.data) {
+      result.value = response.data.error;
+    } else {
+      const { product, batch, process, cpk, status } = response.data;
+      result.value = `产品: ${product}<br>
+      批次: ${batch}<br>
+      工序: ${process}<br>
+      CPK: ${cpk}<br>
+      评级: ${status}`;
+    }
+
+    // 显示计算结果
+    ElMessageBox.alert(result.value, "计算结果", {
+      confirmButtonText: "确定",
+      dangerouslyUseHTMLString: true,
+    });
+  } catch (error) {
+    // 处理错误
+    ElMessageBox.close();
+  }
+};
+
 const tableData = ref([]);
 const printStatus = ref(false);
 const printLoading = ref(false);
@@ -938,13 +968,17 @@ const taskChange = (value) => {
   taskOption.value.forEach((item) => {
     if (item.id == value) {
       addData.value.model = item.prodtModel;
-      accuracysSum.value = Number(item.processCount);
-      addData.value.accuracys = [];
-      let array = [];
-      for (let i = 0; i < accuracysSum.value; i++) {
-        array.push(0);
+      if (item.source === "手动") {
+        accuracysSum.value = Number(item.processCount);
+        addData.value.accuracys = [];
+        let array = [];
+        for (let i = 0; i < accuracysSum.value; i++) {
+          array.push(0);
+        }
+        addData.value.accuracys = array;
+      } else {
+        addData.value.accuracys = [];
       }
-      addData.value.accuracys = array;
     }
   });
 };
@@ -1202,8 +1236,8 @@ const value = ref(opOptions.value[0].remark);
 const currentOption = reactive({
   total: 0,
   page: 1,
-  limit: 4,
-  pageSizes: [4],
+  limit: 10,
+  pageSizes: [10, 20, 30, 40, 50],
 });
 const modelOptions = ref([]);
 const setChart1Info = () => {
@@ -1641,9 +1675,9 @@ const reset = () => {
   addData.value = { ...resItem };
   currentOption.value = {
     total: 0,
-    page: 0,
-    limit: 4,
-    pageSizes: [4],
+    page: 1,
+    limit: 10,
+    pageSizes: [10, 20, 30, 40, 50],
   };
 };
 

+ 2 - 2
src/views/login/index.vue

@@ -137,8 +137,8 @@ const loginFormRef = ref(ElForm); // 登录表单ref
 const { height } = useWindowSize();
 
 const loginData = ref<LoginData>({
-  userName: "admin",
-  password: "admin@123",
+  userName: "",
+  password: "",
 });
 
 const loginRules = computed?.(() => {