Ver Fonte

feat: 学校设置

zhangjie há 2 meses atrás
pai
commit
b633694487

+ 8 - 0
src/modules/admin/api.js

@@ -183,6 +183,14 @@ export const schoolSetRobotUpdate = (datas) => {
   return $post("/api/admin/set/ai/robot/save", datas);
 };
 
+// AI智能评卷配置
+export const schoolSetAiInfo = (schoolId) => {
+  return $postParam("/api/admin/set/ai/select", { schoolId });
+};
+export const schoolSetAiUpdate = (datas) => {
+  return $post("/api/admin/set/ai/save", datas);
+};
+
 // 字体配置
 export const schoolSetFontInfo = (schoolId) => {
   return $postParam("/api/admin/set/font/select", { schoolId });

+ 5 - 37
src/modules/admin/components/ModifySchoolSet.vue

@@ -29,33 +29,21 @@
 </template>
 
 <script>
-import SchoolSetCheck from "./school/SchoolSetCheck.vue";
+import SchoolSetBase from "./school/SchoolSetBase.vue";
 import SchoolSetData from "./school/SchoolSetData.vue";
 import SchoolSetMenu from "./school/SchoolSetMenu.vue";
-import SchoolSetPaper from "./school/SchoolSetPaper.vue";
-import SchoolSetRole from "./school/SchoolSetRole.vue";
 import SchoolSetSync from "./school/SchoolSetSync.vue";
-import SchoolSetTarget from "./school/SchoolSetTarget.vue";
-import SchoolSetStdno from "./school/SchoolSetStdno.vue";
 import SchoolSetDatabaseSync from "./school/SchoolSetDatabaseSync.vue";
-import SchoolSetRobot from "./school/SchoolSetRobot.vue";
-import SchoolSetRecognition from "./school/SchoolSetRecognition.vue";
 import SchoolSetFont from "./school/SchoolSetFont.vue";
 
 export default {
   name: "modify-school-set",
   components: {
-    SchoolSetCheck,
+    SchoolSetBase,
     SchoolSetData,
     SchoolSetMenu,
-    SchoolSetPaper,
-    SchoolSetRole,
     SchoolSetSync,
-    SchoolSetTarget,
-    SchoolSetStdno,
     SchoolSetDatabaseSync,
-    SchoolSetRobot,
-    SchoolSetRecognition,
     SchoolSetFont,
   },
   props: {
@@ -69,40 +57,20 @@ export default {
   data() {
     return {
       modalIsShow: false,
-      curTab: "check",
+      curTab: "base",
       tabs: [
         {
-          name: "用户验证配置",
-          val: "check",
+          name: "基础配置",
+          val: "base",
         },
         {
           name: "菜单管理",
           val: "menu",
         },
-        {
-          name: "学号配置",
-          val: "stdno",
-        },
-        {
-          name: "角色管理",
-          val: "role",
-        },
-        {
-          name: "试卷规格配置",
-          val: "paper",
-        },
         {
           name: "同步配置",
           val: "sync",
         },
-        {
-          name: "课程目标",
-          val: "target",
-        },
-        {
-          name: "识别配置",
-          val: "recognition",
-        },
         {
           name: "数据同步",
           val: "database-sync",

+ 188 - 0
src/modules/admin/components/school/SchoolSetAi.vue

@@ -0,0 +1,188 @@
+<template>
+  <div class="school-set-ai part-box part-box-pad">
+    <el-form
+      ref="modalFormComp"
+      :model="formData"
+      :rules="rules"
+      label-width="240px"
+    >
+      <!-- 是否开启AI智能评卷 -->
+      <el-form-item prop="enableAI" label="是否开启AI智能评卷:">
+        <el-radio-group v-model="formData.enableAI">
+          <el-radio
+            v-for="item in OPEN_STATUS"
+            :key="item.value"
+            :label="item.value"
+            >{{ item.label }}</el-radio
+          >
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 大模型类型 -->
+      <el-form-item prop="modelType" label="大模型类型:">
+        <el-radio-group
+          v-model="formData.modelType"
+          @change="handleModelTypeChange"
+        >
+          <el-radio
+            v-for="item in MODEL_TYPES"
+            :key="item.value"
+            :label="item.value"
+            >{{ item.label }}</el-radio
+          >
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 本地大模型地址 -->
+      <el-form-item
+        v-if="formData.modelType === 'local'"
+        prop="modelUrl"
+        label="大模型地址:"
+      >
+        <el-input
+          v-model.trim="formData.modelUrl"
+          placeholder="请输入大模型地址"
+          clearable
+        >
+        </el-input>
+      </el-form-item>
+
+      <!-- OCR识别地址 -->
+      <el-form-item
+        v-if="formData.modelType === 'local'"
+        prop="ocrUrl"
+        label="OCR识别地址:"
+      >
+        <el-input
+          v-model.trim="formData.ocrUrl"
+          placeholder="请输入OCR识别地址"
+          clearable
+        >
+        </el-input>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" :loading="loading" @click="confirm"
+          >保存</el-button
+        >
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { OPEN_STATUS } from "../../../../constants/enumerate";
+import { schoolSetAiInfo, schoolSetAiUpdate } from "../../api";
+
+export default {
+  name: "school-set-ai",
+  props: {
+    school: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      loading: false,
+      OPEN_STATUS,
+      MODEL_TYPES: [
+        { value: "cloud", label: "云端大模型" },
+        { value: "local", label: "本地大模型" },
+      ],
+      formData: {
+        enableAI: false,
+        modelType: "cloud",
+        modelUrl: "",
+        ocrUrl: "",
+      },
+      cachedFormData: null,
+      rules: {
+        enableAI: [
+          {
+            required: true,
+            message: "请选择是否开启AI智能评卷",
+            trigger: "change",
+          },
+        ],
+        modelType: [
+          {
+            required: true,
+            message: "请选择大模型类型",
+            trigger: "change",
+          },
+        ],
+        modelUrl: [
+          {
+            required: true,
+            message: "请输入大模型地址",
+            trigger: "blur",
+          },
+        ],
+        ocrUrl: [
+          {
+            required: true,
+            message: "请输入OCR识别地址",
+            trigger: "blur",
+          },
+        ],
+      },
+    };
+  },
+  watch: {
+    formData: {
+      deep: true,
+      handler() {
+        const isChanged = this.checkConfigChanged();
+        this.$emit("config-changed", isChanged);
+      },
+    },
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    async initData() {
+      const data = await schoolSetAiInfo(this.school.id);
+      if (data && data.result) {
+        this.formData = data.result;
+      }
+      this.cachedFormData = JSON.parse(JSON.stringify(this.formData));
+    },
+    checkConfigChanged() {
+      if (!this.cachedFormData) return false;
+      return (
+        JSON.stringify(this.formData) !== JSON.stringify(this.cachedFormData)
+      );
+    },
+    handleModelTypeChange(val) {
+      if (val === "cloud") {
+        this.formData.modelUrl = "";
+        this.formData.ocrUrl = "";
+      }
+    },
+    async confirm() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.loading) return;
+      this.loading = true;
+      try {
+        await schoolSetAiUpdate({
+          ...this.formData,
+          schoolId: this.school.id,
+        });
+        this.cachedFormData = JSON.parse(JSON.stringify(this.formData));
+        this.$emit("config-changed", false);
+        this.$message.success("修改成功!");
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
+    },
+  },
+};
+</script>

+ 133 - 0
src/modules/admin/components/school/SchoolSetBase.vue

@@ -0,0 +1,133 @@
+<template>
+  <div class="school-set-base">
+    <div v-for="tab in tabs" :key="tab.val" class="part-box mb-4">
+      <div class="part-title">{{ tab.name }}</div>
+      <component
+        :is="getCompName(tab.val)"
+        :school="school"
+        :ref="tab.val"
+        @config-changed="(isChanged) => handleConfigChanged(tab.val, isChanged)"
+      ></component>
+    </div>
+
+    <div class="text-center mt-4">
+      <el-button type="primary" @click="submit" :loading="loading"
+        >保存</el-button
+      >
+    </div>
+  </div>
+</template>
+
+<script>
+import SchoolSetCheck from "./SchoolSetCheck.vue";
+import SchoolSetStdno from "./SchoolSetStdno.vue";
+import SchoolSetRole from "./SchoolSetRole.vue";
+import SchoolSetPaper from "./SchoolSetPaper.vue";
+import SchoolSetTarget from "./SchoolSetTarget.vue";
+import SchoolSetRecognition from "./SchoolSetRecognition.vue";
+import SchoolSetRobot from "./SchoolSetRobot.vue";
+import SchoolSetAi from "./SchoolSetAi.vue";
+
+export default {
+  name: "school-set-base",
+  components: {
+    SchoolSetCheck,
+    SchoolSetStdno,
+    SchoolSetRole,
+    SchoolSetPaper,
+    SchoolSetTarget,
+    SchoolSetRecognition,
+    SchoolSetRobot,
+    SchoolSetAi,
+  },
+  props: {
+    school: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      loading: false,
+      tabs: [
+        {
+          name: "用户验证配置",
+          val: "check",
+        },
+        {
+          name: "学号配置",
+          val: "stdno",
+        },
+        {
+          name: "角色管理",
+          val: "role",
+        },
+        {
+          name: "试卷规格配置",
+          val: "paper",
+        },
+        {
+          name: "课程目标",
+          val: "target",
+        },
+        {
+          name: "识别配置",
+          val: "recognition",
+        },
+        {
+          name: "机器人配置",
+          val: "robot",
+        },
+        {
+          name: "AI智能评卷",
+          val: "ai",
+        },
+      ],
+      // 记录各个组件的配置是否有变更
+      configChangedMap: {},
+    };
+  },
+  methods: {
+    getCompName(val) {
+      const map = {
+        check: "SchoolSetCheck",
+        stdno: "SchoolSetStdno",
+        role: "SchoolSetRole",
+        paper: "SchoolSetPaper",
+        target: "SchoolSetTarget",
+        recognition: "SchoolSetRecognition",
+        robot: "SchoolSetRobot",
+        ai: "SchoolSetAi",
+      };
+      return map[val];
+    },
+    handleConfigChanged(val, isChanged) {
+      this.$set(this.configChangedMap, val, isChanged);
+    },
+    async submit() {
+      if (this.loading) return;
+      this.loading = true;
+      try {
+        const promises = Object.keys(this.configChangedMap)
+          .filter((key) => this.configChangedMap[key])
+          .map((key) => this.$refs[key][0].confirm());
+        await Promise.all(promises);
+        this.$message.success("保存成功");
+        this.configChangedMap = {};
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.school-set-base {
+  padding: 20px;
+}
+</style>

+ 28 - 7
src/modules/admin/components/school/SchoolSetCheck.vue

@@ -50,9 +50,19 @@ export default {
     return {
       loading: false,
       fields: [],
+      cachedFields: null,
       OPEN_STATUS,
     };
   },
+  watch: {
+    fields: {
+      deep: true,
+      handler(newVal) {
+        const isChanged = this.checkConfigChanged();
+        this.$emit("config-changed", isChanged);
+      },
+    },
+  },
   mounted() {
     this.initData();
   },
@@ -67,17 +77,28 @@ export default {
         if (field.isBoolean && ["true", "false"].includes(field.value))
           field.value = field.value === "true";
       });
+      this.cachedFields = JSON.parse(JSON.stringify(this.fields));
+    },
+    checkConfigChanged() {
+      if (!this.cachedFields) return false;
+      return JSON.stringify(this.fields) !== JSON.stringify(this.cachedFields);
     },
     async confirm() {
       if (this.loading) return;
       this.loading = true;
-      const datas = { param: this.fields, schoolId: this.school.id };
-      const res = await schoolSetCheckUpdate(datas).catch(() => {});
-      this.loading = false;
-      if (!res) return;
-
-      this.$message.success("修改成功!");
-      this.initData();
+      try {
+        await schoolSetCheckUpdate({
+          param: this.fields,
+          schoolId: this.school.id,
+        });
+        this.cachedFields = JSON.parse(JSON.stringify(this.fields));
+        this.$emit("config-changed", false);
+        this.$message.success("修改成功!");
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
     },
   },
 };

+ 32 - 17
src/modules/admin/components/school/SchoolSetPaper.vue

@@ -63,6 +63,7 @@ export default {
         pdfSize: [],
         cardSize: "",
       },
+      cachedModalForm: null,
       sysPdfSize: [],
       sysCardSize: [],
       paperInfo: [],
@@ -74,7 +75,6 @@ export default {
               if (!value || !value.length) {
                 return callback(new Error(`请选择允许上传试卷规格`));
               }
-
               callback();
             },
             trigger: "change",
@@ -90,6 +90,15 @@ export default {
       },
     };
   },
+  watch: {
+    modalForm: {
+      deep: true,
+      handler() {
+        const isChanged = this.checkConfigChanged();
+        this.$emit("config-changed", isChanged);
+      },
+    },
+  },
   mounted() {
     this.initData();
   },
@@ -103,29 +112,35 @@ export default {
         item ? JSON.parse(item) : ""
       );
       const data = await schoolSetPaperInfo(this.school.id);
-      this.paperInfo = data.result;
-      this.modalForm.pdfSize = data.result[0]?.value || [];
-      this.modalForm.cardSize = data.result[1]?.value
-        ? data.result[1].value[0]
-        : "";
+      this.modalForm = data.result || { pdfSize: [], cardSize: "" };
+      this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+    },
+    checkConfigChanged() {
+      if (!this.cachedModalForm) return false;
+      return (
+        JSON.stringify(this.modalForm) !== JSON.stringify(this.cachedModalForm)
+      );
     },
     async confirm() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      const valid = await this.$refs.modalFormComp.validate();
       if (!valid) return;
 
       if (this.loading) return;
       this.loading = true;
 
-      this.paperInfo[0].value = this.modalForm.pdfSize;
-      this.paperInfo[1].value = [this.modalForm.cardSize];
-      let datas = { param: this.paperInfo, schoolId: this.school.id };
-      const res = await schoolSetPaperUpdate(datas).catch(() => {});
-      this.loading = false;
-
-      if (!res) return;
-
-      this.$message.success("修改成功!");
-      this.initData();
+      try {
+        await schoolSetPaperUpdate({
+          ...this.modalForm,
+          schoolId: this.school.id,
+        });
+        this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+        this.$emit("config-changed", false);
+        this.$message.success("修改成功!");
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
     },
   },
 };

+ 31 - 9
src/modules/admin/components/school/SchoolSetRecognition.vue

@@ -36,9 +36,19 @@ export default {
       modalForm: {
         avoidSingleChoice: false,
       },
+      cachedModalForm: null,
       loading: false,
     };
   },
