fuliqi
2024-06-06 94da5161fcc39a88e0337e67573a3dd96691b39c
src/views/Manage/TestPaper/TestPaperGeneration.vue
@@ -3,301 +3,793 @@
  <div class="c">
    <div class="bg">
      <div class="main">
        <!-- 带返回的标题 -->
        <TitleIndex title="试卷生成" />
        <div class="content">
          <!-- 搜索 -->
          <div>
            <el-form
              :inline="true"
              :model="formLabelAlign"
              class="demo-form-inline"
              label-width="80px"
        <div class="paper-main">
          <div class="paper-content">
            <!-- 试题 -->
            <div
              ref="paperContent"
              class="paper-left"
            >
              <el-form-item>
                <el-input
                  v-model="formLabelAlign.type"
                  placeholder="题目名"
                ></el-input>
              </el-form-item>
              <el-form-item>
                <el-select
                  v-model="formLabelAlign.region"
                  placeholder="题目类型"
              <div
                class="subject"
                v-for="item in convertDatas"
                :key="item.id"
                >
                  <el-option
                    label="全部题目"
                    value="shanghai"
                  ></el-option>
                  <el-option
                    label="选择题"
                    value="shanghai"
                  ></el-option>
                  <el-option
                    label="判断题"
                    value="beijing"
                  ></el-option>
                  <el-option
                    label="简单题"
                    value="beijing"
                  ></el-option>
                </el-select>
              </el-form-item>
              <el-form-item>
                <el-select
                  v-model="formLabelAlign.region"
                  placeholder="全部科目"
                >
                  <el-option
                    label="全部科目"
                    value="shanghai"
                  ></el-option>
                  <el-option
                    label="语文"
                    value="beijing"
                  ></el-option>
                  <el-option
                    label="数学"
                    value="beijing"
                  ></el-option>
                </el-select>
              </el-form-item>
              <el-form-item label="">
                <el-button
                  style="width:100px;"
                  type="primary"
                  size="small"
                >查询</el-button>
              </el-form-item>
            </el-form>
                <div class="subject-title">
                  <h2>{{ item.name }}</h2>
                  <span>(共 {{ item.count }} 题,合计 {{ item.totalScore }} 分)</span>
          </div>
          <!-- 表格 -->
          <el-table
            :row-style="{height:'38px'}"
            :cell-style="{padding: '0'}"
            ref="multipleTable"
            :data="tableData"
            tooltip-effect="dark"
            style="width: 100%"
            @selection-change="handleSelectionChange"
            border
                <el-card
                  class="box-card"
                  v-for="(sub,index) in item.childs"
                  :id="item.code+(index+1)"
                  :key="sub.id"
          >
            <el-table-column
              align="center"
              type="selection"
              width="55"
                  <div
                    slot="header"
                    class="clearfix"
            >
            </el-table-column>
            <el-table-column
              prop="title"
              label="题目名"
              align="center"
              width="220"
              min-width="180"
                    <el-tag
                      effect="dark"
                      style="margin-right:10px"
                    > {{ sub.no }}
                    </el-tag>
                    <span>{{ sub.subject }}</span>
                    <span>({{ sub.totalScore }}分)</span>
                    <div
                      v-if="type===2 || type===3"
                      style="float: right; padding: 3px 0"
            >
            </el-table-column>
            <el-table-column
              prop="type"
              label="题目类型"
              align="center"
              width="120"
                      <el-radio-group v-model="sub.isHook">
                        <el-radio-button
                          :disabled="disabledRead"
                          :label="1"
                          @change.native="isHookButtionCheck(sub)"
                        ><i class="el-icon-check"/></el-radio-button>
                        <el-radio-button
                          :disabled="disabledRead"
                          :label="2"
                          @change.native="isHookButtionCheck(sub)"
                        ><i class="el-icon-close"/></el-radio-button>
                      </el-radio-group>
                      <div
                        v-if="sub.type===1 ||sub.type===2||sub.type===3"
                        style="display: inline;"
            >
            </el-table-column>
            <el-table-column
              align="center"
              prop="subject"
              label="科目"
              show-overflow-tooltip
            >
            </el-table-column>
            <el-table-column
              align="center"
              prop="score"
              label="分值"
              show-overflow-tooltip
            >
            </el-table-column>
            <el-table-column
              align="center"
              prop="reference"
              label="参考答案"
              show-overflow-tooltip
            >
            </el-table-column>
          </el-table>
          <div style="margin-top: 20px;text-align: center;">
            <el-button type="primary">生成试卷</el-button>
                        <el-input
                          :disabled="true"
                          v-model="sub.score"
                          style="width:50px"
                        ></el-input>
                        <span>分</span>
          </div>
          <div
            class="block"
            style="display: flex; margin-top: 40px;"
                        v-else
                        style="display: inline;"
          >
            <el-pagination
              style="margin:auto"
              background
              :page-size="10"
              layout="prev, pager, next, jumper"
              :total="100"
                        <el-input
                          :disabled="disabledRead"
                          v-model="sub.score"
                          style="width:50px"
                        ></el-input>
                        <span>分</span>
                      </div>
                    </div>
                  </div>
                  <el-radio-group
                    v-if="sub.type===1"
                    v-model="sub.examineAnswer"
            >
            </el-pagination>
                    <el-radio
                      :disabled="disabledAnswer"
                      v-for="o in sub.answers"
                      :key="o.id"
                      :label="o.no"
                      class="answer-radio"
                      @change="answerButtionCheck($event,item,sub)"
                    >{{ o.no }}.{{ o.answer }}
                    </el-radio>
                  </el-radio-group>
                  <el-checkbox-group
                    v-if="sub.type===2"
                    v-model="sub.examineAnswer"
                  >
                    <el-checkbox
                      :disabled="disabledAnswer"
                      v-for="o in sub.answers"
                      :key="o.id"
                      :label="o.no"
                      class="answer-checkbox"
                      @change="answerButtionCheck($event,item,sub)"
                    >{{ o.no }}.{{ o.answer }}
                    </el-checkbox>
                  </el-checkbox-group>
                  <el-radio-group
                    v-if="sub.type===3"
                    v-model="sub.examineAnswer"
                  >
                    <el-radio
                      :disabled="disabledAnswer"
                      label="对"
                      class="answer-radio"
                      @change="answerButtionCheck($event,item,sub)"
                    >对
                    </el-radio>
                    <el-radio
                      :disabled="disabledAnswer"
                      label="错"
                      class="answer-radio"
                      @change="answerButtionCheck($event,item,sub)"
                    >错
                    </el-radio>
                  </el-radio-group>
                  <el-input
                    :disabled="disabledAnswer"
                    v-if="sub.type===4"
                    type="textarea"
                    :rows="2"
                    v-model="sub.examineAnswer"
                    resize="none"
                    maxlength="150"
                    @blur="answerButtionCheck($event,item,sub)"
                  ></el-input>
                  <el-input
                    :disabled="disabledAnswer"
                    v-if="sub.type===5"
                    type="textarea"
                    :rows="10"
                    v-model="sub.examineAnswer"
                    resize="none"
                    maxlength="2000"
                    @blur="answerButtionCheck($event,item,sub)"
                  ></el-input>
                  <div
                    v-if="type!==1"
                    class="subject-remark"
                  >
                    <div class="item">
                      <span class="title">考生答案:</span>
                      <span>{{ converAnswerStr(sub.examineAnswer) }}</span>
          </div>
                    <div class="item">
                      <span class="title">正确答案:</span>
                      <span>{{ converAnswerStr(sub.correctAnswer) }}</span>
                    </div>
                    <div class="item">
                      <span class="title">考生答案:</span>
                      <span>{{ sub.answerAnalysis }}</span>
                    </div>
                  </div>
                </el-card>
              </div>
            </div>
            <!-- 答题卡 -->
            <div
              ref="paperLeft"
              class="paper-right"
            >
              <div class="paper-title">
                <h1><i class="el-icon-s-grid"></i>答题卡
                  <span class="downTime"><i
                    class="el-icon-alarm-clock"
                    style=" color: #000; font-weight: bold; font-size: 24px;margin-right: 10px;"
                  ></i>{{ hour ? hourString + ':' + minuteString + ':' + secondString : minuteString + ':' + secondString }}</span>
                </h1>
              </div>
              <el-collapse v-model="answerCardActiveName">
                <el-collapse-item
                  v-for="item in convertDatas"
                  :key="item.id"
                  :name="item.code"
                >
                  <template slot="title">
                    <h2>{{ item.name }}</h2>
                    <span>共{{ item.count }}题</span>
                  </template>
                  <el-button
                    class="answer-button"
                    circle
                    size="small"
                    v-for="index of item.count"
                    :key="index"
                    :id="'answer'+item.code+index"
                    @click.native="jump(item.code+index)"
                  >{{ index }}
                  </el-button>
                </el-collapse-item>
              </el-collapse>
        </div>
      </div>
          <div class="paper-footer">
            <el-button
              v-if="type===1"
              type="success"
              @click.native="btnClick('handPaper')"
            >交卷
            </el-button>
            <el-button
              v-if="type===2"
              type="success"
              @click.native="btnClick('readPaper')"
            >阅卷
            </el-button>
            <el-button
              v-if="type===2"
              type="success"
              @click.native="btnClick('readPaperUpper')"
            >上一个
            </el-button>
            <el-button
              v-if="type===2"
              type="success"
              @click.native="btnClick('readPaperNext')"
            >下一个
            </el-button>
    </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
  name: "examinationPaper",
  props: {
    //试卷类型 1 考试 2 阅卷 3 查看
    type: {
      type: Number,
      default: 2,
    },
    //数据源
    dataSource: {
      type: Object,
      default: () => {
    return {
      formLabelAlign: {
        type: "",
        user: "",
        region: "",
      },
      tableData: [
          //试卷ID
          paperId: "",
          //试卷名称
          paperName: "",
          //考生ID
          examineId: "",
          //考生名称
          examineName: "",
          //分数
          score: null,
          //考试时长(分钟)
          examDuration: null,
          //交卷时间
          submissionTime: "",
          //题目集合
          list: [
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
              //题目类型 1.单选题 2.多选题 3.判断题 4.填空题 5.简答题
              type: null,
              //题号
              no: null,
              //题目
              subject: "",
              //题目总分
              totalScore: null,
              //答案集合
              answers: [
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
        },
        {
          // 选择
          isChoice: false,
          //   题目名称
          title: "秋梨膏",
          type: "判断题",
          subject: "语文",
          score: "1",
          reference: "A",
                  //答案序号
                  no: "",
                  //答案
                  answer: "",
        },
      ],
      //提交按钮
      multipleSelection: [],
              //考生答案
              examineAnswer: null,
              //正确答案
              correctAnswer: null,
              //答案解析
              answerAnalysis: "",
              //是否对错  1.对 2.错
              isHook: null,
              //得分
              score: null,
            },
          ],
    };
      },
    },
  },
  data() {
    return {
      //倒计小时
      hour: "",
      //倒计分钟
      minute: "",
      //倒计秒
      second: "",
      //计时器
      promiseTimer: "",
      //数据源
      tempDataSource: {},
      //答题卡激活项
      answerCardActiveName: [],
      //组装后数据集
      convertDatas: [],
      //禁止答题
      disabledAnswer: false,
      //禁止阅卷
      disabledRead: false,
    };
  },
  watch: {
    dataSource(newValue, oldValue) {
      Object.assign(this.tempDataSource, newValue);
      this.convertData();
    },
  },
  created() {
    Object.assign(this.tempDataSource, this.dataSource);
    this.convertData();
    if (this.type === 2) {
      this.disabledAnswer = true;
    }
    if (this.type === 3) {
      this.disabledAnswer = true;
      this.disabledRead = true;
    }
  },
  computed: {
    hourString() {
      return this.hour < 10 ? "0" + this.hour : "" + this.hour;
    },
    minuteString() {
      return this.minute < 10 ? "0" + this.minute : "" + this.minute;
    },
    secondString() {
      return this.second < 10 ? "0" + this.second : "" + this.second;
    },
  },
  mounted() {
    if (this.type === 1) {
      let remainTime = this.dataSource.examDuration * 60;
      if (remainTime > 0) {
        this.hour = Math.floor((remainTime / 3600) % 24);
        this.minute = Math.floor((remainTime / 60) % 60);
        this.second = Math.floor(remainTime % 60);
        this.countDowm();
      }
    }
    if (this.type === 2 || this.type === 3) {
      this.convertDatas.forEach((t) => {
        t.childs.forEach((c) => {
          this.answerButtionCheck(c.examineAnswer, t, c);
        });
      });
    }
  },
  methods: {
    // 返回上一个页面
    goBack() {
      this.$router.back();
    },
    //提交按钮
    onSubmit() {
      console.log("submit!");
    /**
     * 按钮点击事件
     */
    btnClick(type) {
      console.log(this.tempDataSource);
      switch (type) {
        //交卷
        case "handPaper":
          this.$emit("PaperHand", this.tempDataSource);
          break;
        //阅卷
        case "readPaper":
          this.$emit("paperRead", this.tempDataSource);
          break;
        //阅卷 上一个
        case "readPaperUpper":
          this.$emit("paperReadUpper");
          break;
        //阅卷 下一个
        case "readPaperNext":
          this.$emit("paperReadNext");
          break;
      }
    },
    // 修改表单头部的颜色
    getRowClass() {
      return "background:#d2d3d6";
    /**
     * 锚点定位
     */
    // 这儿就是定位
    jump(postion) {
      console.log("postion", postion);
      let jump = this.$refs.paperContent.querySelectorAll("#" + postion);
      // 获取需要滚动的距离
      // 父盒子到浏览器顶部的距离
      let subjectTitleTop = this.$refs.paperContent.offsetTop;
      // 对应的元素到浏览器顶部的距离
      let total = jump[0].offsetTop;
      //实现form锚点定位(使用当前距离减去父元素到顶部距离)
      this.$refs.paperContent.scrollTop = total - subjectTitleTop;
    },
    // 选择
    handleSelectionChange(val) {
    /**
     *对错选择
     */
    isHookButtionCheck(val) {
      console.log(val, "val");
      this.multipleSelection = val;
      if (val.type === 1 || val.type === 2 || val.type === 3) {
        if (val.isHook === 1) {
          val.score = val.totalScore;
        }
        if (val.isHook === 2) {
          val.score = 0;
        }
      }
    },
    /**
     *答题卡选中
     */
    answerButtionCheck(value, parent, child) {
      console.log(value, parent, child);
      let answerId = "answer" + parent.code + child.no;
      let but = this.$refs.paperLeft.querySelectorAll("#" + answerId);
      if (but.length > 0) {
        if (but[0].className.indexOf("answer-button-check") > -1) {
          if (child.examineAnswer && child.examineAnswer.length == 0) {
            but[0].classList.remove("answer-button-check");
          }
        } else {
          if (child.examineAnswer && child.examineAnswer.length > 0) {
            but[0].classList.add("answer-button-check");
          }
        }
      }
    },
    /**
     * 转换答案
     */
    converAnswerStr(answer) {
      if (answer instanceof Array) {
        return answer.join("  ");
      }
      return answer;
    },
    /**
     * 转换数据
     */
    convertData() {
      let sorted = this.groupBy(this.tempDataSource.list, function (item) {
        return [item.type];
      });
      this.convertDatas = [];
      this.answerCardActiveName = [];
      this.orderBy(sorted, "key", "asc");
      sorted.forEach((item) => {
        let totalScore = 0;
        item.value.forEach((t) => {
          totalScore += t.totalScore;
        });
        switch (item.key) {
          case "[1]":
            this.convertDatas.push({
              name: "单选题",
              code: "Single",
              count: item.value.length,
              totalScore: totalScore,
              childs: item.value,
            });
            this.answerCardActiveName.push("Single");
            break;
          case "[2]":
            this.convertDatas.push({
              name: "多选题",
              code: "Multiple",
              count: item.value.length,
              totalScore: totalScore,
              childs: item.value,
            });
            this.answerCardActiveName.push("Multiple");
            break;
          case "[3]":
            this.convertDatas.push({
              name: "判断题",
              code: "Judgment",
              count: item.value.length,
              totalScore: totalScore,
              childs: item.value,
            });
            this.answerCardActiveName.push("Judgment");
            break;
          case "[4]":
            this.convertDatas.push({
              name: "填空题",
              code: "Blank",
              count: item.value.length,
              totalScore: totalScore,
              childs: item.value,
            });
            this.answerCardActiveName.push("Blank");
            break;
          case "[5]":
            this.convertDatas.push({
              name: "简答题",
              code: "Answer",
              count: item.value.length,
              totalScore: totalScore,
              childs: item.value,
            });
            this.answerCardActiveName.push("Answer");
            break;
        }
      });
      console.log(this.convertDatas);
    },
    /**
     * 排序
     * @param {} datas 数组
     * @param {} col 列
     * @param {} type 类型 desc,asc
     * @returns {}
     */
    orderBy(datas, col, type) {
      let m;
      for (let i = 0; i < datas.length; i++) {
        for (let k = 0; k < datas.length; k++) {
          if (type === "asc") {
            if (datas[i][col] < datas[k][col]) {
              m = datas[k];
              datas[k] = datas[i];
              datas[i] = m;
            }
          } else if (type === "desc") {
            if (datas[i][col] > datas[k][col]) {
              m = datas[k];
              datas[k] = datas[i];
              datas[i] = m;
            }
          }
        }
      }
      return datas;
    },
    /**
     * 分组
     * @param array 数据集
     * @param f 函数
     * let sorted = groupBy(list, function(item){ return [item.name];});
     */
    groupBy(array, f) {
      const groups = {};
      const keyValues = [];
      array.forEach(function (o) {
        const group = JSON.stringify(f(o));
        groups[group] = groups[group] || [];
        groups[group].push(o);
      });
      Object.keys(groups).map(function (group) {
        return keyValues.push({key: group, value: groups[group]});
      });
      return keyValues;
    },
    /**
     * 倒计时
     */
    countDowm() {
      let self = this;
      clearInterval(this.promiseTimer);
      this.promiseTimer = setInterval(function () {
        if (self.hour === 0 && self.minute === 0 && self.second === 0) {
          self.disabledAnswer = true;
        }
        if (self.hour === 0) {
          if (self.minute !== 0 && self.second === 0) {
            self.second = 59;
            self.minute -= 1;
          } else if (self.minute === 0 && self.second === 0) {
            self.second = 0;
            self.$emit("countDowmEnd", true);
            clearInterval(self.promiseTimer);
          } else {
            self.second -= 1;
          }
        } else {
          if (self.minute !== 0 && self.second === 0) {
            self.second = 59;
            self.minute -= 1;
          } else if (self.minute === 0 && self.second === 0) {
            self.hour -= 1;
            self.minute = 59;
            self.second = 59;
          } else {
            self.second -= 1;
          }
        }
      }, 1000);
    },
  },
};
</script>
<style scoped lang="scss">
.flex {
  display: flex;
}
// 内容
.content {
  width: 1262px;
  margin-bottom: 80px;
  background-color: #fff;
  padding: 20px 40px;
<style lang="scss" scoped>
.main {
  width: 1227px;
  background: white;
  box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, 0.1);
  border-radius: 10px;
  padding: 32px 40px 0 40px;
}
.paper-main {
  overflow: hidden;
  .paper-header {
    width: 100%;
    height: 60px;
    background-color: #f7f7f7;
    position: absolute;
    top: 0;
    z-index: 1000;
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1);
    -webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1);
  }
  .paper-content {
    display: flex;
    // margin-top: 60px;
    .paper-left {
      flex: 1;
      padding: 10px;
      overflow-x: hidden;
      overflow-y: auto;
      border: 1px solid #e4e4e4;
      border-top: none;
      height: 580px;
    }
    .paper-right {
      height: 580px;
      width: 300px;
      overflow-x: hidden;
      overflow-y: auto;
      box-sizing: border-box;
      padding: 10px;
      border: 1px solid #e4e4e4;
      border-top: none;
    }
  }
}
.paper-footer {
  line-height: 60px;
  overflow: hidden;
  box-sizing: border-box;
  box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
  -webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
  text-align: center;
}
.paper-title {
  padding-left: 10px;
  width: 100%;
  height: 45px;
  line-height: 45px;
  /* background: #f7f7f7; */
}
.paper-title h1 {
  font-size: 1.2em;
  margin: 0;
}
.downTime {
  color: rgb(230, 93, 110);
  font-size: 24px;
  font-weight: bold;
  float: right;
  line-height: 1em;
}
.answer-button {
  padding: 0px;
  color: #0a0a0a;
  /* background-color: #ffffff; */
  border-color: #e4e4e4;
  margin-left: 10px;
  width: 30px;
  height: 30px;
}
.answer-button:hover {
  /* background: #ecf1ef; */
  border-color: #e4e4e4;
  color: #0a0a0a;
}
.answer-button-check {
  background: #13ce66;
  border-color: #30b08f;
}
// 单选样式
.answer-radio {
  display: list-item;
  margin: 5px 0px;
  list-style: none;
}
// 多选样式
.answer-checkbox {
  display: list-item;
  margin: 5px 0px;
  list-style: none;
}
.subject-title {
  padding-left: 10px;
  width: 100%;
  height: 45px;
  line-height: 45px;
  /* background: #f7f7f7; */
  box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
  -webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
}
.subject-title h2 {
  font-size: 16px;
  display: inline-block;
}
.subject-title span {
  font-size: 16px;
  display: inline-block;
}
.subject-remark {
  /* background: #f7f7f7; */
}
.subject-remark .item {
  display: block;
  padding: 5px;
}
.subject-remark .title {
  font-weight: bold;
}
.el-radio > > > .el-radio__input.is-checked .el-radio__inner {
  background-color: #13ce66;
  border-color: #13ce66;
}
.el-radio-button > > > .el-radio-button__inner {
  padding: 10px;
}
.el-collapse-item h2 {
  width: 150px;
  font-size: 14px;
  display: inline-block;
}
.el-form--label-top > > > .el-form-item__label {
  float: none;
  display: inline-block;
  text-align: left;
  padding: 0px;
}
.el-card {
  margin: 10px;
}
.el-card > > > .el-card__header {
  /* background-color: #ffffff; */
  padding: 0px 10px;
  line-height: 35px;
  font-size: 16px;
}
.el-card > > > .el-card__body {
  padding: 5px 20px;
}
</style>