Kaynağa Gözat

feat: 课程考核方式和内容

zhangjie 1 yıl önce
ebeveyn
işleme
38a14fb087

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

@@ -132,3 +132,36 @@ export const courseOutlineTargetRequirementKnowledge = (datas) => {
 // export const updateCourseOutlineKnowledge = (datas) => {
 //   return $post("/api/admin/basic/professional/save", datas);
 // };
+
+// 课程考核方式和内容 ------------------->
+export const courseExamineListPage = (datas) => {
+  return $postParam("/api/admin/obe/course_outline/page", datas);
+};
+export const deleteCourseExamine = (id) => {
+  return $postParam("/api/admin/obe/course_outline/delete", { id });
+};
+
+// 课程考核方式和内容-评价方式 ------------------->
+export const courseExamineEvaluationListPage = (datas) => {
+  return $postParam("/api/admin/obe/course_evaluation/list", datas);
+};
+export const deleteCourseExamineEvaluation = (id) => {
+  return $postParam("/api/admin/obe/course_evaluation/delete", { id });
+};
+export const updateCourseExamineEvaluation = (datas) => {
+  return $post("/api/admin/obe/course_evaluation/save", datas);
+};
+export const courseExamineWeightSettingStatus = (datas) => {
+  return $postParam(
+    "/api/admin/obe/course_outline/find_course_weight_setting_status",
+    datas
+  );
+};
+
+// 课程考核方式和内容-评价方式 ------------------->
+export const courseExamineWeightDetail = (datas) => {
+  return $postParam("/api/admin/obe/course_weight/find", datas);
+};
+export const courseExamineWeightSave = (datas) => {
+  return $post("/api/admin/obe/course_weight/save", datas);
+};

+ 124 - 0
src/modules/target/components/course-examine/CourseExamineEvaluation.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="course-evaluation-manage">
+    <div class="part-box part-box-pad box-justify">
+      <p></p>
+      <div>
+        <el-button type="primary" @click="toAdd">自定义评价方式</el-button>
+      </div>
+    </div>
+    <div class="part-box part-box-pad">
+      <el-table :data="dataList">
+        <!-- <el-table-column type="index" label="序号" width="70"></el-table-column> -->
+        <el-table-column
+          prop="evaluation"
+          label="评价方式"
+          min-width="120"
+        ></el-table-column>
+        <el-table-column
+          prop="evaluationDesc"
+          label="评价方式描述"
+          min-width="300"
+        >
+        </el-table-column>
+        <el-table-column class-name="action-column" label="操作" width="120px">
+          <template slot-scope="scope">
+            <el-button
+              v-if="scope.row.type === 'CUSTOM'"
+              class="btn-primary"
+              type="text"
+              @click="toEdit(scope.row)"
+              >编辑</el-button
+            >
+            <el-button
+              v-if="scope.row.type === 'CUSTOM'"
+              class="btn-danger"
+              type="text"
+              @click="toDelete(scope.row)"
+              >删除</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- ModifyCourseExamineEvaluation -->
+    <modify-course-examine-evaluation
+      ref="ModifyCourseExamineEvaluation"
+      :instance="curRow"
+      @modified="getList"
+    ></modify-course-examine-evaluation>
+  </div>
+</template>
+
+<script>
+import {
+  courseExamineEvaluationListPage,
+  deleteCourseExamineEvaluation,
+} from "../../api";
+import ModifyCourseExamineEvaluation from "./ModifyCourseExamineEvaluation.vue";
+
+export default {
+  name: "CourseExamineEvaluation",
+  components: { ModifyCourseExamineEvaluation },
+  props: {
+    rowData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      dataList: [],
+      curRow: {},
+    };
+  },
+  mounted() {
+    this.getList();
+  },
+  methods: {
+    async getList() {
+      const res = await courseExamineEvaluationListPage({
+        obeCourseOutlineId: this.rowData.id,
+      });
+      this.dataList = res || [];
+    },
+    toAdd() {
+      this.curRow = {
+        obeCourseOutlineId: this.rowData.id,
+      };
+      this.$refs.ModifyCourseExamineEvaluation.open();
+    },
+    toEdit(row) {
+      this.curRow = {
+        obeCourseOutlineId: this.rowData.id,
+        ...row,
+      };
+      this.$refs.ModifyCourseExamineEvaluation.open();
+    },
+    async toDelete(row) {
+      const confirm = await this.$confirm(
+        `删除评价方式会影响权重设置,确定要删除评价方式【${row.evaluation}】吗?`,
+        "提示",
+        {
+          type: "warning",
+        }
+      ).catch(() => {});
+      if (confirm !== "confirm") return;
+
+      const res = await deleteCourseExamineEvaluation(row.evaluationId);
+      if (res.warning) {
+        this.$notify({
+          title: "警告",
+          message: "评价方式有变化,请重新设置权重设置!",
+          type: "warning",
+          duration: 5000,
+        });
+      }
+      this.$message.success("删除成功!");
+      this.getList();
+    },
+  },
+};
+</script>