+  watch: {
+    modalForm: {
+      deep: true,
+      handler() {
+        const isChanged = this.checkConfigChanged();
+        this.$emit("config-changed", isChanged);
+      },
+    },
+  },
   mounted() {
     this.initData();
   },
@@ -46,6 +56,13 @@ export default {
     async initData() {
       const data = await schoolSetRecognitionInfo(this.school.id);
       this.modalForm.avoidSingleChoice = data?.value === "true";
+      this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+    },
+    checkConfigChanged() {
+      if (!this.cachedModalForm) return false;
+      return (
+        JSON.stringify(this.modalForm) !== JSON.stringify(this.cachedModalForm)
+      );
     },
     async confirm() {
       const valid = await this.$refs.modalFormComp.validate().catch(() => {});
@@ -53,15 +70,20 @@ export default {
 
       if (this.loading) return;
       this.loading = true;
-      const datas = {
-        enable: this.modalForm.avoidSingleChoice,
-        schoolId: this.school.id,
-      };
-      const res = await schoolSetRecognitionUpdate(datas).catch(() => {});
-      this.loading = false;
-      if (!res) return;
-
-      this.$message.success("修改成功!");
+      try {
+        const datas = {
+          enable: this.modalForm.avoidSingleChoice,
+          schoolId: this.school.id,
+        };
+        await schoolSetRecognitionUpdate(datas);
+        this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+        this.$emit("config-changed", false);
+        this.$message.success("修改成功!");
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
     },
   },
 };

+ 30 - 12
src/modules/admin/components/school/SchoolSetRole.vue

@@ -47,10 +47,10 @@ export default {
     return {
       loading: false,
       defaultSelectedRole: ["SCHOOL_ADMIN", "SCHOOL_TEACHER", "ASSISTANT"],
-      // SCHOOL_ADMIN("学校管理员"),SCHOOL_TEACHER("学校老师"), ASSISTANT("学院秘书");
       modalForm: {
         roleIds: [],
       },
+      cachedModalForm: null,
       roleList: [],
       rules: {
         roleIds: [
@@ -60,7 +60,6 @@ export default {
               if (!value || !value.length) {
                 return callback(new Error(`请选择系统角色管理`));
               }
-
               callback();
             },
             trigger: "change",
@@ -69,6 +68,15 @@ export default {
       },
     };
   },
+  watch: {
+    modalForm: {
+      deep: true,
+      handler() {
+        const isChanged = this.checkConfigChanged();
+        this.$emit("config-changed", isChanged);
+      },
+    },
+  },
   mounted() {
     this.initData();
   },
@@ -88,6 +96,13 @@ export default {
           if (this.modalForm.roleIds.includes(item.id)) return;
           this.modalForm.roleIds.push(item.id);
         });
+      this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+    },
+    checkConfigChanged() {
+      if (!this.cachedModalForm) return false;
+      return (
+        JSON.stringify(this.modalForm) !== JSON.stringify(this.cachedModalForm)
+      );
     },
     async confirm() {
       const valid = await this.$refs.modalFormComp.validate();
@@ -96,16 +111,19 @@ export default {
       if (this.loading) return;
       this.loading = true;
 
-      let datas = {
-        roleIds: this.modalForm.roleIds,
-        schoolId: this.school.id,
-      };
-      const res = await schoolSetRoleUpdate(datas).catch(() => {});
-      this.loading = false;
-
-      if (!res) return;
-
-      this.$message.success("修改成功!");
+      try {
+        await schoolSetRoleUpdate({
+          roleIds: this.modalForm.roleIds,
+          schoolId: this.school.id,
+        });
+        this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+        this.$emit("config-changed", false);
+        this.$message.success("修改成功!");
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
     },
   },
 };

+ 58 - 122
src/modules/admin/components/school/SchoolSetStdno.vue

@@ -98,7 +98,6 @@
 </template>
 
 <script>
-import { deepCopy } from "@/plugins/utils";
 import { schoolSetStdnoInfo, schoolSetStdnoUpdate } from "../../api";
 import HeadStdno from "../../../../../card/elements/card-head/cardHeadSpin/HeadStdno.vue";
 
@@ -124,6 +123,7 @@ export default {
         relationList: [],
         columnChar: "",
       },
+      cachedModalForm: null,
       curSelectColumn: -1,
       curSelectColumnLetters: [],
       rules: {
@@ -141,7 +141,6 @@ export default {
               if (!value || !value.length) {
                 return callback(new Error(`请选择字母所在列`));
               }
-
               callback();
             },
             trigger: "change",
@@ -151,17 +150,15 @@ export default {
           {
             required: true,
             validator: (rule, value, callback) => {
-              if (!/^[A-Z]{1,10}$/.test(value)) {
-                return callback(new Error(`只能输入大写字母,长度不超过10`));
+              if (!value) {
+                return callback(new Error(`请输入使用字母`));
               }
-
-              const chars = Array.from(new Set((value || "").split(""))).join(
-                ""
-              );
-              if (chars !== value) {
-                return callback(new Error(`大写字母不可重复`));
+              if (!/^[A-Z]+$/.test(value)) {
+                return callback(new Error(`请输入大写字母`));
+              }
+              if (value.length > 10) {
+                return callback(new Error(`最多输入10个字母`));
               }
-
               callback();
             },
             trigger: "change",
@@ -170,14 +167,13 @@ export default {
       },
     };
   },
-  computed: {
-    headStdnoData() {
-      return {
-        examNumberStyle: "FILL",
-        fillNumber: this.modalForm.digit,
-        containsLetter: this.modalForm.containsLetter,
-        relationList: this.modalForm.relationList,
-      };
+  watch: {
+    modalForm: {
+      deep: true,
+      handler() {
+        const isChanged = this.checkConfigChanged();
+        this.$emit("config-changed", isChanged);
+      },
     },
   },
   mounted() {
@@ -186,125 +182,65 @@ export default {
   methods: {
     async initData() {
       const data = await schoolSetStdnoInfo(this.school.id);
-      this.stdnoInfo = data.result;
-      const config = data.result[0].value;
-      if (!config) {
-        this.modalForm = {
-          digit: 10,
-          containsLetter: false,
-          columns: [],
-          relationList: [],
-          columnChar: "",
-        };
-        return;
+      if (data && data.result) {
+        this.modalForm = data.result;
       }
-
-      this.modalForm = {
-        digit: config.digit || 10,
-        containsLetter: config.containsLetter,
-        columns: [],
-        relationList: config.relationList || [],
-        columnChar: "",
-      };
-      this.modalForm.columns = this.modalForm.relationList.map(
-        (item) => item.columnIndex
+      this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+    },
+    checkConfigChanged() {
+      if (!this.cachedModalForm) return false;
+      return (
+        JSON.stringify(this.modalForm) !== JSON.stringify(this.cachedModalForm)
       );
-      if (this.modalForm.columns.length) {
-        this.curSelectColumn = this.modalForm.columns[0];
-        this.selectColumnChange();
-      }
     },
-    toSwitchColumn(val) {
-      this.curSelectColumn = val.columnIndex;
+    columnChange(val) {
+      this.modalForm.relationList = val.map((item) => ({
+        columnIndex: item,
+        letters: [],
+      }));
+      this.curSelectColumn = val[0] || -1;
+      this.curSelectColumnLetters = [];
+      this.modalForm.columnChar = "";
     },
-    columnChange() {
-      const relationList = this.modalForm.relationList || [];
-      const relationMap = {};
-      relationList.forEach((item) => {
-        relationMap[item.columnIndex] = item.letters;
-      });
-
-      this.modalForm.columns.sort((a, b) => a - b);
-
-      this.modalForm.relationList = this.modalForm.columns.map(
-        (columnIndex) => {
-          const letters = relationMap[columnIndex] || [];
-          return {
-            columnIndex,
-            letters,
-          };
-        }
+    selectColumnChange(val) {
+      this.curSelectColumn = val;
+      const relation = this.modalForm.relationList.find(
+        (item) => item.columnIndex === val
       );
-
-      if (
-        this.modalForm.columns.length &&
-        !this.modalForm.columns.includes(this.curSelectColumn)
-      ) {
-        this.curSelectColumn = this.modalForm.columns[0];
-        this.selectColumnChange();
-      }
+      this.curSelectColumnLetters = relation ? relation.letters : [];
+      this.modalForm.columnChar = this.curSelectColumnLetters.join("");
     },
-    selectColumnChange() {
-      const curRelation = this.modalForm.relationList.find(
+    columnCharChange(val) {
+      if (!val) return;
+      const letters = val.split("");
+      const relation = this.modalForm.relationList.find(
         (item) => item.columnIndex === this.curSelectColumn
       );
-      this.modalForm.columnChar = curRelation
-        ? curRelation.letters.join("")
-        : "";
-      this.curSelectColumnLetters = curRelation ? curRelation.letters : [];
-    },
-    validateField(field) {
-      return new Promise((resolve, reject) => {
-        this.$refs.modalFormComp.validateField(field, (unvalid) => {
-          if (unvalid) {
-            reject(false);
-          } else {
-            resolve(true);
-          }
-        });
-      });
-    },
-    async columnCharChange() {
-      const res = await this.validateField("columnChar").catch(() => {});
-      if (!res) {
-        this.curSelectColumnLetters = [];
-        return;
+      if (relation) {
+        relation.letters = letters;
       }
-
-      const curRelation = this.modalForm.relationList.find(
-        (item) => item.columnIndex === this.curSelectColumn
-      );
-      if (!curRelation) return;
-      curRelation.letters = this.modalForm.columnChar.split("");
-      this.curSelectColumnLetters = curRelation.letters;
+      this.curSelectColumnLetters = letters;
     },
     async confirm() {
-      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      const valid = await this.$refs.modalFormComp.validate();
       if (!valid) return;
 
       if (this.loading) return;
       this.loading = true;
 
-      const val = {
-        digit: this.modalForm.digit,
-        containsLetter: this.modalForm.containsLetter,
-        relationList: this.modalForm.containsLetter
-          ? this.modalForm.relationList
-          : [],
-      };
-
-      const datas = deepCopy(this.stdnoInfo);
-      datas[0].value = val;
-      const res = await schoolSetStdnoUpdate({
-        param: datas,
-        schoolId: this.school.id,
-      }).catch(() => {});
-      this.loading = false;
-
-      if (!res) return;
-
-      this.$message.success("修改成功!");
-      this.initData();
+      try {
+        await schoolSetStdnoUpdate({
+          ...this.modalForm,
+          schoolId: this.school.id,
+        });
+        this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+        this.$emit("config-changed", false);
+        this.$message.success("修改成功!");
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
     },
   },
 };

+ 29 - 9
src/modules/admin/components/school/SchoolSetTarget.vue

@@ -57,6 +57,7 @@ export default {
       loading: false,
       info: {},
       modalForm: { schoolId: "", attachmentId: "" },
+      cachedModalForm: null,
       rules: {
         attachmentId: [
           {
@@ -74,6 +75,15 @@ export default {
       },
     };
   },
+  watch: {
+    modalForm: {
+      deep: true,
+      handler() {
+        const isChanged = this.checkConfigChanged();
+        this.$emit("config-changed", isChanged);
+      },
+    },
+  },
   mounted() {
     this.initData();
   },
@@ -83,7 +93,15 @@ export default {
       const data = await schoolSetCourseTargetQuery(this.school.id);
       if (data && data.value) {
         this.info = data;
+        this.modalForm.attachmentId = data.id;
       }
+      this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+    },
+    checkConfigChanged() {
+      if (!this.cachedModalForm) return false;
+      return (
+        JSON.stringify(this.modalForm) !== JSON.stringify(this.cachedModalForm)
+      );
     },
     toView() {
       window.open(this.info.value);
@@ -93,7 +111,6 @@ export default {
     },
     uploadSuccess(data) {
       this.$message.success("上传成功!");
-
       this.modalForm.attachmentId = data.id;
       this.$refs.modalFormComp.validateField("attachmentId");
     },
@@ -103,14 +120,17 @@ export default {
 
       if (this.loading) return;
       this.loading = true;
-      const res = await schoolSetCourseTargetSave(this.modalForm).catch(
-        () => {}
-      );
-      this.loading = false;
-      if (!res) return;
-
-      this.$message.success("修改成功!");
-      this.initData();
+      try {
+        await schoolSetCourseTargetSave(this.modalForm);
+        this.cachedModalForm = JSON.parse(JSON.stringify(this.modalForm));
+        this.$emit("config-changed", false);
+        this.$message.success("修改成功!");
+        this.initData();
+      } catch (error) {
+        this.$message.error(error.message || "保存失败");
+      } finally {
+        this.loading = false;
+      }
     },
   },
 };