zhangjie 1 year ago
parent
commit
3d44c3f319

+ 421 - 0
src/modules/base/components/course-simple/CourseWeightManage.plan.vue

@@ -0,0 +1,421 @@
+<template>
+  <div class="course-weight-manage">
+    <div class="part-box part-box-pad">
+      <p>说明:</p>
+      <p>
+        1.请在课程目标考核对应的评价方式表格中打√,并录入权重占比及目标分值;
+      </p>
+      <p>2.所有课程目标的总目标分值等于100分;</p>
+      <p>3.各课程目标下评价方式的总权重应等于100%;</p>
+      <p>4.目标整体权重应等于100%,用于计算课程整体达成度。</p>
+    </div>
+    <div class="part-box part-box-pad">
+      <el-form class="mb-2" inline>
+        <el-form-item label="整体权重设置:"></el-form-item>
+        <el-form-item label="平均成绩权重:">
+          <el-input-number
+            v-model="customRate"
+            class="width-50"
+            size="small"
+            :min="0"
+            :max="100"
+            :step="1"
+            step-strictly
+            :controls="false"
+          >
+          </el-input-number>
+          <span style="margin-left: 5px">%</span>
+        </el-form-item>
+        <el-form-item label="期末成绩权重:">
+          <el-input-number
+            v-model="defaultRate"
+            class="width-50"
+            size="small"
+            :min="0"
+            :max="100"
+            :step="1"
+            step-strictly
+            :controls="false"
+          >
+          </el-input-number>
+          <span style="margin-left: 5px">%</span>
+        </el-form-item>
+      </el-form>
+      <el-table :data="tableSetData">
+        <el-table-column
+          prop="evaluationName"
+          label="评价方式"
+          width="140"
+          fixed="left"
+        ></el-table-column>
+        <el-table-column v-for="(target, tindex) in dataList" :key="tindex">
+          <template slot="header">
+            <el-row type="flex" align="middle">
+              <el-col :span="10" :offset="2">
+                {{ target.courseTargetName }}
+              </el-col>
+              <el-col :span="12">
+                <span style="margin-right: 5px">目标整体权重</span>
+                <el-input-number
+                  v-model="targetTotalWeight[target.courseTargetId]"
+                  class="width-50"
+                  size="small"
+                  :min="0"
+                  :max="100"
+                  :step="1"
+                  step-strictly
+                  :controls="false"
+                >
+                </el-input-number>
+                <span style="margin-left: 5px">%</span>
+              </el-col>
+            </el-row>
+          </template>
+          <el-table-column min-width="340">
+            <template slot="header">
+              <el-row>
+                <el-col :span="10" :offset="2"> 权重 </el-col>
+              </el-row>
+            </template>
+            <template slot-scope="scope">
+              <el-row type="flex" align="middle">
+                <el-col :span="2">
+                  <el-checkbox
+                    v-if="scope.$index !== endRowNo"
+                    v-model="scope.row.targets[tindex].enable"
+                    :disabled="scope.row.targets[tindex].disabled"
+                    @change="enableChange(scope.$index, tindex)"
+                  ></el-checkbox>
+                </el-col>
+                <el-col :span="10">
+                  <span v-if="scope.$index === endRowNo">
+                    {{ scope.row.targets[tindex].weight }}
+                  </span>
+                  <el-input-number
+                    v-else
+                    v-model="scope.row.targets[tindex].weight"
+                    :disabled="!scope.row.targets[tindex].enable"
+                    class="width-80"
+                    size="small"
+                    :min="0"
+                    :max="100"
+                    :step="1"
+                    step-strictly
+                    :controls="false"
+                  >
+                  </el-input-number>
+                  <span style="margin-left: 5px">%</span>
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table-column>
+      </el-table>
+      <div class="text-center" style="margin: 20px 0">
+        <el-button type="primary" :loading="loading" @click="submit"
+          >保存</el-button
+        >
+      </div>
+      <el-table
+        v-if="resultDataList.length && cwStatus.currentSettingStatus"
+        :data="resultDataList"
+        :header-cell-style="{ textAlign: 'center' }"
+        :cell-style="{ textAlign: 'center' }"
+        :span-method="spanMethod"
+      >
+        <el-table-column
+          prop="courseTargetName"
+          label="课程目标"
+          width="140"
+        ></el-table-column>
+        <el-table-column
+          prop="degreeRequirement"
+          label="支撑毕业要求"
+          min-width="200"
+        ></el-table-column>
+        <el-table-column prop="totalWeight" label="目标整体权重" width="110">
+          <template slot-scope="scope">{{ scope.row.totalWeight }}%</template>
+        </el-table-column>
+        <el-table-column label="考核/评价环节及目标分值">
+          <el-table-column
+            v-for="(ename, eindex) in evaluationData"
+            :key="ename"
+            :label="ename"
+          >
+            <template slot-scope="scope">
+              {{
+                scope.row.evaluationList[eindex].enable
+                  ? scope.row.evaluationList[eindex].targetScore
+                  : ""
+              }}
+            </template>
+          </el-table-column>
+        </el-table-column>
+        <el-table-column label="目标分值统计" width="120">
+          <template slot-scope="scope">
+            {{ getEvaluationSumScore(scope.row.evaluationList) }}
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<script>
+import { courseWeightDetail, courseWeightSave } from "../../api";
+import { omit, pick } from "lodash";
+import { calcSum } from "@/plugins/utils";
+import { mapState, mapActions } from "vuex";
+
+export default {
+  name: "course-weight-manage",
+  props: {
+    course: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      dataList: [],
+      defaultRate: 0,
+      customRate: 0,
+      tableSetData: [],
+      targetTotalWeight: {},
+      evaluationData: [],
+      resultDataList: [],
+      loading: false,
+    };
+  },
+  computed: {
+    ...mapState("base", ["cwStatus"]),
+    endRowNo() {
+      return this.tableSetData.length - 1;
+    },
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    ...mapActions("base", ["updateCwStatus"]),
+    async initData() {
+      await this.updateCwStatus({
+        teachCourseId: this.course.id,
+      });
+      await this.getList();
+      if (this.cwStatus.currentSettingStatus) {
+        this.updateResultDataList();
+      }
+    },
+    async getList() {
+      const res = await courseWeightDetail({
+        teachCourseId: this.course.id,
+      });
+      this.defaultRate = res.defaultRate;
+      this.customRate = res.customRate;
+      this.dataList = res.submitForm || [];
+      this.transformData(this.dataList);
+    },
+    transformData(data) {
+      if (!data || !data.length) return [];
+
+      const tableSetData = [];
+      const evaluationMap = {};
+      const targetTotalWeight = {};
+      data[0].evaluationList.forEach((item, index) => {
+        evaluationMap[item.evaluationName] = index;
+      });
+
+      this.evaluationData = data[0].evaluationList.map(
+        (item) => item.evaluationName
+      );
+
+      data.forEach((target) => {
+        targetTotalWeight[target.courseTargetId] = target.totalWeight;
+        const targetData = {
+          ...omit(target, ["evaluationList"]),
+          totalWeight: target.totalWeight || 0,
+        };
+
+        target.evaluationList.forEach((item) => {
+          const index = evaluationMap[item.evaluationName];
+          if (!tableSetData[index]) {
+            tableSetData[index] = {
+              evaluationName: item.evaluationName,
+              targets: [],
+            };
+          }
+          tableSetData[index].targets.push({
+            ...targetData,
+            enable: item.enable,
+            disabled: false,
+            weight: item.weight || 0,
+            targetScore: item.targetScore || 0,
+          });
+        });
+      });
+
+      this.targetTotalWeight = targetTotalWeight;
+      this.tableSetData = tableSetData;
+      this.updateSetDataDisabled();
+    },
+    updateSetDataDisabled() {
+      this.tableSetData.forEach((item, tindex) => {
+        const hasTargetWeight = item.targets.some((t) => t.weight);
+        item.targets.forEach((t) => {
+          if (hasTargetWeight) {
+            t.disabled = !t.weight;
+          } else {
+            t.disabled = false;
+          }
+        });
+      });
+    },
+    enableChange(rowIndex, tindex) {
+      const target = this.tableSetData[rowIndex].targets[tindex];
+      if (!target.enable) {
+        target.weight = 0;
+        target.targetScore = 0;
+      }
+      this.tableSetData[rowIndex].targets.forEach((item, dindex) => {
+        if (dindex !== tindex) {
+          item.disabled = target.enable;
+        }
+      });
+    },
+    getEvaluationSumScore(evaluationList) {
+      return calcSum(
+        evaluationList.map((item) => (item.enable ? item.targetScore : 0))
+      );
+    },
+    spanMethod({ rowIndex, columnIndex }) {
+      const lineCount = this.resultDataList.length - 1;
+      if (rowIndex === lineCount) {
+        if (columnIndex === 0) {
+          return [1, 2];
+        } else if (columnIndex === 1) {
+          return [0, 0];
+        }
+      }
+    },
+    updateDataList() {
+      const evaluationData = {};
+      this.tableSetData.forEach((item) => {
+        item.targets.forEach((elem) => {
+          const key = `${item.evaluationName}_${elem.courseTargetId}`;
+          evaluationData[key] = pick(elem, ["enable", "weight", "targetScore"]);
+        });
+      });
+
+      this.dataList.forEach((target) => {
+        target.totalWeight = this.targetTotalWeight[target.courseTargetId];
+        target.evaluationList.forEach((item) => {
+          const key = `${item.evaluationName}_${target.courseTargetId}`;
+          Object.assign(item, evaluationData[key]);
+        });
+      });
+    },
+    updateResultDataList() {
+      // 更新合计
+      const tatolEvaluation = this.evaluationData.map(
+        (evaluationName, eindex) => {
+          const targetScore = calcSum(
+            this.dataList.map(
+              (item) => item.evaluationList[eindex].targetScore || 0
+            )
+          );
+          return {
+            evaluationName,
+            enable: true,
+            weight: null,
+            targetScore,
+          };
+        }
+      );
+      this.resultDataList = [
+        ...this.dataList,
+        {
+          courseTargetId: "total",
+          courseTargetName: "合计",
+          degreeRequirement: "",
+          totalWeight: calcSum(
+            this.dataList.map((item) => item.totalWeight || 0)
+          ),
+          evaluationList: tatolEvaluation,
+        },
+      ];
+    },
+    checkDataList() {
+      if (!this.dataList.length) return;
+
+      // 整体权重设置
+      if (!this.customRate) {
+        this.$message.error("请输入平均成绩权重");
+        return;
+      }
+      if (!this.defaultRate) {
+        this.$message.error("请输入期末成绩权重");
+        return;
+      }
+      if (this.defaultRate + this.customRate !== 100) {
+        this.$message.error("平均成绩权重与期末成绩权重合计不等于100%");
+        return;
+      }
+
+      // 目标整体权重
+      if (Object.values(this.targetTotalWeight).some((item) => !item)) {
+        this.$message.error("请设置所有目标的目标整体权重");
+        return;
+      }
+      const totalWeight = calcSum(
+        Object.values(this.targetTotalWeight).map((item) => item || 0)
+      );
+      if (totalWeight !== 100) {
+        this.$message.error("目标整体权重合计不等于100%");
+        return;
+      }
+
+      // 目标权重统计
+      const unvalidTargets = [];
+      const tableData = this.tableSetData.slice(0, -1);
+      this.dataList.forEach((target, tindex) => {
+        const totalWeight = calcSum(
+          tableData.map((item) => item.targets[tindex].weight || 0)
+        );
+        if (totalWeight !== 100) {
+          unvalidTargets.push(target.courseTargetName);
+        }
+      });
+      if (unvalidTargets.length) {
+        this.$message.error(
+          `${unvalidTargets.join()}评价方式权重合计不等于100%`
+        );
+        return;
+      }
+
+      return true;
+    },
+    async submit() {
+      if (this.loading) return;
+      this.updateDataList();
+
+      if (!this.checkDataList()) return;
+
+      this.loading = true;
+      const res = await courseWeightSave({
+        teachCourseId: this.course.id,
+        defaultRate: this.defaultRate,
+        customRate: this.customRate,
+        submitForm: this.dataList,
+      }).catch(() => {});
+      this.loading = false;
+      if (!res) return;
+
+      this.initData();
+      this.$message.success("保存成功!");
+    },
+  },
+};
+</script>