+ 189 - 0
src/modules/target/components/course-examine/CourseExamineWeight.vue

@@ -0,0 +1,189 @@
+<template>
+  <div class="course-weight-manage">
+    <div class="part-box part-box-pad">
+      <el-table :data="dataList" :summary-method="getSummaries" show-summary>
+        <el-table-column
+          prop="courseTargetName"
+          label="课程目标"
+          width="200"
+          fixed="left"
+        ></el-table-column>
+        <el-table-column
+          v-for="(column, cindex) in columns"
+          :key="cindex"
+          :label="column"
+        >
+          <template slot-scope="scope">
+            <el-input-number
+              v-model="scope.row.evaluationList[cindex].weight"
+              class="width-50"
+              size="small"
+              :min="0"
+              :max="100"
+              :step="1"
+              step-strictly
+              :controls="false"
+            >
+            </el-input-number>
+            <span style="margin-left: 5px">%</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="目标整体权重" prop="totalWeight">
+          <template slot-scope="scope">
+            <el-input-number
+              v-model="scope.row.totalWeight"
+              class="width-50"
+              size="small"
+              :min="0"
+              :max="100"
+              :step="1"
+              step-strictly
+              :controls="false"
+            >
+            </el-input-number>
+            <span style="margin-left: 5px">%</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="text-center">
+        <el-button type="primary" :loading="loading" @click="submit"
+          >保存</el-button
+        >
+      </div>
+
+      <div class="part-box part-box-pad">
+        <p>说明:</p>
+        <p>
+          1.课程目标评价依据来源于平时成绩和期末考试成绩二部分,请录入平时成绩,期末成绩整体权重及各目标的权重;
+        </p>
+        <p>2.各课程目标下选择评价方式总权重应等于100%;</p>
+        <p>3.目标整体权重应等于100%,用于计算课程整体达成度。</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { courseExamineWeightDetail, courseExamineWeightSave } from "../../api";
+import { calcSum } from "@/plugins/utils";
+import { mapState, mapActions } from "vuex";
+
+export default {
+  name: "course-weight-manage",
+  props: {
+    rowData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      dataList: [],
+      columns: [],
+      loading: false,
+    };
+  },
+  computed: {
+    ...mapState("base", ["cwStatus"]),
+  },
+  mounted() {
+    this.initData();
+  },
+  methods: {
+    ...mapActions("base", ["updateCwStatus"]),
+    async initData() {
+      await this.updateCwStatus({
+        obeCourseOutlineId: this.rowData.id,
+      });
+      await this.getList();
+    },
+    async getList() {
+      const res = await courseExamineWeightDetail({
+        obeCourseOutlineId: this.rowData.id,
+      });
+      this.dataList = res || [];
+
+      this.columns = this.dataList[0].evaluationList.map(
+        (item) => item.evaluationName
+      );
+    },
+    checkDataList() {
+      if (!this.dataList.length) return;
+
+      // 目标整体权重
+      if (this.dataList.some((item) => !item.totalWeight)) {
+        this.$message.error("请设置所有目标的目标整体权重");
+        return;
+      }
+      const totalWeight = calcSum(
+        this.dataList.some((item) => item.totalWeight || 0)
+      );
+      if (totalWeight !== 100) {
+        this.$message.error("目标整体权重合计不等于100%");
+        return;
+      }
+
+      // 目标权重统计
+      const unvalidTargets = [];
+      this.dataList.forEach((item, tindex) => {
+        const totalWeight = calcSum(
+          item.evaluationList.map((item) => item.weight || 0)
+        );
+        if (totalWeight !== 100) {
+          unvalidTargets.push(item.courseTargetName);
+        }
+      });
+      if (unvalidTargets.length) {
+        this.$message.error(
+          `${unvalidTargets.join()}评价方式权重合计不等于100%`
+        );
+        return;
+      }
+
+      return true;
+    },
+    async submit() {
+      if (this.loading) return;
+      if (!this.checkDataList()) return;
+
+      this.loading = true;
+      const res = await courseExamineWeightSave({
+        obeCourseOutlineId: this.rowData.id,
+        submitForm: this.dataList,
+      }).catch(() => {});
+      this.loading = false;
+      if (!res) return;
+
+      this.initData();
+      this.$message.success("保存成功!");
+    },
+    getSummaries(param) {
+      const { columns } = param;
+      const sums = [];
+      const lastNo = columns.length - 1;
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = "合计";
+          return;
+        }
+
+        if (index === lastNo) {
+          sums[index] =
+            calcSum(this.dataList.map((item) => item.totalWeight || 0)) + "%";
+          return;
+        }
+
+        sums[index] =
+          calcSum(
+            this.dataList.map(
+              (item) => item.evaluationList[index - 1].weight || 0
+            )
+          ) + "%";
+      });
+      return sums;
+    },
+  },
+};
+</script>

