123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- <template>
- <ModuleBase :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>
- <template v-if="data.mode === 'short'">
- <div class="table-rich-toolbar">
- <el-select v-model="data.styles.fontFamily" placeholder="请选择" style="width: 130px">
- <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">
- <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" />
- </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"
- >
- <SvgIcon icon-class="underline" size="20" />
- </span>
- <span :class="[data.styles.isBold ? 'active' : '']" @click="data.styles.isBold = !data.styles.isBold">
- <SvgIcon icon-class="font-bold" size="20" />
- </span>
- <span :class="[data.styles.isItalic ? 'active' : '']" @click="data.styles.isItalic = !data.styles.isItalic">
- <SvgIcon icon-class="font-italic" size="20" />
- </span>
- <span
- :class="[data.styles.isStrikethrough ? 'active' : '']"
- @click="data.styles.isStrikethrough = !data.styles.isStrikethrough"
- >
- <SvgIcon icon-class="strikethrough" size="20" />
- </span>
- <span :class="[data.styles.textAlign === 'start' ? 'active' : '']" @click="data.styles.textAlign = 'left'">
- <SvgIcon icon-class="align-left" size="20" />
- </span>
- <span :class="[data.styles.textAlign === 'center' ? 'active' : '']" @click="data.styles.textAlign = 'center'">
- <SvgIcon icon-class="align-center" size="20" />
- </span>
- <span :class="[data.styles.textAlign === 'end' ? 'active' : '']" @click="data.styles.textAlign = 'right'">
- <SvgIcon icon-class="align-right" size="20" />
- </span>
- </div>
- </template>
- <div class="option-list">
- <div v-for="(item, i) in data.option_list" :key="i" class="table-node">
- <div v-for="li in item" :key="li.mark" class="table-item">
- <!-- eslint-disable max-len -->
- <RichText
- v-if="data.mode === 'normal'"
- ref="richText"
- v-model="li.content"
- :inline="true"
- toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
- @handleRichTextBlur="handleBlurCon"
- />
- <el-input v-else v-model="li.content" @blur="handleBlurCon" />
- </div>
- </div>
- <div class="table-node">
- <div v-for="(value, index) in data.col_width" :key="index" class="table-item">
- <el-input v-model="value.value"> <template slot="prepend">列宽:</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_list" :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" />
- </div>
- </div>
- </div>
- </template>
- <MultilingualFill
- :visible.sync="multilingualVisible"
- :text="multilingualText"
- :translations="data.multilingual"
- @SubmitTranslation="handleMultilingualTranslation"
- />
- <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">拼音效果</el-divider>
- <PinyinText
- v-if="isEnable(data.property.view_pinyin)"
- :id="'table_pinyin_text'"
- ref="PinyinText"
- :paragraph-list="data.paragraph_list"
- :pinyin-position="data.property.pinyin_position"
- @fillCorrectPinyin="fillCorrectPinyin"
- />
- </template>
- </ModuleBase>
- </template>
- <script>
- import { isEnable } from '@/views/book/courseware/data/common';
- import ModuleMixin from '../../common/ModuleMixin';
- import { CrateParsedTextInfo_Pinyin } 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,
- };
- },
- 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_list = this.data.answer_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_list.push(Array.from({ length: this.data.property.column_count }, getAnswerOption));
- }
- }
- },
- },
- '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: 100,
- });
- }
- 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_list = this.data.answer_list.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.createParsedTextInfoPinyin(text);
- }
- },
- 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.createParsedTextInfoPinyin(text);
- }
- },
- 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(),
- };
- });
- },
- },
- },
- methods: {
- // 识别文本
- identifyText() {
- 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;
- }
- });
- // // 去除所有的 font-size 样式
- // .replace(/font-size:\s*\d+(\.\d+)?px;/gi, '')
- // // 匹配 class 名为 rich-fill 的 span 标签和三个以上的_,并将它们组成数组
- // .replace(/<span class="rich-fill".*?>(.*?)<\/span>|([_]{3,})/gi, '###$1$2###');
- });
- }
- text += `${items.content.replace(/<[^>]+>/g, '')}\n`;
- });
- });
- if (isEnable(this.data.property.view_pinyin)) {
- this.createParsedTextInfoPinyin(text);
- }
- },
- // 获取拼音解析文本
- createParsedTextInfoPinyin(text) {
- if (text === '') {
- this.data.paragraph_list_parameter.pinyin_proofread_word_list = [];
- return;
- }
- this.data.paragraph_list_parameter.text = text.replace(/<[^>]+>/g, '');
- 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;
- CrateParsedTextInfo_Pinyin(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;
- 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++;
- }
- });
- });
- }
- });
- },
- // 填充校对后的拼音
- 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() {
- 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;
- }
- }
- }
- }
- .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>
|