Răsfoiți Sursa

手动组卷试题编辑与选题

zhangjie 2 ani în urmă
părinte
comite
a842f8b205

+ 40 - 3
src/modules/paper/components/BuildPaperManual.vue

@@ -70,7 +70,7 @@
           size="mini"
           type="success"
           icon="el-icon-circle-plus-outline"
-          @click="toAddQuestion"
+          @click="toAddQuestion(detail)"
           >新增小题</el-button
         >
       </el-collapse-item>
@@ -82,6 +82,19 @@
       :detail="curDetail"
       @modified="detailModified"
     ></modify-detail-struct>
+    <!-- SelectQuestionDialog -->
+    <select-question-dialog
+      ref="SelectQuestionDialog"
+      :course-d="courseId"
+      :disabled-question-ids="disabledQuestionIds"
+      @confirm="questionSelected"
+    ></select-question-dialog>
+    <!-- QuestionEditDialog -->
+    <question-edit-dialog
+      ref="QuestionEditDialog"
+      :question="curQuestion"
+      @modified="questionModified"
+    ></question-edit-dialog>
   </div>
 </template>
 
@@ -89,6 +102,8 @@
 import ModifyDetailStruct from "../components/ModifyDetailStruct.vue";
 import BuildPaperQuestionBase from "./BuildPaperQuestionBase.vue";
 import BuildPaperQuestionNested from "./BuildPaperQuestionNested.vue";
+import SelectQuestionDialog from "./SelectQuestionDialog.vue";
+import QuestionEditDialog from "../../question/components/QuestionEditDialog.vue";
 import { BASE_QUESTION_TYPES } from "@/constants/constants";
 
 import details from "../datas/details.json";
