|
|
@@ -76,7 +76,7 @@
|
|
|
is_my_edit_task,
|
|
|
is_my_audit_task,
|
|
|
is_show_submit_audit_button,
|
|
|
- } in node_list"
|
|
|
+ } in visibleNodeList"
|
|
|
:key="nodeId"
|
|
|
:class="['menu-item', { active: curSelectId === nodeId }, { courseware: isTrue(is_leaf_chapter) }]"
|
|
|
:style="computedNameStyle(deep, isTrue(is_leaf_chapter))"
|
|
|
@@ -101,6 +101,14 @@
|
|
|
<template v-if="!isTrue(is_leaf_chapter) && isTrue(is_show_submit_audit_button) && type === 'edit_preview'">
|
|
|
<span class="link" @click="submitChapterAllCoursewareToAuditFlow(nodeId)">提交审核</span>
|
|
|
</template>
|
|
|
+ <!-- 章节收缩/展开图标-->
|
|
|
+ <span v-if="!isTrue(is_leaf_chapter)" class="tree-arrow">
|
|
|
+ <i
|
|
|
+ :class="isChapterCollapsed(nodeId) ? 'el-icon-arrow-right' : 'el-icon-arrow-down'"
|
|
|
+ :title="isChapterCollapsed(nodeId) ? '展开' : '收缩'"
|
|
|
+ @click.stop="toggleChapterCollapse(nodeId)"
|
|
|
+ ></i>
|
|
|
+ </span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</aside>
|
|
|
@@ -407,6 +415,18 @@
|
|
|
inactive-value="zh-Hans"
|
|
|
inactive-text="简体"
|
|
|
/>
|
|
|
+ <div class="setting-item">
|
|
|
+ <span class="setting-item__label">字号</span>
|
|
|
+ <el-slider
|
|
|
+ v-model="fontScalePercent"
|
|
|
+ class="setting-item__slider"
|
|
|
+ :min="80"
|
|
|
+ :max="150"
|
|
|
+ :step="5"
|
|
|
+ :show-tooltip="false"
|
|
|
+ />
|
|
|
+ <span class="setting-item__value">{{ fontScalePercent }}%</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -522,6 +542,7 @@ import { toggleFullScreen } from '@/utils/common';
|
|
|
import * as OpenCC from 'opencc-js';
|
|
|
import { isTrue } from '@/utils/validate';
|
|
|
import { buildCoursewareStyle } from '@/views/book/courseware/preview/common/utils/coursewareStyle';
|
|
|
+import { scaleComponentListFontSize } from '@/views/book/courseware/preview/common/utils/fontSizeScale';
|
|
|
import { CreateCoursewarePreviewURL } from '@/api/app';
|
|
|
|
|
|
export default {
|
|
|
@@ -629,8 +650,10 @@ export default {
|
|
|
background: {},
|
|
|
},
|
|
|
node_list: [],
|
|
|
+ collapsedNodeIds: [], // 已收缩的章节节点 id
|
|
|
data: { row_list: [] },
|
|
|
component_list: [],
|
|
|
+ raw_component_list: [], // 未缩放字号的原始组件数据
|
|
|
content_group_row_list: [],
|
|
|
remark_list: [],
|
|
|
remark_list_obj: {}, // 存放以组件为对象的批注数组
|
|
|
@@ -687,6 +710,7 @@ export default {
|
|
|
langList: [],
|
|
|
lang: 'ZH',
|
|
|
chinese: 'zh-Hans', // 语言简体中文zh-Hans,繁体中文zh-Hant
|
|
|
+ fontScalePercent: 100, // 预览字号缩放百分比(80-150)
|
|
|
showPinYin: 'false',
|
|
|
curSelectId: this.id,
|
|
|
navigationShow: true,
|
|
|
@@ -748,6 +772,27 @@ export default {
|
|
|
computedCommonPreviewStyle() {
|
|
|
return buildCoursewareStyle(this.background, 'commonPreview');
|
|
|
},
|
|
|
+ /**
|
|
|
+ * 教材章节树实际展示的节点列表:过滤掉已收缩节点下的所有子节点
|
|
|
+ * @returns {Array} 需要展示的章节节点列表
|
|
|
+ */
|
|
|
+ visibleNodeList() {
|
|
|
+ let collapsedDeep = 0; // 当前被收缩的祖先节点层级,0 表示没有收缩
|
|
|
+ return (this.node_list || []).filter((node) => {
|
|
|
+ const deep = Number(node.deep) || 1;
|
|
|
+ // 处于已收缩节点之下的子节点直接过滤掉
|
|
|
+ if (collapsedDeep && deep > collapsedDeep) return false;
|
|
|
+ collapsedDeep = this.collapsedNodeIds.includes(node.id) ? deep : 0;
|
|
|
+ return true;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 预览字号缩放比例
|
|
|
+ * @returns {number} 缩放比例,如 1.2
|
|
|
+ */
|
|
|
+ fontScaleRatio() {
|
|
|
+ return this.fontScalePercent / 100;
|
|
|
+ },
|
|
|
},
|
|
|
watch: {
|
|
|
curSelectId() {
|
|
|
@@ -767,6 +812,9 @@ export default {
|
|
|
|
|
|
this.multimediaHandleChange();
|
|
|
},
|
|
|
+ fontScaleRatio() {
|
|
|
+ this.applyFontScale();
|
|
|
+ },
|
|
|
},
|
|
|
created() {
|
|
|
if (this.id) {
|
|
|
@@ -924,12 +972,22 @@ export default {
|
|
|
});
|
|
|
|
|
|
if (content_group_row_list) this.content_group_row_list = JSON.parse(content_group_row_list) || [];
|
|
|
+
|
|
|
+ // 保留未缩放的原始组件数据,字号缩放时都从原始数据重新派生,避免多次缩放叠加
|
|
|
+ this.raw_component_list = this.component_list;
|
|
|
+ this.applyFontScale();
|
|
|
})
|
|
|
.catch((error) => {
|
|
|
if (this.isCanceledRequest(error)) return null;
|
|
|
throw error;
|
|
|
});
|
|
|
},
|
|
|
+ /**
|
|
|
+ * 按当前字号比例重新派生展示用的组件列表
|
|
|
+ */
|
|
|
+ applyFontScale() {
|
|
|
+ this.component_list = scaleComponentListFontSize(this.raw_component_list, this.fontScaleRatio);
|
|
|
+ },
|
|
|
getLangList() {
|
|
|
GetLanguageTypeList({
|
|
|
book_id: this.courseware_info.book_id,
|
|
|
@@ -1378,6 +1436,26 @@ export default {
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
+ * 判断章节节点是否已收缩
|
|
|
+ * @param {string} nodeId - 节点ID
|
|
|
+ * @returns {boolean} - 是否已收缩
|
|
|
+ */
|
|
|
+ isChapterCollapsed(nodeId) {
|
|
|
+ return this.collapsedNodeIds.includes(nodeId);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 切换章节节点的收缩/展开状态
|
|
|
+ * @param {string} nodeId - 节点ID
|
|
|
+ */
|
|
|
+ toggleChapterCollapse(nodeId) {
|
|
|
+ const index = this.collapsedNodeIds.indexOf(nodeId);
|
|
|
+ if (index === -1) {
|
|
|
+ this.collapsedNodeIds.push(nodeId);
|
|
|
+ } else {
|
|
|
+ this.collapsedNodeIds.splice(index, 1);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
* 选择节点
|
|
|
* @param {string} nodeId - 节点ID
|
|
|
* @param {boolean} isLeaf - 是否是叶子节点
|
|
|
@@ -1849,8 +1927,26 @@ export default {
|
|
|
computedSelectedGroupCoursewareInfo() {
|
|
|
return this.$refs.courseware.computedSelectedGroupCoursewareInfo();
|
|
|
},
|
|
|
- saveCoursewareStyleTemplate(data) {
|
|
|
- return this.$refs.courseware.saveCoursewareStyleTemplate(data);
|
|
|
+ /**
|
|
|
+ * 保存为样式模板
|
|
|
+ * 字号缩放仅用于预览展示,保存前临时还原为原始数据,避免把缩放后的字号写入模板
|
|
|
+ * @param {object} data 保存参数
|
|
|
+ * @returns {Promise} 保存结果
|
|
|
+ */
|
|
|
+ async saveCoursewareStyleTemplate(data) {
|
|
|
+ if (this.fontScaleRatio === 1 || this.raw_component_list.length === 0) {
|
|
|
+ return this.$refs.courseware.saveCoursewareStyleTemplate(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ const scaledComponentList = this.component_list;
|
|
|
+ try {
|
|
|
+ this.component_list = this.raw_component_list;
|
|
|
+ await this.$nextTick();
|
|
|
+ return await this.$refs.courseware.saveCoursewareStyleTemplate(data);
|
|
|
+ } finally {
|
|
|
+ this.component_list = scaledComponentList;
|
|
|
+ await this.$nextTick();
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
async submitChapterAllCoursewareToAuditFlow(chapter_id) {
|
|
|
@@ -1877,13 +1973,14 @@ export default {
|
|
|
* @param {object} background - 背景信息对象
|
|
|
*/
|
|
|
updateComponentBackground({ courseware_id, component_id }, background) {
|
|
|
- const component = this.component_list.find(
|
|
|
- (c) => c.courseware_id === courseware_id && c.component_id === component_id,
|
|
|
- );
|
|
|
+ // 缩放后的组件列表是原始列表的浅拷贝,两处都需同步,避免切换字号后组件背景丢失
|
|
|
+ [this.component_list, this.raw_component_list].forEach((list) => {
|
|
|
+ const component = list.find((c) => c.courseware_id === courseware_id && c.component_id === component_id);
|
|
|
|
|
|
- if (component) {
|
|
|
- this.$set(component, 'background', background);
|
|
|
- }
|
|
|
+ if (component) {
|
|
|
+ this.$set(component, 'background', background);
|
|
|
+ }
|
|
|
+ });
|
|
|
},
|
|
|
/**
|
|
|
* 获取 main-container 视口内第一个可见的组件 id
|
|
|
@@ -2214,6 +2311,21 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .tree-arrow {
|
|
|
+ display: inline-flex;
|
|
|
+ flex: none;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 16px;
|
|
|
+ margin-left: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+
|
|
|
+ i {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.name {
|
|
|
flex: 1;
|
|
|
padding: 4px 8px 4px 4px;
|
|
|
@@ -2443,6 +2555,29 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
|
|
|
&-content {
|
|
|
padding: 10px;
|
|
|
}
|
|
|
+
|
|
|
+ &-item {
|
|
|
+ display: flex;
|
|
|
+ column-gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 20px;
|
|
|
+
|
|
|
+ &__label {
|
|
|
+ font-size: 14px;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__slider {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__value {
|
|
|
+ width: 42px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #4095e5;
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|