| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- <!-- eslint-disable vue/no-v-html -->
- <template>
- <div :class="['describe-preview']" :style="[getAreaStyle(), getComponentStyle()]">
- <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
- <div class="main">
- <div ref="leftDiv" :style="{ width: data.note_list?.length > 0 ? '' : '100%' }">
- <PinyinText
- v-if="isEnable(data.property.view_pinyin)"
- :is-enable-voice="data.property.is_enable_voice"
- :audio-file-id="data.audio_file_id"
- :unified-attrib="data.unified_attrib"
- :paragraph-list="data.paragraph_list"
- :rich-text-list="data.rich_text_list"
- :rich-text-note-list="data.note_list"
- :pinyin-position="data.property.pinyin_position"
- :pinyin-overall-position="data.property.pinyin_overall_position"
- :pinyin-size="data?.unified_attrib?.pinyin_size"
- :font-size="data?.unified_attrib?.font_size"
- :font-family="data?.unified_attrib?.font"
- :pinyin-padding="data.property.pinyin_padding"
- :is-preview="isPreview"
- />
- <div v-else>
- <AudioPlay
- v-if="isEnable(data.property.is_enable_voice)"
- :file-id="data.audio_file_id"
- :theme-color="data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : ''"
- />
- <span
- class="rich-text"
- @mouseover="handleRichFillClick"
- @mouseleave="onLeave"
- v-html="convertText(sanitizeHTML(data.content))"
- ></span>
- </div>
- </div>
- <div v-show="showLang" class="lang">
- {{ data.multilingual?.find((item) => item.type === getLang())?.translation }}
- </div>
- </div>
- <el-popover
- ref="notePopover"
- :popper-options="{ boundariesElement: 'viewport' }"
- placement="bottom-start"
- trigger="manual"
- >
- <div class="popover-content" v-html="sanitizeHTML(selectedNote)"></div>
- </el-popover>
- </div>
- </template>
- <script>
- import { getRichTextData } from '@/views/book/courseware/data/richText';
- import PreviewMixin from '../common/PreviewMixin';
- import { isEnable } from '@/views/book/courseware/data/common';
- import PinyinText from '@/components/PinyinText.vue';
- import AudioPlay from '../character_base/components/AudioPlay.vue';
- import { getRandomNumber } from '@/utils';
- export default {
- name: 'RichTextPreview',
- components: { PinyinText, AudioPlay },
- mixins: [PreviewMixin],
- data() {
- return {
- isEnable,
- data: getRichTextData(),
- isPreview: true,
- divHeight: 'auto',
- observer: null,
- selectedNote: '',
- };
- },
- mounted() {
- this.observer = new ResizeObserver(() => {
- this.updateHeight();
- });
- this.observer.observe(this.$refs.leftDiv.closest('.describe-preview'));
- },
- beforeDestroy() {
- this.observer.disconnect();
- },
- methods: {
- handleRichFillClick(event) {
- const targetSpan = event.target.closest('.rich-fill');
- if (!targetSpan) {
- return;
- }
- const annotationId = targetSpan.getAttribute('data-annotation-id');
- if (!annotationId) return;
- const noteItem = this.data.note_list.find((item) => item.id === annotationId || item.annota_id === annotationId);
- if (noteItem) {
- this.selectedNote = noteItem.note || noteItem.dataStr || '';
- // 关键:将 popover 的参考元素移动到被点击的文字位置
- // 这样 popover 就会自动围绕这个文字显示
- this.$nextTick(() => {
- const refEl = this.$refs.notePopover;
- refEl.referenceElm = targetSpan;
- refEl.showPopper = true;
- // 强制重新计算定位
- if (refEl.updatePopper) {
- refEl.updatePopper();
- }
- });
- }
- },
- onLeave() {
- this.$refs.notePopover.showPopper = false;
- },
- updateHeight() {
- this.$nextTick(() => {
- const target = this.$refs.leftDiv;
- const parent = target.closest('.describe-preview');
- if (!parent) return;
- setTimeout(() => {
- this.divHeight = parent.offsetHeight - 16;
- }, 800);
- });
- },
- /**
- * 获取无文本内容的数据结构,用于保存为个人模板时的样式模板
- */
- getNoTextContentData() {
- let noTextContentData = JSON.parse(JSON.stringify(this.data));
- const resetFieldMap = {
- paragraph_list: [],
- rich_text_list: [],
- content: '',
- audio_file_id: '',
- note_list: [],
- analysis_list: [],
- answer_list: [],
- };
- Object.assign(noTextContentData, resetFieldMap);
- if (noTextContentData.answer) {
- noTextContentData.answer.answer_list = [];
- noTextContentData.answer.reference_answer = '';
- }
- return noTextContentData;
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- @use '@/styles/mixin.scss' as *;
- .describe-preview {
- @include preview-base;
- &.middle {
- margin-top: calc($title-content-spacing - $component-spacing);
- margin-bottom: calc($title-content-spacing - $component-spacing);
- }
- &.top {
- margin-bottom: calc($title-content-spacing - $component-spacing);
- }
- &.bottom {
- margin-top: calc($title-content-spacing - $component-spacing);
- }
- :deep .el-dialog {
- position: fixed;
- margin: 0 !important;
- transition: all 0.2s;
- .el-dialog__header {
- padding: 0 !important;
- .el-dialog__headerbtn {
- top: 6px !important;
- right: 6px !important;
- }
- }
- .el-dialog__body {
- padding: 10px !important;
- }
- }
- }
- </style>
|