@@ -99,6 +114,8 @@ export default {
     ModifyDetailStruct,
     BuildPaperQuestionBase,
     BuildPaperQuestionNested,
+    SelectQuestionDialog,
+    QuestionEditDialog,
   },
   props: {
     courseId: {
@@ -111,7 +128,9 @@ export default {
       // details: [],
       details,
       curDetail: {},
+      curQuestion: {},
       activeNames: [],
+      disabledQuestionIds: [],
     };
   },
   methods: {
@@ -155,12 +174,30 @@ export default {
       }
       console.log(this.details);
     },
-    toAddQuestion() {
+    toAddQuestion(detail) {
       console.log("add");
+      this.curDetail = detail;
+      let disabledQuestionIds = [];
+      this.details.forEach((detail) => {
+        detail.questions.forEach((q) => {
+          disabledQuestionIds.push(q.id);
+        });
+      });
+      this.disabledQuestionIds = disabledQuestionIds;
+      this.$refs.SelectQuestionDialog.open();
+    },
+    questionSelected(questions) {
+      this.curDetail.questions.push(...questions);
     },
     toEditQuestion(question) {
       this.curQuestion = question;
-      // todo edit question
+      this.$refs.QuestionEditDialog.open();
+    },
+    questionModified(question) {
+      this.details.forEach((detail) => {
+        const qindex = detail.questions.findIndex((q) => q.id === question.id);
+        if (qindex !== -1) detail.questions.splice(qindex, 1, question);
+      });
     },
     toRemoveQuestion(question) {
       this.details.forEach((detail) => {

+ 327 - 0
src/modules/paper/components/SelectQuestionDialog.vue

@@ -0,0 +1,327 @@
+<template>
+  <el-dialog
+    class="select-question-dialog"
+    :visible.sync="modalIsShow"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    append-to-body
+    fullscreen
+    @open="visibleChange"
+  >
+    <div slot="title" class="box-justify">
+      <h3>选择试题</h3>
+      <el-button size="mini" type="primary" @click="confirm">确定</el-button>
+    </div>
+
+    <el-tabs v-model="tabType" type="card" @tab-click="tabChange">
+      <el-tab-pane name="1">
+        <span slot="label">试题筛选</span>
+
+        <div class="part-filter">
+          <div class="part-filter-form">
+            <el-form class="part-filter-form" :inline="true" :model="filter">
+              <el-form-item label="题型">
+                <question-type-select v-model="filter.quesStructType">
+                </question-type-select>
+              </el-form-item>
+              <el-form-item label="题目内容">
+                <el-input
+                  v-model="filter.content"
+                  placeholder="题目内容"
+                ></el-input>
+              </el-form-item>
+              <el-form-item label="属性名">
+                <property-select
+                  v-model="filter.coursePropertyId"
+                  :course-id="filter.courseId"
+                ></property-select>
+              </el-form-item>
+              <el-form-item label="一级属性">
+                <property-sub-select
+                  v-model="filter.firstPropertyId"
+                  :parent-id="filter.coursePropertyId"
+                  data-type="first"
+                ></property-sub-select>
+              </el-form-item>
+              <el-form-item label="二级属性">
+                <property-sub-select
+                  v-model="filter.secondPropertyId"
+                  :parent-id="filter.firstPropertyId"
+                  data-type="second"
+                ></property-sub-select>
+              </el-form-item>
+
+              <el-form-item>
+                <el-button type="danger" @click="toPage(1)">查询</el-button>
+              </el-form-item>
+            </el-form>
+          </div>
+        </div>
+
+        <el-table :data="questions" border>
+          <el-table-column
+            width="60"
+            type="index"
+            :index="indexMethod"
+            label="编号"
+          >
+          </el-table-column>
+          <el-table-column label="课程" width="120">
+            <template slot-scope="scope">
+              <span>{{ scope.row.course.name }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="题型" width="100">
+            <template slot-scope="scope">
+              <span>{{ scope.row.questionType | questionType }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="难度" width="80"> </el-table-column>
+          <el-table-column label="使用量" width="80"> </el-table-column>
+          <el-table-column label="创建人" width="120">
+            <template slot-scope="scope">
+              <span>{{ scope.row.creator }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="创建时间" width="170" prop="creationTime">
+          </el-table-column>
+          <el-table-column label="操作" width="160" fixed="right">
+            <div v-if="!scope.row.disabled" slot-scope="scope">
+              <!-- <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="toDetail(scope.row)"
+              >
+                详情
+              </el-button> -->
+              <el-button
+                v-if="scope.row.selected"
+                size="mini"
+                type="danger"
+                plain
+                @click="toDelete(scope.row)"
+              >
+                删除
+              </el-button>
+              <el-button
+                v-else
+                size="mini"
+                type="primary"
+                plain
+                @click="toSelect(scope.row)"
+              >
+                添加
+              </el-button>
+            </div>
+          </el-table-column>
+        </el-table>
+
+        <div class="part-page">
+          <el-pagination
+            background
+            :current-page="currentPage"
+            :page-size="pageSize"
+            :page-sizes="[10, 20, 50, 100, 200, 300]"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+            @current-change="toPage"
+            @size-change="handleSizeChange"
+          />
+        </div>
+      </el-tab-pane>
+      <el-tab-pane name="2">
+        <span slot="label">
+          <span>已选试题</span>
+          <span class="color-primary">({{ curSelectedQuestions.length }})</span>
+        </span>
+
+        <el-table :data="curSelectedQuestions" border>
+          <el-table-column width="60" type="index" label="编号">
+          </el-table-column>
+          <el-table-column label="课程" width="120">
+            <template slot-scope="scope">
+              <span>{{ scope.row.course.name }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="题型" width="100">
+            <template slot-scope="scope">
+              <span>{{ scope.row.questionType | questionType }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="难度" width="80"> </el-table-column>
+          <el-table-column label="使用量" width="80"> </el-table-column>
+          <el-table-column label="创建人" width="120">
+            <template slot-scope="scope">
+              <span>{{ scope.row.creator }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="创建时间" width="170" prop="creationTime">
+          </el-table-column>
+          <el-table-column label="操作" width="160" fixed="right">
+            <div slot-scope="scope">
+              <!-- <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="toDetail(scope.row)"
+              >
+                详情
+              </el-button> -->
+              <el-button
+                size="mini"
+                type="danger"
+                plain
+                @click="toDeleteSelected(scope.row)"
+              >
+                删除
+              </el-button>
+            </div>
+          </el-table-column>
+        </el-table>
+      </el-tab-pane>
+    </el-tabs>
+
+    <div slot="footer"></div>
+
+    <question-preview-dialog
+      ref="QuestionPreviewDialog"
+      :topic="curQuestion"
+    ></question-preview-dialog>
+  </el-dialog>
+</template>
+
+<script>
+// import QuestionPreviewDialog from "./QuestionPreviewDialog";
+import { questionPageListApi } from "../../question/api";
+import { deepCopy } from "@/plugins/utils";
+
+export default {
+  name: "SelectQuestionDialog",
+  // components: {  QuestionPreviewDialog },
+  props: {
+    courseId: {
+      type: [String, Number],
+      default: "",
+    },
+    selectedQuestionIds: {
+      type: Array,
+      default() {
+        return [];
+      },
+    },
+    disabledQuestionIds: {
+      type: Array,
+      default() {
+        return [];
+      },
+    },
+  },
+  data() {
+    return {
+      modalIsShow: false,
+      filter: {
+        courseId: "",
+        quesStructType: "",
+        content: "",
+        coursePropertyId: "",
+        firstPropertyId: "",
+        secondPropertyId: "",
+      },
+      tabType: "1",
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      questions: [],
+      curSelectedQuestions: [],
+      curSelectedQuestionIds: [],
+      curQuestion: {},
+    };
+  },
+  methods: {
+    initData() {
+      this.filter.courseId = this.courseId;
+      this.curSelectedQuestions = [];
+      this.curSelectedQuestionIds = [];
+      this.toPage(this.currentPage);
+    },
+    visibleChange() {
+      this.initData();
+    },
+    cancel() {
+      this.modalIsShow = false;
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    async getList() {
+      const res = await questionPageListApi({
+        ...this.filter,
+        enabled: 1,
+        curPage: this.currentPage,
+        pageSize: this.pageSize,
+      });
+      this.questions = res.records.map((item) => {
+        return {
+          // ...this.transformQuestionData(item),
+          ...item,
+          selected: this.curSelectedQuestionIds.includes(item.id),
+          disabled: this.disabledQuestionIds.includes(item.id),
+        };
+      });
+      this.total = res.total;
+    },
+    indexMethod(index) {
+      return index + (this.currentPage - 1) * this.pageSize + 1;
+    },
+    toPage(val) {
+      this.currentPage = val;
+      this.getList();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.getList();
+    },
+    tabChange(val) {
+      if (val.name === "1") {
+        this.questions.forEach((item) => {
+          item.selected = this.curSelectedQuestionIds.includes(item.id);
+        });
+      }
+    },
+    // toDetail(row) {
+    //   this.curQuestion = { ...row };
+    //   this.$refs.QuestionPreviewDialog.open();
+    // },
+    toSelect(row) {
+      row.selected = true;
+      this.curSelectedQuestions.push({ ...row });
+      this.updateSelectedQuestionIds();
+    },
+    toDelete(row) {
+      row.selected = false;
+      this.toDeleteSelected(row);
+    },
+    toDeleteSelected(row) {
+      const pos = this.curSelectedQuestions.findIndex(
+        (item) => item.id === row.id
+      );
+      this.curSelectedQuestions.splice(pos, 1);
+
+      this.updateSelectedQuestionIds();
+    },
+    updateSelectedQuestionIds() {
+      this.curSelectedQuestionIds = this.curSelectedQuestions.map(
+        (item) => item.id
+      );
+    },
+    confirm() {
+      this.$emit("confirm", deepCopy(this.curSelectedQuestions));
+      this.cancel();
+    },
+  },
+};
+</script>

+ 11 - 36
src/modules/paper/views/EditPaper.vue

@@ -841,44 +841,19 @@ export default {
 
     // other
     isNested(questionType) {
-      if (
-        questionType == "PARAGRAPH_MATCHING" ||
-        questionType == "BANKED_CLOZE" ||
-        questionType == "CLOZE" ||
-        questionType == "READING_COMPREHENSION" ||
-        questionType == "LISTENING_QUESTION"
-      ) {
-        return true;
-      } else {
-        return false;
-      }
+      const nestedQuestion = [
+        "PARAGRAPH_MATCHING",
+        "BANKED_CLOZE",
+        "CLOZE",
+        "READING_COMPREHENSION",
+        "LISTENING_QUESTION",
+      ];
+
+      return nestedQuestion.includes(questionType);
     },
     isMatchingQuestion(questionType) {
-      if (
-        questionType == "PARAGRAPH_MATCHING" ||
-        questionType == "BANKED_CLOZE"
-      ) {
-        return true;
-      } else {
-        return false;
-      }
-    },
-    isInOtherSelect(optIndex, quesModel) {
-      if (quesModel.parentQuesParam.matchingMode != 1) {
-        return false;
-      }
-      if (!quesModel.optionsSelected) {
-        return false;
-      }
-      if (!quesModel.optionsSelected[optIndex + 1]) {
-        return false;
-      }
-      if (
-        quesModel.optionsSelected[optIndex + 1].includes(quesModel.subNumber)
-      ) {
-        return false;
-      }
-      return true;
+      const typeQuestion = ["PARAGRAPH_MATCHING", "BANKED_CLOZE"];
+      return typeQuestion.includes(questionType);
     },
   },
 };

+ 5 - 1
src/modules/question/components/QuestionEditDialog.vue

@@ -37,6 +37,7 @@
             :key="item.code"
             :type="curQuestionType === item.code ? 'primary' : 'default'"
             size="small"
+            :disabled="isEdit"
             @click="switchType(item)"
             >{{ item.name }}</el-button
           >
@@ -118,6 +119,7 @@ export default {
     },
     initData() {
       this.questionKey = randomCode();
+      this.curQuestionType = this.question.questionType || this.curQuestionType;
       this.questionModel = this.$objAssign(
         getInitQuestionModel(this.curQuestionType),
         this.question
@@ -131,6 +133,7 @@ export default {
       this.modalIsShow = true;
     },
     async switchType(item) {
+      if (this.isEdit) return;
       if (this.curQuestionType === item.code) return;
 
       const confirm = await this.$confirm("确认更改题型吗?", "提示", {
@@ -158,7 +161,8 @@ export default {
       if (!res) return;
 
       this.$message.success(this.title + "成功");
-      // this.cancel();
+      this.cancel();
+      this.$emit("modified", this.questionModel);
     },
   },
 };