| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615 |
- <!-- eslint-disable vue/no-v-html -->
- <template>
- <div class="table-preview" :style="[getAreaStyle(), getComponentStyle()]">
- <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
- <div class="main">
- <div
- class="table-box"
- :style="{
- width: isMobile ? '100%' : data.property.width + 'px',
- }"
- >
- <table style="width: 100%; table-layout: fixed">
- <colgroup>
- <col v-for="(item, i) in data.col_width" :key="`col-${i}`" :style="{ width: `${item.value}%` }" />
- </colgroup>
- <tr v-for="(row, i) in data.option_list" :key="`tr-${i}`" :style="{ height: data.property.height + 'px' }">
- <template v-for="(col, j) in row">
- <td
- v-if="col.is_show_cell"
- :key="col.mark"
- :colspan="col.cell.colspan"
- :rowspan="col.cell.rowspan"
- :style="{
- borderTop: i === 0 ? '1px solid ' + data.property.border_color : '',
- borderBottom: '1px solid ' + data.property.border_color,
- borderLeft:
- i === 0 && data.property.first_line_color
- ? '1px solid ' + data.property.border_color
- : j === 0
- ? '2px solid ' +
- (data.property.decoration_color ? data.property.decoration_color : data.property.border_color)
- : '1px dotted ' + data.property.border_color,
- borderRight:
- i === 0 && data.property.first_line_color
- ? '1px solid ' + data.property.border_color
- : j === row.length - 1
- ? '2px solid ' +
- (data.property.decoration_color ? data.property.decoration_color : data.property.border_color)
- : '1px dotted ' + data.property.border_color,
- borderRadius: i === 0 && data.property.first_line_color ? '4px ' : '0',
- background: col.cell.bgColor
- ? col.cell.bgColor
- : data.cols_bg_list[j]
- ? data.cols_bg_list[j]
- : data.rows_bg_list[i]
- ? data.rows_bg_list[i]
- : '',
- height: data.property.height + 'px',
- }"
- >
- <div class="cell-wrap">
- <!-- , computedRichStyle(col.content) -->
- <div class="cell-wrap-content" :style="[tdStyle(col.content)]">
- <template v-if="isEnable(data.property.view_pinyin)">
- <template v-if="col.rich_text_list">
- <div
- v-for="(item, index) in col.rich_text_list"
- :key="index"
- class="pinyin-text pinyin-text-sentence"
- :class="[
- index === 0 ? 'pinyin-text-left' : '',
- item.type === 'audio' ? 'pinyin-text-audio' : '',
- ]"
- >
- <!-- 文字块:包含 word_list -->
- <span
- v-if="item.type === 'text'"
- :key="'text-' + index"
- class="pinyin-sentence"
- :style="[
- { 'align-items': data.property.pinyin_position === 'top' ? 'flex-end' : 'flex-start' },
- ]"
- >
- <span
- v-for="(word, wIndex) in item.word_list"
- :key="wIndex"
- class="pinyin-text"
- :style="item.styleObj"
- >
- <span v-if="word.type === 'text'">
- <span
- :style="{
- flexDirection:
- data.property.pinyin_position === 'top' ? 'column' : 'column-reverse',
- 'align-items': getWordAlignItems(word, item),
- }"
- >
- <span class="pinyin" :style="getPinyinStyle(word)">
- {{ word.pinyin.trim() ? word.pinyin : ' ' }}</span
- >
- <span class="py-char" :style="getCharStyle(word, item)">{{
- convertText(word.text)
- }}</span>
- </span>
- </span>
- <template v-else-if="word.type === 'input'">
- <span v-if="data.property.pinyin_position === 'top'" class="pinyin"> </span>
- <template v-if="data.property.fill_type === fillTypeList[0].value">
- <el-input
- :key="index"
- v-model="word.value"
- :disabled="disabled"
- :class="[...computedAnswerClass(word, i, j)]"
- :style="getInputStyle(item.styleObj, word)"
- />
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[1].value">
- <el-popover :key="index" placement="top" trigger="click">
- <div class="word-list">
- <span
- v-for="{ content, mark } in data.word_list"
- :key="mark"
- class="word-item"
- @click="handleSelectWord(content, mark, word)"
- >
- {{ convertText(content) }}
- </span>
- </div>
- <el-input
- slot="reference"
- v-model="word.value"
- :readonly="true"
- :class="[...computedAnswerClass(word, i, j)]"
- :style="getInputStyle(item.styleObj, word)"
- />
- </el-popover>
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[2].value">
- <span :key="j" class="write-click" @click="handleWriteClick(word)">
- <img
- v-show="word.write_base64"
- style="background-color: #f4f4f4"
- :src="word.write_base64"
- alt="write-show"
- />
- </span>
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[3].value">
- <SoundRecordBox
- ref="record"
- :key="j"
- type="mini"
- :many-times="false"
- class="record-box"
- :answer-record-list="data.audio_answer_list"
- :task-model="isJudgingRightWrong ? 'ANSWER' : ''"
- :attrib="data.unified_attrib"
- @handleWav="handleMiniWav($event, word)"
- />
- </template>
- <span v-if="data.property.pinyin_position === 'bottom'" class="pinyin"> </span>
- </template>
- </span>
- </span>
- <!-- 图片块 -->
- <div
- v-else-if="item.type === 'image'"
- :key="'image-' + index"
- :style="item.containerStyle"
- class="image-container"
- >
- <img
- :src="item.src"
- :alt="item.alt"
- :width="item.width"
- :height="item.height"
- :style="item.imageStyle"
- class="inline-image"
- />
- </div>
- <!-- 视频块 -->
- <div
- v-else-if="item.type === 'video'"
- :key="'video-' + index"
- :style="item.containerStyle"
- class="video-container"
- >
- <video
- :poster="item.poster"
- :width="item.width"
- :height="item.height"
- :style="item.mediaStyle"
- controls
- class="inline-video"
- >
- <source :src="item.src" :type="item.videoType" />
- 您的浏览器不支持视频播放
- </video>
- </div>
- <!-- 音频块 -->
- <div
- v-else-if="item.type === 'audio'"
- :key="'audio-' + index"
- :style="item.containerStyle"
- class="audio-container"
- >
- <audio :src="item.src" :style="item.mediaStyle" controls class="inline-audio">
- 您的浏览器不支持音频播放
- </audio>
- </div>
- <!-- 换行符 -->
- <br v-else-if="item.type === 'newline'" :key="'newline-' + index" />
- <!--
- <template v-else>
- <span v-if="data.property.pinyin_position === 'top'" class="pinyin">
- {{ item.pinyin.replace(/\s+/g, '') }}
- </span>
- <span :style="{ ...item.activeTextStyle }">
- {{ convertText(item.text) }}
- </span>
- <span v-if="data.property.pinyin_position === 'bottom'" class="pinyin">{{
- item.pinyin.replace(/\s+/g, '')
- }}</span>
- </template> -->
- </div>
- </template>
- </template>
- <template v-else>
- <div v-for="(item, index) in col.model_essay" :key="index" :style="[tdStyle]">
- <span
- v-if="item.type === 'text'"
- :key="index"
- :style="{
- fontSize:
- data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
- fontFamily: data.unified_attrib && data.unified_attrib.font ? data.unified_attrib.font : '',
- }"
- v-html="convertText(sanitizeHTML(item.value))"
- ></span>
- <template v-if="item.type === 'input'">
- <template v-if="data.property.fill_type === fillTypeList[0].value">
- <el-input
- :key="index"
- v-model="item.value"
- :disabled="disabled"
- :class="[...computedAnswerClass(item, i, j)]"
- :style="[
- {
- width: Math.max(40, item.value.length * 21.3) + 'px',
- fontSize:
- data.unified_attrib && data.unified_attrib.font_size
- ? data.unified_attrib.font_size
- : '',
- fontFamily:
- data.unified_attrib && data.unified_attrib.font ? data.unified_attrib.font : '',
- },
- ]"
- />
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[1].value">
- <el-popover :key="index" placement="top" trigger="click">
- <div class="word-list">
- <span
- v-for="{ content, mark } in data.word_list"
- :key="mark"
- class="word-item"
- @click="handleSelectWord(content, mark, item)"
- >
- {{ convertText(content) }}
- </span>
- </div>
- <el-input
- slot="reference"
- v-model="item.value"
- :readonly="true"
- :class="[...computedAnswerClass(item, i, j)]"
- :style="[{ width: Math.max(40, item.value.length * 21.3) + 'px' }]"
- />
- </el-popover>
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[2].value">
- <span :key="j" class="write-click" @click="handleWriteClick(item)">
- <img
- v-show="item.write_base64"
- style="background-color: #f4f4f4"
- :src="item.write_base64"
- alt="write-show"
- />
- </span>
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[3].value">
- <SoundRecordBox
- ref="record"
- :key="j"
- type="mini"
- :many-times="false"
- class="record-box"
- :answer-record-list="data.audio_answer_list"
- :task-model="isJudgingRightWrong ? 'ANSWER' : ''"
- :attrib="data.unified_attrib"
- @handleWav="handleMiniWav($event, item)"
- />
- </template>
- </template>
- </div>
- </template>
- </div>
- <i
- v-if="col.cell.isCross"
- :class="[
- { 'el-icon-check': col.crossAnswer === statusList[1] },
- { 'el-icon-close': col.crossAnswer === statusList[2] },
- ...computedAnswerCrossClass(col, i, j),
- ]"
- :style="{ display: 'block', width: '18px', height: '18px', border: '1px solid #000' }"
- @click="toggle(col)"
- ></i>
- </div>
- <!-- , computedRichStyle(col.content) -->
- <span v-if="showLang" class="multilingual" :style="[tdStyle(col.content)]">
- {{
- multilingualTextList[getLang()] &&
- multilingualTextList[getLang()][i] &&
- multilingualTextList[getLang()][i][j]
- ? multilingualTextList[getLang()][i][j]
- : ''
- }}
- </span>
- </td>
- </template>
- </tr>
- </table>
- </div>
- <PreviewOperation
- v-if="isHasInput"
- @showAnswerAnalysis="showAnswerAnalysis"
- @judgeCorrect="judgeCorrect"
- @retry="retry"
- />
- <AnswerCorrect
- :answer-correct="data?.answer_correct"
- :visible.sync="visibleAnswerCorrect"
- :is-check-correct="isCheckCorrect"
- @closeAnswerCorrect="closeAnswerCorrect"
- />
- <AnswerAnalysis
- :visible.sync="visibleAnswerAnalysis"
- :answer-list="data.answer_list"
- :analysis-list="data.analysis_list"
- @closeAnswerAnalysis="closeAnswerAnalysis"
- >
- <div
- slot="right-answer"
- class="table-box"
- :style="{
- width: isMobile ? '100%' : data.property.width + 'px',
- }"
- >
- <table style="width: 100%; table-layout: fixed">
- <colgroup>
- <col v-for="(item, i) in data.col_width" :key="`col-${i}`" :style="{ width: `${item.value}%` }" />
- </colgroup>
- <tr v-for="(row, i) in data.option_list" :key="`tr-${i}`" :style="{ height: data.property.height + 'px' }">
- <template v-for="(col, j) in row">
- <td
- v-if="col.is_show_cell"
- :key="col.mark"
- :colspan="col.colspan"
- :rowspan="col.rowspan"
- :style="{
- borderTop: i === 0 ? '1px solid ' + data.property.border_color : '',
- borderBottom: '1px solid ' + data.property.border_color,
- borderLeft:
- i === 0 && data.property.first_line_color
- ? '1px solid ' + data.property.border_color
- : j === 0
- ? '2px solid ' +
- (data.property.decoration_color
- ? data.property.decoration_color
- : data.property.border_color)
- : '1px dotted ' + data.property.border_color,
- borderRight:
- i === 0 && data.property.first_line_color
- ? '1px solid ' + data.property.border_color
- : j === row.length - 1
- ? '2px solid ' +
- (data.property.decoration_color
- ? data.property.decoration_color
- : data.property.border_color)
- : '1px dotted ' + data.property.border_color,
- borderRadius: i === 0 && data.property.first_line_color ? '4px ' : '0',
- background: col.cell.bgColor
- ? col.cell.bgColor
- : data.cols_bg_list[j]
- ? data.cols_bg_list[j]
- : data.rows_bg_list[i]
- ? data.rows_bg_list[i]
- : '',
- height: data.property.height + 'px',
- }"
- >
- <div class="cell-wrap">
- <!-- , computedRichStyle(col.content) -->
- <div class="cell-wrap-content" :style="[tdStyle(col.content)]">
- <template v-if="isEnable(data.property.view_pinyin)">
- <template v-if="col.rich_text_list">
- <div
- v-for="(item, index) in col.rich_text_list"
- :key="index"
- class="pinyin-text pinyin-text-sentence"
- :class="[
- index === 0 ? 'pinyin-text-left' : '',
- item.type === 'audio' ? 'pinyin-text-audio' : '',
- ]"
- >
- <!-- 文字块:包含 word_list -->
- <span
- v-if="item.type === 'text'"
- :key="'text-' + index"
- class="pinyin-sentence"
- :style="[
- { 'align-items': data.property.pinyin_position === 'top' ? 'flex-end' : 'flex-start' },
- ]"
- >
- <span
- v-for="(word, wIndex) in item.word_list"
- :key="wIndex"
- class="pinyin-text"
- :style="item.styleObj"
- >
- <span v-if="word.type === 'text'">
- <span
- :style="{
- flexDirection:
- data.property.pinyin_position === 'top' ? 'column' : 'column-reverse',
- 'align-items': getWordAlignItems(word, item),
- }"
- >
- <span class="pinyin" :style="getPinyinStyle(word)">{{
- word.pinyin.trim() ? word.pinyin : ' '
- }}</span>
- <span class="py-char" :style="getCharStyle(word, item)">{{
- convertText(word.text)
- }}</span>
- </span>
- </span>
- <template v-else-if="word.type === 'input'">
- <span v-if="data.property.pinyin_position === 'top'" class="pinyin"> </span>
- <span
- v-show="computedAnswerText(word, i, j).length > 0"
- :key="`answer-${j}`"
- class="right-answer"
- >
- {{ convertText(computedAnswerText(word, i, j)) }}
- </span>
- <span v-if="data.property.pinyin_position === 'bottom'" class="pinyin"> </span>
- </template>
- </span>
- </span>
- <!-- 图片块 -->
- <div
- v-else-if="item.type === 'image'"
- :key="'image-' + index"
- :style="item.containerStyle"
- class="image-container"
- >
- <img
- :src="item.src"
- :alt="item.alt"
- :width="item.width"
- :height="item.height"
- :style="item.imageStyle"
- class="inline-image"
- />
- </div>
- <!-- 视频块 -->
- <div
- v-else-if="item.type === 'video'"
- :key="'video-' + index"
- :style="item.containerStyle"
- class="video-container"
- >
- <video
- :poster="item.poster"
- :width="item.width"
- :height="item.height"
- :style="item.mediaStyle"
- controls
- class="inline-video"
- >
- <source :src="item.src" :type="item.videoType" />
- 您的浏览器不支持视频播放
- </video>
- </div>
- <!-- 音频块 -->
- <div
- v-else-if="item.type === 'audio'"
- :key="'audio-' + index"
- :style="item.containerStyle"
- class="audio-container"
- >
- <audio :src="item.src" :style="item.mediaStyle" controls class="inline-audio">
- 您的浏览器不支持音频播放
- </audio>
- </div>
- <!-- 换行符 -->
- <br v-else-if="item.type === 'newline'" :key="'newline-' + index" />
- </div>
- </template>
- </template>
- <template v-else>
- <div v-for="(item, index) in col.model_essay" :key="index" :style="[tdStyle]">
- <span
- v-if="item.type === 'text'"
- :key="index"
- :style="{
- fontSize:
- data.unified_attrib && data.unified_attrib.font_size
- ? data.unified_attrib.font_size
- : '',
- fontFamily:
- data.unified_attrib && data.unified_attrib.font ? data.unified_attrib.font : '',
- }"
- v-html="convertText(sanitizeHTML(item.value))"
- ></span>
- <template v-if="item.type === 'input'">
- <!-- <template v-if="data.property.fill_type === fillTypeList[0].value">
- <el-input
- :key="index"
- v-model="item.value"
- :disabled="disabled"
- :class="[...computedAnswerClass(item, i, j)]"
- :style="[{ width: Math.max(40, item.value.length * 21.3) + 'px' }]"
- />
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[1].value">
- <el-popover :key="index" placement="top" trigger="click">
- <div class="word-list">
- <span
- v-for="{ content, mark } in data.word_list"
- :key="mark"
- class="word-item"
- @click="handleSelectWord(content, mark, item)"
- >
- {{ convertText(content) }}
- </span>
- </div>
- <el-input
- slot="reference"
- v-model="item.value"
- :readonly="true"
- :class="[...computedAnswerClass(item, i, j)]"
- :style="[{ width: Math.max(40, item.value.length * 21.3) + 'px' }]"
- />
- </el-popover>
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[2].value">
- <span :key="j" class="write-click" @click="handleWriteClick(item)">
- <img
- v-show="item.write_base64"
- style="background-color: #f4f4f4"
- :src="item.write_base64"
- alt="write-show"
- />
- </span>
- </template>
- <template v-else-if="data.property.fill_type === fillTypeList[3].value">
- <SoundRecordBox
- ref="record"
- :key="j"
- type="mini"
- :many-times="false"
- class="record-box"
- :answer-record-list="data.audio_answer_list"
- :task-model="isJudgingRightWrong ? 'ANSWER' : ''"
- :attrib="data.unified_attrib"
- @handleWav="handleMiniWav($event, item)"
- />
- </template> -->
- <span
- v-show="computedAnswerText(item, i, j).length > 0"
- :key="`answer-${j}`"
- class="right-answer"
- >
- {{ convertText(computedAnswerText(item, i, j)) }}
- </span>
- </template>
- </div>
- </template>
- </div>
- <i
- v-if="col.cell.isCross"
- :class="[
- { 'el-icon-check': data.answer_lists[i][j].crossAnswer === statusList[1] },
- { 'el-icon-close': data.answer_lists[i][j].crossAnswer === statusList[2] },
- ]"
- style="display: block; width: 18px; height: 18px; border: 1px solid #30a47d"
- ></i>
- </div>
- <!-- , computedRichStyle(col.content) -->
- <span v-if="showLang" class="multilingual" :style="[tdStyle(col.content)]">
- {{
- multilingualTextList[getLang()] &&
- multilingualTextList[getLang()][i] &&
- multilingualTextList[getLang()][i][j]
- ? multilingualTextList[getLang()][i][j]
- : ''
- }}
- </span>
- </td>
- </template>
- </tr>
- </table>
- </div>
- </AnswerAnalysis>
- </div>
- <WriteDialog :visible.sync="writeVisible" @confirm="handleWriteConfirm" />
- </div>
- </template>
- <script>
- import { getTableData, fillTypeList } from '@/views/book/courseware/data/table';
- import PreviewMixin from '../common/PreviewMixin';
- import WriteDialog from '../fill/components/WriteDialog.vue';
- import SoundRecordBox from '@/views/book/courseware/preview/components/record_input/SoundRecord.vue';
- export default {
- name: 'TablePreview',
- components: { SoundRecordBox, WriteDialog },
- mixins: [PreviewMixin],
- props: {
- isMobile: {
- type: Boolean,
- default: false,
- },
- },
- data() {
- return {
- data: getTableData(),
- table_width: 0,
- multilingualTextList: {}, // 多语言对应的切割后的翻译
- fillTypeList,
- selectedWordList: [], // 用于存储选中的词汇
- writeVisible: false,
- writeMark: '',
- isHasInput: false,
- statusList: ['normal', 'tick', 'cross'],
- };
- },
- computed: {
- tdStyle() {
- return function (content) {
- if (!content) return {};
- let styleObject = {};
- // 提取首个标签的 style 样式属性
- let styleMatch = content.match(/<\w+\s+[^>]*style=["']([^"']*)["'][^>]*>/i);
- if (styleMatch && styleMatch[1]) {
- let styleString = styleMatch[1];
- let styleArray = styleString.split(';').filter((s) => s.trim() !== '');
- styleArray.forEach((style) => {
- let [key, value] = style.split(':');
- if (key && value) {
- styleObject[key.trim()] = value.trim();
- }
- });
- if (styleObject.hasOwnProperty('text-align')) {
- if (styleObject['text-align'] === 'center') {
- this.$set(styleObject, 'justify-content', 'center');
- } else if (styleObject['text-align'] === 'right') {
- this.$set(styleObject, 'justify-content', 'end');
- } else {
- this.$set(styleObject, 'justify-content', 'left');
- }
- }
- }
- let obj = {};
- // if (this.data.mode === 'short') {
- let styles = this.data.styles;
- obj = {
- fontFamily: styles.fontFamily,
- fontSize: styles.fontSize,
- color: styles.fontColor,
- textDecoration: styles.isUnderline ? 'underline' : styles.isStrikethrough ? 'line-through' : '',
- fontWeight: styles.isBold ? 'bold' : '',
- fontStyle: styles.isItalic ? 'italic' : '',
- // justifyContent: styles.textAlign === 'MIDDLE' ? 'center' : styles.textAlign === 'RIGHT' ? 'end' : 'left',
- // textAlign: styles.textAlign === 'MIDDLE' ? 'center' : styles.textAlign === 'RIGHT' ? 'end' : 'left',
- };
- // } else {
- // obj = {
- // width: '100%',
- // };
- // }
- return { ...obj, ...styleObject };
- };
- },
- },
- watch: {
- 'data.col_width': {
- handler(val) {
- this.handleData();
- },
- },
- 'data.property': {
- handler(val) {
- this.computedTableCellShow();
- },
- },
- isJudgingRightWrong(val) {
- if (!val) return;
- this.data.answer_lists = this.answer.answer_list;
- // this.answer.answer_list.forEach(({ mark, value }) => {
- // this.modelEssay.forEach((item) => {
- // item.forEach((li) => {
- // if (li.mark === mark) {
- // li.content = value;
- // }
- // });
- // });
- // });
- },
- },
- created() {
- this.handleData();
- this.computedTableCellShow();
- },
- methods: {
- // 预处理 richTextList,合并分散的视频标签
- normalizedRichTextList(col) {
- if (!col.rich_text_list || col.rich_text_list.length === 0) return [];
- const result = [];
- let i = 0;
- while (i < col.rich_text_list.length) {
- const item = col.rich_text_list[i];
- // 检查是否是视频开始标签(且不包含结束标签)
- if (
- item.text &&
- typeof item.text === 'string' &&
- item.text.includes('<video') &&
- !item.text.includes('</video>')
- ) {
- let combinedText = item.text;
- let j = i + 1;
- // 向后查找 source 和结束标签,直到遇到 </video> 或超出范围
- while (j < col.rich_text_list.length) {
- const nextItem = col.rich_text_list[j];
- if (nextItem.text) {
- combinedText += `${nextItem.text}`;
- }
- // 如果找到了结束标签,合并完成,跳出内部循环
- if (nextItem.text && nextItem.text.includes('</video>')) {
- j++; // 跳过结束标签
- break;
- }
- j++;
- }
- // 将合并后的内容作为一个新项加入结果
- result.push({
- ...item,
- text: combinedText,
- is_merged_video: true, // 标记一下,可选
- });
- // 更新主循环索引,跳过已合并的项
- i = j;
- } else {
- // 其他项直接加入
- result.push(item);
- i++;
- }
- }
- return result;
- },
- // 解析 richTextList 为可渲染的块
- parsedBlocks(col) {
- const listToParse = this.normalizedRichTextList(col);
- if (!listToParse || listToParse.length === 0) return [];
- // if (!col.rich_text_list || col.rich_text_list.length === 0) return [];
- const blocks = [];
- let textBlockIndex = 0;
- let oldIndex = -1;
- let paragraphIndex = 0;
- const tagStack = [];
- for (const item of listToParse) {
- oldIndex += 1;
- if (item.text && typeof item.text === 'string' && item.text.includes('<img')) {
- blocks.push(this.parseImageBlock(item, tagStack));
- } else if (item.text && typeof item.text === 'string' && item.text.includes('<video')) {
- // item.text 是完整的 video 标签串
- blocks.push(this.parseVideoBlock(item, tagStack));
- } else if (item.text && typeof item.text === 'string' && item.text.includes('<audio')) {
- blocks.push(this.parseAudioBlock(item, tagStack));
- } else if (item.is_style === 'true' || item.is_style === true) {
- if (item.text.includes('<br')) {
- blocks.push({ type: 'newline' });
- paragraphIndex += 1;
- } else {
- this.handleStyleTag(item, tagStack);
- }
- } else if (item.text === '\n') {
- blocks.push({ type: 'newline' });
- paragraphIndex += 1;
- } else if (item.word_list && item.word_list.length > 0) {
- const hasEmphasisDot = this.checkHasEmphasisDot(tagStack);
- blocks.push(this.createTextBlock(item, tagStack, textBlockIndex, oldIndex, paragraphIndex, hasEmphasisDot));
- textBlockIndex += 1;
- }
- }
- return blocks;
- },
- // 解析图片块
- parseImageBlock(item, tagStack) {
- const imgMatch = item.text.match(/<img\s+([^>]*)\/?\s*>/i);
- if (!imgMatch) return null;
- const attrs = imgMatch[1];
- const srcMatch = attrs.match(/src=["']([^"']*)["']/i);
- const altMatch = attrs.match(/alt=["']([^"']*)["']/i);
- const widthMatch = attrs.match(/width=["']?(\d+)["']?/i);
- const heightMatch = attrs.match(/height=["']?(\d+)["']?/i);
- const styleMatch = attrs.match(/style=["']([^"']*)["']/i);
- const containerStyleObj = {};
- tagStack.forEach((tagItem) => {
- if (tagItem.style) {
- this.mergeStyleString(containerStyleObj, tagItem.style);
- }
- });
- const imageStyleObj = {};
- if (styleMatch) {
- this.mergeStyleString(imageStyleObj, styleMatch[1]);
- }
- return {
- type: 'image',
- src: srcMatch ? srcMatch[1] : '',
- alt: altMatch ? altMatch[1] : '',
- width: widthMatch ? widthMatch[1] : null,
- height: heightMatch ? heightMatch[1] : null,
- containerStyle: containerStyleObj,
- imageStyle: imageStyleObj,
- };
- },
- // 处理样式标签
- handleStyleTag(item, tagStack) {
- const tagText = item.text || '';
- const closeTagMatch = tagText.match(/^<\/(\w+)>$/);
- if (closeTagMatch) {
- const tagName = closeTagMatch[1];
- this.removeTagFromStack(tagStack, tagName);
- } else {
- const openTagMatch = tagText.match(/^<(\w+)([^>]*)>$/);
- if (openTagMatch) {
- const tagName = openTagMatch[1];
- const attrs = openTagMatch[2];
- const { style, className } = this.extractTagInfo(attrs, tagName);
- tagStack.push({ tag: tagName, style, className });
- }
- }
- },
- // 检查标签栈中是否有着重点样式
- checkHasEmphasisDot(tagStack) {
- return tagStack.some((tagItem) => {
- if (!tagItem.className) return false;
- return tagItem.className.includes('rich-text-emphasis-dot');
- });
- },
- // 创建文本块
- createTextBlock(item, tagStack, textBlockIndex, oldIndex, paragraphIndex, hasEmphasisDot) {
- const combinedStyle = tagStack
- .filter((tagItem) => tagItem.style)
- .map((tagItem) => tagItem.style)
- .join(';');
- const styleObj = combinedStyle
- ? this.parseStyleToObject(combinedStyle)
- : {
- fontSize:
- this.data.unified_attrib && this.data.unified_attrib.font_size ? this.data.unified_attrib.font_size : '',
- fontFamily: this.data.unified_attrib && this.data.unified_attrib.font ? this.data.unified_attrib.font : '',
- };
- return {
- type: 'text',
- word_list: item.word_list,
- index: textBlockIndex,
- oldIndex,
- paragraphIndex,
- styleObj,
- hasEmphasisDot,
- };
- },
- // 拼音固定为拼音字体,跟随汉字的字号、颜色、粗细的样式
- getPinyinStyle(item) {
- const styles = {};
- // 固定使用 League 字体
- styles['font-family'] = 'League';
- styles['font-size'] =
- this.data.unified_attrib && this.data.unified_attrib.pinyin_size ? this.data.unified_attrib.pinyin_size : '';
- // 只继承字号和颜色
- if (item.activeTextStyle) {
- if (item.activeTextStyle.fontSize) {
- styles['font-size'] = item.activeTextStyle.fontSize;
- }
- if (item.activeTextStyle.color) {
- styles['color'] = item.activeTextStyle.color;
- }
- }
- // 如果设置了全局字号,优先使用全局字号
- if (this.isAllSetting && this.fontSize) {
- styles['font-size'] = this.fontSize;
- this.isAllSetting = false;
- }
- // 明确重置不应该继承的样式
- styles['text-decoration'] = 'none';
- styles['font-style'] = 'normal';
- styles['letter-spacing'] = '0';
- return styles;
- },
- // 获取单个字符的样式(包括着重点)
- getCharStyle(word, block, charIndex) {
- const baseStyle = { ...word.activeTextStyle };
- baseStyle['font-size'] = baseStyle.fontSize;
- baseStyle['font-family'] = baseStyle.fontFamily;
- if (this.isAllSetting) {
- baseStyle['font-size'] = this.fontSize;
- baseStyle['font-family'] = this.fontFamily;
- this.isAllSetting = false;
- }
- // 如果该文字块有着重点标记,应用到字符上
- if (block.hasEmphasisDot) {
- const letterSpacing = block.styleObj?.letterSpacing || '0';
- const letterSpacingValue = parseFloat(letterSpacing) || 0;
- baseStyle['border-bottom'] = 'none';
- baseStyle['background-image'] = 'radial-gradient(circle at center, currentColor 0.15em, transparent 0.16em)';
- baseStyle['background-repeat'] = 'repeat-x';
- baseStyle['background-position'] = `${letterSpacingValue * -0.5}em 100%`;
- baseStyle['background-size'] = `${1 + letterSpacingValue}em 0.3em`;
- baseStyle['padding-bottom'] = '0.3em';
- baseStyle['display'] = 'inline';
- }
- return baseStyle;
- },
- // input的高度
- getInputStyle(styles, words) {
- const baseStyle = {};
- baseStyle['font-size'] = styles ? styles.fontSize : '16px';
- baseStyle['--input-font-family'] = styles ? styles.fontFamily : '';
- baseStyle['width'] = Math.max(40, words.value.length * 21.3) + 'px';
- baseStyle['height'] = styles && styles.fontSize ? styles.fontSize.replace('pt', '') * 1.5 + 'pt' : '24px';
- return baseStyle;
- },
- // 如果汉字有字间距,拼音与汉字左对齐,如果没有则居中对齐
- getWordAlignItems(word, block) {
- const letterSpacing = block.styleObj?.letterSpacing;
- if (letterSpacing && letterSpacing !== '0' && letterSpacing !== '0px') {
- return 'flex-start';
- }
- return 'center';
- },
- // 提取标签信息(样式和类名)
- extractTagInfo(attrs, tagName) {
- const styleMatch = attrs.match(/style=["']([^"']*)["']/);
- let style = styleMatch ? styleMatch[1] : null;
- const classMatch = attrs.match(/class=["']([^"']*)["']/);
- const className = classMatch ? classMatch[1] : '';
- if (!style) {
- style = this.getDefaultTagStyle(tagName);
- }
- return { style, className };
- },
- // 获取标签默认样式
- getDefaultTagStyle(tagName) {
- const tagStyleMap = {
- strong: 'font-weight: bold',
- b: 'font-weight: bold',
- em: 'font-style: italic',
- i: 'font-style: italic',
- u: 'text-decoration: underline',
- s: 'text-decoration: line-through',
- del: 'text-decoration: line-through',
- sub: 'vertical-align: sub',
- sup: 'vertical-align: super',
- };
- return tagStyleMap[tagName] || null;
- },
- // 解析样式字符串为对象
- parseStyleToObject(styleStr) {
- const styleObj = {};
- if (!styleStr) return styleObj;
- this.mergeStyleString(styleObj, styleStr);
- return styleObj;
- },
- // 解析视频块
- parseVideoBlock(item, tagStack) {
- const videoMatch = item.text.match(/<video\s+([^>]*)>/i);
- if (!videoMatch) return null;
- const attrs = videoMatch[1];
- const posterMatch = attrs.match(/poster=["']([^"']*)["']/i);
- const widthMatch = attrs.match(/width=["']?(\d+)["']?/i);
- const heightMatch = attrs.match(/height=["']?(\d+)["']?/i);
- const styleMatch = attrs.match(/style=["']([^"']*)["']/i);
- // 直接从完整的文本中匹配 source
- const sourceMatch = item.text.match(/<source\s+([^>]*)\/?\s*>/i);
- let src = '';
- let videoType = '';
- if (sourceMatch) {
- const sourceAttrs = sourceMatch[1];
- const srcMatch = sourceAttrs.match(/src\s*=\s*["']?([^"'\s>]+)["']?/i);
- const typeMatch = sourceAttrs.match(/type\s*=\s*["']?([^"'\s>]+)["']?/i);
- src = srcMatch ? srcMatch[1] : '';
- videoType = typeMatch ? typeMatch[1] : '';
- }
- const containerStyleObj = {};
- tagStack.forEach((tagItem) => {
- if (tagItem.style) {
- this.mergeStyleString(containerStyleObj, tagItem.style);
- }
- });
- const mediaStyleObj = {};
- if (styleMatch) {
- this.mergeStyleString(mediaStyleObj, styleMatch[1]);
- }
- return {
- type: 'video',
- poster: posterMatch ? posterMatch[1] : '',
- width: widthMatch ? widthMatch[1] : null,
- height: heightMatch ? heightMatch[1] : null,
- src,
- videoType,
- containerStyle: containerStyleObj,
- mediaStyle: mediaStyleObj,
- };
- },
- // 解析音频块
- parseAudioBlock(item, tagStack) {
- const audioMatch = item.text.match(/<audio\s+([^>]*)>/i);
- if (!audioMatch) return null;
- const attrs = audioMatch[1];
- const srcMatch = attrs.match(/src=["']([^"']*)["']/i);
- const styleMatch = attrs.match(/style=["']([^"']*)["']/i);
- const containerStyleObj = {};
- tagStack.forEach((tagItem) => {
- if (tagItem.style) {
- this.mergeStyleString(containerStyleObj, tagItem.style);
- }
- });
- const mediaStyleObj = {};
- if (styleMatch) {
- this.mergeStyleString(mediaStyleObj, styleMatch[1]);
- }
- return {
- type: 'audio',
- src: srcMatch ? srcMatch[1] : '',
- containerStyle: containerStyleObj,
- mediaStyle: mediaStyleObj,
- };
- },
- // 合并样式字符串到对象
- mergeStyleString(styleObj, styleStr) {
- styleStr.split(';').forEach((rule) => {
- if (rule.trim()) {
- const [prop, value] = rule.split(':').map((s) => s.trim());
- if (prop && value) {
- const camelProp = prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
- styleObj[camelProp] = value;
- }
- }
- });
- },
- removeTagFromStack(tagStack, tagName) {
- for (let i = tagStack.length - 1; i >= 0; i--) {
- if (tagStack[i].tag === tagName) {
- tagStack.splice(i, 1);
- break;
- }
- }
- },
- 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];
- },
- handleData() {
- this.table_width = 0;
- this.isHasInput = false;
- this.data.col_width.forEach((item) => {
- this.table_width += Number(item.value);
- });
- this.data.multilingual.forEach((item) => {
- let trans_arr = item.translation.split('\n');
- let chunkSize = this.data.property.column_count;
- let chunkedArr = trans_arr.reduce((acc, curr, index) => {
- // 当索引是chunkSize的倍数时,开始一个新的子数组
- if (index % chunkSize === 0) {
- acc.push([curr]); // 开始新的子数组并添加当前元素
- } else {
- acc[acc.length - 1].push(curr); // 将当前元素添加到最后一个子数组中
- }
- return acc;
- }, []);
- this.$set(this.multilingualTextList, item.type, chunkedArr);
- });
- if (!this.isJudgingRightWrong) {
- this.answer.answer_list = this.data.answer_lists;
- }
- this.data.option_list.forEach((item, index) => {
- item.forEach((items, indexs) => {
- items.model_essay.forEach((li) => {
- if (li.type === 'input') {
- this.isHasInput = true;
- }
- });
- if (items.rich_text_list) {
- this.data.option_list[index][indexs].rich_text_list = this.parsedBlocks(items);
- }
- });
- });
- },
- // 计算单元格是否显示
- computedTableCellShow() {
- this.data.option_list.forEach((item, i, arr) => {
- item.forEach(({ cell, is_show_cell }, j, arr2) => {
- let col = cell.colspan === undefined ? 1 : Number(cell.colspan);
- let row = cell.rowspan === undefined ? 1 : Number(cell.rowspan);
- if (col > 1 && row === 1) {
- for (let k = 1; k < col; k++) {
- this.$set(arr[i][j + k], 'is_show_cell', false);
- }
- }
- if (row > 1 && col === 1) {
- for (let k = 1; k < row; k++) {
- this.$set(arr[i + k][j], 'is_show_cell', false);
- }
- }
- if (row > 1 && col > 1) {
- for (let k = 1; k < row; k++) {
- this.$set(arr[i + k][j], 'is_show_cell', false);
- for (let l = 1; l < col; l++) {
- this.$set(arr[i + k][j + l], 'is_show_cell', false);
- this.$set(arr[i][j + l], 'is_show_cell', false);
- }
- }
- }
- if (is_show_cell === undefined) {
- arr2[j].is_show_cell = true;
- }
- });
- });
- },
- computedAnswerText(item, i, j) {
- if (!this.isShowRightAnswer) return '';
- let answerOption =
- this.data.answer_lists[i] && this.data.answer_lists[i][j] ? this.data.answer_lists[i][j].answer : '';
- let answerOptionList = answerOption.split('\n');
- if (!item) return '';
- let answerValue = answerOptionList[item.inputIndex] ? answerOptionList[item.inputIndex].split('/') : '';
- if (!answerValue) return '';
- return `${answerValue.join('/')}`;
- },
- /**
- * 计算答题对错选项字体颜色
- * @param {string} mark 选项标识
- */
- computedAnswerClass(item, i, j) {
- if (!this.isJudgingRightWrong && !this.isShowRightAnswer) {
- return '';
- }
- let answerOption =
- this.data.answer_lists[i] && this.data.answer_lists[i][j] ? this.data.answer_lists[i][j].answer : '';
- let answerOptionList = answerOption.split('\n');
- if (!item) return '';
- let selectValue = item.value ? item.value.trim() : '';
- let answerValue = answerOptionList[item.inputIndex] ? answerOptionList[item.inputIndex].split('/') : '';
- let classList = [];
- let isRight = answerValue && answerValue.includes(selectValue);
- if (this.isJudgingRightWrong && answerValue) {
- isRight ? classList.push('right') : classList.push('wrong');
- }
- if (this.isShowRightAnswer && !isRight) {
- classList.push('show-right-answer');
- }
- return classList;
- },
- computedAnswerCrossClass(item, i, j) {
- if (!this.isJudgingRightWrong && !this.isShowRightAnswer) {
- return '';
- }
- let answerOption =
- this.data.answer_lists[i] && this.data.answer_lists[i][j] ? this.data.answer_lists[i][j].crossAnswer : '';
- if (!item) return '';
- let selectValue = item.crossAnswer;
- let classList = [];
- let isRight = answerOption === selectValue;
- if (this.isJudgingRightWrong && answerOption) {
- isRight ? classList.push('right') : classList.push('wrong');
- }
- if (this.isShowRightAnswer && !isRight) {
- classList.push('show-right-answer');
- }
- return classList;
- },
- /**
- * 处理小音频录音
- * @param {Object} data 音频数据
- * @param {String} mark 选项标识
- */
- handleMiniWav(data, mark) {
- if (!data || !mark) return;
- this.$set(mark, 'audio_answer_list', data);
- },
- /**
- * 处理选中词汇
- * @param {String} content 选中的词汇内容
- * @param {String} mark 选项标识
- * @param {Object} li 当前输入框对象
- */
- handleSelectWord(content, mark, li) {
- if (!content || !mark || !li || this.disabled) return;
- li.value = content;
- this.selectedWordList.push(mark);
- },
- /**
- * 处理书写区确认
- * @param {String} data 书写区数据
- */
- handleWriteConfirm(data) {
- if (!data) return;
- this.$set(this.writeMark, 'write_base64', data);
- },
- handleWriteClick(mark) {
- this.writeVisible = true;
- this.writeMark = mark;
- },
- computedRichStyle(content) {
- // if (this.data.mode === 'short') return {};
- if (!content) return {};
- // 提取首个标签的 style 样式属性
- let styleMatch = content.match(/<\w+\s+[^>]*style=["']([^"']*)["'][^>]*>/i);
- if (styleMatch && styleMatch[1]) {
- let styleString = styleMatch[1];
- let styleArray = styleString.split(';').filter((s) => s.trim() !== '');
- let styleObject = {};
- styleArray.forEach((style) => {
- let [key, value] = style.split(':');
- if (key && value) {
- styleObject[key.trim()] = value.trim();
- }
- });
- if (styleObject.hasOwnProperty('text-align') && styleObject['text-align'] === 'center') {
- this.$set(styleObject, 'justify-content', 'center');
- }
- return styleObject;
- }
- return {};
- },
- // 重做
- retry() {
- this.data.option_list.forEach((item) => {
- item.forEach((items) => {
- items.model_essay.forEach((li) => {
- if (li.type === 'input') {
- li.value = '';
- li.write_base64 = '';
- }
- });
- });
- });
- if (this.$refs.record) {
- if (this.$refs.record.length > 0) {
- this.$refs.record.forEach((item) => {
- item.handleReset();
- });
- } else {
- this.$refs.record.handleReset();
- }
- }
- this.isJudgingRightWrong = false;
- this.isShowRightAnswer = false;
- },
- /**
- * 获取无文本内容的数据结构,用于保存为个人模板时的样式模板
- */
- getNoTextContentData() {
- let noTextContentData = JSON.parse(JSON.stringify(this.data));
- const resetFieldMap = {
- record_list: [],
- vocabulary: '', // 用于选词的词汇
- word_list: [], // 选词列表
- has_identify: 'false', // 是否已识别
- paragraph_list: [],
- paragraph_list_parameter: {
- text: '',
- is_first_sentence_first_hz_pinyin_first_char_upper_case: 'true',
- pinyin_proofread_word_list: [],
- },
- analysis_list: [],
- answer_list: [],
- };
- Object.assign(noTextContentData, resetFieldMap);
- noTextContentData.answer_lists.forEach((item) => {
- item.answer = '';
- item.answer_list = [];
- });
- noTextContentData.option_list.forEach((item) => {
- item.content = '';
- item.model_essay = [];
- });
- if (noTextContentData.answer) {
- noTextContentData.answer.answer_list = [];
- noTextContentData.answer.reference_answer = '';
- }
- return noTextContentData;
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- @use '@/styles/mixin.scss' as *;
- $select-color: #1890ff;
- $border-color: #e6e6e6;
- .table-preview {
- @include preview-base;
- .main {
- color: #262626;
- .table-box {
- margin: 0 auto;
- overflow: auto;
- }
- table {
- // border-spacing: 3px;
- border-collapse: separate;
- }
- .cell-wrap {
- display: flex;
- flex-flow: wrap;
- grid-area: fill;
- // align-items: end;
- align-items: center; // 为了勾选框居中对齐
- &-content {
- display: flex;
- flex: 1;
- flex-flow: wrap;
- grid-area: fill;
- align-items: end;
- }
- p {
- flex-grow: 1; // 为了常规模式时设置居中显示撑满整个单元格
- max-width: 100%;
- margin: 0;
- }
- .record-box {
- display: inline-flex;
- align-items: center;
- background-color: #fff;
- // border-bottom: 1px solid $font-color;
- :deep(.playBack) {
- width: 20px;
- height: 20px;
- }
- }
- .write-click {
- display: inline-block;
- width: 104px;
- height: 32px;
- padding: 0 4px;
- vertical-align: bottom;
- cursor: pointer;
- border-bottom: 1px solid $font-color;
- img {
- width: 100%;
- height: 100%;
- }
- }
- .el-input {
- display: inline-flex;
- align-items: center;
- width: 120px;
- margin: 0 2px;
- font-size: 16px;
- &.pinyin :deep input.el-input__inner {
- font-family: 'PINYIN-B', sans-serif;
- }
- &.chinese :deep input.el-input__inner {
- font-family: 'arial', sans-serif;
- }
- &.english :deep input.el-input__inner {
- font-family: 'arial', sans-serif;
- }
- &.right {
- :deep input.el-input__inner {
- color: $right-color;
- }
- }
- &.wrong {
- :deep input.el-input__inner {
- color: $error-color;
- }
- }
- :deep input.el-input__inner {
- padding: 0;
- font-family: var(--input-font-family);
- // font-size: 16pt;
- color: $font-color;
- text-align: center;
- background-color: transparent;
- border-width: 0;
- border-bottom: 1px solid $font-color;
- border-radius: 0;
- }
- }
- .right {
- border-color: $right-color !important;
- }
- .wrong {
- border-color: $error-color !important;
- }
- &-normal {
- p {
- width: 100%; // 为了解决换行问题
- }
- }
- }
- .right-answer {
- position: relative;
- left: 0;
- display: inline-block;
- min-height: 24px;
- line-height: 24px;
- vertical-align: bottom;
- border-bottom: 1px solid $font-color;
- }
- .multilingual {
- display: block;
- word-break: break-word;
- }
- .pinyin-text {
- display: flex;
- flex-direction: column;
- padding: 0 1px;
- font-size: 16px;
- span {
- // text-align: center; // 注释掉是为了带拼音分词后折行左对齐显示了居中对齐
- }
- .pinyin {
- height: 18px;
- font-family: 'League';
- font-size: 12px;
- }
- &-left {
- span {
- // text-align: left;
- }
- }
- }
- .cell-wrap-content {
- p {
- display: inline-flex;
- flex-wrap: wrap;
- margin: 0.5em 0;
- }
- span {
- display: inline-flex;
- flex-wrap: wrap;
- }
- .pinyin-sentence {
- display: inline;
- white-space: normal;
- }
- .image-container {
- display: block;
- .inline-image {
- display: inline-block;
- max-width: 100%;
- height: auto;
- }
- }
- .video-container {
- display: inline-block;
- margin: 26px 5px 26px 0;
- .inline-video {
- display: inline-block;
- max-width: 100%;
- height: auto;
- }
- }
- .audio-container {
- display: inline-block;
- .inline-audio {
- display: inline-block;
- width: 100%;
- max-width: 500px;
- }
- }
- .pinyin-text-audio {
- width: 100%;
- }
- }
- :deep .el-input--small .el-input__inner {
- height: 100%;
- line-height: 1;
- }
- :deep audio,
- :deep video {
- max-width: 100%;
- }
- }
- }
- .word-list {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- align-items: center;
- .word-item {
- cursor: pointer;
- }
- }
- </style>
|