+ 0 - 31
src/modules/base/components/course-simple/CourseWeightManage.vue

@@ -10,37 +10,6 @@
       <p>4.目标整体权重应等于100%,用于计算课程整体达成度。</p>
     </div>
     <div class="part-box part-box-pad">
-      <el-form class="mb-2" inline>
-        <el-form-item label="整体权重设置:"></el-form-item>
-        <el-form-item label="平均成绩权重:">
-          <el-input-number
-            v-model="customRate"
-            class="width-50"
-            size="small"
-            :min="0"
-            :max="100"
-            :step="1"
-            step-strictly
-            :controls="false"
-          >
-          </el-input-number>
-          <span style="margin-left: 5px">%</span>
-        </el-form-item>
-        <el-form-item label="期末成绩权重:">
-          <el-input-number
-            v-model="defaultRate"
-            class="width-50"
-            size="small"
-            :min="0"
-            :max="100"
-            :step="1"
-            step-strictly
-            :controls="false"
-          >
-          </el-input-number>
-          <span style="margin-left: 5px">%</span>
-        </el-form-item>
-      </el-form>
       <el-table :data="tableSetData">
         <el-table-column
           prop="evaluationName"

+ 8 - 4
src/modules/course/components/NormalScoreManage.vue

@@ -111,9 +111,13 @@ export default {
       downloading: false,
     };
   },
-  mounted() {
+  async mounted() {
     this.filter = this.$objAssign(this.filter, this.course);
-    this.toPage(1);
+    await this.toPage(1);
+
+    if (this.dataList[0] && this.dataList[0].change) {
+      this.$notify.warning("课程管理评价方式发生变化,请重新导入平时成绩");
+    }
   },
   methods: {
     async getList() {
@@ -142,9 +146,9 @@ export default {
         this.normalScoreItems = JSON.parse(score).map((item) => item.name);
       }
     },
-    toPage(page) {
+    async toPage(page) {
       this.current = page;
-      this.getList();
+      await this.getList();
     },
     toImport() {
       this.$refs.ImportFile.open();