+ 85 - 0
src/modules/target/components/course-examine/DetailCourseExamine.vue

@@ -0,0 +1,85 @@
+<template>
+  <el-dialog
+    class="page-dialog"
+    :visible.sync="modalIsShow"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    fullscreen
+    @close="closeHandle"
+  >
+    <div slot="title">{{ rowData.name }}</div>
+    <div class="mb-4 tab-btns">
+      <el-button
+        v-for="tab in tabs"
+        :key="tab.val"
+        size="medium"
+        :type="curTab == tab.val ? 'primary' : 'default'"
+        @click="selectMenu(tab.val)"
+        >{{ tab.name }}
+      </el-button>
+    </div>
+
+    <div v-if="modalIsShow">
+      <component :is="curTab" :row-data="rowData"></component>
+    </div>
+
+    <div slot="footer"></div>
+  </el-dialog>
+</template>
+
+<script>
+import CourseExamineEvaluation from "./CourseExamineEvaluation.vue";
+import CourseExamineWeight from "./CourseExamineWeight.vue";
+import { mapActions } from "vuex";
+
+export default {
+  name: "detail-course-evaluation",
+  components: {
+    CourseExamineEvaluation,
+    CourseExamineWeight,
+  },
+  props: {
+    rowData: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      curTab: "CourseExamineEvaluation",
+      tabs: [
+        {
+          name: "评价方式管理",
+          val: "CourseExamineEvaluation",
+        },
+        {
+          name: "权重设置",
+          val: "CourseExamineWeight",
+        },
+      ],
+    };
+  },
+  methods: {
+    ...mapActions("base", ["updateCwStatus"]),
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    selectMenu(tab) {
+      this.curTab = tab;
+      this.updateCwStatus({
+        obeCourseOutlineId: this.rowData.id,
+      });
+    },
+    closeHandle() {
+      this.curTab = "CourseExamineEvaluation";
+    },
+  },
+};
+</script>

+ 157 - 0
src/modules/target/components/course-examine/ModifyCourseExamineEvaluation.vue

