Bladeren bron

feature/预警规则添加

dengrui 10 maanden geleden
bovenliggende
commit
3b679b7f1e

+ 1 - 1
public/version.json

@@ -1,3 +1,3 @@
 {
-  "version": "2.1"
+  "version": "2.2"
 }

+ 67 - 0
src/api/spc/index.js

@@ -0,0 +1,67 @@
+import request from "@/utils/request";
+
+//获取spc任务分页列表
+export function getTableData(data) {
+  return request({
+    url: "/api/v1/quality/task/page",
+    method: "post",
+    data,
+  });
+}
+//获取spc任务内基础绑定信息部分
+export function getBaseData() {
+  return request({
+    url: "/api/v1/quality/warningRule/prodtOperations",
+    method: "get",
+  });
+}
+//新增任务
+export function add(data) {
+  return request({
+    url: "/api/v1/quality/task/add",
+    method: "post",
+    data,
+  });
+}
+//修改任务
+export function updateData(data) {
+  return request({
+    url: "/api/v1/quality/task/update",
+    method: "post",
+    data,
+  });
+}
+
+//删除任务
+export function deleteData(data) {
+  return request({
+    url: "/api/v1/quality/task/del",
+    method: "post",
+    data,
+  });
+}
+
+//获取SPC任务规则
+export function getRuleData(data) {
+  return request({
+    url: "/api/v1/quality/warningRule/page",
+    method: "post",
+    data,
+  });
+}
+//新增SPC任务规则
+export function addRuleData(data) {
+  return request({
+    url: "/api/v1/quality/warningRule/add",
+    method: "post",
+    data,
+  });
+}
+//新增SPC任务规则
+export function deleteRuleData(data) {
+  return request({
+    url: "/api/v1/quality/warningRule/del",
+    method: "post",
+    data,
+  });
+}

+ 25 - 5
src/components/Search/index.vue

@@ -6,7 +6,7 @@
           <span class="label">{{ option.label }}:</span>
           <el-input
             class="tep"
-            model="input"
+            v-model="searchForm[option.prop]"
             placeholder="Please input"
             size="small"
           />
@@ -16,7 +16,7 @@
 
           <el-select
             class="tep"
-            v-model="value"
+            v-model="searchForm[option.prop]"
             size="small"
             placeholder="Select"
           >
@@ -31,20 +31,24 @@
         <div class="item" v-if="option.type == 'daterange'">
           <span class="label">{{ option.label }}:</span>
           <el-date-picker
-            v-model="value1"
+            v-model="searchForm[option.prop]"
             type="daterange"
             class="tep"
             range-separator="-"
             start-placeholder="开始日期"
             end-placeholder="结束日期"
             size="small"
+            value-format="YYYY-MM-DD"
+            format="YYYY-MM-DD"
           />
         </div>
       </template>
     </div>
     <div class="oprea">
-      <el-button type="primary" size="small" class="btn">查询</el-button>
-      <el-button class="btn" size="small">重置</el-button>
+      <el-button type="primary" size="small" class="btn" @click="getData"
+        >查询</el-button
+      >
+      <el-button class="btn" size="small" @click="reset">重置</el-button>
     </div>
   </div>
 </template>
@@ -55,7 +59,23 @@ const props = defineProps({
     type: Array,
   },
 });
+const emit = defineEmits(["dataList"]);
 const searchForm = ref({});
+const getData = () => {
+  emit("dataList");
+};
+const reset = () => {
+  console.log(searchForm.value, "222");
+};
+const setSearchFrom = () => {
+  props.searchOptions.forEach((option) => {});
+};
+const options = [
+  {
+    lable: "a",
+    value: "a",
+  },
+];
 defineExpose({ searchForm });
 </script>
 <style lang="scss" scoped>

+ 10 - 0
src/router/modules/analysis.ts

