| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- <template>
- <ModuleBase ref="base" :type="data.type">
- <template #content>
- <!-- <div class="fun-type">
- <a
- v-for="{ value, label } in tableTypeList"
- :key="value"
- :class="[data.mode === value ? 'active' : '']"
- @click="data.mode = value"
- >{{ label }}</a
- >
- </div> -->
- <div class="table-rich-toolbar">
- <el-select
- v-model="data.styles.fontFamily"
- placeholder="请选择"
- style="width: 130px"
- @change="BatchSetFormat('fontFamily', data.styles.fontFamily)"
- >
- <el-option v-for="{ value, label } in fontFamilyList" :key="value" :label="label" :value="value" />
- </el-select>
- <el-select
- v-model="data.styles.fontSize"
- placeholder="请选择"
- style="width: 130px"
- @change="BatchSetFormat('fontSize', data.styles.fontSize)"
- >
- <el-option
- v-for="(value, index) in 16"
- :key="index"
- :label="6 + value * 2 + 'pt'"
- :value="6 + value * 2 + 'pt'"
- />
- </el-select>
- <el-form :model="property" inline>
- <el-form-item label="文字颜色">
- <el-color-picker v-model="data.styles.fontColor" @change="BatchSetFormat('color', data.styles.fontColor)" />
- </el-form-item>
- <el-form-item label="背景色">
- <el-color-picker v-model="data.styles.bgColor" />
- </el-form-item>
- </el-form>
- <span
- :class="[data.styles.isUnderline ? 'active' : '']"
- @click="
- data.styles.isUnderline = !data.styles.isUnderline;
- BatchSetFormat('underline');
- "
- >
- <SvgIcon icon-class="underline" size="20" />
- </span>
- <span
- :class="[data.styles.isBold ? 'active' : '']"
- @click="
- data.styles.isBold = !data.styles.isBold;
- BatchSetFormat('bold');
- "
- >
- <SvgIcon icon-class="font-bold" size="20" />
- </span>
- <span
- :class="[data.styles.isItalic ? 'active' : '']"
- @click="
- data.styles.isItalic = !data.styles.isItalic;
- BatchSetFormat('italic');
- "
- >
- <SvgIcon icon-class="font-italic" size="20" />
- </span>
- <span
- :class="[data.styles.isStrikethrough ? 'active' : '']"
- @click="
- data.styles.isStrikethrough = !data.styles.isStrikethrough;
- BatchSetFormat('strikethrough');
- "
- >
- <SvgIcon icon-class="strikethrough" size="20" />
- </span>
- <span
- :class="[data.styles.textAlign === 'LEFT' ? 'active' : '']"
- @click="
- data.styles.textAlign = 'LEFT';
- BatchSetFormat('align', 'LEFT');
- "
- >
- <SvgIcon icon-class="align-left" size="20" />
- </span>
- <span
- :class="[data.styles.textAlign === 'MIDDLE' ? 'active' : '']"
- @click="
- data.styles.textAlign = 'MIDDLE';
- BatchSetFormat('align', 'MIDDLE');
- "
- >
- <SvgIcon icon-class="align-center" size="20" />
- </span>
- <span
- :class="[data.styles.textAlign === 'RIGHT' ? 'active' : '']"
- @click="
- data.styles.textAlign = 'RIGHT';
- BatchSetFormat('align', 'RIGHT');
- "
- >
- <SvgIcon icon-class="align-right" size="20" />
- </span>
- </div>
- <div class="option-list">
- <div v-for="(item, i) in data.option_list" :key="i" class="table-node">
- <el-color-picker v-model="data.rows_bg_list[i]" />
- <div v-for="(li, j) in item" :key="li.mark" class="table-item">
- <!-- eslint-disable max-len -->
- <RichText
- v-if="property.isGetContent"
- ref="richText"
- v-model="li.content"
- :font-size="data?.unified_attrib?.font_size"
- :font-family="data?.unified_attrib?.font"
- :font-color="data?.unified_attrib?.text_color"
- :inline="true"
- :item-index="i + '#' + j"
- toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright image media link"
- @handleRichTextBlur="handleBlurCon"
- />
- <el-input v-else v-model="li.content" @blur="handleBlurCon" />
- <el-button size="mini" @click="editCell(li.cell)">配置</el-button>
- </div>
- </div>
- <div class="table-node">
- <div style="width: 32px"></div>
- <div v-for="(value, index) in data.col_width" :key="index" class="table-item table-items">
- <el-color-picker v-model="data.cols_bg_list[index]" />
- <el-input v-model="value.value">
- <template slot="prepend">列宽:</template><template slot="append">%</template></el-input
- >
- </div>
- </div>
- </div>
- <el-input
- v-if="data.property.fill_type === fillTypeList[1].value"
- v-model="data.vocabulary"
- type="textarea"
- :autosize="{ minRows: 2, maxRows: 4 }"
- resize="none"
- placeholder="请输入词汇,用于选词填空"
- />
- <p class="tips">在需要作答的单元格内输入三个以上下划线“___”</p>
- <el-button @click="identifyText()">识别</el-button>
- <el-button @click="handleMultilingual">多语言</el-button>
- <template v-if="isEnable(data.has_identify)">
- <p class="tips">在需要作答的单元格内录入标准答案,多个填空答案用换行录入,同一个填空有多个答案用斜线“/”隔开</p>
- <div class="option-list">
- <div v-for="(item, i) in data.answer_lists" :key="i" class="table-node">
- <div v-for="(li, j) in item" :key="i + 'col' + j" class="table-item">
- <el-input v-model="li.answer" type="textarea" :readonly="!data.option_list[i][j].hasInput" />
- <span
- v-if="data.option_list[i][j].cell.isCross"
- style="display: flex; align-items: center; font-size: 14px"
- >勾叉答案<i
- :class="[
- { 'el-icon-check': li.crossAnswer === statusList[1] },
- { 'el-icon-close': li.crossAnswer === statusList[2] },
- ]"
- style="display: block; width: 14px; height: 14px; border: 1px solid #000"
- @click="toggle(li)"
- ></i
- ></span>
- </div>
- </div>
- </div>
- </template>
- <AnswerAnalysisList
- v-if="data.answer_list?.length > 0 || data.analysis_list?.length > 0"
- :answer-list="data.answer_list"
- :analysis-list="data.analysis_list"
- :unified-attrib="data.unified_attrib"
- @updateAnswerAnalysisFileList="updateAnswerAnalysisFileList"
- @deleteAnswerAnalysis="deleteAnswerAnalysis"
- />
- <MultilingualFill
- :visible.sync="multilingualVisible"
- :text="multilingualText"
- :translations="data.multilingual"
- @SubmitTranslation="handleMultilingualTranslation"
- />
- <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">
- 拼音效果
- <el-button
- v-show="isEnable(data.property.view_pinyin)"
- type="text"
- icon="el-icon-refresh"
- title="刷新"
- class="refresh-pinyin-btn"
- @click.native="identifyText()"
- /></el-divider>
- <template v-if="isEnable(data.property.view_pinyin)">
- <template v-for="(item, index) in data.option_list">
- <PinyinText
- v-for="(items, indexs) in item"
- :id="'table_pinyin_text_' + index + '_' + indexs"
- :key="index + '_' + indexs"
- ref="PinyinText"
- :rich-text-list="items.rich_text_list"
- :pinyin-position="data.property.pinyin_position"
- :pinyin-size="data?.unified_attrib?.pinyin_size"
- :font-size="data?.unified_attrib?.font_size"
- :font-family="data?.unified_attrib?.font"
- @fillCorrectPinyin="fillCorrectPinyin"
- />
- </template>
- </template>
- <el-dialog
- v-if="editCellFlag"
- title="配置单元格"
- :visible="editCellFlag"
- width="460px"
- :close-on-click-modal="false"
- @close="editCellFlag = false"
- >
- <el-form :model="activeCell" :inline="true">
- <el-form-item label="背景色">
- <el-color-picker v-model="activeCell.bgColor" />
- </el-form-item>
- <el-form-item label="勾叉">
- <el-switch v-model="activeCell.isCross" />
- </el-form-item>
- <el-form-item label="合并行">
- <el-input v-model="activeCell.rowspan" class="rowspan" type="number" />
- </el-form-item>
- <el-form-item label="合并列">
- <el-input v-model="activeCell.colspan" class="colspan" type="number" />
- </el-form-item>
- </el-form>
- <div slot="footer">
- <el-button @click="editCellFlag = false">取消</el-button>
- <el-button type="primary" @click="editCellFlag = false">确定</el-button>
- </div>
- </el-dialog>
- </template>
- </ModuleBase>
- </template>
- <script>
- import { isEnable } from '@/views/book/courseware/data/common';
- import ModuleMixin from '../../common/ModuleMixin';
- import { PinyinBuild_OldFormat } from '@/api/book';
- import { getRandomNumber } from '@/utils';
- import PinyinText from '@/components/PinyinText.vue';
- import {
- getTableData,
- getOption,
- tableTypeList,
- fontFamilyList,
- getAnswerOption,
- fillTypeList,
- } from '@/views/book/courseware/data/table';
- export default {
- name: 'TablePage',
- components: {
- PinyinText,
- },
- mixins: [ModuleMixin],
- data() {
- return {
- isEnable,
- data: getTableData(),
- tableTypeList,
- fontFamilyList,
- multilingualText: '',
- isViewExplanatoryNoteDialog: false,
- oldRichData: {},
- fillTypeList,
- activeCell: null,
- editCellFlag: false,
- statusList: ['normal', 'tick', 'cross'],
- editContentIndex: '', // 编辑内容的单元格索引
- };
- },
- watch: {
- 'data.property.row_count': {
- handler(val) {
- if (val < this.data.option_list.length) {
- this.data.option_list = this.data.option_list.slice(0, val);
- this.data.answer_lists = this.data.answer_lists.slice(0, val);
- this.data.rows_bg_list = this.data.rows_bg_list.slice(0, val);
- } else {
- const diff = val - this.data.option_list.length;
- for (let i = 0; i < diff; i++) {
- this.data.option_list.push(Array.from({ length: this.data.property.column_count }, getOption));
- this.data.answer_lists.push(Array.from({ length: this.data.property.column_count }, getAnswerOption));
- this.data.rows_bg_list.push('');
- }
- }
- },
- },
- 'data.property.column_count': {
- handler(val) {
- const diff = val - this.data.option_list[0].length;
- for (let i = 0; i < diff; i++) {
- this.data.col_width.push({
- value: '',
- });
- this.data.cols_bg_list.push('');
- }
- this.data.cols_bg_list = this.data.cols_bg_list.slice(0, val);
- this.data.option_list = this.data.option_list.map((row) => {
- if (val < row.length) {
- this.data.col_width = this.data.col_width.slice(0, val);
- return row.slice(0, val);
- }
- const diff = val - row.length;
- return row.concat(Array.from({ length: diff }, getOption));
- });
- this.data.answer_lists = this.data.answer_lists?.map((row) => {
- if (val < row.length) {
- return row.slice(0, val);
- }
- const diff = val - row.length;
- return row.concat(Array.from({ length: diff }, getAnswerOption));
- });
- },
- },
- 'data.property.view_pinyin': {
- handler(val) {
- let text = '';
- this.data.option_list.forEach((item) => {
- item.forEach((items) => {
- text += `${items.content.replace(/<[^>]+>/g, '')}\n`;
- });
- });
- if (!isEnable(val)) {
- this.data.paragraph_list = [];
- return;
- }
- if (isEnable(val) && text && this.data.paragraph_list.length <= 0) {
- this.data.paragraph_list_parameter.text = text;
- this.data.paragraph_list_parameter.is_first_sentence_first_hz_pinyin_first_char_upper_case =
- this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case;
- this.data.option_list.forEach((item, index) => {
- item.forEach((items, indexs) => {
- this.createParsedTextInfoPinyin(items.content, `${index}#${indexs}`);
- });
- });
- }
- },
- deep: true,
- },
- 'data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case': {
- handler(val, oldVal) {
- if (val === oldVal) return;
- let text = '';
- this.data.option_list.forEach((item) => {
- item.forEach((items) => {
- text += `${items.content.replace(/<[^>]+>/g, '')}\n`;
- });
- });
- if (!isEnable(this.data.property.view_pinyin)) {
- this.data.paragraph_list = [];
- return;
- }
- if (text && isEnable(this.data.property.view_pinyin)) {
- this.data.paragraph_list_parameter.text = text;
- this.data.paragraph_list_parameter.is_first_sentence_first_hz_pinyin_first_char_upper_case = val;
- this.data.option_list.forEach((item, index) => {
- item.forEach((items, indexs) => {
- this.createParsedTextInfoPinyin(items.content, `${index}#${indexs}`);
- });
- });
- }
- },
- deep: true,
- },
- 'data.vocabulary': {
- handler(val) {
- if (!val) return;
- this.data.word_list = val
- .split(/[\n\r]+/)
- .map((item) => item.split(' ').filter((s) => s))
- .flat()
- .map((content) => {
- return {
- content,
- mark: getRandomNumber(),
- };
- });
- },
- },
- 'data.unified_attrib': {
- handler(val) {
- if (val) {
- this.data.styles.fontFamily = this.data?.unified_attrib?.font || '楷体,微软雅黑';
- this.data.styles.fontSize = this.data?.unified_attrib?.font_size || '12pt';
- this.data.styles.fontColor = this.data?.unified_attrib?.text_color || '#1d2129';
- this.data.styles.textAlign = this.data?.unified_attrib?.align || 'LEFT';
- }
- },
- deep: true,
- immediate: true,
- },
- },
- methods: {
- toggle(status) {
- const index = this.statusList.findIndex((item) => status.crossAnswer === item);
- status.crossAnswer = index === this.statusList.length - 1 ? this.statusList[0] : this.statusList[index + 1];
- },
- editCell(cell) {
- this.activeCell = cell;
- this.editCellFlag = true;
- },
- // 设置样式
- BatchSetFormat(type, val = '') {
- this.$refs.richText.forEach((richText) => {
- richText.setRichFormat(type, val);
- });
- },
- // 识别文本
- identifyText(editIndex) {
- let text = '';
- this.data.has_identify = 'true';
- this.data.option_list.forEach((item, index) => {
- item.forEach((items, indexs) => {
- items.model_essay = [];
- // this.data.answer_list[index][indexs].answer_list = [];
- if (items.content) {
- let inputIndex = 0;
- items.content
- .split(/<(p|div)[^>]*>(.*?)<\/(p|div)>/g)
- .filter((s) => s && !s.match(/^(p|div)$/))
- .forEach((itemss) => {
- if (itemss.charCodeAt() === 10) return;
- let strArr = itemss.split(/_{3,}/g);
- strArr.forEach((itemS, index) => {
- items.model_essay.push({
- value: itemS,
- type: 'text',
- });
- if (index !== strArr.length - 1) {
- items.model_essay.push({
- value: '',
- type: 'input',
- mark: getRandomNumber(),
- inputIndex,
- write_base64: '',
- audio_answer_list: [],
- });
- inputIndex += 1;
- items.hasInput = true;
- }
- });
- // // 去除所有的 font-size 样式
- // .replace(/font-size:\s*\d+(\.\d+)?px;/gi, '')
- // // 匹配 class 名为 rich-fill 的 span 标签和三个以上的_,并将它们组成数组
- // .replace(/<span class="rich-fill".*?>(.*?)<\/span>|([_]{3,})/gi, '###$1$2###');
- });
- }
- items.crossAnswer = 'normal';
- // text += `${items.content.replace(/<[^>]+>/g, '')}\n`;
- text += `${items.content}\n`;
- });
- });
- if (isEnable(this.data.property.view_pinyin)) {
- if (editIndex) {
- let arr = editIndex.split('#');
- text = this.data.option_list[arr[0]][arr[1]].content;
- this.createParsedTextInfoPinyin(text, editIndex);
- } else {
- this.data.option_list.forEach((item, index) => {
- item.forEach((items, indexs) => {
- this.createParsedTextInfoPinyin(items.content, `${index}#${indexs}`);
- });
- });
- }
- }
- },
- // 获取拼音解析文本
- createParsedTextInfoPinyin(text, editIndex) {
- if (text === '') {
- this.data.paragraph_list_parameter.pinyin_proofread_word_list = [];
- return;
- }
- // .replace(/<[^>]+>/g, '')
- this.data.paragraph_list_parameter.text = text;
- this.data.paragraph_list_parameter.is_first_sentence_first_hz_pinyin_first_char_upper_case =
- this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case;
- this.data.paragraph_list_parameter.is_rich_text = 'true';
- PinyinBuild_OldFormat(this.data.paragraph_list_parameter).then((res) => {
- if (res.parsed_text) {
- const mergedData = res.parsed_text.paragraph_list.map((outerArr, i) =>
- outerArr.map((innerArr, j) =>
- innerArr.map((newItem, k) => {
- // 从 originalData 中找到对应的项
- const originalItem = this.data.paragraph_list[i]?.[j]?.[k];
- // 如果 originalItem 有 activeTextStyle,就合并到 newItem
- if (originalItem?.activeTextStyle) {
- return {
- ...newItem,
- activeTextStyle: originalItem.activeTextStyle,
- };
- }
- // 否则直接返回 newItem
- return newItem;
- }),
- ),
- );
- this.data.paragraph_list = mergedData;
- if (editIndex) {
- let arr = editIndex.split('#');
- let list = res.rich_text.text_list;
- list.forEach((item) => {
- let inputIndex = 0;
- if (item.word_list) {
- item.word_list.forEach((items, index) => {
- let isUnderline = /^_{3,}$/.test(items.text);
- if (isUnderline) {
- let obj = {
- value: '',
- type: 'input',
- mark: getRandomNumber(),
- inputIndex,
- write_base64: '',
- audio_answer_list: [],
- };
- inputIndex++;
- item.word_list[index] = { ...items, ...obj };
- } else {
- items.type = 'text';
- }
- });
- } else {
- item.type = 'text';
- }
- });
- this.$set(this.data.option_list[arr[0]][arr[1]], 'rich_text_list', list);
- } else {
- this.$set(this.data, 'rich_text_list', res.rich_text.text_list);
- }
- let pinyin_index = 0;
- this.data.option_list.forEach((item, index) => {
- item.forEach((items, indexs) => {
- items.model_pinyin = [];
- let inputIndex = 0;
- if (items.content && mergedData[pinyin_index] && mergedData[pinyin_index][0]) {
- mergedData[pinyin_index][0].forEach((itemP) => {
- let isUnderline = /^_{3,}$/.test(itemP.text);
- if (isUnderline) {
- items.model_pinyin.push({
- value: '',
- type: 'input',
- mark: getRandomNumber(),
- inputIndex,
- });
- inputIndex++;
- } else {
- items.model_pinyin.push(itemP);
- }
- });
- pinyin_index++;
- }
- });
- });
- if (this.editContentIndex) {
- this.editContentIndex = '';
- }
- }
- });
- console.log(this.data);
- },
- // 填充校对后的拼音
- fillCorrectPinyin({ selectContent: { text, pinyin, activeTextStyle }, i, j, k }) {
- this.data.paragraph_list_parameter.pinyin_proofread_word_list.push({
- paragraph_index: i,
- sentence_index: j,
- word_index: k,
- word: text,
- pinyin,
- });
- if (pinyin) this.data.paragraph_list[i][j][k].pinyin = pinyin;
- if (activeTextStyle) this.data.paragraph_list[i][j][k].activeTextStyle = activeTextStyle;
- },
- // 思维导图数据
- handleMindMap() {
- let node_list = [];
- this.data.option_list.forEach((item) => {
- item.forEach((items) => {
- node_list.push({
- name: items.content.replace(/<[^>]*>?/gm, ''),
- id: Math.random().toString(36).substring(2, 12),
- });
- });
- });
- this.data.mind_map.node_list = node_list;
- },
- handleBlurCon(index) {
- this.editContentIndex = index;
- this.identifyText(index);
- this.handleMindMap();
- },
- handleMultilingual() {
- this.multilingualText = '';
- this.data.option_list.forEach((item) => {
- item.forEach((items) => {
- this.multilingualText += items.content ? `<p>${items.content}</p>` : '<p> </p>';
- });
- });
- this.multilingualVisible = true;
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .option-list {
- margin-bottom: 12px;
- .table-node {
- display: flex;
- row-gap: 16px;
- .table-item {
- flex: 1;
- min-width: 75px;
- padding: 8px;
- border: 1px solid $border-color;
- &:not(:last-child) {
- border-right: 0;
- }
- :deep p {
- margin: 0;
- }
- :deep .el-input-group__append,
- :deep .el-input-group__prepend {
- padding: 0 5px;
- }
- &.table-items {
- display: flex;
- align-items: center;
- }
- }
- }
- }
- .fun-type {
- display: flex;
- gap: 5px;
- width: 100%;
- padding-bottom: 10px;
- border-bottom: 1px solid #e5e6eb;
- a {
- padding: 5px 10px;
- font-weight: normal;
- color: #1d2129;
- cursor: pointer;
- background: #f2f3f5;
- border: 1px solid #f2f3f5;
- border-radius: 2px;
- &.active {
- color: #165dff;
- background: #e7eeff;
- border-color: #165dff;
- }
- }
- }
- .table-rich-toolbar {
- display: flex;
- gap: 5px;
- padding: 5px 0;
- /* align-items: center; */
- :deep .el-form-item--small.el-form-item {
- margin: 0 5px 0 0;
- }
- :deep .el-form-item__label {
- padding-right: 3px;
- }
- > span {
- height: 30px;
- padding: 5px 6px;
- color: #222f3e;
- cursor: pointer;
- border-radius: 5px;
- &:hover {
- background: #cce2fa;
- }
- &.active {
- background: #a6ccf7;
- }
- }
- }
- .tips {
- font-size: 12px;
- color: #999;
- }
- </style>
|