@@ -0,0 +1,157 @@
+<template>
+  <el-dialog
+    class="modify-semester"
+    :visible.sync="modalIsShow"
+    :title="title"
+    top="10vh"
+    width="600px"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-width="120px"
+    >
+      <el-form-item prop="evaluation" label="评价方式:">
+        <el-input
+          v-model.trim="modalForm.evaluation"
+          placeholder="请输入评价方式"
+          clearable
+          :disabled="isEdit"
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="evaluationDesc" label="评价方式描述:">
+        <el-input
+          v-model="modalForm.evaluationDesc"
+          placeholder="请输入评价方式描述"
+          type="textarea"
+          :autosize="{ minRows: 2, maxRows: 6 }"
+          :maxlength="999"
+          resize="none"
+          show-word-limit
+        ></el-input>
+      </el-form-item>
+    </el-form>
+    <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 { updateCourseExamineEvaluation } from "../../api";
+import { mapState } from "vuex";
+
+const initModalForm = {
+  evaluationId: null,
+  teachCourseId: "",
+  evaluation: "",
+  evaluationDesc: "",
+};
+
+export default {
+  name: "modify-course-evaluation",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  computed: {
+    ...mapState("target", ["cwStatus"]),
+    isEdit() {
+      return !!this.instance.evaluationId;
+    },
+    title() {
+      return (this.isEdit ? "编辑" : "新增") + "评价方式";
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      rules: {
+        evaluation: [
+          {
+            required: true,
+            message: "请输入评价方式",
+            trigger: "change",
+          },
+          {
+            message: "评价方式不能超过30个字",
+            max: 30,
+            trigger: "change",
+          },
+        ],
+        evaluationDesc: [
+          {
+            required: true,
+            max: 999,
+            message: "评价方式描述不能超过999个字",
+            trigger: "change",
+          },
+        ],
+      },
+    };
+  },
+  methods: {
+    initData(val) {
+      this.modalForm = this.$objAssign(initModalForm, val);
+    },
+    visibleChange() {
+      this.initData(this.instance);
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async submit() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.cwStatus.everSettingStatus) {
+        const confirm = await this.$confirm(
+          `${this.title}会影响权重设置,确定要${this.title}吗?`,
+          "提示",
+          {
+            type: "warning",
+          }
+        ).catch(() => {});
+        if (confirm !== "confirm") return;
+      }
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      let datas = { ...this.modalForm, id: this.modalForm.evaluationId };
+      const res = await updateCourseExamineEvaluation(datas).catch(() => {});
+      this.isSubmit = false;
+
+      if (!res) return;
+
+      this.$message.success(this.title + "成功!");
+      if (res.warning) {
+        this.$notify({
+          title: "警告",
+          message: "评价方式有变化,请重新设置权重设置!",
+          type: "warning",
+          duration: 5000,
+        });
+      }
+      this.$emit("modified");
+      this.cancel();
+    },
+  },
+};
+</script>

+ 35 - 0
src/modules/target/store.js

