|
@@ -25,7 +25,7 @@
|
|
|
<div class="rich">
|
|
|
<RichText
|
|
|
ref="modelEssay"
|
|
|
- v-model="data.option_list[i - 1][j - 1].text"
|
|
|
+ v-model="data.option_list[j - 1][i - 1].text"
|
|
|
:is-fill="true"
|
|
|
:toolbar="false"
|
|
|
:font-size="12"
|
|
@@ -107,15 +107,15 @@
|
|
|
import QuestionMixin from '../common/QuestionMixin.js';
|
|
|
|
|
|
import { getRandomNumber } from '@/utils';
|
|
|
-import { getFillFormData, getOption } from '@/views/exercise_questions/data/fillForm';
|
|
|
+import { getTableFillData, getOption } from '@/views/exercise_questions/data/tableFill.js';
|
|
|
|
|
|
export default {
|
|
|
- name: 'FillFormQuestion',
|
|
|
+ name: 'TableFillQuestion',
|
|
|
mixins: [QuestionMixin],
|
|
|
data() {
|
|
|
return {
|
|
|
isMask: false,
|
|
|
- data: getFillFormData(),
|
|
|
+ data: getTableFillData(),
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
@@ -126,18 +126,17 @@ export default {
|
|
|
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;
|
|
|
- });
|
|
|
+ if (val > this.data.option_list.length) {
|
|
|
+ this.data.option_list.push(Array.from({ length: this.data.property.column_number }, () => getOption()));
|
|
|
+ } else if (val < this.data.option_list.length) {
|
|
|
+ this.data.option_list.pop();
|
|
|
+ }
|
|
|
},
|
|
|
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) => {
|
|
@@ -148,27 +147,35 @@ export default {
|
|
|
});
|
|
|
this.data.option_header_list.push({ mark: getRandomNumber(), text: '', width });
|
|
|
} else if (val < this.data.option_header_list.length) {
|
|
|
+ let width = this.data.option_header_list[val].width;
|
|
|
this.data.option_header_list.pop();
|
|
|
+ this.data.option_header_list.forEach((item) => {
|
|
|
+ item.width += width / val;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- 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();
|
|
|
- }
|
|
|
+ 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,
|
|
|
},
|
|
|
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);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if (newVal.length !== oldVal.length || newVal[0].length !== oldVal[0].length) return;
|
|
|
+ newVal.forEach((item, i) => {
|
|
|
+ item.forEach((li, j) => {
|
|
|
+ let newValue = newVal[i][j];
|
|
|
+ let oldValue = oldVal[i][j];
|
|
|
+ if (newValue.text === oldValue.text) return;
|
|
|
+ this.handleOptionChange(newValue, oldValue, i, j);
|
|
|
+ });
|
|
|
+ });
|
|
|
},
|
|
|
immediate: true,
|
|
|
deep: true,
|
|
@@ -216,6 +223,112 @@ export default {
|
|
|
width: `${this.data.property.form_width}px`,
|
|
|
};
|
|
|
},
|
|
|
+ /**
|
|
|
+ * 处理选项内容变化
|
|
|
+ * @param {object} newValue 新值
|
|
|
+ * @param {object} oldValue 旧值
|
|
|
+ * @param {number} i 选项列表的索引
|
|
|
+ * @param {number} j 选项列表中的索引
|
|
|
+ */
|
|
|
+ handleOptionChange(newValue, oldValue, i, j) {
|
|
|
+ let arr = newValue.text.split(/<p>(.*?)<\/p>/gi).filter((item) => item);
|
|
|
+ // 答案数组中的索引
|
|
|
+ let answerIndex = this.data.answer.answer_list.findIndex(({ mark }) => mark === newValue.mark);
|
|
|
+ if (arr.length === 0) {
|
|
|
+ if (answerIndex !== -1) {
|
|
|
+ this.data.answer.answer_list.splice(answerIndex, 1);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let isStart = /^##/.test(arr[0]); // 是否以 ## 开头
|
|
|
+ if (isStart) {
|
|
|
+ this.handleSpecialCharacterStart(arr, answerIndex, newValue.mark, i, j);
|
|
|
+ } else {
|
|
|
+ let str = arr.join().replace(/<span class="rich-fill".*?>(.*?)<\/span>|([_]{3,})/gi, '###$1$2###');
|
|
|
+ this.handleFill(str, answerIndex, newValue.mark, i, j);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 处理填空或其他情况
|
|
|
+ * @param {string} str 处理后的字符串
|
|
|
+ * @param {number} answerIndex 答案数组的索引
|
|
|
+ * @param {string} newValueMark 新值的标识
|
|
|
+ * @param {number} i 选项列表的索引
|
|
|
+ * @param {number} j 选项列表中的索引
|
|
|
+ */
|
|
|
+ handleFill(str, answerIndex, newValueMark, i, j) {
|
|
|
+ let _str = str;
|
|
|
+ let start = 0;
|
|
|
+ let index = 0;
|
|
|
+ let arr = [];
|
|
|
+ let matchNum = 0;
|
|
|
+ while (index !== -1) {
|
|
|
+ index = _str.indexOf('###', start);
|
|
|
+ if (index === -1) break;
|
|
|
+ matchNum += 1;
|
|
|
+ arr.push({ content: _str.slice(start, index), type: 'text', mark: '' });
|
|
|
+ if (matchNum % 2 === 0 && arr.length > 0) {
|
|
|
+ arr[arr.length - 1].type = 'input';
|
|
|
+ let mark = getRandomNumber();
|
|
|
+ arr[arr.length - 1].mark = mark;
|
|
|
+ }
|
|
|
+ start = index + 3;
|
|
|
+ }
|
|
|
+ let last = _str.slice(start);
|
|
|
+ if (last) {
|
|
|
+ arr.push({ content: last, type: 'text' });
|
|
|
+ }
|
|
|
+ let value_list = arr
|
|
|
+ .filter(({ type }) => type === 'input')
|
|
|
+ .map(({ content, mark }) => ({ value: content, mark }));
|
|
|
+ if (answerIndex === -1 && value_list.length > 0) {
|
|
|
+ this.data.answer.answer_list.push({
|
|
|
+ value_list,
|
|
|
+ mark: newValueMark,
|
|
|
+ });
|
|
|
+ } else if (answerIndex !== -1) {
|
|
|
+ this.data.answer.answer_list[answerIndex].value_list = value_list;
|
|
|
+ }
|
|
|
+ this.data.option_list[i][j].content_list = arr.map(({ content, type, mark }) => {
|
|
|
+ return {
|
|
|
+ type,
|
|
|
+ mark,
|
|
|
+ content: type === 'input' ? '' : content,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 处理特殊字符开头
|
|
|
+ * @param {array} arr 文本数组
|
|
|
+ * @param {number} answerIndex 答案数组的索引
|
|
|
+ * @param {string} newValueMark 新值的标识
|
|
|
+ * @param {number} i 选项列表的索引
|
|
|
+ * @param {number} j 选项列表中的索引
|
|
|
+ */
|
|
|
+ handleSpecialCharacterStart(arr, answerIndex, newValueMark, i, j) {
|
|
|
+ // 去除第一个元素的 ##
|
|
|
+ arr[0] = arr[0].slice(2);
|
|
|
+ let _arr = arr.map((item) => {
|
|
|
+ return item.replace(/<span class="rich-fill".*?>(.*?)<\/span>|([_]{3,})/gi, '$1$2');
|
|
|
+ });
|
|
|
+ let mark = getRandomNumber();
|
|
|
+
|
|
|
+ if (answerIndex === -1) {
|
|
|
+ this.data.answer.answer_list.push({
|
|
|
+ value_list: [{ mark, value: _arr.join('') }],
|
|
|
+ mark: newValueMark,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.data.answer.answer_list[answerIndex].value_list = [{ mark, value: _arr.join('') }];
|
|
|
+ }
|
|
|
+ this.data.option_list[i][j].content_list = [
|
|
|
+ {
|
|
|
+ type: 'fill',
|
|
|
+ mark,
|
|
|
+ content: '',
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ },
|
|
|
},
|
|
|
};
|
|
|
</script>
|