|
@@ -1,11 +1,50 @@
|
|
|
<template>
|
|
|
<QuestionBase>
|
|
|
<template #content>
|
|
|
+ <div v-show="isMask" class="mask"></div>
|
|
|
<div class="stem">
|
|
|
<RichText v-model="data.stem" :font-size="18" placeholder="输入题干" />
|
|
|
</div>
|
|
|
|
|
|
- <div class="content"></div>
|
|
|
+ <div class="content">
|
|
|
+ <div class="option-wrapper" :style="computedOptionStyle()">
|
|
|
+ <template v-for="i in data.property.column_number">
|
|
|
+ <div :key="i" class="fill-form">
|
|
|
+ <!-- 头部 -->
|
|
|
+ <span class="form-header">
|
|
|
+ <span v-if="i === 1 && isEnable(data.property.is_enable_number_column)" class="header-serial-number">
|
|
|
+ #
|
|
|
+ </span>
|
|
|
+ <el-input v-model="data.option_header_list[i - 1].text" placeholder="请输入" />
|
|
|
+ </span>
|
|
|
+
|
|
|
+ <span v-for="j in data.property.row_number" :key="j" :class="['form-item', `form-item-${i}-${j}`]">
|
|
|
+ <span v-if="i === 1 && isEnable(data.property.is_enable_number_column)" class="serial-number">
|
|
|
+ {{ j }}
|
|
|
+ </span>
|
|
|
+ <div class="rich">
|
|
|
+ <RichText
|
|
|
+ ref="modelEssay"
|
|
|
+ v-model="data.option_list[i - 1][j - 1].text"
|
|
|
+ :is-fill="true"
|
|
|
+ :toolbar="false"
|
|
|
+ :font-size="12"
|
|
|
+ :wordlimit-num="false"
|
|
|
+ placeholder="请输入"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <span
|
|
|
+ v-if="i !== data.property.column_number"
|
|
|
+ :key="`resize-${i}`"
|
|
|
+ class="resize"
|
|
|
+ @mousedown="resize($event, i)"
|
|
|
+ ></span>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <div class="tips">在输入框最前插入##,代表输入框中内容作为正确答案在作答时隐藏。</div>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<template #property>
|
|
@@ -40,6 +79,25 @@
|
|
|
:step="data.property.score_type === scoreTypeList[0].value ? 1 : 0.1"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
+ <el-form-item label="序号列">
|
|
|
+ <el-radio
|
|
|
+ v-for="{ value, label } in switchOption"
|
|
|
+ :key="value"
|
|
|
+ v-model="data.property.is_enable_number_column"
|
|
|
+ :label="value"
|
|
|
+ >
|
|
|
+ {{ label }}
|
|
|
+ </el-radio>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="列表宽度">
|
|
|
+ <el-input-number v-model="data.property.form_width" :min="100" :max="3000" :step="100" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="行数">
|
|
|
+ <el-input-number v-model="data.property.row_number" :min="1" :max="30" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="列数">
|
|
|
+ <el-input-number v-model="data.property.column_number" :min="2" :max="5" />
|
|
|
+ </el-form-item>
|
|
|
</el-form>
|
|
|
</template>
|
|
|
</QuestionBase>
|
|
@@ -48,18 +106,210 @@
|
|
|
<script>
|
|
|
import QuestionMixin from '../common/QuestionMixin.js';
|
|
|
|
|
|
-import { getFillFormData } from '@/views/exercise_questions/data/fillForm';
|
|
|
+import { getRandomNumber } from '@/utils';
|
|
|
+import { getFillFormData, getOption } from '@/views/exercise_questions/data/fillForm';
|
|
|
|
|
|
export default {
|
|
|
name: 'FillFormQuestion',
|
|
|
mixins: [QuestionMixin],
|
|
|
data() {
|
|
|
return {
|
|
|
+ isMask: false,
|
|
|
data: getFillFormData(),
|
|
|
};
|
|
|
},
|
|
|
- methods: {},
|
|
|
+ computed: {
|
|
|
+ optionList() {
|
|
|
+ return JSON.parse(JSON.stringify(this.data.option_list));
|
|
|
+ },
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ 'data.property.row_number': {
|
|
|
+ handler(val) {
|
|
|
+ this.data.option_list = this.data.option_list.map((item) => {
|
|
|
+ const arr = [];
|
|
|
+ for (let i = 0; i < val; i++) {
|
|
|
+ arr.push(item[i] || getOption());
|
|
|
+ }
|
|
|
+ return arr;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ immediate: true,
|
|
|
+ },
|
|
|
+ 'data.property.column_number': {
|
|
|
+ handler(val) {
|
|
|
+ if (val > this.data.option_header_list.length) {
|
|
|
+ let width = 0;
|
|
|
+ this.data.option_header_list.forEach((item) => {
|
|
|
+ // 平均分配宽度,最小宽度为10
|
|
|
+ let w = Math.max((item.width / val) * this.data.option_header_list.length, 10);
|
|
|
+ width += item.width - w;
|
|
|
+ item.width = w;
|
|
|
+ });
|
|
|
+ this.data.option_header_list.push({ mark: getRandomNumber(), text: '', width });
|
|
|
+ } else if (val < this.data.option_header_list.length) {
|
|
|
+ this.data.option_header_list.pop();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (val > this.data.option_list.length) {
|
|
|
+ this.data.option_list.push(Array.from({ length: this.data.property.row_number }, () => getOption()));
|
|
|
+ } else if (val < this.data.option_list.length) {
|
|
|
+ this.data.option_list.pop();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ immediate: true,
|
|
|
+ },
|
|
|
+ optionList: {
|
|
|
+ handler(newVal, oldVal) {
|
|
|
+ if (newVal.length === 0 || !oldVal) return;
|
|
|
+ for (let i = 0; i < newVal.length; i++) {
|
|
|
+ for (let j = 0; j < newVal[i].length; j++) {
|
|
|
+ if (newVal[i][j].text !== oldVal[i][j].text) {
|
|
|
+ console.log('text属性发生变化:', newVal[i][j].text);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ immediate: true,
|
|
|
+ deep: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ resize(e, i) {
|
|
|
+ let target = e.target;
|
|
|
+ let startX = e.clientX;
|
|
|
+ this.isMask = true;
|
|
|
+
|
|
|
+ // 鼠标拖动事件
|
|
|
+ document.onmousemove = (e) => {
|
|
|
+ const endX = e.clientX;
|
|
|
+ let list = this.data.option_header_list;
|
|
|
+ // 计算拖动的距离与总宽度的比例
|
|
|
+ let w = ((endX - startX) / this.data.property.form_width) * 100;
|
|
|
+ // 限制最小宽度为10,最大宽度为总宽度-10
|
|
|
+ list[i - 1].width = Math.min(Math.max(10, list[i - 1].width + w), list[i - 1].width + list[i].width - 10);
|
|
|
+ list[i].width = Math.min(Math.max(10, list[i].width - w), list[i - 1].width + list[i].width - 10);
|
|
|
+
|
|
|
+ startX = endX;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 鼠标松开事件
|
|
|
+ document.onmouseup = () => {
|
|
|
+ this.isMask = false;
|
|
|
+ document.onmousemove = null;
|
|
|
+ document.onmouseup = null;
|
|
|
+ target.releaseCapture && target.releaseCapture(); // 当不在需要继续获得鼠标消息就要应该调用 ReleaseCapture() 释放掉
|
|
|
+ };
|
|
|
+ target.setCapture && target.setCapture(); // 该函数在属于当前线程的指定窗口里设置鼠标捕获
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ computedOptionStyle() {
|
|
|
+ let gridTemplateColumns = this.data.option_header_list.reduce((acc, { width }, i) => {
|
|
|
+ if (i === this.data.option_header_list.length - 1) {
|
|
|
+ return `${acc}${width}%`;
|
|
|
+ }
|
|
|
+ return `${acc}${width}% 2px `;
|
|
|
+ }, '');
|
|
|
+
|
|
|
+ return {
|
|
|
+ gridTemplateColumns,
|
|
|
+ width: `${this.data.property.form_width}px`,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
-<style lang="scss" scoped></style>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.mask {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ z-index: 1;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ cursor: col-resize;
|
|
|
+}
|
|
|
+
|
|
|
+.content {
|
|
|
+ width: calc(100vw - 645px);
|
|
|
+ overflow: auto;
|
|
|
+
|
|
|
+ .option-wrapper {
|
|
|
+ display: grid;
|
|
|
+ grid-auto-flow: column;
|
|
|
+
|
|
|
+ .fill-form {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .form-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 40px;
|
|
|
+ background-color: $fill-color;
|
|
|
+
|
|
|
+ .header-serial-number {
|
|
|
+ padding: 8px 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .el-input__inner {
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-item {
|
|
|
+ display: flex;
|
|
|
+ height: 55px;
|
|
|
+ border-bottom: $border;
|
|
|
+
|
|
|
+ .serial-number {
|
|
|
+ width: 40px;
|
|
|
+ min-width: 40px;
|
|
|
+ line-height: 55px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .rich {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .tox-tinymce {
|
|
|
+ border-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .tox .tox-sidebar-wrap {
|
|
|
+ border-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .tox-editor-header {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .resize {
|
|
|
+ width: 2px;
|
|
|
+ height: 100%;
|
|
|
+ cursor: col-resize;
|
|
|
+ background-color: #165dff;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .tips {
|
|
|
+ margin-top: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.property {
|
|
|
+ .el-input-number {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|