@@ -0,0 +1,35 @@
+import { courseExamineWeightSettingStatus } from "./api";
+
+const state = {
+  cwStatus: {
+    // 曾经设置过情况,true:曾经设置过(编辑课程设置时警告提示),false:从未设置过(不需要提示)
+    everSettingStatus: false,
+    // 当前设置情况,true:当前课程设置过权重,权重设置界面需要展示两个表单,false:当前未设置权重,权重设置界面只展示1个表单
+    currentSettingStatus: false,
+    // 教学课程管理者(创建人)id 当前登录人和课程创建人一致时“教学班”“教师团队”两个页签才能编辑,否则只能查看
+    courseCreateId: "",
+  },
+};
+
+const mutations = {
+  setCwStatus(state, data) {
+    state.cwStatus = Object.assign({}, state.cwStatus, data);
+  },
+  resetState(state) {
+    state.cwStatus = {};
+  },
+};
+
+const actions = {
+  async updateCwStatus({ commit }, data) {
+    const res = await courseExamineWeightSettingStatus(data);
+    commit("setCwStatus", res || {});
+  },
+};
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 165 - 0
src/modules/target/views/CourseExamine.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="training-plan-manage">
+    <div class="part-box part-box-filter part-box-flex">
+      <el-form ref="FilterForm" label-position="left" label-width="85px" inline>
+        <template v-if="checkPrivilege('condition', 'condition')">
+          <el-form-item label="专业:">
+            <professional-select
+              v-model="filter.professionalId"
+              placeholder="专业"
+              :org-id="userOrgId"
+            ></professional-select>
+          </el-form-item>
+          <el-form-item label="培养方案:">
+            <professional-select
+              v-model="filter.professionalId"
+              placeholder="培养方案"
+              :org-id="userOrgId"
+            ></professional-select>
+          </el-form-item>
+          <el-form-item label="课程:">
+            <course-select
+              v-model="filter.courseId"
+              placeholder="课程"
+            ></course-select>
+          </el-form-item>
+        </template>
+        <el-form-item label-width="0px">
+          <el-button
+            v-if="checkPrivilege('button', 'select')"
+            type="primary"
+            @click="search"
+            >查询</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <div class="part-box part-box-pad">
+      <el-table ref="TableList" :data="dataList">
+        <el-table-column
+          type="index"
+          label="序号"
+          width="70"
+          :index="indexMethod"
+        ></el-table-column>
+        <el-table-column prop="courseName" label="课程名称"> </el-table-column>
+        <el-table-column prop="courseCode" label="课程代码"> </el-table-column>
+        <el-table-column prop="outlineName" label="课程大纲名称">
+        </el-table-column>
+        <el-table-column prop="evaluationMode" label="考核方式" width="100">
+          <span slot-scope="scope">
+            {{ scope.row.evaluationMode | evaluationModeFilter }}
+          </span>
+        </el-table-column>
+        <el-table-column prop="userName" label="负责教师"> </el-table-column>
+        <el-table-column
+          class-name="action-column"
+          label="操作"
+          width="120"
+          fixed="right"
+        >
+          <template slot-scope="scope">
+            <el-button
+              v-if="checkPrivilege('link', 'edit')"
+              class="btn-primary"
+              type="text"
+              @click="toEdit(scope.row)"
+              >编辑</el-button
+            >
+            <el-button
+              v-if="checkPrivilege('link', 'delete')"
+              class="btn-danger"
+              type="text"
+              @click="toDelete(scope.row)"
+              >删除</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="part-page">
+        <el-pagination
+          background
+          layout="total, sizes, prev, pager, next, jumper"
+          :pager-count="5"
+          :current-page="current"
+          :total="total"
+          :page-size="size"
+          @current-change="toPage"
+          @size-change="pageSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <!-- DetailCourseExamine -->
+    <detail-course-examine
+      v-if="checkPrivilege('button', 'add')"
+      ref="DetailCourseExamine"
+      :row-data="curRow"
+      @modified="getList"
+    ></detail-course-examine>
+  </div>
+</template>
+
+<script>
+import { courseExamineListPage, deleteCourseExamine } from "../api";
+import DetailCourseExamine from "../components/course-examine/DetailCourseExamine.vue";
+
+export default {
+  name: "course-examine-manage",
+  components: { DetailCourseExamine },
+  data() {
+    return {
+      filter: {
+        professionalId: "",
+        cultureProgramId: "",
+        courseId: "",
+      },
+      current: 1,
+      size: this.GLOBAL.pageSize,
+      total: 0,
+      dataList: [],
+      curRow: {},
+      userOrgId: this.$ls.get("orgId", ""),
+    };
+  },
+  mounted() {
+    this.toPage(1);
+  },
+  methods: {
+    async getList() {
+      if (!this.checkPrivilege("list", "list")) return;
+
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current,
+        pageSize: this.size,
+      };
+      const data = await courseExamineListPage(datas);
+      this.dataList = data.records;
+      this.total = data.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    search() {
+      this.toPage(1);
+    },
+    toEdit(row) {
+      this.curRow = row;
+      this.$refs.DetailCourseExamine.open();
+    },
+    async toDelete(row) {
+      const confirm = await this.$confirm(`确定要删除当前记录吗?`, "提示", {
+        type: "warning",
+      }).catch(() => {});
+      if (confirm !== "confirm") return;
+
+      await deleteCourseExamine(row.id);
+      this.$message.success("删除成功!");
+      this.deletePageLastItem();
+    },
+  },
+};
+</script>

+ 2 - 2
src/modules/target/views/CourseOutlineManage.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="training-plan-manage">
+  <div class="course-outline-manage">
     <div class="part-box part-box-filter part-box-flex">
       <el-form ref="FilterForm" label-position="left" label-width="85px" inline>
         <template v-if="checkPrivilege('condition', 'condition')">
@@ -144,7 +144,7 @@ import ModifyCourseOutline from "../components/course-outline/ModifyCourseOutlin
 import DetailCourseOutline from "../components/course-outline/DetailCourseOutline.vue";
 
 export default {
-  name: "training-plan-manage",
+  name: "course-outline-manage",
   components: { ModifyCourseOutline, DetailCourseOutline },
   data() {
     return {