Procházet zdrojové kódy

feat: 培养方案管理

zhangjie před 1 rokem
rodič
revize
9285a76b14

+ 16 - 0
src/modules/target/api.js

@@ -60,3 +60,19 @@ export const deleteRrainingPlanCourse = (id) => {
 export const sortRrainingPlanCourse = (datas) => {
   return $post("/api/admin/obe/culture/program/course/sort", datas);
 };
+export const trainingPlanCourseQueryList = (datas) => {
+  return $post("/admin/obe/culture/program/course/query_list", datas);
+};
+export const trainingPlanCourseSave = (datas) => {
+  return $post("/api/admin/obe/culture/program/course/save", datas);
+};
+
+// 培养方案管理-课程支撑毕业要求达成矩阵 ------------------->
+export const trainingPlanCourseMatrixDetail = (datas) => {
+  return $postParam("/api/admin/obe/culture/program/course/matrix/get", datas);
+};
+export const trainingPlanCourseMatrixSave = (datas) => {
+  return $post("/api/admin/obe/culture/program/course/matrix/save", datas, {
+    silence: true,
+  });
+};

+ 152 - 0
src/modules/target/components/training-plan/AddTrainingPlanCourse.vue

@@ -0,0 +1,152 @@
+<template>
+  <el-dialog
+    :visible.sync="modalIsShow"
+    title="选择课程"
+    top="10vh"
+    width="800px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @opened="visibleChange"
+  >
+    <el-form ref="FilterForm" label-position="left" inline label-width="0px">
+      <el-form-item>
+        <org-select v-model="filter.orgId" placeholder="开课学院"></org-select>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" :disabled="!canSearch" @click="getList"
+          >查询</el-button
+        >
+      </el-form-item>
+    </el-form>
+    <div class="box-justify mb-2">
+      <div>全部共{{ dataList.length }}门课</div>
+      <div>已选{{ multipleSelection.length }}门</div>
+    </div>
+    <el-table
+      ref="TableList"
+      :data="dataList"
+      border
+      max-height="500px"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column
+        type="selection"
+        fixed="left"
+        width="55"
+        align="center"
+        :selectable="(row) => row.canSelect"
+      ></el-table-column>
+      <el-table-column
+        type="index"
+        label="序号"
+        width="70"
+        :index="indexMethod"
+      ></el-table-column>
+      <el-table-column
+        prop="courseName"
+        label="课程名称"
+        min-width="120"
+      ></el-table-column>
+      <el-table-column
+        prop="courseCode"
+        label="课程编码"
+        min-width="120"
+      ></el-table-column>
+      <el-table-column
+        prop="orgName"
+        label="开课学院"
+        min-width="120"
+      ></el-table-column>
+    </el-table>
+
+    <div slot="footer">
+      <el-button type="primary" :disabled="isSubmit" @click="submit"
+        >确认</el-button
+      >
+      <el-button @click="cancel">取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { trainingPlanCourseQueryList, trainingPlanCourseSave } from "../../api";
+
+export default {
+  name: "add-professional-course",
+  props: {
+    rowData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  computed: {
+    canSearch() {
+      return this.filter.orgId;
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      filter: {
+        cultureProgramId: "",
+        orgId: "",
+      },
+      isSubmit: false,
+      dataList: [],
+      multipleSelection: [],
+    };
+  },
+  methods: {
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    visibleChange() {
+      this.filter.orgId = this.rowData.orgId;
+      this.filter.cultureProgramId = this.rowData.id;
+      this.getList();
+      this.$nextTick(() => {
+        this.$refs.TableList?.clearSelection();
+      });
+    },
+    async getList() {
+      this.dataList = [];
+      const data = await trainingPlanCourseQueryList(this.filter);
+      this.dataList = data || [];
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
+    async submit() {
+      if (!this.multipleSelection.length) {
+        this.$message.error("请选择课程!");
+        return;
+      }
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const data = await trainingPlanCourseSave({
+        cultureProgramId: this.rowData.id,
+        courses: this.multipleSelection.map((item) => {
+          return {
+            courseId: item.courseId,
+          };
+        }),
+      }).catch(() => {});
+      this.isSubmit = false;
+
+      if (!data) return;
+      this.multipleSelection = [];
+      this.$message.success("选择成功!");
+      this.$emit("modified");
+      this.cancel();
+    },
+  },
+};
+</script>

+ 7 - 7
src/modules/target/components/training-plan/TrainingPlanCourse.vue

@@ -48,13 +48,13 @@
       </el-table>
     </div>
 
-    <!-- AddProfessionalCourse -->
-    <add-professional-course
+    <!-- AddTrainingPlanCourse -->
+    <add-training-plan-course
       v-if="canEdit"
-      ref="AddProfessionalCourse"
+      ref="AddTrainingPlanCourse"
       :row-data="rowData"
       @modified="getList"
-    ></add-professional-course>
+    ></add-training-plan-course>
   </div>
 </template>
 
@@ -65,12 +65,12 @@ import {
   sortRrainingPlanCourse,
 } from "../../api";
 
-import AddProfessionalCourse from "./AddProfessionalCourse.vue";
+import AddTrainingPlanCourse from "./AddTrainingPlanCourse.vue";
 
 export default {
   name: "professional-course",
   components: {
-    AddProfessionalCourse,
+    AddTrainingPlanCourse,
   },
   props: {
     rowData: {
@@ -98,7 +98,7 @@ export default {
       this.total = this.dataList.length;
     },
     toAdd() {
-      this.$refs.AddProfessionalCourse.open();
+      this.$refs.AddTrainingPlanCourse.open();
     },
     exchangeSortNumber(row1, row2) {
       const sortNum = row1.sortNum;

+ 271 - 0
src/modules/target/components/training-plan/TrainingPlanCourseMatrix.vue

@@ -0,0 +1,271 @@
+<template>
+  <div class="part-box part-box-pad">
+    <el-table
+      :data="dataList"
+      border
+      :cell-style="cellStyleHandle"
+      :span-method="spanMethod"
+    >
+      <el-table-column
+        lang="课程信息"
+        fixed="left"
+        :summary-method="getSummaries"
+        show-summary
+      >
+        <el-table-column label="课程名称(代码)" min-width="200">
+          <template slot-scope="scope">
+            {{ scope.row.courseName }}({{ scope.row.courseCode }})
+          </template>
+        </el-table-column>
+        <el-table-column lang="学分" width="60"></el-table-column>
+      </el-table-column>
+      <el-table-column label="毕业要求" align="center">
+        <template v-if="hasSubRequirements">
+          <el-table-column
+            v-for="(column, cindex) in columns"
+            :key="cindex"
+            :label="column.name"
+            align="center"
+          >
+            <el-table-column
+              v-for="subr in column.subRequirements"
+              :key="subr.name"
+              :label="subr.name === 'null' ? '' : subr.name"
+              align="center"
+            >
+              <template slot-scope="scope">
+                <el-input-number
+                  v-if="scope.row.canEdit"
+                  v-model="scope.row[`${column.name}_${subr.name}`].value"
+                  class="width-50"
+                  :min="0"
+                  :max="1"
+                  :step="0.01"
+                  step-strictly
+                  :controls="false"
+                  @change="
+                    () =>
+                      unitChange(
+                        scope.row,
+                        `${column.name}_${subr.name}`,
+                        subr.columnIndex
+                      )
+                  "
+                ></el-input-number>
+                <span v-else>{{
+                  scope.row[`${column.name}_${subr.name}`].value
+                }}</span>
+              </template>
+            </el-table-column>
+          </el-table-column>
+        </template>
+
+        <template v-else>
+          <el-table-column
+            v-for="(column, cindex) in columns"
+            :key="cindex"
+            :label="column.name"
+            align="center"
+          >
+            <template slot-scope="scope">
+              <el-input-number
+                v-if="scope.row.canEdit"
+                v-model="scope.row[`${column.name}_null`].value"
+                class="width-50"
+                :min="0"
+                :max="1"
+                :step="0.01"
+                step-strictly
+                :controls="false"
+                @change="
+                  () =>
+                    unitChange(
+                      scope.row,
+                      `${column.name}_null`,
+                      column.columnIndex
+                    )
+                "
+              ></el-input-number>
+              <span v-else>{{ scope.row[`${column.name}_null`].value }}</span>
+            </template>
+          </el-table-column>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+import {
+  trainingPlanCourseMatrixDetail,
+  trainingPlanCourseMatrixSave,
+} from "../../api";
+import { calcSum } from "@/plugins/utils";
+
+export default {
+  name: "professional-matrix",
+  props: {
+    rowData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      dataList: [],
+      columns: [],
+      hasSubRequirements: false,
+      loading: false,
+      errorIndexs: [],
+    };
+  },
+  mounted() {
+    this.getList();
+  },
+  methods: {
+    async getList() {
+      const res = await trainingPlanCourseMatrixDetail({
+        professionalId: this.rowData.id,
+      });
+      const tableData = res || [];
+      this.dataList = tableData.map((item, index) => {
+        if (!index) this.parseColumns(item.requirements);
+
+        const nitem = {
+          courseId: item.courseId,
+          courseCode: item.courseCode,
+          courseName: item.courseName,
+          canEdit: item.canEdit,
+        };
+        item.requirements.forEach((requirement) => {
+          requirement.subRequirements.forEach((subr) => {
+            nitem[`${requirement.name}_${subr.name + ""}`] = {
+              id: subr.id,
+              value: subr.content || undefined,
+            };
+          });
+        });
+
+        return nitem;
+      });
+    },
+    parseColumns(requirements) {
+      this.hasSubRequirements = requirements.some(
+        (item) => item.subRequirements[0].name !== null
+      );
+      if (!this.hasSubRequirements) {
+        this.columns = requirements.map((item, index) => {
+          return { name: item.name, columnIndex: index + 1 };
+        });
+        return;
+      }
+
+      let cindex = 0;
+      this.columns = requirements.map((item) => {
+        return {
+          name: item.name,
+          subRequirements: item.subRequirements.map((subr) => {
+            return {
+              name: subr.name + "",
+              columnIndex: ++cindex,
+            };
+          }),
+        };
+      });
+    },
+    updateErrorIndexs([rowIndex, columnIndex]) {
+      const pos = this.errorIndexs.findIndex((item) => item[1] === columnIndex);
+      if (pos !== -1) {
+        this.errorIndexs.splice(pos, 1);
+      }
+
+      this.errorIndexs.push([rowIndex, columnIndex]);
+    },
+    clearErrorIndexs(columnIndex) {
+      this.errorIndexs = this.errorIndexs.filter(
+        (item) => item[1] !== columnIndex
+      );
+    },
+    async unitChange(row, key, columnIndex) {
+      const [fieldName, nodeName] = key.split("_");
+      const totalVal = calcSum(
+        this.dataList.map((item) => item[key].value || 0)
+      );
+      if (totalVal > 1) {
+        const columnName =
+          nodeName === "null" ? fieldName : `${fieldName}:${nodeName}`;
+        this.$message.error(`${columnName}列总和大于1,当前修改值将不会保存!`);
+
+        const rowIndex = this.dataList.findIndex(
+          (item) => item.courseCode === row.courseCode
+        );
+
+        this.updateErrorIndexs([rowIndex, columnIndex]);
+        return;
+      }
+
+      let saveData = [];
+      const curColumnHasError = this.errorIndexs.some(
+        (item) => item[1] === columnIndex
+      );
+      if (curColumnHasError) {
+        saveData = this.dataList.map((item) => {
+          return {
+            id: item[key].id,
+            content: item[key].value,
+          };
+        });
+      } else {
+        saveData = [
+          {
+            id: row[key].id,
+            content: row[key].value,
+          },
+        ];
+      }
+
+      this.clearErrorIndexs(columnIndex);
+      await trainingPlanCourseMatrixSave(saveData);
+    },
+    cellStyleHandle({ rowIndex, columnIndex }) {
+      if (!this.errorIndexs.length) return;
+      if (
+        this.errorIndexs.some(
+          (item) => rowIndex === item[0] && columnIndex === item[1]
+        )
+      ) {
+        return {
+          backgroundColor: "#fde2e2",
+        };
+      }
+    },
+    spanMethod({ rowIndex, columnIndex }) {
+      if (rowIndex === 0 && columnIndex === 0) {
+        return { rowspan: 2, colspan: this.hasSubRequirements ? 2 : 1 };
+      }
+    },
+    getSummaries(param) {
+      const { columns } = param;
+      const sums = [];
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = "合计";
+          return;
+        }
+        if (index === 1) {
+          sums[index] = "";
+          return;
+        }
+
+        const key = ``;
+        sums[index] = calcSum(
+          this.dataList.map((item) => item[key].value || 0)
+        );
+      });
+      return sums;
+    },
+  },
+};
+</script>