|
|
@@ -0,0 +1,1801 @@
|
|
|
+<template>
|
|
|
+ <div class="common-preview">
|
|
|
+ <div class="common-preview__header">
|
|
|
+ <div class="menu-container">
|
|
|
+ {{ courseware_info.book_name }}
|
|
|
+ </div>
|
|
|
+ <div class="courseware">
|
|
|
+ <div class="permission-control">
|
|
|
+ <span class="student"> 学生:张三 </span>
|
|
|
+ <el-checkbox v-model="permissionControl.can_answer">可作答</el-checkbox>
|
|
|
+ <el-checkbox v-model="permissionControl.can_judge_correct">可判断对错(客观题)</el-checkbox>
|
|
|
+ <el-checkbox v-model="permissionControl.can_show_answer">可查看答案</el-checkbox>
|
|
|
+ <el-checkbox v-model="permissionControl.can_correct">可批改</el-checkbox>
|
|
|
+ <el-checkbox v-model="permissionControl.can_check_correct">可查看批改</el-checkbox>
|
|
|
+ </div>
|
|
|
+ <!-- <span class="link">
|
|
|
+ <el-select v-model="lang" placeholder="请选择语言" size="mini" class="lang-select">
|
|
|
+ <el-option v-for="item in langList" :key="item.type" :label="item.name" :value="item.type" />
|
|
|
+ </el-select>
|
|
|
+ </span>
|
|
|
+ <span class="link">
|
|
|
+ <el-checkbox v-model="chinese" true-label="zh-Hant" false-label="zh-Hans">繁体</el-checkbox>
|
|
|
+ </span> -->
|
|
|
+ <div class="operator">
|
|
|
+ <slot name="operator" :courseware="courseware_info"></slot>
|
|
|
+ <span class="link">提交答案</span>
|
|
|
+ <span class="link">读取答案</span>
|
|
|
+ <span class="link">提交批改</span>
|
|
|
+ <span class="link">读取批改</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="audit-content">
|
|
|
+ <div ref="previewMain" class="main-container" :style="{ paddingRight: sidebarShow ? '15px' : '315px' }">
|
|
|
+ <main :class="['preview-main']">
|
|
|
+ <div class="preview-left"></div>
|
|
|
+ <CoursewarePreview
|
|
|
+ v-if="courseware_info.book_name"
|
|
|
+ ref="courserware"
|
|
|
+ :is-show-group="isShowGroup"
|
|
|
+ :group-show-all="groupShowAll"
|
|
|
+ :group-row-list="content_group_row_list"
|
|
|
+ :data="data"
|
|
|
+ :courseware-id="curSelectId"
|
|
|
+ :component-list="component_list"
|
|
|
+ :background="background"
|
|
|
+ :can-remark="isTrue(courseware_info.is_my_audit_task) && isTrue(courseware_info.is_can_add_audit_remark)"
|
|
|
+ :show-remark="false"
|
|
|
+ :project="project"
|
|
|
+ :component-remark-obj="remark_list_obj"
|
|
|
+ @computeScroll="computeScroll"
|
|
|
+ @editNote="handEditNote"
|
|
|
+ @saveCollect="saveCollect"
|
|
|
+ />
|
|
|
+ <div class="preview-right"></div>
|
|
|
+ </main>
|
|
|
+
|
|
|
+ <!-- 右侧菜单栏 - 收缩 -->
|
|
|
+ <aside v-if="!sidebarShow && !isFullScreen" class="sidebar-bar">
|
|
|
+ <aside class="toolbar">
|
|
|
+ <div class="toolbar-special">
|
|
|
+ <img :src="require('@/assets/icon/sidebar-fullscreen.png')" alt="全屏" @click="fullScreen" />
|
|
|
+ <img :src="require('@/assets/icon/sidebar-toolkit.png')" alt="工具箱" />
|
|
|
+ <img :src="require(`@/assets/icon/arrow-down.png`)" alt="伸缩" @click="toggleSidebarShow" />
|
|
|
+ </div>
|
|
|
+ </aside>
|
|
|
+ </aside>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="!sidebarShow" class="back-top" @click="backTop">
|
|
|
+ <img :src="require(`@/assets/icon/back-top.png`)" alt="返回顶部" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右侧工具栏 -->
|
|
|
+ <aside v-if="sidebarShow" ref="sidebarMenu" class="sidebar">
|
|
|
+ <aside class="toolbar">
|
|
|
+ <div class="toolbar-special">
|
|
|
+ <img :src="require('@/assets/icon/sidebar-fullscreen.png')" alt="全屏" @click="fullScreen" />
|
|
|
+ <img :src="require('@/assets/icon/sidebar-toolkit.png')" alt="工具箱" />
|
|
|
+ </div>
|
|
|
+ <div v-if="sidebarShow" class="toolbar-list">
|
|
|
+ <div
|
|
|
+ v-for="{ icon, title, handle, param, children } in sidebarIconList"
|
|
|
+ :key="icon"
|
|
|
+ :class="['sidebar-item', { active: curToolbarIcon === icon }]"
|
|
|
+ :title="title"
|
|
|
+ @click="handleSidebarClick(handle, param, icon, children)"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="sidebar-icon icon-mask"
|
|
|
+ :style="{
|
|
|
+ backgroundColor: curToolbarIcon === icon ? '#fff' : '#1E2129',
|
|
|
+ maskImage: `url(${require(`@/assets/icon/sidebar-${icon}.svg`)})`,
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="adjustable" @click="toggleSidebarShow">
|
|
|
+ <img :src="require(`@/assets/icon/arrow-up.png`)" alt="伸缩" />
|
|
|
+ </div>
|
|
|
+ </aside>
|
|
|
+ <div class="content">
|
|
|
+ <div v-if="curToolbarIcon === 'search'" class="resource_box">
|
|
|
+ <h5>{{ drawerTitle }}</h5>
|
|
|
+ <div style="height: 40px"></div>
|
|
|
+ <el-row :gutter="10" style="margin: 5px">
|
|
|
+ <el-col :span="16">
|
|
|
+ <el-input v-model="searchContent" placeholder="请输入文本内容" clearable />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <el-button type="primary" @click="querySearchList"> 查询 </el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <div>
|
|
|
+ <el-table :data="searchList" :show-header="false">
|
|
|
+ <!-- <el-table-column prop="courseware_name" label="课件" />
|
|
|
+ <el-table-column prop="component_type" label="组件" /> -->
|
|
|
+ <el-table-column>
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.courseware_name + ' / ' + row.component_type_name }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="" width="50">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-link type="primary" @click="handleLocation(row, 3)">定位</el-link>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- <div v-if="curToolbarIcon === 'totalResources'" class="resource_box"></div> -->
|
|
|
+ <div v-if="['image', 'audio', 'video'].includes(twoCurToolbarIcon)" class="resource_box">
|
|
|
+ <div class="source-toolbar-list">
|
|
|
+ <div
|
|
|
+ v-for="{ icon, title, handle, param } in twoSidebarList"
|
|
|
+ :key="icon"
|
|
|
+ :class="['sidebar-item', { active: twoCurToolbarIcon === icon }]"
|
|
|
+ :title="title"
|
|
|
+ @click="handleSidebarClick(handle, param, icon, '', 2)"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="sidebar-icon icon-mask"
|
|
|
+ :style="{
|
|
|
+ backgroundColor: twoCurToolbarIcon === icon ? '#fff' : '#1E2129',
|
|
|
+ maskImage: `url(${require(`@/assets/icon/sidebar-${icon}.svg`)})`,
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div style="height: 40px"></div>
|
|
|
+
|
|
|
+ <el-collapse v-model="activeBookChapterId" accordion @change="multimediaHandleChange">
|
|
|
+ <el-collapse-item
|
|
|
+ v-for="chapter in bookChapterList"
|
|
|
+ :key="chapter.id"
|
|
|
+ :name="chapter.id"
|
|
|
+ :title="chapter.name"
|
|
|
+ >
|
|
|
+ <!-- 加载状态 -->
|
|
|
+ <div v-if="multimediaLoadingStates" class="loading-text">加载中...</div>
|
|
|
+
|
|
|
+ <!-- 加载完成显示数据 -->
|
|
|
+ <div v-else-if="chapter.data">
|
|
|
+ <ul class="scroll-container" infinite-scroll-disabled="disabled" :infinite-scroll-immediate="false">
|
|
|
+ <li
|
|
|
+ v-for="(item, index) in chapter.data"
|
|
|
+ :key="`${chapter.id}-${index}`"
|
|
|
+ class="list-item"
|
|
|
+ @click="handleFileClick(item?.courseware_id, item?.component_id)"
|
|
|
+ >
|
|
|
+ <template v-if="parseInt(drawerType) === 0">
|
|
|
+ <el-image v-if="shouldShowItem(chapter, item)" :src="item.file_url" fit="contain" />
|
|
|
+ <div class="mark">
|
|
|
+ <span class="word">{{ item.file_name }}</span>
|
|
|
+ <el-link type="primary" class="el-icon-place linkLocation" @click="handleLocation(item, 3)" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="parseInt(drawerType) === 1">
|
|
|
+ <AudioPlay
|
|
|
+ v-if="shouldShowItem(chapter, item)"
|
|
|
+ view-size="middle"
|
|
|
+ :file-id="item.file_id"
|
|
|
+ :file-name="item.file_name.slice(0, item.file_name.lastIndexOf('.'))"
|
|
|
+ :show-slider="true"
|
|
|
+ :audio-index="index"
|
|
|
+ />
|
|
|
+ <div class="mark">
|
|
|
+ <span class="word"></span>
|
|
|
+ <el-link type="primary" class="el-icon-place linkLocation" @click="handleLocation(item, 3)" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="parseInt(drawerType) === 2">
|
|
|
+ <VideoPlay
|
|
|
+ v-if="shouldShowItem(chapter, item)"
|
|
|
+ view-size="small"
|
|
|
+ :file-id="item.file_id"
|
|
|
+ :video-index="index"
|
|
|
+ />
|
|
|
+ <div class="mark">
|
|
|
+ <span class="word">{{ item.file_name }}</span>
|
|
|
+ <el-link type="primary" class="el-icon-place linkLocation" @click="handleLocation(item, 3)" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 加载失败或未加载 -->
|
|
|
+ <div v-else class="error-text">没有资源</div>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="curToolbarIcon === 'collect'" class="resource_box">
|
|
|
+ <h5>{{ drawerTitle }}</h5>
|
|
|
+ <div style="height: 40px"></div>
|
|
|
+ <ul v-if="allCottectList.length > 0" class="card-box">
|
|
|
+ <li v-for="item in allCottectList" :key="item.id">
|
|
|
+ <span class="el-icon-notebook-2"> 原文</span>
|
|
|
+ <span>{{ item.text }}</span>
|
|
|
+ <div>
|
|
|
+ <el-button type="text" class="el-icon-delete" @click="handDelCollect(item.id)"> 删除</el-button>
|
|
|
+ <el-divider direction="vertical" />
|
|
|
+ <el-button type="text" class="el-icon-place" @click="handleLocation(item, 2)"> 定位</el-button>
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ <div v-if="curToolbarIcon === 'note'" class="resource_box">
|
|
|
+ <h5>{{ drawerTitle }}</h5>
|
|
|
+ <div style="height: 40px"></div>
|
|
|
+ <ul v-if="allNoteList.length > 0" class="card-box">
|
|
|
+ <li v-for="item in allNoteList" :key="item.id">
|
|
|
+ <span class="el-icon-notebook-2"> 原文</span>
|
|
|
+ <span>{{ item.text }}</span>
|
|
|
+ <el-divider class="mt10" />
|
|
|
+ <span v-html="item.note"></span>
|
|
|
+ <div>
|
|
|
+ <el-button type="text" class="el-icon-edit" @click="handEditNote(item)"> 编辑</el-button>
|
|
|
+ <el-divider direction="vertical" />
|
|
|
+ <el-button type="text" class="el-icon-delete" @click="handDelNote(item.id)"> 删除</el-button>
|
|
|
+ <el-divider direction="vertical" />
|
|
|
+ <el-button type="text" class="el-icon-place" @click="handleLocation(item, 1)"> 定位</el-button>
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ <template v-if="curToolbarIcon === 'audit'">
|
|
|
+ <AuditRemark :remark-list="remark_list" :is-audit="isShowAudit" @deleteRemarks="deleteRemarks" />
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="back-top" @click="backTop">
|
|
|
+ <img :src="require(`@/assets/icon/back-top.png`)" alt="返回顶部" />
|
|
|
+ </div>
|
|
|
+ </aside>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-dialog title="" :visible="visibleMindMap" width="1100px" class="audit-dialog" @close="dialogClose('MindMap')">
|
|
|
+ <MindMap
|
|
|
+ v-if="isChildDataLoad"
|
|
|
+ ref="mindMapRef"
|
|
|
+ :project-id="projectId"
|
|
|
+ :mind-map-json-data="mindMapJsonData"
|
|
|
+ @child-click="handleNodeClick"
|
|
|
+ />
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog
|
|
|
+ title=""
|
|
|
+ :visible="visibleVisNetwork"
|
|
|
+ width="1100px"
|
|
|
+ class="audit-dialog"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ @close="dialogClose('VisNetwork')"
|
|
|
+ >
|
|
|
+ <VisNetwork ref="visNetworkRef" :book-id="projectId" @child-click="handleNodeClick" />
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <ExplanatoryNoteDialog
|
|
|
+ ref="explanatoryNote"
|
|
|
+ :open.sync="editDialogOpen"
|
|
|
+ :init-data="oldRichData"
|
|
|
+ title-text="笔记"
|
|
|
+ @confirm="saveNote"
|
|
|
+ @cancel="delNote"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview.vue';
|
|
|
+import { isTrue } from '@/utils/validate';
|
|
|
+import MindMap from '@/components/MindMap.vue';
|
|
|
+import VideoPlay from '@/views/book/courseware/preview/components/common/VideoPlay.vue';
|
|
|
+import AudioPlay from '@/views/book/courseware/preview/components/common/AudioPlay.vue';
|
|
|
+import ExplanatoryNoteDialog from '@/components/ExplanatoryNoteDialog.vue';
|
|
|
+import VisNetwork from '@/components/VisNetwork.vue';
|
|
|
+import * as OpenCC from 'opencc-js';
|
|
|
+
|
|
|
+import { GetBookCoursewareInfo, GetCoursewareAuditRemarkList, GetProjectInfo } from '@/api/project';
|
|
|
+import {
|
|
|
+ ContentGetCoursewareContent_View,
|
|
|
+ ChapterGetBookChapterStructExpandList,
|
|
|
+ GetBookBaseInfo,
|
|
|
+ MangerGetBookMindMap,
|
|
|
+ GetBookChapterStructExpandList,
|
|
|
+ PageQueryBookResourceList,
|
|
|
+ GetLanguageTypeList,
|
|
|
+ GetBookUnifiedAttrib,
|
|
|
+ GetMyNoteList,
|
|
|
+ DeleteMyNote,
|
|
|
+ AddMyNote,
|
|
|
+ UpdateMyNote,
|
|
|
+ AddMyCollect,
|
|
|
+ GetMyCollectList,
|
|
|
+ DeleteMyCollect,
|
|
|
+ SearchBookContentText,
|
|
|
+} from '@/api/book';
|
|
|
+import { getLocalStore } from '@/utils/auth';
|
|
|
+import { toggleFullScreen } from '@/utils/common';
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'CommonPreview',
|
|
|
+ components: {
|
|
|
+ CoursewarePreview,
|
|
|
+ MindMap,
|
|
|
+ VideoPlay,
|
|
|
+ AudioPlay,
|
|
|
+ ExplanatoryNoteDialog,
|
|
|
+ VisNetwork,
|
|
|
+ },
|
|
|
+ provide() {
|
|
|
+ return {
|
|
|
+ getLang: () => this.lang,
|
|
|
+ getChinese: () => this.chinese,
|
|
|
+ getLangList: () => this.langList,
|
|
|
+ convertText: this.convertText,
|
|
|
+ getProjectId: () => this.projectId,
|
|
|
+ getSelectId: () => this.select_node,
|
|
|
+ getTitleList: () => this.title_list,
|
|
|
+ getPermissionControl: () => this.permissionControl,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ const sidebarIconList = [
|
|
|
+ // { icon: 'search', title: '搜索', handle: 'getSearch', param: { type: '5' } },
|
|
|
+ // { icon: 'mindmap', title: '思维导图', handle: 'openMindMap', param: {} },
|
|
|
+ // { icon: 'knowledge', title: '知识图谱', handle: 'openVisNetwork', param: {} },
|
|
|
+ // {
|
|
|
+ // icon: 'totalResources',
|
|
|
+ // title: '总资源',
|
|
|
+ // handle: '',
|
|
|
+ // param: {},
|
|
|
+ // children: [
|
|
|
+ // { icon: 'audio', title: '音频', handle: 'openDrawer', param: { type: '1' } },
|
|
|
+ // { icon: 'image', title: '图片', handle: 'openDrawer', param: { type: '0' } },
|
|
|
+ // { icon: 'video', title: '视频', handle: 'openDrawer', param: { type: '2' } },
|
|
|
+ // ],
|
|
|
+ // },
|
|
|
+ // { icon: 'collect', title: '收藏', handle: 'getCollect', param: { type: '3' } },
|
|
|
+ // { icon: 'note', title: '笔记', handle: 'getNote', param: { type: '4' } },
|
|
|
+ // { icon: 'translate', title: '翻译', handle: '', param: {} },
|
|
|
+ // { icon: 'setting', title: '设置', handle: '', param: {} },
|
|
|
+ ];
|
|
|
+
|
|
|
+ return {
|
|
|
+ id: getLocalStore('courseware_id'),
|
|
|
+ projectId: getLocalStore('book_id') || '',
|
|
|
+ task_id: getLocalStore('task_id') || '',
|
|
|
+ select_node: this.id,
|
|
|
+ courseware_info: {
|
|
|
+ book_name: '',
|
|
|
+ is_can_start_edit: 'false',
|
|
|
+ is_can_submit_audit: 'false',
|
|
|
+ is_can_audit_pass: 'false',
|
|
|
+ is_can_audit_reject: 'false',
|
|
|
+ is_can_add_audit_remark: 'false',
|
|
|
+ is_can_finish_audit: 'false',
|
|
|
+ is_can_request_shangjia_book: 'false',
|
|
|
+ is_can_request_rollback_project: 'false',
|
|
|
+ is_can_shangjia_book: 'false',
|
|
|
+ is_can_rollback_project: 'false',
|
|
|
+ },
|
|
|
+ background: {
|
|
|
+ background_image_url: '',
|
|
|
+ background_position: {
|
|
|
+ left: 0,
|
|
|
+ top: 0,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ node_list: [],
|
|
|
+ data: { row_list: [] },
|
|
|
+ component_list: [],
|
|
|
+ content_group_row_list: [],
|
|
|
+ remark_list: [],
|
|
|
+ remark_list_obj: {}, // 存放以组件为对象的数组
|
|
|
+ searchList: [],
|
|
|
+ searchContent: '',
|
|
|
+ visible: false,
|
|
|
+ remark_content: '',
|
|
|
+ submit_loading: false,
|
|
|
+ isTrue,
|
|
|
+ menuPosition: {
|
|
|
+ x: -1,
|
|
|
+ y: -1,
|
|
|
+ componentId: 'WHOLE',
|
|
|
+ },
|
|
|
+ curToolbarIcon: this.isShowAudit ? 'audit' : '',
|
|
|
+ sidebarIconList,
|
|
|
+ twoSidebarList: [],
|
|
|
+ twoCurToolbarIcon: '',
|
|
|
+ visibleMindMap: false,
|
|
|
+ visibleVisNetwork: false,
|
|
|
+ isChildDataLoad: false,
|
|
|
+ mindMapJsonData: {}, // 思维导图json数据
|
|
|
+ drawerType: '', // 抽屉类型
|
|
|
+ drawerStyle: {
|
|
|
+ top: '0',
|
|
|
+ height: '0',
|
|
|
+ right: '0',
|
|
|
+ },
|
|
|
+ page_capacity: 10,
|
|
|
+ cur_page: 1,
|
|
|
+ file_list: [],
|
|
|
+ total_count: 0,
|
|
|
+ loading: false,
|
|
|
+ lastLoadTime: 0,
|
|
|
+ minLoadInterval: 3 * 1000,
|
|
|
+ isShowGroup: false,
|
|
|
+ groupShowAll: true,
|
|
|
+ opencc: OpenCC.Converter({ from: 'cn', to: 'tw' }),
|
|
|
+ langList: [],
|
|
|
+ lang: 'ZH',
|
|
|
+ chinese: 'zh-Hans',
|
|
|
+ isJudgeCorrect: false,
|
|
|
+ isShowAnswer: false,
|
|
|
+ unified_attrib: {},
|
|
|
+ curSelectId: this.id,
|
|
|
+ sidebarShow: true,
|
|
|
+ project: {
|
|
|
+ editor: '', // 作者
|
|
|
+ cover_image_file_id: null, // 封面图片ID
|
|
|
+ cover_image_file_url: '', // 封面图片URL
|
|
|
+ },
|
|
|
+ allNoteList: [],
|
|
|
+ editDialogOpen: false,
|
|
|
+ oldRichData: {},
|
|
|
+ newSelectedInfo: null,
|
|
|
+ allCottectList: [],
|
|
|
+ bookChapterList: [],
|
|
|
+ book_id: '',
|
|
|
+ activeBookChapterId: '',
|
|
|
+ multimediaLoadingStates: true,
|
|
|
+ isFullScreen: false, // 是否全屏状态
|
|
|
+ title_list: [],
|
|
|
+ // 模拟答题权限控制
|
|
|
+ permissionControl: {
|
|
|
+ can_answer: false, // 可作答
|
|
|
+ can_judge_correct: false, // 可判断对错(客观题)
|
|
|
+ can_show_answer: false, // 可查看答案
|
|
|
+ can_correct: false, // 可批改
|
|
|
+ can_check_correct: false, // 可查看批改
|
|
|
+ },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ disabled() {
|
|
|
+ const result = this.loading || this.noMore;
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ noMore() {
|
|
|
+ const result = this.file_list.length >= this.total_count;
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ drawerTitle() {
|
|
|
+ const titleMap = {
|
|
|
+ 0: '图片资源',
|
|
|
+ 1: '音频资源',
|
|
|
+ 2: '视频资源',
|
|
|
+ 3: '收藏列表',
|
|
|
+ 4: '笔记列表',
|
|
|
+ 5: '搜索结果',
|
|
|
+ };
|
|
|
+ return titleMap[this.drawerType] || '资源列表';
|
|
|
+ },
|
|
|
+ shouldShowItem() {
|
|
|
+ return (chapter, item) => {
|
|
|
+ return this.activeBookChapterId === chapter.id && item && item.file_id;
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ isJudgeCorrect(newVal) {
|
|
|
+ if (!newVal) {
|
|
|
+ this.isShowAnswer = false;
|
|
|
+ }
|
|
|
+ this.simulateAnswer(newVal);
|
|
|
+ },
|
|
|
+ isShowAnswer() {
|
|
|
+ this.simulateAnswer();
|
|
|
+ },
|
|
|
+ curSelectId() {
|
|
|
+ if (this.curToolbarIcon === 'note') {
|
|
|
+ this.getNote();
|
|
|
+ } else if (this.curToolbarIcon === 'collect') {
|
|
|
+ this.getCollect();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.calcDrawerPosition();
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.getBookBaseInfo();
|
|
|
+ this.getBookChapterStructExpandList();
|
|
|
+ this.getCoursewareComponentContent_View(this.id);
|
|
|
+ this.getBookUnifiedAttr();
|
|
|
+ this.getProjectInfo();
|
|
|
+ // 监听全屏事件
|
|
|
+ document.addEventListener('fullscreenchange', () => {
|
|
|
+ if (document.fullscreenElement) {
|
|
|
+ this.isFullScreen = true;
|
|
|
+ } else {
|
|
|
+ this.isFullScreen = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ document.removeEventListener('fullscreenchange', () => {});
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ selectFirstLeafNode() {
|
|
|
+ if (!this.node_list || this.node_list.length === 0) return;
|
|
|
+ let node = this.node_list.find((x) => this.isTrue(x.is_leaf_chapter));
|
|
|
+ if (!node) return;
|
|
|
+ this.selectChapterNode(node.id, true);
|
|
|
+ },
|
|
|
+ getBookBaseInfo() {
|
|
|
+ GetBookBaseInfo({ id: this.projectId }).then(({ book_info }) => {
|
|
|
+ this.courseware_info = { ...this.courseware_info, ...book_info, book_name: book_info.name };
|
|
|
+ if (book_info.cover_image_file_id) {
|
|
|
+ this.project.cover_image_file_url = book_info.cover_image_file_url;
|
|
|
+ }
|
|
|
+ if (book_info.editor) {
|
|
|
+ this.project.editor = book_info.editor;
|
|
|
+ }
|
|
|
+ if (book_info.cover_image_file_url) {
|
|
|
+ this.project.cover_image_file_url = book_info.cover_image_file_url;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ getProjectInfo() {
|
|
|
+ GetProjectInfo({ id: this.projectId }).then(({ project_info }) => {
|
|
|
+ if (project_info.cover_image_file_url) {
|
|
|
+ this.project = project_info;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 得到教材课件信息
|
|
|
+ * @param {string} id - 课件ID
|
|
|
+ */
|
|
|
+ getBookCoursewareInfo(id) {
|
|
|
+ GetBookCoursewareInfo({ id, is_contain_producer: 'true', is_contain_auditor: 'true' }).then(
|
|
|
+ ({ courseware_info }) => {
|
|
|
+ this.courseware_info = { ...this.courseware_info, ...courseware_info };
|
|
|
+ this.getLangList();
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 得到课件内容(展示内容)
|
|
|
+ * @param {string} id - 课件ID
|
|
|
+ */
|
|
|
+ getCoursewareComponentContent_View(id) {
|
|
|
+ ContentGetCoursewareContent_View({ id }).then(
|
|
|
+ ({ content, component_list, content_group_row_list, title_list }) => {
|
|
|
+ if (title_list) this.title_list = title_list || [];
|
|
|
+ if (content) {
|
|
|
+ const _content = JSON.parse(content);
|
|
|
+ this.data = _content;
|
|
|
+ this.background = {
|
|
|
+ background_image_url: _content.background_image_url,
|
|
|
+ background_position: _content.background_position,
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ this.data = { row_list: [] };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (component_list) this.component_list = component_list;
|
|
|
+ this.component_list.forEach((x) => {
|
|
|
+ if (x.component_type === 'audio') {
|
|
|
+ let _c = JSON.parse(x.content);
|
|
|
+ let p = _c.property || {};
|
|
|
+ if (!p.file_name_display_mode) p.file_name_display_mode = 'true';
|
|
|
+ if (p.view_method === 'independent' && !p.style_mode) {
|
|
|
+ p.style_mode = 'middle';
|
|
|
+ }
|
|
|
+ if (!p.style_mode) p.style_mode = 'big';
|
|
|
+ if (p.view_method === 'icon') {
|
|
|
+ p.file_name_display_mode = 'false';
|
|
|
+ p.view_method = 'independent';
|
|
|
+ p.style_mode = 'small';
|
|
|
+ }
|
|
|
+
|
|
|
+ x.content = JSON.stringify(_c);
|
|
|
+ } else if (x.component_type === 'richtext') {
|
|
|
+ let _c = JSON.parse(x.content);
|
|
|
+ let p = _c.property || {};
|
|
|
+ let lev = Number(p.title_style_level);
|
|
|
+ if (p.is_title !== 'true' || lev < 1 || !_c.content) return;
|
|
|
+
|
|
|
+ let style = title_list.find((y) => y.level === lev) || {};
|
|
|
+ if (style && style.style) {
|
|
|
+ style = JSON.parse(style.style);
|
|
|
+ let c_text = _c.content;
|
|
|
+
|
|
|
+ const parser = new DOMParser();
|
|
|
+ const doc = parser.parseFromString(c_text, 'text/html');
|
|
|
+ const body = doc.body;
|
|
|
+ const pElements = body.querySelectorAll('p');
|
|
|
+ this.processHtmlString(pElements, style);
|
|
|
+ _c.content = body.innerHTML;
|
|
|
+
|
|
|
+ x.content = JSON.stringify(_c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (content_group_row_list) this.content_group_row_list = JSON.parse(content_group_row_list) || [];
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ processHtmlString(pElements, styleConfig, isClear) {
|
|
|
+ pElements.forEach((pElement) => {
|
|
|
+ if (isClear) {
|
|
|
+ pElement.removeAttribute('style');
|
|
|
+ } else {
|
|
|
+ const style = pElement.style;
|
|
|
+
|
|
|
+ if (styleConfig.font) style.fontFamily = styleConfig.font;
|
|
|
+ if (styleConfig.font_size) style.fontSize = styleConfig.font_size;
|
|
|
+ if (styleConfig.text_color) style.color = styleConfig.text_color;
|
|
|
+
|
|
|
+ style.fontWeight = styleConfig.bold === 'true' ? 'bold' : 'normal';
|
|
|
+ style.fontStyle = styleConfig.italic === 'true' ? 'italic' : 'normal';
|
|
|
+ if (styleConfig.indent || styleConfig.indent === 0) style.marginLeft = `${styleConfig.indent}px`;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ getLangList() {
|
|
|
+ GetLanguageTypeList({ book_id: this.courseware_info.book_id, is_contain_zh: 'true' }).then(
|
|
|
+ ({ language_type_list }) => {
|
|
|
+ this.langList = language_type_list;
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 得到教材章节结构展开列表
|
|
|
+ */
|
|
|
+ getBookChapterStructExpandList() {
|
|
|
+ ChapterGetBookChapterStructExpandList({
|
|
|
+ book_id: this.projectId,
|
|
|
+ node_deep_mode: 0,
|
|
|
+ is_contain_producer: 'true',
|
|
|
+ is_contain_auditor: 'true',
|
|
|
+ }).then(({ node_list }) => {
|
|
|
+ this.node_list = node_list;
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ getBookUnifiedAttr() {
|
|
|
+ GetBookUnifiedAttrib({ book_id: this.projectId }).then(({ content }) => {
|
|
|
+ if (content) {
|
|
|
+ this.unified_attrib = JSON.parse(content);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 选择节点
|
|
|
+ * @param {string} nodeId - 节点ID
|
|
|
+ */
|
|
|
+ selectNode(nodeId) {
|
|
|
+ this.getCoursewareComponentContent_View(nodeId);
|
|
|
+ this.getBookCoursewareInfo(nodeId);
|
|
|
+ this.getCoursewareAuditRemarkList(nodeId);
|
|
|
+ this.select_node = nodeId;
|
|
|
+ },
|
|
|
+ // 审校批注列表
|
|
|
+ getCoursewareAuditRemarkList(id) {
|
|
|
+ this.remark_list = [];
|
|
|
+ let remarkListObj = {};
|
|
|
+ GetCoursewareAuditRemarkList({
|
|
|
+ courseware_id: id,
|
|
|
+ }).then(({ remark_list }) => {
|
|
|
+ this.remark_list = remark_list;
|
|
|
+ if (!remark_list) return;
|
|
|
+ remarkListObj = remark_list.reduce((acc, item) => {
|
|
|
+ if (!acc[item.component_id]) {
|
|
|
+ acc[item.component_id] = [];
|
|
|
+ }
|
|
|
+ acc[item.component_id].push(item);
|
|
|
+ return acc;
|
|
|
+ }, {});
|
|
|
+
|
|
|
+ this.remark_list_obj = remarkListObj;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ dialogClose(type) {
|
|
|
+ this[`visible${type}`] = false;
|
|
|
+ },
|
|
|
+ // 计算previewMain滑动距离
|
|
|
+ computeScroll() {
|
|
|
+ this.$refs.courserware.handleResult(
|
|
|
+ this.$refs.previewMain.scrollTop,
|
|
|
+ this.$refs.previewMain.scrollLeft,
|
|
|
+ this.select_node,
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理侧边栏图标点击事件
|
|
|
+ * @param {string} handle - 处理函数名
|
|
|
+ * @param {any} param - 处理函数参数
|
|
|
+ * @param {string} icon - 图标名称
|
|
|
+ */
|
|
|
+ handleSidebarClick(handle, param, icon, children, barLevel) {
|
|
|
+ if (typeof handle === 'string' && handle && typeof this[handle] === 'function') {
|
|
|
+ this[handle](param);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (barLevel === 2) {
|
|
|
+ this.twoCurToolbarIcon = icon;
|
|
|
+ } else {
|
|
|
+ this.curToolbarIcon = icon;
|
|
|
+ this.twoCurToolbarIcon = '';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (children && children.length > 0 && Array.isArray(children)) {
|
|
|
+ this.twoSidebarList = children;
|
|
|
+ this.twoCurToolbarIcon = children[0].icon;
|
|
|
+ this.openDrawer(children[0].param);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 打开知识图谱
|
|
|
+ */
|
|
|
+ openMindMap() {
|
|
|
+ MangerGetBookMindMap({ book_id: this.projectId }).then(({ content }) => {
|
|
|
+ if (content) {
|
|
|
+ this.mindMapJsonData = JSON.parse(content);
|
|
|
+ this.isChildDataLoad = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.visibleMindMap = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ async handleNodeClick(data) {
|
|
|
+ let [nodeId, componentId] = data.split('#');
|
|
|
+ if (nodeId) this.selectNode(nodeId);
|
|
|
+ if (componentId) {
|
|
|
+ let node = await this.$refs.courserware.findChildComponentByKey(componentId);
|
|
|
+ if (node) {
|
|
|
+ await this.$nextTick();
|
|
|
+ this.$refs.previewMain.scrollTo({
|
|
|
+ top: node.$el.offsetTop - 50,
|
|
|
+ left: node.$el.offsetLeft - 50,
|
|
|
+ behavior: 'smooth',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.visibleMindMap = false;
|
|
|
+ this.visibleVisNetwork = false;
|
|
|
+ },
|
|
|
+
|
|
|
+ async openVisNetwork() {
|
|
|
+ this.visibleVisNetwork = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 计算抽屉滑出位置
|
|
|
+ calcDrawerPosition() {
|
|
|
+ const menu = this.$refs.sidebarMenu;
|
|
|
+ if (menu) {
|
|
|
+ const rect = menu.getBoundingClientRect();
|
|
|
+ this.drawerStyle = {
|
|
|
+ top: `${rect.top}px`,
|
|
|
+ height: `${rect.height}px`,
|
|
|
+ left: `${rect.right - 240}px`,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 打开抽屉并初始化加载
|
|
|
+ * @param {Object} param - 抽屉参数
|
|
|
+ * @param {string} param.type - 抽屉类型(0: 图片, 1: 音频, 2: 视频)
|
|
|
+ */
|
|
|
+ openDrawer({ type }) {
|
|
|
+ if (this.drawerType === type) {
|
|
|
+ this.drawerType = '';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 重置所有加载状态
|
|
|
+ this.activeBookChapterId = '';
|
|
|
+ this.resetLoadState();
|
|
|
+ this.drawerType = type;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ // 确保DOM更新后触发加载
|
|
|
+ // this.loadMore();
|
|
|
+ this.loadBookChapterStructExpandList();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ openAudit() {
|
|
|
+ this.drawerType = '';
|
|
|
+ },
|
|
|
+ resetLoadState() {
|
|
|
+ this.cur_page = 1;
|
|
|
+ this.file_list = [];
|
|
|
+ this.total_count = 0;
|
|
|
+ this.loading = false;
|
|
|
+ this.lastLoadTime = 0; // 重置时间戳,允许立即加载
|
|
|
+ this.loadCount = 0;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 加载章节
|
|
|
+ */
|
|
|
+ async loadBookChapterStructExpandList() {
|
|
|
+ const params = {
|
|
|
+ book_id: this.projectId,
|
|
|
+ node_deep_mode: 3, // 节点深度模式 0【全部】,1【只查询章节】2【只查询非叶子章节】3【只查询第一层】
|
|
|
+ is_contain_root_node: 'false', // 是否包含根节点(把教材作为根节点)
|
|
|
+ is_contain_producer: 'false', // 是否包含制作人信息
|
|
|
+ is_contain_auditor: 'false', // 是否包含审核人信息
|
|
|
+ };
|
|
|
+ await GetBookChapterStructExpandList(params).then(({ node_list }) => {
|
|
|
+ this.bookChapterList = node_list || [];
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 加载章节下的资源
|
|
|
+ */
|
|
|
+ async loadmultimediaList() {
|
|
|
+ const params = {
|
|
|
+ page_capacity: this.page_capacity,
|
|
|
+ cur_page: this.cur_page,
|
|
|
+ book_id: this.projectId,
|
|
|
+ book_chapter_node_id: this.activeBookChapterId,
|
|
|
+ type: parseInt(this.drawerType),
|
|
|
+ };
|
|
|
+ await PageQueryBookResourceList(params)
|
|
|
+ .then(({ total_count, resource_list }) => {
|
|
|
+ this.total_count = total_count;
|
|
|
+ this.file_list = resource_list || [];
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.loading = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 点击章节,切换数据
|
|
|
+ */
|
|
|
+ async multimediaHandleChange() {
|
|
|
+ const item = this.bookChapterList.find((item) => item.id === this.activeBookChapterId);
|
|
|
+ if (item) {
|
|
|
+ this.multimediaLoadingStates = true;
|
|
|
+ if (!item.data && !item.error) {
|
|
|
+ await this.loadmultimediaList();
|
|
|
+ let tmpList = this.file_list && this.file_list.length > 0 ? [...this.file_list] : null;
|
|
|
+ this.$set(item, 'data', tmpList);
|
|
|
+ this.multimediaLoadingStates = false;
|
|
|
+ } else {
|
|
|
+ this.multimediaLoadingStates = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载更多数据
|
|
|
+ async loadMore() {
|
|
|
+ const now = Date.now();
|
|
|
+ // 只有当lastLoadTime不为0(不是第一次)且时间间隔太短时才return
|
|
|
+ if (this.lastLoadTime > 0 && now - this.lastLoadTime < this.minLoadInterval) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.disabled || this.loading) {
|
|
|
+ if (this.lastLoadTime > 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.loading = true;
|
|
|
+ const params = {
|
|
|
+ page_capacity: this.page_capacity,
|
|
|
+ cur_page: this.cur_page,
|
|
|
+ book_id: this.projectId,
|
|
|
+ book_chapter_node_id: this.activeBookChapterId,
|
|
|
+ type: parseInt(this.drawerType),
|
|
|
+ };
|
|
|
+ await PageQueryBookResourceList(params)
|
|
|
+ .then(({ total_count, resource_list }) => {
|
|
|
+ this.total_count = total_count;
|
|
|
+ // 记录加载前的滚动高度
|
|
|
+ const scrollContainer = this.$el.querySelector('.scroll-container');
|
|
|
+ const isAtBottom = this.isScrollAtBottom(scrollContainer);
|
|
|
+
|
|
|
+ this.file_list = this.cur_page === 1 ? resource_list : [...this.file_list, ...resource_list];
|
|
|
+ if (!resource_list || resource_list.length === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.cur_page += 1;
|
|
|
+
|
|
|
+ // 只有当前已经在底部时才微调滚动位置
|
|
|
+ if (isAtBottom) {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ // 轻微向上滚动,创造滚动空间
|
|
|
+ scrollContainer.scrollTop -= 5;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.loading = false;
|
|
|
+ this.lastLoadTime = now;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ isScrollAtBottom(container) {
|
|
|
+ if (!container) return false;
|
|
|
+ return container.scrollHeight - container.scrollTop <= container.clientHeight + 5;
|
|
|
+ },
|
|
|
+ async handleFileClick(courseware_id, component_id) {
|
|
|
+ if (courseware_id) this.selectNode(courseware_id);
|
|
|
+ if (component_id) {
|
|
|
+ let node = await this.$refs.courserware.findChildComponentByKey(component_id);
|
|
|
+ if (node) {
|
|
|
+ await this.$nextTick();
|
|
|
+ this.$refs.previewMain.scrollTo({
|
|
|
+ top: node.offsetTop - 50,
|
|
|
+ left: node.offsetLeft - 50,
|
|
|
+ behavior: 'smooth',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文本转换
|
|
|
+ * @param {string} text - 要转换的文本
|
|
|
+ * @returns {string} - 转换后的文本
|
|
|
+ */
|
|
|
+ convertText(text) {
|
|
|
+ if (this.chinese === 'zh-Hant' && this.opencc) {
|
|
|
+ try {
|
|
|
+ if (/<[a-z][\s\S]*>/i.test(text)) {
|
|
|
+ return this.convertHtmlPreserveAttributes(text);
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ return text;
|
|
|
+ }
|
|
|
+ return this.opencc(text);
|
|
|
+ }
|
|
|
+ return text;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用OpenCC解析HTML并仅转换文本节点,保留属性(包括内联样式/font-family)保持不变。防止字体名称像“微软雅黑' 正在转换为'微軟雅黑'.
|
|
|
+ */
|
|
|
+ convertHtmlPreserveAttributes(html) {
|
|
|
+ try {
|
|
|
+ const parser = new DOMParser();
|
|
|
+ const doc = parser.parseFromString(html, 'text/html');
|
|
|
+
|
|
|
+ // 跳过这些标记内的转换
|
|
|
+ const skipTags = new Set(['SCRIPT', 'STYLE']);
|
|
|
+
|
|
|
+ const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT, null, false);
|
|
|
+ let node = walker.nextNode();
|
|
|
+ while (node) {
|
|
|
+ const parent = node.parentNode;
|
|
|
+ if (parent && !skipTags.has(parent.nodeName)) {
|
|
|
+ const v = node.nodeValue;
|
|
|
+ if (v && v.trim()) {
|
|
|
+ node.nodeValue = this.opencc(v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ node = walker.nextNode();
|
|
|
+ }
|
|
|
+
|
|
|
+ return doc.body.innerHTML;
|
|
|
+ } catch (err) {
|
|
|
+ try {
|
|
|
+ return this.opencc(html);
|
|
|
+ } catch (e) {
|
|
|
+ return html;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ simulateAnswer(disabled = true) {
|
|
|
+ this.$refs.courserware.simulateAnswer(this.isJudgeCorrect, this.isShowAnswer, disabled);
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 选择节点
|
|
|
+ * @param {string} nodeId - 节点ID
|
|
|
+ * @param {boolean} isLeaf - 是否是叶子节点
|
|
|
+ */
|
|
|
+ selectChapterNode(nodeId, isLeaf) {
|
|
|
+ if (!isLeaf) return;
|
|
|
+ if (this.curSelectId === nodeId) return;
|
|
|
+ this.curSelectId = nodeId;
|
|
|
+ this.selectNode(nodeId);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 计算章节名称样式
|
|
|
+ * @param {number} deep - 节点深度
|
|
|
+ * @param {boolean} isLeaf - 是否是叶子节点
|
|
|
+ * @returns {Object} - 样式对象
|
|
|
+ */
|
|
|
+ computedNameStyle(deep, isLeaf) {
|
|
|
+ return {
|
|
|
+ 'padding-left': `${(deep - 1) * 8}px`,
|
|
|
+ cursor: isLeaf ? 'pointer' : 'auto',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 切换右侧工具栏显示与隐藏
|
|
|
+ */
|
|
|
+ toggleSidebarShow() {
|
|
|
+ this.sidebarShow = !this.sidebarShow;
|
|
|
+ },
|
|
|
+
|
|
|
+ backTop() {
|
|
|
+ this.$refs.previewMain.scrollTo({
|
|
|
+ top: 0,
|
|
|
+ left: 0,
|
|
|
+ behavior: 'smooth',
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 定位到对应位置
|
|
|
+ * @param {Object} item - 位置对象
|
|
|
+ * @param {number} type - 定位类型(1: 笔记定位, 2: 收藏定位, 3: 资源定位)
|
|
|
+ */
|
|
|
+ handleLocation(item, type) {
|
|
|
+ if (type === 3) {
|
|
|
+ let did = `${item.courseware_id}#${item.component_id}`;
|
|
|
+ this.handleNodeClick(did);
|
|
|
+ this.curSelectId = item.courseware_id;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.$refs.courserware && this.$refs.courserware.handleLocation) {
|
|
|
+ item.type = type;
|
|
|
+ this.$refs.courserware.handleLocation(item);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async getNote(params) {
|
|
|
+ if (params && params.type) this.drawerType = Number(params.type);
|
|
|
+ this.allNoteList = [];
|
|
|
+ await GetMyNoteList({ courseware_id: this.curSelectId }).then((res) => {
|
|
|
+ if (res.status === 1) {
|
|
|
+ res.note_list.forEach((x) => {
|
|
|
+ if (x.note_desc) {
|
|
|
+ let n = JSON.parse(x.note_desc);
|
|
|
+ let obj = {
|
|
|
+ coursewareId: x.courseware_id,
|
|
|
+ id: x.id,
|
|
|
+ blockId: n.blockId,
|
|
|
+ startIndex: n.startIndex,
|
|
|
+ endIndex: n.endIndex,
|
|
|
+ text: n.text,
|
|
|
+ note: n.note,
|
|
|
+ };
|
|
|
+ this.allNoteList.push(obj);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ async handEditNote(note) {
|
|
|
+ this.oldRichData = {};
|
|
|
+ if (this.allNoteList.length === 0) {
|
|
|
+ await this.getNote();
|
|
|
+ }
|
|
|
+ let old = this.allNoteList.find(
|
|
|
+ (x) =>
|
|
|
+ x.coursewareId === note.coursewareId &&
|
|
|
+ x.blockId === note.blockId &&
|
|
|
+ x.startIndex === note.startIndex &&
|
|
|
+ x.endIndex === note.endIndex,
|
|
|
+ );
|
|
|
+ if (old) {
|
|
|
+ this.oldRichData = old;
|
|
|
+ }
|
|
|
+ this.newSelectedInfo = note;
|
|
|
+ this.editDialogOpen = true;
|
|
|
+ },
|
|
|
+ saveNote(note) {
|
|
|
+ let noteInfo = {
|
|
|
+ blockId: this.newSelectedInfo.blockId,
|
|
|
+ startIndex: this.newSelectedInfo.startIndex,
|
|
|
+ endIndex: this.newSelectedInfo.endIndex,
|
|
|
+ note: note.note,
|
|
|
+ text: this.newSelectedInfo.text,
|
|
|
+ };
|
|
|
+
|
|
|
+ let reqData = {
|
|
|
+ courseware_id: this.newSelectedInfo.coursewareId, // 课件 ID
|
|
|
+ component_id: 'WHOLE',
|
|
|
+ note_desc: JSON.stringify(noteInfo), // 位置描述
|
|
|
+ };
|
|
|
+ if (note.id) {
|
|
|
+ if (!noteInfo.note) {
|
|
|
+ this.delNote(note.id);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let upDate = {
|
|
|
+ id: note.id,
|
|
|
+ note_desc: reqData.note_desc,
|
|
|
+ };
|
|
|
+ UpdateMyNote(upDate).then(() => {
|
|
|
+ this.getNote();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ AddMyNote(reqData).then(() => {
|
|
|
+ this.getNote();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.editDialogOpen = false;
|
|
|
+ this.newSelectedInfo = null;
|
|
|
+ this.selectedInfo = null;
|
|
|
+ },
|
|
|
+ handDelNote(id) {
|
|
|
+ this.$confirm('确定要删除此条笔记吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ this.delNote(id);
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+ },
|
|
|
+ delNote(id) {
|
|
|
+ const noteId = id || (this.oldRichData && this.oldRichData.id);
|
|
|
+ if (!noteId) return;
|
|
|
+ DeleteMyNote({ id: noteId }).then(() => {
|
|
|
+ this.allNoteList = this.allNoteList.filter((x) => x.id !== noteId);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ async getCollect(params) {
|
|
|
+ if (params && params.type) this.drawerType = Number(params.type);
|
|
|
+ this.allCottectList = [];
|
|
|
+ await GetMyCollectList({ courseware_id: this.curSelectId }).then((res) => {
|
|
|
+ if (res.status === 1) {
|
|
|
+ res.collect_list.forEach((x) => {
|
|
|
+ if (x.collect_desc) {
|
|
|
+ let n = JSON.parse(x.collect_desc);
|
|
|
+ let obj = {
|
|
|
+ coursewareId: x.courseware_id,
|
|
|
+ id: x.id,
|
|
|
+ blockId: n.blockId,
|
|
|
+ startIndex: n.startIndex,
|
|
|
+ endIndex: n.endIndex,
|
|
|
+ text: n.text,
|
|
|
+ };
|
|
|
+ this.allCottectList.push(obj);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ async saveCollect(collect) {
|
|
|
+ if (this.allCottectList.length === 0) {
|
|
|
+ await this.getCollect();
|
|
|
+ }
|
|
|
+
|
|
|
+ let old = this.allCottectList.find(
|
|
|
+ (x) =>
|
|
|
+ x.coursewareId === collect.coursewareId &&
|
|
|
+ x.blockId === collect.blockId &&
|
|
|
+ x.startIndex === collect.startIndex &&
|
|
|
+ x.endIndex === collect.endIndex,
|
|
|
+ );
|
|
|
+ if (old) {
|
|
|
+ this.$message({
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
+ message: "<i class='el-icon-check' />已收藏",
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.newSelectedInfo = collect;
|
|
|
+ let collectInfo = {
|
|
|
+ blockId: this.newSelectedInfo.blockId,
|
|
|
+ startIndex: this.newSelectedInfo.startIndex,
|
|
|
+ endIndex: this.newSelectedInfo.endIndex,
|
|
|
+ text: this.newSelectedInfo.text,
|
|
|
+ };
|
|
|
+
|
|
|
+ let reqData = {
|
|
|
+ courseware_id: this.newSelectedInfo.coursewareId, // 课件 ID
|
|
|
+ component_id: 'WHOLE',
|
|
|
+ collect_desc: JSON.stringify(collectInfo), // 位置描述
|
|
|
+ };
|
|
|
+ AddMyCollect(reqData)
|
|
|
+ .then(() => {
|
|
|
+ this.getCollect();
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ this.$message({
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
+ message: "<i class='el-icon-check' />已收藏",
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handDelCollect(id) {
|
|
|
+ this.$confirm('确定要删除此条收藏吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ DeleteMyCollect({ id }).then(() => {
|
|
|
+ this.allCottectList = this.allCottectList.filter((x) => x.id !== id);
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+ },
|
|
|
+ getSearch(params) {
|
|
|
+ if (params && params.type) this.drawerType = Number(params.type);
|
|
|
+ },
|
|
|
+ async querySearchList() {
|
|
|
+ this.searchList = [];
|
|
|
+ if (!this.searchContent) return;
|
|
|
+ await SearchBookContentText({ book_id: this.projectId, text: this.searchContent }).then((res) => {
|
|
|
+ if (res.status === 1) {
|
|
|
+ this.searchList = res.courseware_component_list;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ fullScreen() {
|
|
|
+ toggleFullScreen(this.$refs.previewMain);
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+@use '@/styles/mixin.scss' as *;
|
|
|
+
|
|
|
+$total-width: $courseware-width + $courseware-left-margin + $courseware-right-margin;
|
|
|
+
|
|
|
+.common-preview {
|
|
|
+ &__header {
|
|
|
+ position: sticky;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ z-index: 9;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 40px;
|
|
|
+ padding: 6px 4px;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-top: $border;
|
|
|
+ border-bottom: $border;
|
|
|
+
|
|
|
+ .menu-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 240px;
|
|
|
+ padding: 4px 8px;
|
|
|
+ border-right: $border;
|
|
|
+ }
|
|
|
+
|
|
|
+ .courseware {
|
|
|
+ display: flex;
|
|
|
+ flex-grow: 1;
|
|
|
+ column-gap: 16px;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ height: 40px;
|
|
|
+ padding: 0 8px;
|
|
|
+
|
|
|
+ .lang-select {
|
|
|
+ :deep .el-input {
|
|
|
+ width: 100px;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .el-input__inner {
|
|
|
+ height: 24px;
|
|
|
+ line-height: 24px;
|
|
|
+ background-color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .el-input__icon {
|
|
|
+ line-height: 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .permission-control {
|
|
|
+ .student {
|
|
|
+ margin-right: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .operator {
|
|
|
+ display: flex;
|
|
|
+ column-gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .link {
|
|
|
+ + .link {
|
|
|
+ margin-left: 0;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #999;
|
|
|
+ content: '|';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .main-container {
|
|
|
+ position: relative;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 1110px;
|
|
|
+ padding: 15px 0;
|
|
|
+ overflow: auto;
|
|
|
+ background-color: #ececec;
|
|
|
+
|
|
|
+ .catalogue-bar {
|
|
|
+ position: absolute;
|
|
|
+ top: 15px;
|
|
|
+ left: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 54px;
|
|
|
+ height: 54px;
|
|
|
+ margin: -9px 6px 0 240px;
|
|
|
+ cursor: pointer;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 2px;
|
|
|
+ box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 40%);
|
|
|
+ }
|
|
|
+
|
|
|
+ .sidebar-bar {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ right: 240px;
|
|
|
+ display: flex;
|
|
|
+ width: 60px;
|
|
|
+ height: calc(100vh - 166px);
|
|
|
+
|
|
|
+ .toolbar {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ width: 60px;
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ img {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ &-special {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 16px;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ background-color: #fff;
|
|
|
+
|
|
|
+ img {
|
|
|
+ width: 36px;
|
|
|
+ height: 36px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .back-top {
|
|
|
+ position: absolute;
|
|
|
+ right: 240px;
|
|
|
+ bottom: 0;
|
|
|
+ display: flex;
|
|
|
+ place-content: center center;
|
|
|
+ align-items: center;
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ cursor: pointer;
|
|
|
+ background-color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ main.preview-main {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ width: calc($total-width);
|
|
|
+ min-width: calc($total-width);
|
|
|
+ max-width: calc($total-width);
|
|
|
+ min-height: 100%;
|
|
|
+ margin: 0 auto;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 40%);
|
|
|
+
|
|
|
+ .preview-left {
|
|
|
+ width: $courseware-left-margin;
|
|
|
+ min-width: $courseware-left-margin;
|
|
|
+ max-width: $courseware-left-margin;
|
|
|
+ background-color: $courseware-bgColor;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-right {
|
|
|
+ width: $courseware-right-margin;
|
|
|
+ min-width: $courseware-right-margin;
|
|
|
+ max-width: $courseware-right-margin;
|
|
|
+ background-color: $courseware-bgColor;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.no-audit {
|
|
|
+ margin: 0 auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .audit-content {
|
|
|
+ display: flex;
|
|
|
+ min-width: 1810px;
|
|
|
+ height: calc(100vh - 46px);
|
|
|
+
|
|
|
+ .left-menu {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ width: $catalogue-width;
|
|
|
+ font-family: 'Microsoft YaHei', 'Arial', sans-serif;
|
|
|
+ background-color: #fff;
|
|
|
+
|
|
|
+ .courseware-info {
|
|
|
+ display: flex;
|
|
|
+ column-gap: 18px;
|
|
|
+ width: 100%;
|
|
|
+ height: 186px;
|
|
|
+ padding: 6px 6px 24px;
|
|
|
+ border-bottom: $border;
|
|
|
+
|
|
|
+ .cover-image {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 111px;
|
|
|
+ height: 157px;
|
|
|
+ background-color: rgba(229, 229, 229, 100%);
|
|
|
+
|
|
|
+ img {
|
|
|
+ max-width: 111px;
|
|
|
+ max-height: 157px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ .catalogue-icon {
|
|
|
+ text-align: right;
|
|
|
+
|
|
|
+ .svg-icon {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .courseware {
|
|
|
+ width: 159px;
|
|
|
+ height: 64px;
|
|
|
+ font-size: 16px;
|
|
|
+
|
|
|
+ .name {
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+
|
|
|
+ .editor {
|
|
|
+ display: -webkit-box;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ word-break: break-word;
|
|
|
+ white-space: normal;
|
|
|
+ -webkit-line-clamp: 2; /* 多行省略行数,按需调整 */
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .courseware-tree {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 8px;
|
|
|
+ padding: 12px;
|
|
|
+ margin-top: 12px;
|
|
|
+ overflow: auto;
|
|
|
+
|
|
|
+ .menu-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ &:not(.courseware) {
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.courseware {
|
|
|
+ &:hover {
|
|
|
+ .name {
|
|
|
+ background-color: #f3f3f3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .svg-icon {
|
|
|
+ margin-left: 4px;
|
|
|
+
|
|
|
+ &.my-edit-task {
|
|
|
+ color: $right-color;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .name {
|
|
|
+ flex: 1;
|
|
|
+ padding: 4px 8px 4px 4px;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ .name {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #4095e5;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .sidebar {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ width: $sidebar-width;
|
|
|
+
|
|
|
+ .toolbar {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ width: 60px;
|
|
|
+ height: 100%;
|
|
|
+ background-color: rgba(247, 248, 250, 100%);
|
|
|
+
|
|
|
+ img {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ &-special {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 16px;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 16px;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ .sidebar-item {
|
|
|
+ width: 100%;
|
|
|
+ padding-top: 2px;
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ .sidebar-icon {
|
|
|
+ width: 36px;
|
|
|
+ height: 36px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background-color: #4095e5;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ flex: 1;
|
|
|
+ background-color: #fff;
|
|
|
+
|
|
|
+ .resource_box {
|
|
|
+ height: 100%;
|
|
|
+ overflow-y: auto;
|
|
|
+ border: 1px solid #e5e5e5;
|
|
|
+
|
|
|
+ .source-toolbar-list {
|
|
|
+ position: fixed;
|
|
|
+ z-index: 999;
|
|
|
+ display: flex;
|
|
|
+ width: 240px;
|
|
|
+ background: #f2f3f5;
|
|
|
+
|
|
|
+ .sidebar-item {
|
|
|
+ width: 100%;
|
|
|
+ padding-top: 5px;
|
|
|
+ text-align: center;
|
|
|
+ border-right: 1px solid #ccc;
|
|
|
+
|
|
|
+ .sidebar-icon {
|
|
|
+ width: 26px;
|
|
|
+ height: 26px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background-color: #4095e5;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ h5 {
|
|
|
+ position: fixed;
|
|
|
+ z-index: 999;
|
|
|
+ width: 240px;
|
|
|
+ padding: 0 5px;
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ line-height: 40px;
|
|
|
+ background: #f2f3f5;
|
|
|
+ }
|
|
|
+
|
|
|
+ .scroll-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ row-gap: 8px;
|
|
|
+ margin: 6px;
|
|
|
+
|
|
|
+ .list-item {
|
|
|
+ // display: flex;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+ border: 1px solid #ccc;
|
|
|
+ border-radius: 8px;
|
|
|
+
|
|
|
+ :deep .el-slider {
|
|
|
+ .el-slider__runway {
|
|
|
+ background-color: #eee;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-image {
|
|
|
+ display: flex;
|
|
|
+ width: 100%;
|
|
|
+ min-width: 100%;
|
|
|
+ height: 90px;
|
|
|
+ background-color: #ccc;
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .video-play {
|
|
|
+ width: 100%;
|
|
|
+ min-width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .text-box {
|
|
|
+ word-break: break-word;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ p {
|
|
|
+ color: #999;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-box li {
|
|
|
+ padding: 10px;
|
|
|
+ border-bottom: 1px solid #ccc;
|
|
|
+
|
|
|
+ .el-icon-notebook-2 {
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: grey;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .mark {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 3px;
|
|
|
+
|
|
|
+ .word {
|
|
|
+ flex: 1;
|
|
|
+ margin-right: 10px;
|
|
|
+ word-break: break-all;
|
|
|
+ }
|
|
|
+
|
|
|
+ .linkLocation {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-collapse-item__header {
|
|
|
+ margin-left: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .loading-text {
|
|
|
+ padding: 5px;
|
|
|
+ color: #999;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .error-text {
|
|
|
+ padding: 5px;
|
|
|
+ color: #999;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .back-top {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ display: flex;
|
|
|
+ place-content: center center;
|
|
|
+ align-items: center;
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+:deep .scroll-container .audio-wrapper {
|
|
|
+ width: 100% !important;
|
|
|
+
|
|
|
+ .audio-middle {
|
|
|
+ width: 100% !important;
|
|
|
+ padding: 6px 8px !important;
|
|
|
+ margin-left: 0;
|
|
|
+ border: none;
|
|
|
+ border-radius: 8px;
|
|
|
+
|
|
|
+ .audio-name {
|
|
|
+ text-align: left;
|
|
|
+ }
|
|
|
+
|
|
|
+ .slider-area {
|
|
|
+ column-gap: 8px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .remark-dialog {
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 5px 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep .audit-dialog {
|
|
|
+ .el-dialog__body {
|
|
|
+ height: calc(100vh - 260px);
|
|
|
+ padding: 5px 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .mind-map-container .mind-map {
|
|
|
+ height: calc(100vh - 310px);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.mt10 {
|
|
|
+ margin: 10px 0 0 !important;
|
|
|
+ background-color: #eee;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.tox-tinymce-aux {
|
|
|
+ z-index: 9999 !important;
|
|
|
+}
|
|
|
+</style>
|