@@ -20,6 +20,16 @@ export default {
       },
     },
     {
+      path: "spcrules",
+      component: () => import("@/views/analysis/spcRules/index.vue"),
+      name: "SPCrules",
+      meta: {
+        title: "SPC预警规则",
+        icon: "Guide",
+      },
+      show: false,
+    },
+    {
       path: "process",
       component: () => import("@/views/analysis/process/index.vue"),
       name: "Process",

+ 1 - 0
src/store/index.ts

@@ -17,4 +17,5 @@ export * from "./modules/tagsView";
 export * from "./modules/user";
 export * from "./modules/common";
 export * from "./modules/dictionary";
+export * from "./modules/spc";
 export { store };

+ 3 - 1
src/store/modules/permission.ts

@@ -158,7 +158,9 @@ export const usePermissionStore = defineStore("permission", () => {
   function setMixLeftMenus(topMenuPath: string) {
     const matchedItem = routes.value.find((item) => item.path === topMenuPath);
     if (matchedItem && matchedItem.children) {
-      mixLeftMenus.value = matchedItem.children;
+      //未使用后端配套代码 这里添加逻辑 使用show字段控制左侧菜单显示
+      const array = matchedItem.children.filter((item) => item.show != false);
+      mixLeftMenus.value = array;
     }
   }
   return {

+ 18 - 0
src/store/modules/spc.ts

@@ -0,0 +1,18 @@
+import { store } from "@/store";
+import { defineStore } from "pinia";
+
+export const useSpcStore = defineStore("spcStore", {
+  state: () => {
+    const spcTaskObj = ref(null);
+    return {
+      spcTaskObj,
+    };
+  },
+  persist: {
+    enabled: true,
+  },
+});
+
+export function useSpcStoreHook() {
+  return useSpcStore(store);
+}

+ 360 - 342
src/views/analysis/spc/index.vue

@@ -1,396 +1,414 @@
 <template>
   <div class="container">
-    <div class="infobox">
-      <div class="header">
-        <div class="item">
-          <div class="text">产品型号:</div>
-          <el-select v-model="value" size="small" placeholder="Select">
-            <el-option
-              v-for="item in options"
-              :key="item.value"
-              :label="item.label"
-              :value="item.value"
-            />
-          </el-select>
-        </div>
-        <div class="item">
-          <div class="text">工序名称:</div>
-          <el-select v-model="value" size="small" placeholder="Select">
-            <el-option
-              v-for="item in options"
-              :key="item.value"
-              :label="item.label"
-              :value="item.value"
-            />
-          </el-select>
-        </div>
-        <div class="item">
-          <div class="text">控制参数:</div>
-          <el-select v-model="value" size="small" placeholder="Select">
-            <el-option
-              v-for="item in options"
-              :key="item.value"
-              :label="item.label"
-              :value="item.value"
-            />
-          </el-select>
-        </div>
-      </div>
-      <div class="body">
-        <el-scrollbar>
-          <div class="text">规格上限:1%</div>
-          <div class="text">规格下限:-</div>
-          <div class="text">计量单位:%</div>
-          <div class="text">Xbar-UCL:0.3096</div>
-          <div class="text">Xbar-LCL:0.0071</div>
-          <div class="text">R-UCL:0.5543</div>
-          <div class="text">R-LCL:0</div>
-          <div class="text">规格上限:1%</div>
-          <div class="text">规格下限:-</div>
-          <div class="text">计量单位:%</div>
-          <div class="text">Xbar-UCL:0.3096</div>
-          <div class="text">Xbar-LCL:0.0071</div>
-          <div class="text">R-UCL:0.5543</div>
-          <div class="text">R-LCL:0</div>
-          <div class="text">规格上限:1%</div>
-          <div class="text">规格下限:-</div>
-          <div class="text">计量单位:%</div>
-          <div class="text">Xbar-UCL:0.3096</div>
-          <div class="text">Xbar-LCL:0.0071</div>
-          <div class="text">R-UCL:0.5543</div>
-          <div class="text">R-LCL:0</div>
-          <div class="text">规格上限:1%</div>
-          <div class="text">规格下限:-</div>
-          <div class="text">计量单位:%</div>
-          <div class="text">Xbar-UCL:0.3096</div>
-          <div class="text">Xbar-LCL:0.0071</div>
-          <div class="text">R-UCL:0.5543</div>
-          <div class="text">R-LCL:0</div>
-          <div class="text">规格上限:1%</div>
-          <div class="text">规格下限:-</div>
-          <div class="text">计量单位:%</div>
-          <div class="text">Xbar-UCL:0.3096</div>
-          <div class="text">Xbar-LCL:0.0071</div>
-          <div class="text">R-UCL:0.5543</div>
-          <div class="text">R-LCL:0</div>
-        </el-scrollbar>
-      </div>
+    <div class="header" v-show="!addStatus && !editStatus">
+      <Search :searchOptions="searchForm" ref="searchRef" />
     </div>
-    <div class="databox">
-      <div class="boxstitle">
-        <div class="boxtitle">
-          <div class="bg"></div>
-          手工录入
-        </div>
-        <div class="boxinfo">
-          <div class="item">
-            <div class="lable">组数:</div>
-            <el-input placeholder="Please input" size="small" />
-          </div>
-          <div class="item">
-            <div class="lable">样本数:</div>
-            <el-input placeholder="Please input" size="small" />
-          </div>
-        </div>
-      </div>
-      <div class="box">
-        <div class="title">
-          <div class="bg"></div>
-          样本数据录入
-        </div>
-        <div class="info">
-          <el-table
-            :data="tableData"
-            border
-            :style="{ height: maxHeight + 'px' }"
-          >
-            <el-table-column prop="date" label="Date" width="180" />
-            <el-table-column prop="name" label="Name" width="180" />
-            <el-table-column prop="address" label="Address" />
-          </el-table>
-        </div>
+    <div class="table" v-if="!addStatus && !editStatus">
+      <el-button
+        type="primary"
+        size="small"
+        class="btn"
+        style="margin-bottom: 10px"
+        @click="toAdd"
+        >新增</el-button
+      >
+      <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="address" label="预警规则">
+          <template #default="{ row }">
+            <el-button
+              type="primary"
+              class="btn"
+              style="height: 25px"
+              link
+              @click="toRuler(row)"
+              >编辑</el-button
+            >
+          </template>
+        </el-table-column>
+        <el-table-column prop="created" label="创建时间" />
+        <el-table-column
+          align="center"
+          width="160"
+          prop=""
+          label="操作"
+          id="opear"
+        >
+          <template #default="{ row }">
+            <el-button
+              type="primary"
+              class="btn"
+              style="height: 25px"
+              @click="toEdit(row)"
+              link
+              >编辑</el-button
+            >
+            <el-button
+              type="info"
+              class="btn"
+              style="height: 25px"
+              link
+              @click="toDelete(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"
+        @pagination="getData"
+      />
+    </div>
+    <div v-if="addStatus || editStatus" class="formView">
+      <div class="formTitle">
+        {{ addStatus ? "新增任务" : "编辑任务" }}
       </div>
-      <div class="box">
-        <div class="title">
-          <div class="bg"></div>
-          控制图绘制
+      <el-scrollbar style="height: calc(100% - 60px)">
+        <el-form
+          ref="ruleFormRef"
+          :model="formData"
+          :rules="rules"
+          label-width="auto"
+          class="formStyle"
+        >
+          <el-form-item label="产品型号" prop="Index1">
+            <el-select v-model="formData.Index1">
+              <el-option
+                v-for="(item, index) in opInfoData"
+                :key="index"
+                :label="item.prodtModel"
+                :value="index"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="工序名称" prop="Index2">
+            <el-select v-model="formData.Index2">
+              <el-option
+                v-for="(item, index) in opInfoData2"
+                :key="index"
+                :label="item.name"
+                :value="index"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="采集数据来源" prop="source">
+            <el-select v-model="formData.source">
+              <el-option label="自动" value="自动" />
+              <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>
+        <div style="display: flex; justify-content: space-evenly">
           <el-button
             type="primary"
-            v-print="'#charts'"
-            style="margin-left: 10px; height: 25px"
-            >打 印</el-button
+            size="small"
+            style="margin-top: 10px"
+            class="btn"
+            @click="toSubmit"
+            >提交</el-button
+          >
+          <el-button
+            type="info"
+            size="small"
+            style="margin-top: 10px"
+            class="btn"
+            @click="toCancel"
+            >取消</el-button
           >
         </div>
-        <div class="info">
-          <div
-            id="charts"
-            :style="{ height: maxHeight + 'px', width: maxWidth + 'px' }"
-          ></div>
-        </div>
-      </div>
+      </el-scrollbar>
     </div>
   </div>
 </template>
 
 <script setup>
-import * as echarts from "echarts";
-const value = ref("");
-const options = [
-  {
-    value: "1",
-    label: "调阻1",
-  },
-  {
-    value: "23",
-    label: "调阻2",
-  },
-];
-const maxHeight = ref(null);
-const maxWidth = ref(null);
-const charts = shallowRef(null);
-const tableData = [
-  {
-    date: "2016-05-03",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-02",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-04",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-01",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-02",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-04",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-01",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-02",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-04",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-01",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-02",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-04",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-01",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-02",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-04",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
-  {
-    date: "2016-05-01",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
-  },
+import Search from "@/components/Search/index.vue";
+import { useSpcStore } from "@/store";
+import {
+  getTableData,
+  getBaseData,
+  add,
+  updateData,
+  deleteData,
+} from "@/api/spc";
+defineOptions({
+  name: "SPCrules",
+});
+const store = useSpcStore();
+const addStatus = ref(false);
+const editStatus = ref(false);
+const router = useRouter();
+const formData = ref({
+  source: "手动",
+  chart: "X-R",
+});
+const currentOption = reactive({
+  total: 0,
+  page: 1,
+  limit: 10,
+  pageSizes: [10, 20, 40],
+});
+const showStatus = ref(true);
+const tableData = ref([]);
+const searchRef = ref(null);
+const searchForm = [
   {
-    date: "2016-05-02",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
+    label: "创建时间",
+    prop: "created",
+    type: "daterange",
   },
   {
-    date: "2016-05-04",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
+    label: "任务编号",
+    prop: "taskCode",
+    type: "input",
   },
   {
-    date: "2016-05-01",
-    name: "Tom",
-    address: "No. 189, Grove St, Los Angeles",
+    label: "产品型号",
+    prop: "prodtModel",
+    type: "input",
   },
+  // {
+  //   label: "工序名称",
+  //   prop: "sss",
+  //   type: "select",
+  // },
 ];
-const chartsOption = ref({
-  title: [
-    {
-      text: "调阻精度的Xbar-R控制图",
-      left: "40%",
-    },
-    {
-      text: "样",
-      left: "4%",
-      top: "28%",
-    },
+const maxHeight = ref(null);
+const setHeight = () => {
+  maxHeight.value = document.querySelector(".table").clientHeight;
+};
+const opInfoData2 = computed(() => {
+  if (formData.value.Index1 != null) {
+    return opInfoData.value[formData.value.Index1].operations;
+  } else {
+    return [];
+  }
+});
+const toCancel = () => {
+  reset();
+};
+const toRuler = (row) => {
+  store.spcTaskObj = row;
+  router.push({ name: "SPCrules" });
+};
+const getData = async (obj) => {
+  if (obj) {
+    currentOption.page = obj.page;
+    currentOption.limit = obj.limit;
+  }
+  const { data, code } = await getTableData({
+    ...searchRef.value.searchForm,
+    pageNo: currentOption.page,
+    pageSize: currentOption.limit,
+  });
+  if (code == "200") {
+    tableData.value = data.records;
+    currentOption.total = data.totalCount;
+  }
+};
+const ruleFormRef = ref(null);
+
+const rules = {
+  Index1: [
     {
-      text: "本",
-      left: "4%",
-      top: "35%",
+      required: true,
+      trigger: "change",
+      message: "请选择产品型号",
     },
+  ],
+  Index2: [
     {
-      text: "均",
-      left: "4%",
-      top: "42%",
+      required: true,
+      message: "请选择工序名称",
+      trigger: "change",
     },
+  ],
+  source: [
     {
-      text: "值",
-      left: "4%",
-      top: "49%",
+      required: true,
+
+      trigger: "change",
     },
   ],
-  toolbox: {
-    feature: {
-      saveAsImage: {},
-    },
-  },
-  xAxis: [
+  chart: [
     {
-      type: "category",
-      boundaryGap: false,
-      data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+      required: true,
+
+      trigger: "change",
     },
   ],
-  yAxis: [
+  param: [
     {
-      type: "value",
+      required: true,
+
+      trigger: "change",
     },
   ],
-  series: [
+  unit: [
     {
-      data: [150, 230, 224, 218, 135, 147, 260],
-      type: "line",
-      endLabel: {
-        show: true,
-      },
+      required: true,
+
+      trigger: "change",
     },
   ],
-});
-defineOptions({
-  name: "Dashboard",
-  inheritAttrs: false,
-});
-const setHeight = () => {
-  maxHeight.value = document.querySelector(".info").clientHeight;
-  maxWidth.value = document.querySelector(".info").clientWidth;
 };
+const opInfoData = ref([]);
+const toAdd = () => {
+  addStatus.value = true;
+};
+const setEditFormData = (row) => {
+  opInfoData.value.forEach((item, index) => {
+    if (item.prodtCode == row.prodtCode) {
+      formData.value.Index1 = index;
+    }
+  });
+  opInfoData.value[formData.value.Index1].operations.forEach((item, index) => {
+    if (item.code == row.operationCode) {
+      formData.value.Index2 = index;
+    }
+  });
+};
+const toEdit = (row) => {
+  formData.value = row;
+  //设置index1 index2
+  setEditFormData(row);
+  editStatus.value = true;
+};
+const toDelete = async (id) => {
+  const { code } = await deleteData({ id });
+  if (code == "200") {
+    ElMessage.success("操作成功");
+    getData();
+  }
+};
+const reset = () => {
+  formData.value = {
+    source: "手动",
+    chart: "X-R",
+  };
+  addStatus.value = false;
+  editStatus.value = false;
+};
+const toSubmit = async () => {
+  if (addStatus.value) {
+    await ruleFormRef.value.validate(async (valid, fields) => {
+      if (valid) {
+        const { data, code } = await add({
+          chart: formData.value.chart,
+          operationCode:
+            opInfoData.value[formData.value.Index1].operations[
+              formData.value.Index2
+            ].code,
+          operationName:
+            opInfoData.value[formData.value.Index1].operations[
+              formData.value.Index2
+            ].name,
 
+          prodtCode: opInfoData.value[formData.value.Index1].prodtCode,
+          prodtModel: opInfoData.value[formData.value.Index1].prodtModel,
+          prodtName: opInfoData.value[formData.value.Index1].prodtName,
+          unit: opInfoData.value[formData.value.Index1].unit,
+          param: formData.value.param,
+
+          source: formData.value.source,
+        });
+        if (code == "200") {
+          ElMessage.success("添加成功!");
+          reset();
+          getData();
+        }
+      } else {
+        ElMessage.error("请检查表单信息");
+      }
+    });
+  } else {
+    await ruleFormRef.value.validate(async (valid, fields) => {
+      if (valid) {
+        const { data, code } = await updateData({
+          ...formData.value,
+          operationCode:
+            opInfoData.value[formData.value.Index1].operations[
+              formData.value.Index2
+            ].code,
+          operationName:
+            opInfoData.value[formData.value.Index1].operations[
+              formData.value.Index2
+            ].name,
+          prodtCode: opInfoData.value[formData.value.Index1].prodtCode,
+          prodtModel: opInfoData.value[formData.value.Index1].prodtModel,
+          prodtName: opInfoData.value[formData.value.Index1].prodtName,
+          unit: opInfoData.value[formData.value.Index1].unit,
+        });
+        if (code == "200") {
+          ElMessage.success("修改成功!");
+          reset();
+          getData();
+        }
+      } else {
+        ElMessage.error("请检查表单信息");
+      }
+    });
+  }
+};
+const getBaseDatas = async () => {
+  const { data, code } = await getBaseData();
+  opInfoData.value = data;
+};
 onMounted(() => {
   setHeight();
-  nextTick(() => {
-    charts.value = echarts.init(document.getElementById("charts"));
-    charts.value.setOption(chartsOption.value);
-  });
+  getBaseDatas();
+  getData();
 });
 </script>
 
-<style lang="scss" scoped>
+<style scoped lang="scss">
 .container {
+  background-color: white;
   width: 100%;
   height: 100%;
+  padding: 20px;
   display: flex;
-  background-color: white;
-  .infobox {
-    width: 200px;
-    display: flex;
-    flex-direction: column;
-    .header {
-      border-bottom: 2px solid #00000010;
-      padding: 20px;
-      .item {
-        display: flex;
-        align-items: center;
-        margin-bottom: 10px;
-        .text {
-          white-space: nowrap;
-          line-height: 100%;
-          display: inline-block;
-          margin-bottom: 0;
-        }
-      }
-    }
-    .body {
-      padding: 20px;
-      flex: 1;
-      height: calc(100% - 300px);
-    }
+  flex-direction: column;
+  .header {
+    width: 100%;
+    height: auto;
   }
-  .databox {
+  .table {
     flex: 1;
-    border-left: 2px solid #00000010;
+    padding-bottom: 20px;
+  }
+  .formView {
+    width: 100%;
     height: 100%;
-    .box {
-      height: calc(50% - 40px);
-      padding: 5px 20px;
-      display: flex;
-      flex-direction: column;
-      .title {
-        height: 30px;
-        display: flex;
-        align-items: center;
-      }
-      .info {
-        flex: 1;
-      }
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .formTitle {
+      text-align: center;
+      font-size: 20px;
+      margin-bottom: 20px;
     }
-    .boxstitle {
-      height: 80px;
-      padding: 5px 20px;
-      display: flex;
-      flex-direction: column;
-      .boxtitle {
-        height: 30px;
-        display: flex;
-        align-items: center;
-      }
-      .boxinfo {
-        display: flex;
-        .item {
-          width: 260px;
-          display: flex;
-          align-items: center;
-          margin: 10px;
-
-          .lable {
-            margin-right: 5px;
-            white-space: nowrap;
-            font-size: 15px;
-          }
-        }
-      }
+    .formStyle {
+      width: 400px;
     }
   }
 }

+ 697 - 0
src/views/analysis/spcRules/index.vue

@@ -0,0 +1,697 @@
+<template>
+  <div class="container">
+    <div class="databox">
+      <div class="box">
+        <div class="title">
+          <div class="bg"></div>
+          配置列表
+        </div>
+        <div class="info">
+          <el-table
+            :data="tableData"
+            border
+            :style="{ height: maxHeight - 30 + 'px' }"
+          >
+            <el-table-column type="index" label="序号" width="60" />
+            <el-table-column prop="operationName" label="工序名称" />
+            <el-table-column prop="field" label="参数项" />
+            <el-table-column prop="chart" label="控制图" />
+            <el-table-column prop="ruleContent" label="规则内容" width="200" />
+            <el-table-column prop="unit" label="参数单位" />
+            <el-table-column
+              align="center"
+              width="160"
+              prop=""
+              label="操作"
+              id="opear"
+            >
+              <template #default="{ row }">
+                <el-button
+                  v-if="addRuleStatus"
+                  class="btn"
+                  link
+                  @click="editSubmit(row)"
+                  >编辑</el-button
+                >
+                <el-button v-else class="btn" link @click="cancelEdit"
+                  >取消</el-button
+                >
+                <el-button link @click="deleteRules(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"
+          />
+        </div>
+      </div>
+      <div class="box">
+        <div class="title">
+          <div class="bg"></div>
+          预警规则配置
+        </div>
+        <div class="info">
+          <div class="taskInfo">
+            <span class="item"
+              >当前规则编辑状态:{{ addRuleStatus ? "新增" : "编辑" }}</span
+            >
+            <span class="item"
+              >工序名称:{{
+                !addRuleStatus
+                  ? editRow.operationName
+                  : store.spcTaskObj.operationName
+              }}</span
+            >
+            <span class="item">
+              参数项:{{
+                !addRuleStatus ? editRow.field : store.spcTaskObj.param
+              }}
+            </span>
+          </div>
+          <div class="form" :key="formKey">
+            <el-scrollbar
+              :style="{ height: maxHeight - 100 + 'px' }"
+              style="padding: 20px"
+            >
+              <template v-for="(item, index) in formData" :key="index">
+                <el-form
+                  :ref="
+                    (el) => {
+                      if (item) {
+                        formRefs.push(el);
+                      }
+                    }
+                  "
+                  :model="item"
+                  style="display: flex; align-items: center"
+                >
+                  <el-form-item>
+                    <div style="display: flex; align-items: center">
+                      <span class="itemText" style="width: 50px"
+                        >条件{{ index + 1 }}:</span
+                      >
+                    </div>
+                  </el-form-item>
+                  <el-form-item
+                    :rules="[
+                      {
+                        required: true,
+                        trigger: 'change',
+                      },
+                    ]"
+                    prop="sum1type"
+                  >
+                    <div
+                      style="
+                        display: flex;
+                        align-items: center;
+                        margin-right: 10px;
+                      "
+                    >
+                      <el-select
+                        v-model="item.sum1type"
+                        style="width: 160px"
+                        class="formItem"
+                        placeholder="请选择规则点类型"
+                      >
+                        <el-option
+                          v-for="(item, index) in sum1Options"
+                          :key="index"
+                          :label="item.label"
+                          :value="item.value"
+                        />
+                      </el-select>
+                    </div>
+                  </el-form-item>
+                  <el-form-item
+                    v-if="item.sum1type == '连续'"
+                    :rules="[
+                      {
+                        required: true,
+                        trigger: 'change',
+                      },
+                    ]"
+                    prop="sum1"
+                    ><div
+                      style="
+                        display: flex;
+                        align-items: center;
+                        margin-right: 10px;
+                      "
+                    >
+                      <el-input-number
+                        class="formItem"
+                        style="width: 80px"
+                        v-model="item.sum1"
+                        :min="1"
+                        :precision="0"
+                        controls-position="right"
+                      /><span class="itemText">个点</span>
+                    </div>
+                  </el-form-item>
+                  <el-form-item
+                    v-if="item.sum1type == '连续'"
+                    prop="sum2"
+                    :rules="[
+                      {
+                        validator: validateSum2,
+                        required: true,
+                        trigger: 'change',
+                        sum1: item.sum1,
+                        message: '应小于或等于前连续点数',
+                      },
+                    ]"
+                    ><div style="display: flex; align-items: center">
+                      <span style="margin-left: 0" class="itemText">中的</span>
+                      <el-input-number
+                        class="formItem"
+                        style="width: 80px"
+                        v-model="item.sum2"
+                        :min="1"
+                        :precision="0"
+                        controls-position="right"
+                      /><span class="itemText">个点</span>
+                    </div>
+                  </el-form-item>
+                  <el-form-item
+                    :rules="[
+                      {
+                        required: true,
+                        trigger: 'change',
+                      },
+                    ]"
+                    prop="judgeType"
+                  >
+                    <el-select
+                      class="formItem"
+                      style="width: 170px; margin-right: 5px"
+                      v-model="item.judgeType"
+                      placeholder="请选择判定类型"
+                    >
+                      <el-option
+                        v-for="(item, index) in item.sum1type == '连续'
+                          ? judgeTypeOptions2
+                          : judgeTypeOptions1"
+                        :key="index"
+                        :label="item.label"
+                        :value="item.value"
+                      />
+                    </el-select>
+                  </el-form-item>
+                  <el-form-item
+                    :rules="[
+                      {
+                        required: true,
+                        trigger: 'change',
+                      },
+                    ]"
+                    prop="numerical"
+                    v-if="
+                      item.judgeType == '大于' ||
+                      item.judgeType == '小于' ||
+                      item.judgeType == '等于'
+                    "
+                  >
+                    <el-select
+                      class="formItem"
+                      style="width: 170px; margin-right: 5px"
+                      v-model="item.numerical"
+                      placeholder="请选择择值范围"
+                    >
+                      <el-option
+                        v-for="(item, index) in numericalOptions"
+                        :key="index"
+                        :label="item.label"
+                        :value="item.value"
+                      />
+                    </el-select>
+                  </el-form-item>
+                  <el-form-item
+                    :rules="[
+                      {
+                        required: false,
+                        trigger: 'change',
+                      },
+                    ]"
+                    label=""
+                  >
+                    <el-button
+                      v-if="index == formData.length - 1"
+                      type="success"
+                      class="btn"
+                      @click="addRule"
+                      style="margin-left: 5px"
+                      >添加</el-button
+                    >
+                    <el-button
+                      v-if="formData.length > 1"
+                      type="warning"
+                      class="btn"
+                      style="margin-left: 5px"
+                      @click="deleteRule(index)"
+                      >删除</el-button
+                    >
+                  </el-form-item>
+                </el-form>
+                <div class="titleText" v-if="index < formData.length - 1">
+                  且
+                </div>
+              </template>
+              <el-form style="display: flex; align-items: center">
+                <el-form-item>
+                  <div style="display: flex; align-items: center">
+                    <span class="itemText" style="width: 80px">预警提示:</span>
+                    <el-input
+                      class="formItem"
+                      style="width: 400px"
+                      v-model="ruleTips"
+                    />
+                  </div> </el-form-item
+              ></el-form>
+            </el-scrollbar>
+          </div>
+          <div class="btns">
+            <el-button type="primary" class="btn" @click="tosubmit"
+              >提交</el-button
+            >
+          </div>
+        </div>
+      </div>
+    </div>
+    <el-dialog
+      v-model="dialogVisible"
+      title="预警规则编辑"
+      width="500"
+      :before-close="handleClose"
+    >
+      <div class="dialogTitle">请确认规则修改为:</div>
+      <div class="contentTitle">{{ itemContent }}</div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="sumitRule">确定</el-button>
+          <el-button @click="dialogVisible = false"> 取消 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { useSpcStore } from "@/store";
+import { getRuleData, addRuleData, deleteRuleData } from "@/api/spc";
+const store = useSpcStore();
+const dialogVisible = ref(false);
+const ruleTips = ref("");
+const formRefs = ref([]);
+const formData = ref([]);
+const formKey = ref(false);
+const itemObj = {};
+const addRule = () => {
+  formRefs.value = [];
+  formData.value.push({ ...itemObj });
+};
+const editRow = ref({});
+const validateSum2 = (rule, value, callback) => {
+  if (value === "") {
+    callback(new Error("Please input the sum2"));
+  } else {
+    if (value <= rule.sum1) {
+      callback();
+    } else {
+      callback(new Error("Please check the sum2"));
+    }
+  }
+};
+
+const deleteRule = (index) => {
+  formRefs.value = [];
+  formData.value.splice(index, 1);
+  ElMessage.success("删除成功");
+};
+const deleteRules = async (id) => {
+  const { code } = await deleteRuleData({ id });
+  if (code == "200") {
+    ElMessage.success("操作成功");
+    reset();
+  }
+};
+const sumitRule = () => {
+  addSubmit();
+  reset();
+  dialogVisible.value = false;
+};
+const reset = () => {
+  formKey.value = !formKey.value;
+  getData();
+  refsData();
+  ruleTips.value = "";
+  editRow.value = {};
+  addRuleStatus.value = true;
+};
+const addRuleStatus = ref(true);
+const editSubmit = (row) => {
+  editRow.value = { ...row };
+  formData.value = JSON.parse(editRow.value.ruleExpression);
+  ruleTips.value = editRow.value.ruleTips;
+  addRuleStatus.value = false;
+};
+const sum1Options = [
+  {
+    label: "连续",
+    value: "连续",
+  },
+  {
+    lable: "单点",
+    value: "单点",
+  },
+];
+const judgeTypeOptions1 = [
+  {
+    label: "大于",
+    value: "大于",
+  },
+  {
+    label: "小于",
+    value: "小于",
+  },
+  {
+    label: "等于",
+    value: "等于",
+  },
+];
+const judgeTypeOptions2 = [
+  {
+    label: "大于",
+    value: "大于",
+  },
+  {
+    label: "小于",
+    value: "小于",
+  },
+  {
+    label: "等于",
+    value: "等于",
+  },
+  {
+    label: "递增",
+    value: "递增",
+  },
+  {
+    label: "递减",
+    value: "递减",
+  },
+  {
+    label: "升降",
+    value: "升降",
+  },
+];
+const numericalOptions = [
+  {
+    label: "上限值",
+    value: "上限值",
+  },
+  {
+    label: "下限值",
+    value: "下限值",
+  },
+  {
+    label: "标准值",
+    value: "标准值",
+  },
+  {
+    label: "三分之一上限值",
+    value: "三分之一上限值",
+  },
+  {
+    label: "三分之二上限值",
+    value: "三分之二上限值",
+  },
+  {
+    label: "三分之一下限值",
+    value: "三分之一下限值",
+  },
+  {
+    label: "三分之二下限值",
+    value: "三分之二下限值",
+  },
+  {
+    label: "上限范围C",
+    value: "上限范围C",
+  },
+  {
+    label: "上限范围B",
+    value: "上限范围B",
+  },
+  {
+    label: "上限范围A",
+    value: "上限范围A",
+  },
+  {
+    label: "下限范围C",
+    value: "下限范围C",
+  },
+  {
+    label: "下限范围B",
+    value: "下限范围B",
+  },
+  {
+    label: "下限范围A",
+    value: "下限范围A",
+  },
+];
+const currentOption = reactive({
+  total: 0,
+  page: 1,
+  limit: 10,
+  pageSizes: [10, 20],
+});
+const itemContent = ref("");
+const setContent = () => {
+  itemContent.value = "";
+  formData.value.forEach((item, index) => {
+    if (item.sum1type == "单点") {
+      itemContent.value =
+        itemContent.value +
+        `当${item.sum1type}${item.judgeType}${item.numerical}`;
+    } else {
+      itemContent.value =
+        itemContent.value +
+        `当${item.sum1type}的${item.sum1}个点中的${item.sum2}个点${item.judgeType}${item.judgeType == "大于" || item.judgeType == "小于" || item.judgeType == "等于" ? item.numerical : ""}`;
+    }
+    if (index < formData.value.length - 1) {
+      itemContent.value = itemContent.value + "且";
+    }
+    if (index == formData.value.length - 1) {
+      itemContent.value = itemContent.value + "时预警";
+    }
+  });
+};
+const tableData = ref([]);
+const cancelEdit = () => {
+  formKey.value = !formKey.value;
+  refsData();
+  ruleTips.value = "";
+  editRow.value = {};
+  addRuleStatus.value = true;
+};
+const tosubmit = async () => {
+  //首先进行动态校验
+  const promiseArray = ref([]);
+  formRefs.value.forEach((item) => {
+    if (item) {
+      promiseArray.value.push(
+        new Promise((resolve, reject) => {
+          item.validate((valid, fields) => {
+            if (valid) {
+              resolve();
+            } else {
+              reject();
+            }
+          });
+        })
+      );
+    }
+  });
+
+  Promise.all(promiseArray.value)
+    .then(() => {
+      //校验成功 进入下层逻辑
+      setContent();
+      dialogVisible.value = true;
+    })
+    .catch(() => {
+      //校验失败
+      ElMessage.error("请检查表单红项填写");
+    });
+};
+const addSubmit = async () => {
+  //编辑和新加都在此调用(依靠id是否传递区分)
+  const { data, code } = await addRuleData({
+    chart: store.spcTaskObj.chart,
+    field: store.spcTaskObj.param,
+    label: store.spcTaskObj.operationName,
+    operationCode: store.spcTaskObj.operationCode,
+    operationName: store.spcTaskObj.operationName,
+    ruleContent: itemContent.value,
+    ruleExpression: JSON.stringify(formData.value),
+    ruleTips: ruleTips.value,
+    taskCode: store.spcTaskObj.taskCode,
+    unit: store.spcTaskObj.unit,
+    id: addRuleStatus.value == true ? null : editRow.value.id,
+  });
+  if (code == "200") {
+    ElMessage.success("操作成功");
+  }
+};
+const getData = async () => {
+  const { data, msg } = await getRuleData({
+    pageNo: currentOption.page,
+    pageSize: currentOption.limit,
+    taskCode: store.spcTaskObj.taskCode,
+  });
+  tableData.value = data.records;
+  currentOption.total = data.totalCount;
+};
+const maxHeight = ref(null);
+const setHeight = () => {
+  maxHeight.value = document.querySelector(".info").clientHeight;
+};
+const refsData = () => {
+  formRefs.value = [];
+  formData.value = [{}];
+};
+onMounted(() => {
+  setHeight();
+  getData();
+  refsData();
+});
+</script>
+
+<style lang="scss" scoped>
+.dialogTitle {
+  font-size: 15px;
+  font-weight: bolder;
+}
+
+.contentTitle {
+  margin: 10px;
+  font-size: 16px;
+  font-weight: 500;
+}
+.el-form-item {
+  margin-bottom: 10px !important;
+}
+.titleText {
+  font-size: 14px;
+  font-weight: 500;
+  color: #67c23a;
+  padding-left: 40px;
+  margin: 5px;
+}
+.container {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  background-color: white;
+  .infobox {
+    width: 200px;
+    display: flex;
+    flex-direction: column;
+  }
+  .databox {
+    flex: 1;
+    border-left: 2px solid #00000010;
+    height: 100%;
+    .box {
+      height: calc(50%);
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .title {
+        height: 30px;
+        display: flex;
+        align-items: center;
+      }
+      .info {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        .taskInfo {
+          height: 30px;
+          .item {
+            font-size: 15px;
+            margin-right: 20px;
+          }
+        }
+        .form {
+          flex: 1;
+          border: 1px dashed #00000020;
+          border-radius: 16px;
+
+          .itemText {
+            margin: 0 5px;
+          }
+          .formItem {
+            display: inline-block;
+          }
+        }
+        .btns {
+          height: 80px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          .btn {
+            height: 40px;
+          }
+        }
+      }
+    }
+    .boxstitle {
+      height: 80px;
+      padding: 5px 20px;
+      display: flex;
+      flex-direction: column;
+      .boxtitle {
+        height: 30px;
+        display: flex;
+        align-items: center;
+      }
+      .boxinfo {
+        display: flex;
+        .item {
+          width: 260px;
+          display: flex;
+          align-items: center;
+          margin: 10px;
+
+          .lable {
+            margin-right: 5px;
+            white-space: nowrap;
+            font-size: 15px;
+          }
+        }
+      }
+    }
+  }
+}
+.submitBox {
+  height: 60vh;
+  display: flex;
+  flex-direction: column;
+  .submitInfo {
+    flex: 1;
+
+    .text {
+      font-size: 16px;
+      font-weight: 500;
+    }
+  }
+  .btns {
+    height: 60px;
+  }
+}
+</style>