package.json
@@ -104,7 +104,7 @@ "publish": [ { "provider": "generic", "url": "http://182.92.203.7:3007/" "url": "https://jsek.bnuic.com/" } ], "releaseInfo": { src/App.vue
@@ -25,7 +25,8 @@ if (window.electronAPI) { window.electronAPI.onLogout((data) => { localStorage.clear() localStorage.removeItem("token") localStorage.removeItem("userInfo") router.replace({ path: '/login' }) @@ -117,21 +118,23 @@ } } let tokens = getUrlParam('token') let bookId = getUrlParam('bookId') let token = getUrlParam('token') let tryPageCount = getUrlParam('tryPageCount') localStorage.setItem('bookId', bookId) localStorage.setItem('tryPageCount', tryPageCount) const token = localStorage.getItem('token') || tokens localStorage.setItem('token', token) if (tokens && bookId) { if(bookId){ localStorage.setItem('bookId', bookId) } if(tryPageCount){ localStorage.setItem('tryPageCount', tryPageCount) } if(token && token != null && token != 'null'){ localStorage.setItem('token', token) } if (bookId) { router.replace({ path: '/home', query: { bookId: bookId } }) } else { router.replace({ path: '/login' }) } </script> src/assets/base.css
@@ -72,14 +72,18 @@ transition: color 0.5s, background-color 0.5s; font-family:PingFang SC, PingFang SC; font-family: PingFang SC, PingFang SC; font-size: 14px; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } ::-webkit-scrollbar { width: 8px; height: 10px; src/assets/images/menu/allSearch.svg
New file @@ -0,0 +1 @@ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1717382814360" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10470" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M192 64a128 128 0 0 0-128 128v128a128 128 0 0 0 128 128h6.4A320.384 320.384 0 0 1 448 198.4V192a128 128 0 0 0-128-128H192z" fill="#467CF3" p-id="10471"></path><path d="M576 192a128 128 0 0 1 128-128h128a128 128 0 0 1 128 128v128a128 128 0 0 1-128 128h-6.4A320.384 320.384 0 0 0 576 198.4V192zM198.4 576H192a128 128 0 0 0-128 128v128a128 128 0 0 0 128 128h128a128 128 0 0 0 128-128v-6.4A320.384 320.384 0 0 1 198.4 576z" fill="#BECFF5" p-id="10472"></path><path d="M640 512a128 128 0 1 1-256 0 128 128 0 0 1 256 0z m2.048 220.544a256 256 0 1 1 90.496-90.496l176.704 176.64a64 64 0 0 1-90.496 90.56l-176.704-176.64z" fill="#467CF3" p-id="10473"></path></svg> src/assets/images/operation/delete1.svg
New file @@ -0,0 +1 @@ <svg xmlns="http://www.w3.org/2000/svg" width="15.512" height="15.514" viewBox="0 0 14.512 14.514"><defs><style>.a{fill:#fff;}</style></defs><g transform="translate(0 0)"><path class="a" d="M0,2.591a.514.514,0,0,1,.516-.518H14a.523.523,0,0,1,.516.518A.514.514,0,0,1,14,3.109H.516A.52.52,0,0,1,0,2.591ZM4.666,0H9.848a.518.518,0,0,1,0,1.035H4.666A.518.518,0,1,1,4.666,0ZM11.4,12.953V4.666a.518.518,0,1,1,1.036,0v8.287c0,.862-.92,1.561-2.073,1.561H4.146c-1.145,0-2.073-.7-2.073-1.561V4.666a.518.518,0,1,1,1.035,0v8.287c0,.215.411.524,1.037.524h6.222c.631,0,1.035-.306,1.035-.524Z" transform="translate(0 0)"/><path class="a" d="M366.3,365.653a.517.517,0,0,1,.517.518v5.183a.518.518,0,1,1-1.035,0v-5.181a.517.517,0,0,1,.518-.519Zm3.108,0a.518.518,0,0,1,.518.518v5.183a.518.518,0,0,1-1.035,0v-5.181a.519.519,0,0,1,.518-.519Z" transform="translate(-360.598 -360.47)"/></g></svg> src/assets/images/operation/list-search.svg
New file @@ -0,0 +1 @@ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-list-search" style="color:#999999"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 15m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /><path d="M18.5 18.5l2.5 2.5" /><path d="M4 6h16" /><path d="M4 12h4" /><path d="M4 18h4" /></svg> src/assets/js/config.ts
@@ -1,6 +1,4 @@ // 测试 export const ctxUrl = "http://182.92.203.7:3007/books/book"; //服务器地址 export const resourceUrl = "http://182.92.203.7:3007/books/resource"; //资源地址 export const requestCtx = "https://jsek.bnuic.com"; //jsek export const appId = 3; export const requestTimeOut = 300000; // 请求超时时间 src/child.ts
@@ -10,12 +10,11 @@ // }) export const microApps = { book1: { childHealth: { // - 必选,微应用的名称,微应用之间必须确保唯一 name: 'app-content', // - 必选,微应用的入口 entry: '//182.92.203.7:3007/books/book', // entry: '//192.168.3.203:8080', entry: '//jsek.bnuic.com/books/book/childHealth', // - 必选,微应用的容器节点的选择器或者 Element 实例 container: '#container', // - 必选,微应用的激活规则 @@ -23,6 +22,30 @@ //支持配置一个 active function 函数或一组 active function。函数会传入当前 location 作为参数,函数返回 true 时表明当前微应用会被激活。如 location => location.pathname.startsWith('/app1')。 activeRule: '/home' //匹配所有以/subPath开头的为子应用 //loader - (loading: boolean) => void - 可选,loading 状态发生变化时会调用的方法。 }, artAndDance: { name: 'app-content', entry: '//jsek.bnuic.com/books/book/artAndDance', container: '#container', activeRule: '/home' //匹配所有以/subPath开头的为子应用 }, english: { name: 'app-content', entry: '//jsek.bnuic.com/books/book/english', container: '#container', activeRule: '/home' //匹配所有以/subPath开头的为子应用 }, lifeCare: { name: 'app-content', entry: '//jsek.bnuic.com/books/book/lifeCare', container: '#container', activeRule: '/home' //匹配所有以/subPath开头的为子应用 }, sportsAndHealth: { name: 'app-content', entry: '//jsek.bnuic.com/books/book/sportsAndHealth', container: '#container', activeRule: '/home' //匹配所有以/subPath开头的为子应用 } } src/main.ts
@@ -17,23 +17,24 @@ // 路由执行之前的一些操作 router.beforeEach((to, from, next) => { if (handleGetToken()) { // 是否是登录页面,直接到首页 if (to.path === '/login') { next({ path: '/home', query: { bookId: localStorage.getItem('bookId') } }) } else { // 如果不是登录页面,跳转到目标的页面 next() } } else { // 没有token if (!to.meta || !to.meta.auth) { // 在免登录白名单,直接进入 next() } else { next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 } } next() // if (handleGetToken()) { // // 是否是登录页面,直接到首页 // if (to.path === '/login') { // next({ path: '/home', query: { bookId: localStorage.getItem('bookId') } }) // } else { // // 如果不是登录页面,跳转到目标的页面 // next() // } // } else { // // 没有token // if (!to.meta || !to.meta.auth) { // // 在免登录白名单,直接进入 // next() // } else { // next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 // } // } }) // qiankun主应用与微应用通讯 src/plugin/axios/index.ts
@@ -37,6 +37,7 @@ let msg = '请求发生错误'; if ((error.response && error.response.status == 401) || error.code == "ERR_NETWORK") { localStorage.removeItem("token") localStorage.removeItem("userInfo") router.replace({ path: '/login' }) src/qiankunState/state.ts
@@ -10,6 +10,10 @@ delSign:null, scale:100, fontSize:16, bgColor:'#FBF9F4', disableSign:false, searchBookByKeyword: null, jumpSearchItem:null, } export default state src/router/index.ts
@@ -1,6 +1,6 @@ import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router' import Layout from '@/layout/layout.vue' const Reader = () => import('@/views/home.vue') const Reader = () => import('@/views/readerPages/home.vue') const Login = () => import('@/views/login.vue') const router = createRouter({ @@ -54,6 +54,12 @@ name: 'dictionary', meta: { auth: true, name: '词典' }, component: () => import('@/views/components/dictionary.vue') }, { path: '/newWord', name: 'newWord', meta: { auth: true, name: '生词' }, component: () => import('@/views/components/newWord.vue') } ] }, @@ -64,6 +70,18 @@ component: Reader }, { path: '/webHome', name: 'webHome', meta: { auth: true, name: 'web阅读器' }, component: () => import('@/views/readerPages/webHome.vue') }, { path: '/mobileHome', name: 'mobileHome', meta: { auth: true, name: '移动阅读器' }, component: () => import('@/views/readerPages/mobileHome.vue') }, { path: '/login', name: 'login', component: Login src/views/bookshelfList/list.vue
@@ -134,7 +134,7 @@ bookId: item.refCode, } }) // window.open(config.textReaderUrl+ '?bookId=' + item.product.refCode + "&token=" + localStorage.getItem('jsek-token')) // window.open(config.textReaderUrl+ '?bookId=' + item.product.refCode + "&token=" + localStorage.getItem('token')) } //.。激活商品 src/views/components/newWord.vue
New file @@ -0,0 +1,368 @@ <template> <div class="woedPage"> <div class="wordMenu"> <el-tree ref="catalogTree" default-expand-all="true" :expand-on-click-node="false" :data="catalogueList" :props="defaultProps" current-node-key="2" v-if="catalogueList.length > 0" @node-click="handleNodeClick1" > </el-tree> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> <div class="wordContent"> <div class="tabs-box"> <div :class="activeName == 'study' ? 'activeName tabs-Item' : 'tabs-Item'" @click="handleClick('study')" > 学习模式 </div> <div :class="activeName == 'recite' ? 'activeName tabs-Item' : 'tabs-Item'" @click="handleClick('recite')" > 背诵模式 </div> </div> <div class="lists-study" v-if="activeName == 'study'"> <div v-for="result in wordLists" :key="result"> <div class="list-title">{{ result.word }}</div> <div class="list-con"> <div class="phone_con"> <div class="per-phone"> 英 <span>/{{ result.ukPhone }}/</span ><img :src="sound" class="soundBtn hover" @click="soundWord(result.word)" /> </div> <div class="per-phone"> 美 <span>/{{ result.usPhone }}/</span ><img :src="sound" class="soundBtn hover" @click="soundWord(result.word)" /> </div> </div> <div class="trans"> <div v-for="item in result.trans" :key="item"> <div class="itemList"> <div class="pos">{{ item.pos }}.</div> <div class="tranCn">{{ item.tranCn }}</div> </div> </div> </div> </div> </div> </div> <div class="lists-recite" v-if="activeName == 'recite'"> <div class="setting"> <el-select v-model="showData" placeholder="Select" style="width: 120px"> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> </el-select> <img :src="setting" class="settingImg" /> </div> <div class="list-box"> <div v-for="(result, index) in wordLists" :key="result" class="list-con"> <div class="list-title hover" @click="isShow = index"> <div class="" v-if="showData == 'newWord'">{{ result.word }}</div> <div class="trans" v-if="showData == 'translation'"> <div v-for="item in result.trans" :key="item"> <div class="itemList"> <div class="pos">{{ item.pos }}.</div> <div class="tranCn">{{ item.tranCn }}</div> </div> </div> </div> </div> <div class="boxCon" v-show="isShow == index"> <div class="word" v-if="showData == 'translation'">{{ result.word }}</div> <div class="phone_con" > <div class="per-phone"> 英 <span>/{{ result.ukPhone }}/</span ><img :src="sound" class="soundBtn hover" @click="soundWord(result.word)" /> </div> <div class="per-phone"> 美 <span>/{{ result.usPhone }}/</span ><img :src="sound" class="soundBtn hover" @click="soundWord(result.word)" /> </div> </div> <div class="trans" v-if="showData == 'newWord'"> <div v-for="item in result.trans" :key="item"> <div class="itemList"> <div class="pos">{{ item.pos }}.</div> <div class="tranCn">{{ item.tranCn }}</div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </template> <script setup lang="ts"> import { ref, reactive, watch, onMounted, inject } from 'vue' import sound from '@/assets/images/operation/sound.png' import setting from '@/assets/images/operation/setting.png' import axios from 'axios' const MG: any = inject('MG') const props = defineProps<{ resourceUrl: string }>() const activeName = ref('study') const catalogueList = ref([]) const wordData = ref([]) const wordLists = ref([]) const showData = ref('newWord') const isShow = ref(null) const options = [ { value: 'newWord', label: '显示生词' }, { value: 'translation', label: '显示译文' } ] onMounted(() => { getWordListData() }) const defaultProps = { children: 'children', label: 'label' } const getWordListData = () => { console.log(props, 2) axios .get(props.resourceUrl + '/newWords.json') .then(function (response) { // 处理获取到的json数据 console.log(response.data, '单词') catalogueList.value = response.data wordData.value = response.data[0].children[0].words getSearchResult() }) .catch(function (error) { console.log(error) }) } const handleNodeClick1 = (data) => { wordData.value = [] if (data.words) { wordData.value = data.words } console.log(wordData.value, 22) getSearchResult() } const handleClick = (type) => { activeName.value = type } const getSearchResult = () => { let wordList = [] if (wordData.value.length > 0) { wordData.value.forEach((item) => { wordList.push({ word: item, isFull: false }) }) } MG.edu.findWords(wordList).then((res) => { console.log(res) if (res.length > 0) { wordLists.value = [] res.forEach((item) => { console.log(item) if (item[0]) { wordLists.value.push({ word: item[0].word, usPhone: item[0].usPhone, ukPhone: item[0].ukPhone, trans: JSON.parse(item[0].trans) }) } }) } }) } const soundWord = (word) => { window.speechSynthesis.cancel() const synth = window.speechSynthesis const utterances = new SpeechSynthesisUtterance(word) // utterances.lang = 'EN' // 设置语言为中文 synth.speak(utterances) } </script> <style lang="less"> .woedPage { height: 100%; overflow-y: auto; display: flex; .wordMenu { height: 100%; width: 300px; border-right: 1px solid #ececec; .el-tree-node__label { padding: 5px 0; } } .wordContent { padding: 10px 20px; flex: 1; .tabs-box { width: 100%; display: flex; background: #f5f6f9; border-radius: 10px; padding: 5px; .tabs-Item { flex: 1; text-align: center; padding: 10px; font-size: 16px; border-radius: 7px; cursor: pointer; } .activeName { background: #fff; } } .lists-study { padding: 10px 0; .list-title { font-weight: bold; padding: 10px; } .list-con { padding: 10px 15px; background: #f3faff; border-radius: 5px; .phone_con { display: flex; .per-phone { margin-right: 20px; align-items: center; display: flex; span { padding: 0 10px; } } .soundBtn { width: 16px; height: 14px; } } .trans { margin-top: 10px; .itemList { align-items: center; display: flex; margin: 10px 0; .pos { width: 35px; line-height: 16px; text-align: center; background: rgba(90, 180, 246, 0.18); border-radius: 11px; font-family: FZQiTi-S14T, FZQiTi-S14T; font-size: 16px; color: #0093ff; } .tranCn { padding-left: 10px; flex: 1; } } } } } .lists-recite { .setting { padding: 10px 0; text-align: right; align-items: center; display: flex; justify-content: flex-end; .settingImg { height: 16px; width: 16px; margin-left: 10px; } } .list-con { padding: 10px 15px; margin: 10px 0; background: #f3faff; border-radius: 5px; .list-title { div,.tranCn{ font-weight: bold; } } .boxCon { background: #fff; border-radius: 5px; padding: 10px; margin-top: 10px; .word{ font-weight: bold; } } .phone_con { display: flex; margin-top:5px; .per-phone { margin-right: 20px; align-items: center; display: flex; span { padding: 0 10px; } } .soundBtn { width: 16px; height: 14px; } } .trans { .itemList { align-items: center; display: flex; margin: 10px 0; .pos { width: 35px; line-height: 16px; text-align: center; background: rgba(90, 180, 246, 0.18); border-radius: 11px; font-family: FZQiTi-S14T, FZQiTi-S14T; font-size: 16px; color: #0093ff; } .tranCn { padding-left: 10px; flex: 1; } } } } } } } </style> src/views/index.vue
@@ -24,7 +24,7 @@ onMounted(() => {}) const goRouter = (item) => { if (!localStorage.getItem('jsek-token')) { if (!localStorage.getItem('token')) { return router.push({ path: '/home', query: { src/views/login.vue
@@ -8,19 +8,19 @@ <el-form-item prop="username"> <!-- <el-input v-model="loginData.username"></el-input> --> <el-input v-model="loginData.username" placeholder="请输入手机号" size="large"> <template #prepend> <el-select v-model="select" placeholder="Select" class="selectPhone" > <el-option label="中国+86" value="86" /> </el-select> </template> </el-input> <template #prepend> <el-select v-model="select" placeholder="Select" class="selectPhone"> <el-option label="中国+86" value="86" /> </el-select> </template> </el-input> </el-form-item> <el-form-item prop="password"> <el-input type="password" v-model="loginData.password" placeholder="请输入密码"></el-input> <el-input type="password" v-model="loginData.password" placeholder="请输入密码" ></el-input> </el-form-item> <div class="btnBox"> <el-button @@ -106,13 +106,9 @@ let phoneInfo = res.secretList.find((item) => item.type == 'MobilePhone') let nameAndPassword = res.secretList.find((item) => item.type == 'LoginNameAndPassword') if (nameAndPassword) { if (teacherRole && teacherInfos) { let userInfo = { name: nameAndPassword.credential } localStorage.setItem('userInfo', JSON.stringify(userInfo)) } else if (teacherRole && teacherInfos) { let userInfo = { ...teacherInfos, name: teacherInfos.name, role: 'Teacher', roleId: teacherRole.role.id @@ -120,6 +116,7 @@ localStorage.setItem('userInfo', JSON.stringify(userInfo)) } else if (phoneInfo) { let userInfo = { ...phoneInfo, name: phoneInfo.credential } localStorage.setItem('userInfo', JSON.stringify(userInfo)) @@ -141,13 +138,13 @@ width: 800px; height: 540px; border-radius: 20px; overflow:hidden; overflow: hidden; display: flex; background: #FFFFFF; box-shadow: 0px 0px 10px 1px rgba(0,0,0,0.1); background: #ffffff; box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.1); .loginForm { width: 410px; padding:40px; padding: 40px; height: 100%; display: flex; flex-direction: column; @@ -177,30 +174,35 @@ box-sizing: border-box; width: 403px; } .el-input{ .el-input { height: 40px; } .el-select { width: 100px; height: 40px; .el-select__wrapper { width: 100px; height: 40px; .el-select__wrapper { height: 100% !important; .el-input--suffix { height: 100% !important; .el-input--suffix { height: 100% !important; background-color: #fff; } background-color: #fff; } } } .logInBtn { width: 304px; height: 40px; margin-top: 10px; margin-bottom: 20px; border-radius: 3px; color: #fff; font-size: 14px; } .logInBtn { width: 304px; height: 40px; margin-top: 10px; margin-bottom: 20px; border-radius: 3px; color: #fff; font-size: 14px; } } } @media screen and (max-width: 450px) { .rightImg { display: none; } } </style> src/views/readerPages/home.vue
New file @@ -0,0 +1,30 @@ <template> <div class="homePage"> <webHome v-if="!homeBoxHide"/> <mobileHome v-if="homeBoxHide"/> </div> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue' import webHome from '@/views//readerPages/webHome.vue' import mobileHome from '@/views//readerPages/mobileHome.vue' const screenWidth = ref( window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth ) const homeBoxHide = ref(false) onMounted(() => { if (screenWidth.value < 420) { homeBoxHide.value = true }else{ homeBoxHide.value = false } }) </script> <style lang="less"> .homePage{ width:100%; height:100%; } </style> src/views/readerPages/mobileHome.vue
New file @@ -0,0 +1,2127 @@ <template> <div class="mobileHomeBox"> <div class="headerBox"> <el-icon @click="goHome"><ArrowLeft /></el-icon> <div class="bookName">{{ bookConfig.bookName }}</div> </div> <div class="contentBox"> <div class="pageBox" :style="{ background: settingForm.bgColorActive }" v-show="activeIndex == '0'" > <!-- 头部显示 --> <div class="pageBox-header"> <div class="progress"> <div v-if="headerData.totlePage && bookConfig.bookName"> {{ headerData.process }}/{{ headerData.totlePage }} </div> </div> </div> <!-- 微应用盒子 --> <div class="pageBox-content"> <div id="container"></div> </div> </div> <div class="catalogBox" v-show="activeIndex == '1'"> <div class="catalogTab"> <div class="tabItem hover" @click="selectTabs('catalog')"> <div :class="activeTabs == 'catalog' ? 'text textActive' : 'text'">目录</div> </div> <div class="tabItem hover" @click="selectTabs('reMark')"> <div :class="activeTabs == 'reMark' ? 'text textActive' : 'text'">书签</div> </div> </div> <div class="catalogList" v-if="activeTabs == 'catalog'"> <el-tree ref="catalogTree" default-expand-all="true" :expand-on-click-node="false" node-key="start" highlight-current :data="catalogueData" :props="defaultProps" v-if="catalogueData.length > 0" @node-click="handleNodeClick" > <template #default="{ node, data }"> <div class="custom-tree-node"> <div class="catalogueLabel" :title="node.label">{{ node.label }}</div> <span class="pageNum">{{ data.page }}</span> </div> </template> </el-tree> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> <div class="reMarkList" v-if="activeTabs == 'reMark'"> <div v-if="reMarkList.length > 0"> <div v-for="(item, index) in reMarkList" :key="index" class="reMarkItem"> <div class="reMarkCon hover" @click="goReMark(item)"> <div>P{{ item.page }}</div> <div class="con">{{ item.content }}</div> </div> <div class="deleteReMarkImg"> <img :src="shanchu" class="icon hover" @click="deleteReMark(item)" /> </div> </div> </div> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> </div> <div class="resourceBox" v-show="activeIndex == '2'"> <div class="resourceTab"> <div class="tabItem hover" @click="selectResourceType('default')"> <div>默认资源</div> <div :class="resourceType == 'default' ? 'text' : 'line'"></div> </div> <div class="tabItem hover" @click="selectResourceType('teacher')"> <div>教师资源</div> <div :class="resourceType == 'teacher' ? 'text' : 'line'"></div> </div> <div class="tabItem hover" @click="selectResourceType('collection')"> <div>收藏</div> <div :class="resourceType == 'collection' ? 'text' : 'line'"></div> </div> </div> <div class="resourceSearchBox" v-if="classifySelectList.length > 0"> <div class="classification"> <div v-for="item in classifySelectList" :key="item.key" class="flex1 hover" @click="classifyClick(item)" > <div :class="item.key == activeClassify ? 'activeClassify classifyItem' : 'classifyItem'" > <div class="title">{{ item.title }}</div> <div class="count">{{ item.count }}</div> </div> </div> <div class="showSearch flex1 hover"> <div :class="searchShow ? 'imgBox' : 'imgBox activeSearch'" @click="searchBtn"> <img :src="searchShow ? search : search1" /> </div> </div> </div> <div class="searchBox"> <div class="inputBox" v-if="!searchShow"> <el-input class="custom-input" placeholder="请输入内容" v-model="searchText" @keyup.enter="searchBook" clearable @clear="handleClear" > <template #prefix> <img :src="listSearch" @click="searchBook" /> </template> </el-input> </div> </div> </div> <div v-if="resourceDataList.length > 0" class="resourceList"> <div v-for="(item, index) in resourceDataList" :key="item" class="resourceItem"> <div class="resourceImg"> <img :src="item.resourcePath" mode="" v-if="activeClassify == 'image'" /> <img :src="item.icon" mode="" v-else-if="item.icon && activeClassify != 'image'" /> <el-icon v-else-if="activeClassify == 'audio'" size="30"><Headset /></el-icon> <el-icon v-else-if="activeClassify == 'video'" size="30"><VideoCamera /></el-icon> <el-icon v-else-if="activeClassify == 'other'" size="30"><Files /></el-icon> <el-icon v-else-if="activeClassify == 'exercises'" size="30"><Tickets /></el-icon> </div> <div class="rName"> <div>{{ item.resourceName }}</div> <div class="handleBox"> <el-icon class="icon hover" size="20" @click="JumpPosition(item)" v-if="resourceType == 'default'" ><LocationInformation /></el-icon> <el-icon @click="goPlay(item)" size="20" class="icon hover" v-if="item.resourceType == '视频'" ><VideoPlay /></el-icon> <el-icon @click="goPlay(item, index)" size="20" class="icon hover" v-if="item.resourceType == '音频' && playIndex != index" ><VideoPlay /></el-icon> <el-icon @click="goPause()" size="20" class="icon hover" v-if="item.resourceType == '音频' && playIndex == index" ><VideoPause /></el-icon> <el-icon @click="getCapture(item, index)" size="20" class="icon hover" v-if="item.resourceType == '图片'" ><View /></el-icon> <el-icon size="20" class="icon hover" @click="goPlay(item)" v-if=" !( item.resourceType == '视频' || item.resourceType == '音频' || item.resourceType == '习题' || item.resourceType == '图片' ) " ><Download /></el-icon> </div> </div> </div> </div> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> <div class="noteBox" v-show="activeIndex == '3'"> <div class="searchBox"> <div class="inputBox"> <el-input class="custom-input" placeholder="请输入内容" v-model="searchText" @keyup.enter="searchBook" clearable @clear="handleClear" > <template #prefix> <img :src="listSearch" @click="searchBook" /> </template> </el-input> </div> </div> <div class="screenBox"> <div class="title">筛选</div> <div class="flex1 hover" @click="searchClick('all')"> <div :class="menuState.notesColor == 'all' ? ' allActive' : 'all'">全部</div> </div> <div v-for="item in colorSelectList" :key="item.key" class="flex1 hover" @click="searchClick(item)" > <div :style="{ background: item.key }" :class="item.key == menuState.notesColor ? 'activeScribe scribeItem' : 'scribeItem'" ></div> </div> </div> <div v-if="scribeData.noteList.length > 0"> <div v-for="(noteItem, index) in scribeData.noteList" :key="noteItem"> <div class="chapterName"> <el-icon @click="noteClose(index)" v-if="scribeData.isShow && scribeData.openIndex == index" class="hover" ><ArrowDown /></el-icon> <el-icon @click="noteOpen(index)" v-else class="hover"><ArrowRight /></el-icon> <span>{{ noteItem.chapterName }}</span> </div> <div v-for="(item, index1) in noteItem.noteList" :key="item.key" class="listItem" v-show="scribeData.isShow && scribeData.openIndex == index" > <div class="textBox"> <div class="title"> <div class="title-con"> <div class="border-left" :style="{ 'border-right-color': item.color }"></div> <span>{{ moment(item.createDate).format('YYYY-MM-DD') }}</span> </div> <div> <img :src="bianji" @click="update(item)" class="hover" /> <img :src="shanchu" @click="deleteBtn(item)" class="hover" /> </div> </div> <div class="noteText hover 123456" @click="jumpContent(item)" :style="{ background: item.color == '#F5E12A' ? 'rgba(255,234,41,0.1)' : item.color == '#76F0AE' ? 'rgba(83,255,162,0.1)' : item.color == '#59CFF5' ? 'rgba(93,216,255,0.1)' : item.color == '#CAA5FC' ? 'rgba(205,167,255,0.1)' : item.color == '#F5A0B9' ? 'rgba(255,167,193,0.1)' : item.color }" > <div class="con hover"> {{ item.note }} </div> </div> <div class="chapter">{{ item.txt }}</div> </div> </div> </div> </div> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> <div class="allSearchBox" v-show="activeIndex == '7'"> <div class="searchBox"> <div class="inputBox"> <el-input class="custom-input" placeholder="请输入内容" v-model="searchText" @keyup.enter="searchBook" clearable @clear="handleClear" > <template #prefix> <img :src="listSearch" @click="searchBook" /> </template> </el-input> </div> </div> <div class="allSearch"> <div v-if="allSearchReault.length > 0" class="allSearchList"> <div v-for="(reault, index) in allSearchReault" :key="index"> <div class="chapterName"> <el-icon @click="searchClose(index)" v-if="searchReaultData.isShow && searchReaultData.openIndex == index" class="hover" ><ArrowDown /></el-icon> <el-icon @click="searchOpen(index)" v-else class="hover"><ArrowRight /></el-icon> <span>{{ reault.chapterName }} ({{ reault.itemList.length }})</span> </div> <div v-for="(item, index1) in reault.itemList" :key="index1" class="searchItem" v-show="searchReaultData.isShow && searchReaultData.openIndex == index" > <div class="index">{{ index1 + 1 }}.</div> <div class="searchCon hover" @click="goSearchContent(item)"> {{ item.txt }} </div> </div> </div> </div> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> </div> </div> <div class="footerBox"> <el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" ellipsis @select="handleSelect" > <el-menu-item index="1" class="menu-item">目录</el-menu-item> <el-menu-item index="2" class="menu-item">资源</el-menu-item> <el-menu-item index="3" class="menu-item">笔记</el-menu-item> <el-menu-item index="4" class="menu-item">设置</el-menu-item> <!-- <el-menu-item index="5" class="menu-item">番茄钟</el-menu-item> <el-menu-item index="6" class="menu-item">习题</el-menu-item> --> <el-menu-item index="7" class="menu-item">检索</el-menu-item> </el-menu> </div> <!-- 设置 --> <el-drawer v-model="settingDrawer" title="设置" direction ="btt" size="35%" > <div> <div class="settingBox"> <el-form :model="settingForm" label-width="auto" style="max-width: 400px"> <el-form-item label="字体大小"> <div class="lineStyle"> <div class="lineTypeBox"> <div v-for="item in settingForm.fontSizeList" :key="item.key" :class=" settingForm.fontSizeActive == item.key ? 'typeItem lineTypeActive' : 'typeItem' " @click="fontSizeSelect(item)" > {{ item.lable }} <div class="activeIcon" v-if="settingForm.fontSizeActive == item.key"> <img :src="xuanzhong" /> </div> </div> </div> </div> </el-form-item> <el-form-item label="底色"> <div class="bgColor"> <div v-for="item in settingForm.bgColorList" :key="item.key" class="flex1 hover" @click="bgColorSelect(item)" > <div :style="{ background: item.key, 'border-color': item.key == settingForm.bgColorActive ? '#0093FF' : '#EBEBEB' }" class="scribeItem" > <img :src="xuanzhong1" v-if="item.key == settingForm.bgColorActive" /> </div> </div> </div> </el-form-item> </el-form> </div> </div> </el-drawer> <!-- 选中工具栏 --> <div class="dialogToolBox" id="dialogToolBox" v-show="showToolBox" :style="{ top: `${dialogToolData.top}px`, left: `${dialogToolData.left}px` }" > <div class="colorSelectBox" v-show="toolActive == '高亮' || toolActive == '划线'"> <div v-for="item in colorSelectList" :key="item.key" class="flex1 hover" @click="clickSelect(item)" > <div :style="{ background: item.key }" class="scribeItem"> <img :src="xuanzhong1" v-if="item.key == colorActive" /> </div> </div> </div> <div class="toolSelectBox" v-show="toolActive != '高亮' && toolActive != '划线'"> <div v-for="item in dialogToolList" :key="item.icon" @click="dialogToolHandle(item)"> <div :class="item.name == toolActive ? 'dialogToolItem active' : 'dialogToolItem'" v-if="item.isShow" > <el-tooltip class="box-item" effect="dark" :content="item.name" placement="bottom"> <img :src="item.icon" alt="" /> </el-tooltip> </div> <!-- <span>{{ item.name }}</span> --> </div> </div> </div> <!-- 划线,高亮删除 --> <div class="lineDeleteBox" v-show="lineDelete.showLineDelete" :style="{ top: `${lineDelete.top}px`, left: `${lineDelete.left}px` }" > <!-- <el-button @click="delUserKey">删除</el-button> --> <div class="dialogToolItem"> <el-tooltip class="box-item" effect="dark" content="删除" placement="bottom"> <img :src="deleteIcon" @click="delUserKey" /> </el-tooltip> </div> <div class="dialogToolItem" v-if="isUpdate"> <el-tooltip class="box-item" effect="dark" content="笔记" placement="bottom"> <img :src="biji2" @click="updateNote" /> </el-tooltip> </div> </div> </div> <el-dialog title="添加笔记" align-center v-model="addNoteVisble" :before-close="handleClose" class="myNoteDialogs" > <div class="formBox"> <el-input type="textarea" :rows="4" v-model="formData.desc" placeholder="请输入笔记内容" ></el-input> </div> <div class="noteColorSelectBox"> <div v-for="item in colorSelectList" :key="item.key" class="flex1 hover" @click="clickSelectColor(item)" > <div :style="{ background: item.key }" class="scribeItem"> <img :src="xuanzhong" v-if="item.key == noteColorActive" /> </div> </div> </div> <template #footer> <span class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="addUserKey" v-if="!isUpdate">确 定</el-button> <el-button type="primary" @click="updateUserKey" v-if="isUpdate">确 定</el-button> </span> </template> </el-dialog> <!-- 图片 --> <el-image-viewer v-if="confirmDialog" :zoom-rate="1.2" @close="closePreview" :initial-index="previewIndex" :url-list="imgPreviewList" /> <el-dialog title="资源" align-center v-model="resourVisble" width="845" class="resourDialog" :before-close="resourVisbleClose" > <div class="videoBox" v-if="resourType == '视频'"> <video controls controlslist="nodownload" :src="testVideo"></video> </div> <!-- <div class="wordBox" v-if="resourType == 'word'"> <vue-office-docx :src="testWord" /> </div> --> </el-dialog> <div class="audioBox" v-show="false"> <audio ref="audioPlayer" :src="testAudio" controls @loadedmetadata="autoPlay"></audio> </div> </template> <script setup lang="ts"> import { ref, reactive, watch, onMounted, onBeforeMount, inject } from 'vue' import axios from 'axios' import { useRouter, useRoute } from 'vue-router' import { ElMessage, ElMessageBox, valueEquals } from 'element-plus' import useClipboard from 'vue-clipboard3' const { toClipboard } = useClipboard() const MG: any = inject('MG') const toolClass = inject('toolClass') const qiankunActions = inject('qiankunActions') import { loadMicroApp } from 'qiankun' import { microApps } from '@/child.ts' //获取路由器 let router = useRouter() //获取当前路由的信息 let route = useRoute() import moment from 'moment' import bianji from '@/assets/images/operation/bianji.png' import shanchu from '@/assets/images/operation/delete.png' import listSearch from '@/assets/images/operation/list-search.svg' import search from '@/assets/images/operation/search.png' import search1 from '@/assets/images/operation/search1.png' import xuanzhong from '@/assets/images/operation/xuanzhong.png' let microApp = null // 微应用实例 const bookConfig = ref({}) //当前显示章节 const activeCatalog = ref() onMounted(() => { // 加载微应用 microApp = loadMicroApp(microApps[localStorage.getItem('bookId')], { sandbox: { strictStyleIsolation: true, experimentalStyleIsolation: true }, singular: true }) window.qiankunActions.onGlobalStateChange((state, prev) => { console.log('父层state变化', state) window.qiankunState = state if (window.qiankunState && window.qiankunState.initTestBook) { window.qiankunState.initTestBook( localStorage.getItem('bookId') + '', localStorage.getItem('tryPageCount') ) } }) // 定义父层方法 window.qiankunActions.setGlobalState({ disableSign: false, // windowSelection: (data) => { // // 绑定子应用选择监听事件 // console.log(data, '子应用选择') // if (data.txt) { // getSelection(data) // } else { // showToolBox.value = false // lineDelete.showLineDelete = false // dictionaryData.showContent = false // isUpdate.value = false // toolActive.value = '' // lineDelete.top = data.y + 20 // lineDelete.left = data.x // } // }, // chooseWords: (data) => { // console.log(data, '英语生词') // switch (data.type) { // case 'word': // if (data) { // dialogToolData.txt = data.word // dialogToolData.left = data.x // dialogToolData.top = data.y + 20 // // getSearchResult() // } // break // case 'swdt': // siweiVisble.value = true // break // case 'readText': // window.speechSynthesis.cancel() // const synth = window.speechSynthesis // const utterances = new SpeechSynthesisUtterance(data.data) // // utterances.lang = 'EN' // 设置语言为中文 // synth.speak(utterances) // break // } // }, getBookConfig: (data) => { console.log(data.bookConfig, '图书配置') bookConfig.value = data.bookConfig getCatalogueData() // getResourceData() // getTextbookComponents() // getReMarkList() }, catalogChange: (data) => { activeCatalog.value = data.showCatalogList if (window.qiankunState.disableSign) { // getSignData() } // getSignData() console.log(data, '章节切换:目前显示的三个章节') }, pageChange: (data) => { headerData.process = Number(data.page) // currentChapter.value = data.catalog // console.log(data, '页面切换:目前显示页面和对应的章节信息') // if (catalogTree.value) { // catalogTree.value.setCurrentKey(Number(data.page)) // } // showToolBox.value = false // lineDelete.showLineDelete = false // dictionaryData.showContent = false // reMarkCon.value = data.text // reMarResult.value = reMarkData.value.find((item) => { // return item.chapterNum == data.catalog && item.page == data.page // }) // if (reMarResult.value) { // floatingToolData.activeToolData = '书签' // } else { // floatingToolData.activeToolData = '' // } } }) }) //菜单 const activeIndex = ref('0') const handleSelect = (key: string, keyPath: string[]) => { console.log(key, keyPath) if (key == '4') { settingDrawer.value=true } else { activeIndex.value = key switch (activeIndex.value) { case '1': getReMarkList() break case '2': getResourceData() break case '3': getNotesList() break } } } const goHome = () => { activeIndex.value = '0' } const catalogueData = ref([]) //获取目录 const reMarkList = ref([]) //书签 const reMarkData = ref([]) //书签 const defaultProps = { children: 'children', label: 'label', start: 'start' } const headerData = reactive({ process: 0, totlePage: 0 }) //目录菜单 const activeTabs = ref('catalog') const selectTabs = (type) => { activeTabs.value = type if (type == 'catalog') { getCatalogueData() } else { getReMarkList() } } //目录列表 const getCatalogueData = () => { axios .get(bookConfig.value.resourceUrl + '/information.json') .then(function (response) { var json = response.data // 处理获取到的json数据 console.log(json, '目录') catalogueData.value = json.data headerData.totlePage = json.data[json.data.length - 1].end }) .catch(function (error) { console.log(error) }) } const handleNodeClick = (data) => { console.log(data) activeIndex.value = '0' if (localStorage.getItem('tryPageCount')) { if (data.start < localStorage.getItem('tryPageCount')) { headerData.process = data.start if (window.qiankunState && window.qiankunState.gotoPage) window.qiankunState.gotoPage(data.chapter, data.start) } else { ElMessage({ message: '试读已结束!', type: 'warning' }) } } else { headerData.process = data.start if (window.qiankunState && window.qiankunState.gotoPage) window.qiankunState.gotoPage(data.chapter, data.start) } } //全文检索 const catalogTree = ref() const allSearchReault = ref([]) const searchReaultData = reactive({ isShow: true, openIndex: 0 }) const getAllSearchReault = () => { allSearchReault.value = [] console.log(window.qiankunState.searchBookByKeyword) if (window.qiankunState && window.qiankunState.searchBookByKeyword) { let searchReault = window.qiankunState.searchBookByKeyword(searchText.value) if (searchReault.length > 0) { let catalogueList = sortArr(catalogueData.value, 'chapter') if (catalogueList.length > 0) { catalogueList.forEach((item) => { let labels = '' if (item.length > 1) { labels = item.map((i) => i.label).join('/') } let itemList = [] searchReault.forEach((item1) => { if (item[0].chapter == item1.catalog) { itemList.push(item1) } }) if (itemList.length > 0) { allSearchReault.value.push({ chapter: item[0].chapter, chapterName: labels ? labels : item[0].label, itemList: itemList }) } }) } console.log(allSearchReault.value, 444) } } } function sortArr(arr, str) { var _arr = [], _t = [], // 临时的变量 _tmp // 按照特定的参数将数组排序将具有相同值得排在一起 arr = arr.sort(function (a, b) { var s = a[str], t = b[str] return s < t ? -1 : 1 }) if (arr.length) { _tmp = arr[0][str] } // console.log( arr ); // 将相同类别的对象添加到统一个数组 for (var i in arr) { if (arr[i][str] === _tmp) { _t.push(arr[i]) } else { _tmp = arr[i][str] _arr.push(_t) _t = [arr[i]] } } // 将最后的内容推出新数组 _arr.push(_t) return _arr } //检索章节收起 const searchOpen = (index) => { searchReaultData.isShow = true searchReaultData.openIndex = index } //检索章节打开 const searchClose = (index) => { searchReaultData.isShow = false searchReaultData.openIndex = index } //检索跳转 const goSearchContent = (item) => { console.log(item) activeIndex.value = '0' if (window.qiankunState && window.qiankunState.gotoPage) { window.qiankunState.jumpSearchItem(item) } } //书签列表 const getReMarkList = () => { reMarkList.value = [] reMarkData.value = [] MG.identity .getUserKey({ domain: 'reMark', keys: [bookConfig.value.bookId] }) .then((res) => { if (res && res.length > 0 && res[0].value) { reMarkData.value = JSON.parse(res[0].value) let list = JSON.parse(res[0].value) list.forEach((item) => { let text = searchText.value.replace(/^\s*|\s*$/g, '') if (searchText.value) { if (item.name.indexOf(text) > -1) { reMarkList.value.push(item) } } else { reMarkList.value.push(item) } }) } }) } //跳转 const goReMark = (item) => { console.log(item) activeIndex.value = '0' if (window.qiankunState && window.qiankunState.gotoPage) { window.qiankunState.gotoPage(Number(item.chapterNum), Number(item.page)) } } //书签列表删除书签 const deleteReMark = (item) => { let list = reMarkList.value.filter((itemData) => itemData.id !== item.id) MG.identity .setUserKey({ setKeyRequests: [ { domain: 'reMark', key: bookConfig.value.bookId, value: JSON.stringify(list) } ] }) .then((res) => { ElMessage({ message: '删除书签成功!', type: 'success' }) getReMarkList() }) } //资源 const classifySelectList = ref([]) //资源列表 //资源类型 const resourceType = ref('default') //默认/教师 const activeClassify = ref('') const searchShow = ref(true) const defaultResourceList = ref([]) const teacherResourceList = ref([]) const resourceDataList = ref([]) const resourVisble = ref(false) const resourType = ref('') //资源类型图片/音频 const testVideo = ref('') //视频链接 const testAudio = ref('') //音频链接 const testWord = ref('') //word链接 const playIndex = ref(null) //音频播放 const audioPlayer = ref(null) const imgPreviewList = ref([]) const imgUrl = ref() const confirmDialog = ref<Boolean>(false) const previewIndex = ref(0) const getResourceData = () => { imgPreviewList.value = [] axios .get(bookConfig.value.resourceUrl + '/resource.json?t=12') .then(function (response) { var json = response.data // 处理获取到的json数据 if (json.length > 0) { defaultResourceList.value = [] teacherResourceList.value = [] // 获取数组中所有对象的age属性的数量 json.forEach((item) => { if (item.iconPath) { item.icon = bookConfig.value.resourceUrl + '/' + item.iconPath } else { item.icon = '' } if (resourceType.value == 'default') { if (item.isTeacherResource == '否') { defaultResourceList.value.push(item) } } else { if (item.isTeacherResource == '是') { teacherResourceList.value.push(item) } } }) if (defaultResourceList.value.length > 0) { let imgCount = 0 let audioCount = 0 let videoCount = 0 let exercisesCount = 0 let otherCount = 0 resourceDataList.value = [] defaultResourceList.value.forEach((resItem) => { if (resItem.resourceType == '图片') { if (resItem.resourcePath) { resItem.resourcePath = bookConfig.value.resourceUrl + '/' + resItem.resourcePath } else { resItem.resourcePath = bookConfig.value.requestCtx + '/file/GetPreViewImage?md5=' + resItem.md5 } imgPreviewList.value.push(resItem.resourcePath) if (activeClassify.value == 'image') { resourceDataList.value.push(resItem) } imgCount++ } else if (resItem.resourceType == '音频') { if (activeClassify.value == 'audio') { resourceDataList.value.push(resItem) } audioCount++ } else if (resItem.resourceType == '视频') { if (activeClassify.value == 'video') { resourceDataList.value.push(resItem) } videoCount++ } else if (resItem.resourceType == '习题') { if (activeClassify.value == 'exercises') { resourceDataList.value.push(resItem) } exercisesCount++ } else { if (activeClassify.value == 'other') { resourceDataList.value.push(resItem) } otherCount++ } }) classifySelectList.value = [] if (imgCount > 0) { classifySelectList.value.push({ title: '图片', count: imgCount, key: 'image' }) } if (audioCount > 0) { classifySelectList.value.push({ title: '音频', count: audioCount, key: 'audio' }) } if (videoCount > 0) { classifySelectList.value.push({ title: '视频', count: videoCount, key: 'video' }) } if (exercisesCount > 0) { classifySelectList.value.push({ title: '习题', count: exercisesCount, key: 'exercises' }) } if (otherCount > 0) { classifySelectList.value.push({ title: '其他', count: otherCount, key: 'other' }) } if (!activeClassify.value) { activeClassify.value = classifySelectList.value[0].key defaultResourceList.value.forEach((resItem1) => { if (resItem1.resourceType == classifySelectList.value[0].title) { resourceDataList.value.push(resItem1) } }) } } else { let imgCount = 0 let audioCount = 0 let videoCount = 0 let exercisesCount = 0 let otherCount = 0 resourceDataList.value = [] teacherResourceList.value.forEach((resItem) => { if (resItem.resourceType == '图片') { if (resItem.resourcePath) { resItem.resourcePath = bookConfig.value.resourceUrl + '/' + resItem.resourcePath } else { resItem.resourcePath = bookConfig.value.requestCtx + '/file/GetPreViewImage?md5=' + resItem.md5 } imgPreviewList.value.push(resItem.resourcePath) if (activeClassify.value == 'image') { resourceDataList.value.push(resItem) } imgCount++ } else if (resItem.resourceType == '音频') { if (activeClassify.value == 'audio') { resourceDataList.value.push(resItem) } audioCount++ } else if (resItem.resourceType == '视频') { if (activeClassify.value == 'video') { resourceDataList.value.push(resItem) } videoCount++ } else if (resItem.resourceType == '习题') { if (activeClassify.value == 'exercises') { resourceDataList.value.push(resItem) } exercisesCount++ } else { if (activeClassify.value == 'other') { resourceDataList.value.push(resItem) } otherCount++ } }) classifySelectList.value = [] if (imgCount > 0) { classifySelectList.value.push({ title: '图片', count: imgCount, key: 'image' }) } if (audioCount > 0) { classifySelectList.value.push({ title: '音频', count: audioCount, key: 'audio' }) } if (videoCount > 0) { classifySelectList.value.push({ title: '视频', count: videoCount, key: 'video' }) } if (exercisesCount > 0) { classifySelectList.value.push({ title: '习题', count: exercisesCount, key: 'exercises' }) } if (otherCount > 0) { classifySelectList.value.push({ title: '其他', count: otherCount, key: 'other' }) } if (!activeClassify.value) { activeClassify.value = classifySelectList.value[0].key teacherResourceList.value.forEach((resItem1) => { if (resItem1.resourceType == classifySelectList.value[0].title) { resourceDataList.value.push(resItem1) } }) } } } }) .catch(function (error) { console.log(error) }) } //资源播放视频 const goPlay = (data, index) => { resourType.value = data.resourceType if (data.resourceType == '视频') { if (data.md5) { testVideo.value = bookConfig.value.requestCtx + '/file/api/ApiDownload?md5=' + data.md5 } else { testVideo.value = bookConfig.value.resourceUrl + '/' + data.resourcePath } resourVisble.value = true } else if (data.resourceType == '音频') { playIndex.value = index if (data.md5) { testAudio.value = bookConfig.value.requestCtx + '/file/api/ApiDownload?md5=' + data.md5 } else { testAudio.value = bookConfig.value.resourceUrl + '/' + data.resourcePath } autoPlay() } else { if (data.md5) { window.open(bookConfig.value.requestCtx + '/file/api/ApiDownload?md5=' + data.md5) } else if (data.resourcePath) { window.open(bookConfig.value.resourceUrl + '/' + data.resourcePath) } } // else if (data.resourceType == 'word') { // testWord.value = bookConfig.value.resourceUrl + '/' + data.resourcePath // } } // 关闭视频 const resourVisbleClose = () => { resourVisble.value = false testVideo.value = '' } const autoPlay = () => { if (audioPlayer.value) { audioPlayer.value.play() } } const goPause = () => { playIndex.value = null testAudio.value = '' } //查看截图 const getCapture = (item, index) => { imgUrl.value = item.imgUrl || item.resourcePath previewIndex.value = index confirmDialog.value = true } const closePreview = () => { confirmDialog.value = false } //资源跳转到指定位置 const JumpPosition = (data) => { console.log(data, '跳转') activeIndex.value = '0' if (window.qiankunState && window.qiankunState.gotoPage) { window.qiankunState.gotoPage(Number(data.chapterNum), Number(data.pagination)) } } //资源类型选择默认/教师 const selectResourceType = (type) => { resourceType.value = type searchText.value = '' if (type == 'collection') { } else { getResourceData() } } //资源类型图片/视频/音频/习题/其他 const classifyClick = (item) => { activeClassify.value = item.key resourceDataList.value = [] searchText.value = '' getResourceData() } const searchBtn = () => { if (searchShow.value) { searchShow.value = false } else { searchShow.value = true } } //笔记,高亮,划线 const scribeData = reactive({ isShow: true, openIndex: 0, loading: false, scribeDataList: [], //划线菜单列表 lineHeightList: [], //高亮菜单列表 noteList: [] //笔记菜单列表 }) const noteColorActive = ref('') //笔记弹窗 const addNoteVisble = ref(false) const isUpdate = ref(false) const formData = reactive({ id: '', desc: '' }) const colorSelectList = reactive([ { label: '黄色', key: '#F5E12A' }, { label: '绿色', key: '#76F0AE' }, { label: '蓝色', key: '#59CFF5' }, { label: '紫色', key: '#CAA5FC' }, { label: '粉色', key: '#F5A0B9' } ]) //菜单笔记列表 const getNotesList = () => { let chapterList = [] catalogueData.value.forEach((item) => { chapterList.push(item.chapter + '') }) let uniqueArray = [...new Set(chapterList)] scribeData.noteList = [] MG.identity .getUserKey({ domain: 'notes-' + bookConfig.value.bookId, keys: uniqueArray }) .then((res) => { if (res && res.length > 0) { for (let i = 0; i < res.length; i++) { const item = res[i] // 储值 let data = catalogueData.value.find((item1) => item1.chapter == Number(item.key)) // console.log(data,'666') let dataList = [] if (catalogueData.value.length > 0) { catalogueData.value.forEach((item1) => { if (item1.chapter == Number(item.key)) { dataList.push(item1) } }) } let labels = '' if (dataList.length > 1) labels = dataList.map((item) => item.label).join('/') let list = JSON.parse(item.value) let itemList = [] if (list.length > 0) { list.forEach((item) => { if (searchText.value) { let text = searchText.value.replace(/^\s*|\s*$/g, '') if (item.note.indexOf(text) > -1 || item.txt.indexOf(text) > -1) { if (menuState.notesColor == 'all') { itemList = list } else if (item.color == menuState.notesColor) { itemList.push(item) } } } else { if (menuState.notesColor == 'all') { itemList = list } else if (item.color == menuState.notesColor) { itemList.push(item) } } }) } if (dataList[0].chapter == Number(item.key) && itemList.length > 0) { scribeData.noteList.push({ chapter: dataList[0].chapter, chapterName: labels ? labels : dataList[0].label, noteList: itemList }) } } console.log(scribeData.noteList, 'scribeData.noteList') } }) } //笔记颜色筛选 const searchClick = (item) => { if (item != 'all') { menuState.notesColor = item.key } else { menuState.notesColor = 'all' } getNotesList() } //笔记章节收起 const noteOpen = (index) => { console.log(scribeData.isShow, index) scribeData.isShow = true scribeData.openIndex = index } //笔记章节打开 const noteClose = (index) => { scribeData.isShow = false scribeData.openIndex = index } //笔记跳转 const jumpContent = (item) => { console.log(item) activeIndex.value = '0' if (window.qiankunState && window.qiankunState.gotoPage) { window.qiankunState.gotoPage(Number(item.chapterNum), Number(item.page)) } } //笔记选颜色 const clickSelectColor = (item) => { noteColorActive.value = item.key } //菜单笔记编辑 const update = (item) => { console.log(item) dialogToolData.chapter = item.chapterNum noteColorActive.value = item.color formData.id = item.id formData.desc = item.note addNoteVisble.value = true isUpdate.value = true } const updateUserKey = () => { let data = scribeData.noteList.find((item1) => item1.chapter == dialogToolData.chapter) data.noteList.forEach((itemNote) => { if ((itemNote.id = formData.id)) { itemNote.note = formData.desc itemNote.color = noteColorActive.value } }) MG.identity .setUserKey({ setKeyRequests: [ { domain: 'notes-' + bookConfig.value.bookId, key: dialogToolData.chapter, value: JSON.stringify(data.noteList) } ] }) .then((res) => { addNoteVisble.value = false isUpdate.value = false getNotesList() if (!window.qiankunState.disableSign) { getSignData() } }) } //笔记弹窗关闭 const handleClose = () => { addNoteVisble.value = false showToolBox.value = false colorActive.value = '' } //菜单删除笔记 const deleteBtn = (item) => { let data = scribeData.noteList.find((item1) => item1.chapter == Number(item.chapterNum)) // 移除 let list = data.noteList.filter((itemData) => itemData.id !== item.id) ElMessageBox.confirm('确定要删除此笔记吗?', { confirmButtonText: '确定', cancelButtonText: '取消', autofocus: false, type: 'warning' }) .then(() => { MG.identity .setUserKey({ setKeyRequests: [ { domain: 'notes-' + bookConfig.value.bookId, key: item.chapterNum, value: JSON.stringify(list) } ] }) .then((res) => { ElMessage({ message: '删除笔记成功!', type: 'success' }) getNotesList() if (window.qiankunState && window.qiankunState.delSign) { window.qiankunState.delSign({ ids: [item.id] }) } }) }) .catch(() => {}) } const searchText = ref('') const menuState = reactive({ open: true, notesColor: 'all' //笔记颜色 }) const searchBook = async () => { switch (activeIndex.value) { case '3': getNotesList() break case '2': let dataList = JSON.parse(JSON.stringify(resourceDataList.value)) if (searchText.value) { resourceDataList.value = [] searchText.value = searchText.value.replace(/^\s*|\s*$/g, '') let text = searchText.value.replace(/^\s*|\s*$/g, '') dataList.forEach((item) => { if (item.resourceName.indexOf(text) > -1) { resourceDataList.value.push(item) } }) } else { getResourceData() } break case '1': getReMarkList() break case '7': getAllSearchReault() break } } const handleClear = () => { switch (activeIndex.value) { case '3': getNotesList() break case '2': getResourceData() break case '1': getReMarkList() break case '7': break } } //选中文字工具栏 const showToolBox = ref(false) const toolActive = ref('') const colorActive = ref('') const dialogToolData = reactive({ left: 500, top: 300, txt: '', page: '', chapter: '', //选中文字所在章节 lineHeight: [], //高亮 scribeList: [], //划线 notesList: [] //笔记 }) ///内容选中 const getSelection = (data) => { if (data.txt) { toolActive.value = '' dialogToolData.txt = data.txt dialogToolData.page = data.page dialogToolData.chapter = data.chapterNum dialogToolData.left = data.x dialogToolData.top = data.y showToolBox.value = true dictionaryData.showContent = false lineDelete.showLineDelete = false dictionaryData.showContent = false } } const clickSelect = (item) => { colorActive.value = item.key addUserKey() } const addUserKey = () => { let dom = '' let data = null switch (toolActive.value) { case '高亮': dom = 'highLightData-' + bookConfig.value.bookId data = dialogToolData.lineHeight[dialogToolData.chapter] if (!data) data = [] data.push({ id: toolClass.uuid(8), txt: dialogToolData.txt, page: dialogToolData.page, chapterNum: dialogToolData.chapter, type: 'Highlight', color: colorActive.value }) break case '划线': dom = 'underline-' + bookConfig.value.bookId data = dialogToolData.scribeList[dialogToolData.chapter] if (!data) data = [] data.push({ id: toolClass.uuid(8), txt: dialogToolData.txt, page: dialogToolData.page, chapterNum: dialogToolData.chapter, type: 'Dashing', color: colorActive.value }) break case '笔记': if (!formData.desc.replace(/^\s*|\s*$/g, '')) { ElMessage.error('笔记内容不能为空!') return } if (!noteColorActive.value) { ElMessage.error('请选择笔记颜色!') return } dom = 'notes-' + bookConfig.value.bookId data = dialogToolData.notesList[dialogToolData.chapter] if (!data) data = [] data.push({ id: toolClass.uuid(8), txt: dialogToolData.txt, page: dialogToolData.page, chapterNum: dialogToolData.chapter, type: 'Note', color: noteColorActive.value, createDate: new Date(), note: formData.desc.replace(/^\s*|\s*$/g, '') }) break } MG.identity .setUserKey({ setKeyRequests: [ { domain: dom, key: dialogToolData.chapter, value: JSON.stringify(data) } ] }) .then((res) => { showToolBox.value = false addNoteVisble.value = false colorActive.value = '' noteColorActive.value = '' toolActive.value = '' getSignData() getNotesList() }) } const getSignData = () => { MG.identity .getUserKey({ domain: 'highLightData-' + bookConfig.value.bookId, keys: activeCatalog.value.map((item) => item + '') }) .then((res) => { if (res && res.length > 0) { for (let i = 0; i < res.length; i++) { const item = res[i] const data = JSON.parse(item.value) // 储值 dialogToolData.lineHeight[item.key] = data // 渲染 for (let j = 0; j < data.length; j++) { const citem = data[j] if (window.qiankunState && window.qiankunState.renderSign) window.qiankunState.renderSign(citem.type, citem) } } } }) MG.identity .getUserKey({ domain: 'underline-' + bookConfig.value.bookId, keys: activeCatalog.value.map((item) => item + '') }) .then((res) => { if (res && res.length > 0) { for (let i = 0; i < res.length; i++) { const item = res[i] const data = JSON.parse(item.value) // 储值 dialogToolData.scribeList[item.key] = data // 渲染 if (data.length > 0) { for (let j = 0; j < data.length; j++) { const citem = data[j] if (window.qiankunState && window.qiankunState.renderSign) window.qiankunState.renderSign(citem.type, citem) } } } } }) MG.identity .getUserKey({ domain: 'notes-' + bookConfig.value.bookId, keys: activeCatalog.value.map((item) => item + '') }) .then((res) => { if (res && res.length > 0) { for (let i = 0; i < res.length; i++) { const item = res[i] const data = JSON.parse(item.value) // 储值 dialogToolData.notesList[item.key] = data // 渲染 if (data.length > 0) { for (let j = 0; j < data.length; j++) { const citem = data[j] if (window.qiankunState && window.qiankunState.renderSign) window.qiankunState.renderSign(citem.type, citem) } } } } }) } //正文中删除高亮,划线,笔记操作框 const lineDelete = reactive({ showLineDelete: false, top: 0, left: 0, deleteType: '', id: '' }) //设置 const settingDrawer = ref(false) const settingForm = reactive({ fontSizeList: [ { lable: '小', key: 14 }, { lable: '中', key: 16 }, { lable: '大', key: 18 } ], fontSizeActive: 16, bgColorList: [ { lable: '白', key: '#fff' }, { lable: '黄', key: '#FBF9F4' }, { lable: '绿', key: '#F2FFF7' }, { lable: '紫', key: '#F6F4FC' }, { lable: '粉', key: '#FFFAF9' } ], bgColorActive: '#FBF9F4' }) // 设置字号 const fontSizeSelect = (item) => { settingForm.fontSizeActive = item.key window.qiankunActions.setGlobalState({ fontSize: item.key }) } //设置背景色 const bgColorSelect = (item) => { settingForm.bgColorActive = item.key } </script> <style lang="less"> .mobileHomeBox { width: 100%; height: 100%; .headerBox { width: 100%; height: 50px; display: flex; align-items: center; position: fixed; left: 0; top: 0; z-index: 2; background-color: #fff; border-bottom: 1px solid #e0e0e0; .el-icon { margin: 0 10px; } .bookName { flex: 1; text-align: center; } } .footerBox { width: 100%; height: 50px; position: fixed; left: 0; bottom: 0; z-index: 2020; background-color: #fff; border-top: 1px solid #e0e0e0; .el-menu-demo { width: 100%; justify-content: center; .menu-item { min-width: calc(100% / 4); } } } .contentBox { margin: 50px 0; height: 100%; width: 100%; .pageBox { .pageBox-header { width: 100%; height: 30px; padding: 0 10px; line-height: 30px; text-align: right; } .pageBox-content { height: 100%; height: calc(100vh - 130px); #container { background: #fbf9f4; height: 100%; #__qiankun_microapp_wrapper_for_app_content_1__ { height: 100%; } } } } .catalogBox { height: calc(100vh - 100px); .catalogTab { height: 50px; display: flex; // justify-content: center; align-items: center; border-bottom: 1px solid #e0e0e0; .tabItem { flex: 1; text-align: center; .textActive { color: #0093ff; } } } .catalogList { height: calc(100vh - 150px); overflow: auto; padding: 20px; } .reMarkList { padding: 20px; .reMarkItem { border-bottom: 1px solid #efefef; padding: 10px 0; .deleteReMarkImg { text-align: right; } } } } .searchBox { padding: 5px; .inputBox { width: 85%; margin: 10px auto; .custom-input { border: 1px solid #0093ff !important; border-radius: 50px; overflow: hidden; background: #fff; height: 34px; } .is-focus, .el-input__wrapper { box-shadow: none !important; .el-input__inner { border: none !important; height: 34px !important; } } .el-input-group__append { padding: 0 10px !important; background: none !important; } } } .resourceBox { .resourceTab { width: 100%; height: 50px; display: flex; justify-content: center; align-items: center; font-size: 16px; border-bottom: 1px solid #e0e0e0; .tabItem { flex: 1; text-align: center; line-height: 47px; } .text { width: 43px; height: 3px; margin: 0 auto; background: #0093ff; border-radius: 3px 3px 0px 0px; } .line { height: 3px; } } .resourceSearchBox { border-bottom: 1px solid #efefef; } .classification { width: 100%; display: flex; justify-content: space-between; align-items: center; padding: 10px; .flex1 { flex: 1; text-align: center; } .title { color: #999999; } .count { width: 35px; margin: 0 auto; margin-top: 5px; border-radius: 10px; background: #fff; color: #c8c8c8; height: 18px; font-size: 12px; } .activeClassify, .classifyItem:hover { .title { color: #0093ff; } .count { background: #0093ff; color: #fff; } } .showSearch { .imgBox { width: 39px; height: 24px; border-radius: 16px; margin: 0 auto; } .imgBox:hover, .activeSearch { border: 1px solid #0093ff; } } } .inputBox { margin: 0 auto !important; } .resourceList { padding: 20px; .resourceItem { padding: 10px 0; display: flex; .resourceImg { width: 150px; height: 80px; background: #fff; border-radius: 5px; overflow: hidden; display: flex; align-items: center; justify-content: center; position: relative; border: 1px solid #efefef; img { height: 100%; width: 100%; object-fit: contain; } } .rName { flex: 1; margin-left: 20px; position: relative; .handleBox { position: absolute; bottom: 0; left: 0; .el-icon { margin-right: 10px; } } } } } } .screenBox { display: flex; padding: 0 10px; width: 350px; .title { margin: 0; padding: 0 10px; border: none; } .flex1 { flex: 1; display: flex; // align-items: center; justify-content: space-between; } .all { width: 50px; height: 22px; border-radius: 17px; margin: 0px 10px 10px 0; border: 1px solid #d8d8d8; text-align: center; line-height: 20px; font-size: 12px; } .allActive { width: 50px; height: 22px; border-radius: 17px; margin: 0px 10px 10px 0; text-align: center; line-height: 20px; color: #fff; background: #0093ff; border: 1px solid #0093ff; font-size: 12px; } .scribeItem { width: 18px; height: 18px; border-radius: 3px; margin: 0px auto; border: none; } .activeScribe { border: 1px solid #0093ff; } } .noteBox { .chapterName { margin: 15px; display: flex; align-items: center; span { margin-left: 5px; } } .listItem { border-bottom: 1px solid rgba(212, 212, 212, 0.16); position: relative; margin: 10px; line-height: 20px; background: #fff; border-radius: 5px; padding: 10px 0; .textBox { .title { display: flex; justify-content: space-between; align-items: center; margin-right: 10px; .border-left { height: 25px; border-right: 4px solid; border-radius: 0 5px 5px 0; margin-right: 10px; } .title-con { display: flex; align-items: center; color: #949494; .round { width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; } } img { margin-left: 5px; } } .chapter { color: #b7b7b7; margin: 5px 5px 5px 14px; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; border-left: 3px solid #b7b7b7; border-left-radius: 20px; padding: 0 5px; } } .noteText { margin: 10px 10px 10px 15px; border-radius: 3px; padding: 2px 5px; .con { max-height: 65px; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; } } } } .allSearch { padding: 20px; .allSearchList { .searchItem { margin: 15px; background: #fff; border-radius: 10px; padding: 10px; display: flex; .index { line-height: 24px; width: 25px; } .searchCon { flex: 1; width: 240px; overflow: hidden; margin-right: 8px; height: 45px; line-height: 22px; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; text-overflow: ellipsis; } } } } } .settingBox{ margin-bottom: 50px !important; } } </style> src/views/readerPages/webHome.vue
File was renamed from src/views/home.vue @@ -6,13 +6,19 @@ <span></span> <div class="userInfoBox"> <div class="userName" v-if="userInfo.name">{{ userInfo.name }}</div> <div><div class="layout hover" @click="layoutBtn">退出</div></div> <div v-if="token"><div class="layout hover" @click="layoutBtn">退出</div></div> <div v-else><div class="layout hover" @click="goLogin">登录</div></div> </div> </div> <div class="contentBox"> <!-- 菜单 --> <div class="menuBox"> <div v-for="(item, index) in menuData" :key="index" @click="menuItemClick(item.name)"> <div v-for="(item, index) in menuData" :key="index" @click="menuItemClick(item.name)" :class="token ? '' : 'notClick'" > <div :class="['menuItem', activeMenu == item.name ? 'active' : '']" v-if="item.isShow"> <div class="menuIcon imgBox"> <img :src="item.icon" /> @@ -94,31 +100,11 @@ placeholder="请输入内容" v-model="searchText" @keyup.enter="searchBook" clearable @clear="handleClear" > <template #prefix> <el-icon><Search /></el-icon> </template> <template #suffix> <svg @click="searchBook" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-list-search hover" > <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M15 15m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /> <path d="M18.5 18.5l2.5 2.5" /> <path d="M4 6h16" /> <path d="M4 12h4" /> <path d="M4 18h4" /> </svg> <img :src="listSearch" @click="searchBook" class="hover"/> </template> </el-input> </div> @@ -135,18 +121,19 @@ </div> </div> <!-- 目录 --> <div class="menuList" v-if="activeMenu == '目录'"> <div class="menuList" v-if="activeMenu == '目录'" v-loading="listLoading"> <el-tree ref="catalogTree" default-expand-all="true" :expand-on-click-node="false" highlight-current node-key="start" highlight-current :data="catalogueData" :props="defaultProps" v-if="catalogueData.length > 0" @node-click="handleNodeClick" :filter-node-method="filterNode" empty-text="暂无数据" > <template #default="{ node, data }"> <div class="custom-tree-node"> @@ -157,6 +144,32 @@ </el-tree> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> <div class="allSearch" v-if="activeMenu == '检索'"> <div class="list-box" v-loading="listLoading"> <div v-if="allSearchReault.length > 0" class="allSearchList"> <div v-for="(reault, index) in allSearchReault" :key="index"> <div class="chapterName"> <el-icon @click="searchClose(index)" v-if="searchReaultData.isShow && searchReaultData.openIndex == index" class="hover" ><ArrowDown /></el-icon> <el-icon @click="searchOpen(index)" v-else class="hover"><ArrowRight /></el-icon> <span>{{ reault.chapterName }} ({{ reault.itemList.length }})</span> </div> <div v-for="(item, index1) in reault.itemList" :key="index1" class="searchItem" v-show="searchReaultData.isShow && searchReaultData.openIndex == index"> <div class="index">{{ index1 + 1 }}.</div> <div class="searchCon hover" @click="goSearchContent(item)" v-html="item.txt1" :title="item.txt"> </div> </div> </div> </div> <div v-else> <el-empty :image-size="60" description="暂无数据" /> </div> </div> </div> <!-- 笔记 --> @@ -178,7 +191,7 @@ ></div> </div> </div> <div class="list-box"> <div class="list-box" v-loading="listLoading"> <div v-if="scribeData.noteList.length > 0"> <div v-for="(noteItem, index) in scribeData.noteList" :key="noteItem"> <div class="chapterName"> @@ -244,8 +257,8 @@ </div> </div> <!-- 资源 --> <div class="" v-if="activeMenu == '资源'"> <div class="resourceBox" v-if="classifySelectList.length > 0"> <div class="resourceBox" v-if="activeMenu == '资源'" v-loading="listLoading"> <div class="resourceSearchBox" v-if="classifySelectList.length > 0"> <div class="classification"> <div v-for="item in classifySelectList" @@ -274,31 +287,11 @@ placeholder="请输入内容" v-model="searchText" @keyup.enter="searchBook" clearable @clear="handleClear" > <template #prefix> <el-icon><Search /></el-icon> </template> <template #suffix> <svg @click="searchBook" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-list-search hover" > <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M15 15m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /> <path d="M18.5 18.5l2.5 2.5" /> <path d="M4 6h16" /> <path d="M4 12h4" /> <path d="M4 18h4" /> </svg> <img :src="listSearch" @click="searchBook" class="hover"/> </template> </el-input> </div> @@ -313,8 +306,12 @@ @mouseover="showHandle = index" @mouseleave="showHandle = null" > <img :src="item.resourcePath" mode="" v-if="activeClassify == 'image'"/> <img :src="item.icon" mode="" v-else-if="item.icon && activeClassify != 'image'" /> <img :src="item.resourcePath" mode="" v-if="activeClassify == 'image'" /> <img :src="item.icon" mode="" v-else-if="item.icon && activeClassify != 'image'" /> <!-- <el-icon v-else-if="activeClassify == 'image'" size="30"></el-icon> --> <el-icon v-else-if="activeClassify == 'audio'" size="30"><Headset /></el-icon> <el-icon v-else-if="activeClassify == 'video'" size="30" @@ -341,11 +338,18 @@ ><VideoPlay /></el-icon> <el-icon @click="goPlay(item)" @click="goPlay(item, index)" size="20" class="icon hover" v-if="item.resourceType == '音频'" ><Headset v-if="item.resourceType == '音频' && playIndex != index" ><VideoPlay /></el-icon> <el-icon @click="goPause()" size="20" class="icon hover" v-if="item.resourceType == '音频' && playIndex == index" ><VideoPause /></el-icon> <el-icon @click="getCapture(item, index)" @@ -390,8 +394,8 @@ </div> </div> <!-- 截图 --> <div class="" v-if="activeMenu == '截图'"> <div class="list-box"> <div class="screenshotList" v-if="activeMenu == '截图'"> <div class="list-box" v-loading="listLoading"> <div class="screenshot" v-if="screenshotList.length > 0"> <div v-for="(item, index) in screenshotList" :key="item" class="captureItem"> <div class="imgBox" @mouseover="showDelete = index" @mouseleave="showDelete = null"> @@ -414,12 +418,12 @@ </div> </div> </div> <!-- 标签 --> <div class="" v-if="activeMenu == '书签'"> <div class="list-box"> <!-- 书签 --> <div class="reMarkList" v-if="activeMenu == '书签'"> <div class="list-box" v-loading="listLoading"> <div v-if="reMarkList.length > 0"> <div v-for="(item, index) in reMarkList" :key="index" class="reMarkItem"> <div class="reMarkCon"> <div class="reMarkCon hover" @click="goReMark(item)"> <span class="con">{{ item.content }}</span> <span>P{{ item.page }}</span> </div> <div class="deleteReMarkImg"> @@ -474,9 +478,9 @@ </div> <div class="rightBox"> <div class="pageSizeBox"> <div><img :src="zoomOut" @click="changePageSize('reduce')" /></div> <div class="hover"><img :src="zoomOut" @click="changePageSize('reduce')" /></div> <div>{{ headerData.pageSize }}%</div> <div><img :src="zoomIn" @click="changePageSize('add')" /></div> <div class="hover"><img :src="zoomIn" @click="changePageSize('add')" /></div> </div> <!-- <div class="brushImgBox"> <div @click="jumpContent('note' + index)"> @@ -718,7 +722,7 @@ <ul class="menu" v-if="teachToolsMenuData.length > 0"> <li v-for="item in teachToolsMenuData" :key="item.key"> <div :class="item.name === activeTool ? 'activeItem hover' : 'menuItem hover'" :class="token ? 'hover' : 'notClick'" :style="!toolState.open ? 'padding:10px 15px' : ''" v-if="item.isShow" @click="selectTeachTools(item)" @@ -817,7 +821,7 @@ v-if="floatingToolBox.length > 0" > <div class="hover" :class="token ? 'hover' : 'notClick'" v-for="item in floatingToolBox" :key="item.name" @click="floatItemHandle(item)" @@ -858,27 +862,28 @@ </div> </template> </el-popover> <div class="floatToolItem mark" v-if="floatingToolData.activeToolData == '书签'" > <div class="floatToolItem mark" v-if="floatingToolData.activeBookmark == '书签'"> <el-tooltip class="box-item" effect="dark" :content="item.name" placement="right"> <div class="imgBox"> <img :src="tagW" alt="" v-if="floatingToolData.activeToolData == item.name"/> <img :src="item.icon" alt="" v-else/> <img :src="tagW" alt="" v-if="floatingToolData.activeBookmark == item.name" /> <img :src="item.icon" alt="" v-else /> </div> </el-tooltip> </div> <div <div :class=" floatingToolData.activeToolData == item.name ? 'floatToolActive floatToolItem' : 'floatToolItem' " v-else v-else-if=" floatingToolData.activeToolData != '书签' && floatingToolData.activeToolData != '标记' " > <el-tooltip class="box-item" effect="dark" :content="item.name" placement="right"> <div class="imgBox"> <img :src="item.icon" alt=""/> <img :src="item.icon" alt="" /> </div> </el-tooltip> <!-- <div class="text">{{ item.name }}</div> --> @@ -906,7 +911,7 @@ </div> </div> </div> <div class="toolSelectBox" v-show="!(toolActive == '高亮' || toolActive == '划线')"> <div class="toolSelectBox" v-show="toolActive != '高亮' && toolActive != '划线'"> <div v-for="item in dialogToolList" :key="item.icon" @click="dialogToolHandle(item)"> <div :class="item.name == toolActive ? 'dialogToolItem active' : 'dialogToolItem'" @@ -931,12 +936,12 @@ <!-- <el-button @click="delUserKey">删除</el-button> --> <div class="dialogToolItem"> <el-tooltip class="box-item" effect="dark" content="删除" placement="bottom"> <img :src="trash" @click="delUserKey" /> <img :src="deleteIcon" @click="delUserKey" /> </el-tooltip> </div> <div class="dialogToolItem"> <el-tooltip class="box-item" effect="dark" content="笔记" placement="bottom" v-if="isUpdate"> <img :src="noteUpdate" @click="updateNote" /> <div class="dialogToolItem" v-if="isUpdate"> <el-tooltip class="box-item" effect="dark" content="笔记" placement="bottom"> <img :src="biji2" @click="updateNote" /> </el-tooltip> </div> </div> @@ -989,18 +994,26 @@ </div> </div> <el-dialog title="资源" align-center v-model="resourVisble" width="845" class="resourDialog"> <el-dialog title="资源" align-center v-model="resourVisble" width="845" class="resourDialog" :before-close="resourVisbleClose" > <div class="videoBox" v-if="resourType == '视频'"> <video controls controlslist="nodownload" :src="testVideo"></video> </div> <div class="videoBox" v-if="resourType == '音频'"> <!-- <video controls controlslist="nodownload" :src="testVideo"></video> --> <audio ref="audioPlayer" :src="testVideo" controls></audio> </div> <!-- <div class="wordBox" v-if="resourType == 'word'"> <vue-office-docx :src="testWord" /> </div> --> </el-dialog> <div class="audioBox" v-show="false"> <!-- <video controls controlslist="nodownload" :src="testVideo"></video> --> <audio ref="audioPlayer" :src="testAudio" controls @loadedmetadata="autoPlay"></audio> </div> <!-- 图片 --> <el-image-viewer v-if="confirmDialog" @@ -1107,11 +1120,15 @@ <iframe src="https://adjam93.github.io/threejs-model-viewer/#" frameborder="0"></iframe> </div> </el-dialog> <el-dialog title="生词卡片" align-center v-model="shengciVisble" width="800" class="myDialogs"> <div class="wendabox"> <newWord :resourceUrl="resourceUrl" /> </div> </el-dialog> </template> <script setup lang="ts"> import { ref, reactive, watch, onMounted, onBeforeMount, inject } from 'vue' import { ctxUrl } from '@/assets/js/config' import { useRouter, useRoute } from 'vue-router' import useClipboard from 'vue-clipboard3' const { toClipboard } = useClipboard() @@ -1124,6 +1141,7 @@ let route = useRoute() import moment from 'moment' import dictionary from '@/views/components/dictionary.vue' import newWord from '@/views/components/newWord.vue' import { ElMessage, ElMessageBox, valueEquals } from 'element-plus' import logo from '@/assets/images/header/logo.png' import mulu from '@/assets/images/menu/mulu.png' @@ -1132,11 +1150,13 @@ import zhishitupu from '@/assets/images/menu/zhishitupu.png' import jietu from '@/assets/images/menu/jietu.png' import biaoqian from '@/assets/images/menu/biaoqian.png' import allSearch from '@/assets/images/menu/allSearch.svg' import topbg from '@/assets/images/header/top-bg.png' import setting from '@/assets/images/operation/setting.png' import search from '@/assets/images/operation/search.png' import search1 from '@/assets/images/operation/search1.png' import sound from '@/assets/images/operation/sound.png' import listSearch from '@/assets/images/operation/list-search.svg' import aIzhinengwenda from '@/assets/images/menu/AIzhinengwenda.png' import aIyuyinyuedu from '@/assets/images/menu/AIyuyinyuedu.png' @@ -1161,51 +1181,51 @@ import dati_charu_blue from '@/assets/images/operation/dati_charu_blue.png' import xiake from '@/assets/images/operation/xiake.png' import huabi from '../assets/images/operation/huabi-w.png' import wenzi2 from '../assets/images/operation/wenzi.png' import biaoqianw from '../assets/images/operation/biaoqian-w.png' import tagW from '../assets/images/operation/tag-w.svg' import baiban from '../assets/images/operation/baiban-w.png' import biaozhu from '../assets/images/operation/pencil-minus.svg' import jieping from '../assets/images/operation/screenshot-w.png' import clearPrevious from '../assets/images/operation/clearPrevious.png' import hide from '../assets/images/operation/hide.png' import show from '../assets/images/operation/show.png' import tuichu from '../assets/images/operation/tuichu.png' import tuxing from '../assets/images/operation/tuxing.png' import chongzuo from '../assets/images/operation/chongzuo.png' import qingchu2 from '../assets/images/operation/qingchu.png' import huabi from '@/assets/images/operation/huabi-w.png' import wenzi2 from '@/assets/images/operation/wenzi.png' import biaoqianw from '@/assets/images/operation/biaoqian-w.png' import tagW from '@/assets/images/operation/tag-w.svg' import baiban from '@/assets/images/operation/baiban-w.png' import biaozhu from '@/assets/images/operation/pencil-minus.svg' import jieping from '@/assets/images/operation/screenshot-w.png' import clearPrevious from '@/assets/images/operation/clearPrevious.png' import hide from '@/assets/images/operation/hide.png' import show from '@/assets/images/operation/show.png' import tuichu from '@/assets/images/operation/tuichu.png' import tuxing from '@/assets/images/operation/tuxing.png' import chongzuo from '@/assets/images/operation/chongzuo.png' import qingchu2 from '@/assets/images/operation/qingchu.png' import gaoliang from '../assets/images/operation/gaoliang.png' import gaoliang1 from '../assets/images/operation/gaoliang-b.png' import huaxian from '../assets/images/operation/huaxian.png' import huaxian1 from '../assets/images/operation/huaxian1.png' import biji2 from '../assets/images/operation/biji.png' import biji1 from '../assets/images/operation/biji-b.png' import biaozhu2 from '../assets/images/operation/biaozhu.png' import biaozhu3 from '../assets/images/operation/biaozhu1.png' import fuzhi from '../assets/images/operation/fuzhi.png' import fuzhi1 from '../assets/images/operation/fuzhi-b.png' import AIyuedu from '../assets/images/operation/AIyuedu.png' import AIyuedu1 from '../assets/images/operation/AIyuedu-b.png' import cidian2 from '../assets/images/operation/cidian.png' import cidian1 from '../assets/images/operation/cidian-b.png' import baidu from '../assets/images/operation/baidu.png' import baidu1 from '../assets/images/operation/baidu-b.png' import yuyinyuedu from '../assets/images/operation/yuyinyuedu.png' import yuyinyuedu1 from '../assets/images/operation/yuyinyuedu-b.png' import xuanzhong from '../assets/images/operation/xuanzhong.png' import xuanzhong1 from '../assets/images/operation/xuanzhong1.png' import gaoliang from '@/assets/images/operation/gaoliang.png' import gaoliang1 from '@/assets/images/operation/gaoliang-b.png' import huaxian from '@/assets/images/operation/huaxian.png' import huaxian1 from '@/assets/images/operation/huaxian1.png' import biji2 from '@/assets/images/operation/biji.png' import biji1 from '@/assets/images/operation/biji-b.png' import biaozhu2 from '@/assets/images/operation/biaozhu.png' import biaozhu3 from '@/assets/images/operation/biaozhu1.png' import fuzhi from '@/assets/images/operation/fuzhi.png' import fuzhi1 from '@/assets/images/operation/fuzhi-b.png' import AIyuedu from '@/assets/images/operation/AIyuedu.png' import AIyuedu1 from '@/assets/images/operation/AIyuedu-b.png' import cidian2 from '@/assets/images/operation/cidian.png' import cidian1 from '@/assets/images/operation/cidian-b.png' import baidu from '@/assets/images/operation/baidu.png' import baidu1 from '@/assets/images/operation/baidu-b.png' import yuyinyuedu from '@/assets/images/operation/yuyinyuedu.png' import yuyinyuedu1 from '@/assets/images/operation/yuyinyuedu-b.png' import xuanzhong from '@/assets/images/operation/xuanzhong.png' import xuanzhong1 from '@/assets/images/operation/xuanzhong1.png' import zoomIn from '../assets/images/operation/zoomIn.png' import zoomOut from '../assets/images/operation/zoomOut.png' import huabi2 from '../assets/images/operation/huabi.svg' import trash from '../assets/images/operation/trash.svg' import noteUpdate from '../assets/images/operation/Note-b.svg' import zoomIn from '@/assets/images/operation/zoomIn.png' import zoomOut from '@/assets/images/operation/zoomOut.png' import huabi2 from '@/assets/images/operation/huabi.svg' import deleteIcon from '@/assets/images/operation/delete1.svg' import noteUpdate from '@/assets/images/operation/Note-b.svg' import shanchu from '../assets/images/operation/delete.png' import bianji from '../assets/images/operation/bianji.png' import video from '../assets/images/content/resource.png' import shanchu from '@/assets/images/operation/delete.png' import bianji from '@/assets/images/operation/bianji.png' import video from '@/assets/images/content/resource.png' //引入VueOfficeDocx组件 import VueOfficeDocx from '@vue-office/docx' //引入相关样式 @@ -1218,6 +1238,7 @@ import { loadMicroApp } from 'qiankun' import { microApps } from '@/child.ts' let token = localStorage.getItem('token') const canvasWith = ref(1000) const canvasheight = ref(3000) const screenWidth = ref( @@ -1229,7 +1250,9 @@ const activeCatalog = ref() const reMarResult = ref() onMounted(() => { getUserInfo() if (token) { getUserInfo() } setTimeout(() => { canvasWith.value = document.querySelector('.content-box').offsetWidth canvasheight.value = document.querySelector('.content-box').offsetHeight @@ -1249,7 +1272,7 @@ } // 加载微应用 microApp = loadMicroApp(microApps.book1, { microApp = loadMicroApp(microApps[localStorage.getItem('bookId')], { sandbox: { strictStyleIsolation: true, experimentalStyleIsolation: true @@ -1260,6 +1283,7 @@ window.qiankunActions.onGlobalStateChange((state, prev) => { console.log('父层state变化', state) window.qiankunState = state if (window.qiankunState && window.qiankunState.initTestBook) { window.qiankunState.initTestBook( localStorage.getItem('bookId') + '', @@ -1270,8 +1294,8 @@ // 定义父层方法 window.qiankunActions.setGlobalState({ state: 1, // 加载子应用 state:1, disableSign: false, windowSelection: (data) => { // 绑定子应用选择监听事件 console.log(data, '子应用选择') @@ -1281,6 +1305,7 @@ showToolBox.value = false lineDelete.showLineDelete = false dictionaryData.showContent = false isUpdate.value = false toolActive.value = '' lineDelete.top = data.y + 20 lineDelete.left = data.x @@ -1288,11 +1313,27 @@ }, chooseWords: (data) => { console.log(data, '英语生词') if (data) { dialogToolData.txt = data.word dialogToolData.left = data.x dialogToolData.top = data.y + 20 getSearchResult() switch (data.type) { case 'word': if (data) { dialogToolData.txt = data.word dialogToolData.left = data.x dialogToolData.top = data.y + 20 getSearchResult() } break case 'swdt': console.log() siweiVisble.value = true break case 'readText': window.speechSynthesis.cancel() const synth = window.speechSynthesis const utterances = new SpeechSynthesisUtterance(data.data) // utterances.lang = 'EN' // 设置语言为中文 synth.speak(utterances) break } }, getBookConfig: (data) => { @@ -1305,7 +1346,9 @@ }, catalogChange: (data) => { activeCatalog.value = data.showCatalogList getSignData() if (!window.qiankunState.disableSign && token) { getSignData() } console.log(data, '章节切换:目前显示的三个章节') }, pageChange: (data) => { @@ -1324,14 +1367,18 @@ return item.chapterNum == data.catalog && item.page == data.page }) if (reMarResult.value) { floatingToolData.activeBookmark = '书签' floatingToolData.activeToolData = '书签' } else { floatingToolData.activeBookmark = '' floatingToolData.activeToolData = '' } } }) // 调用子层方法 // if (window.qiankunState && window.qiankunState.aa) window.qiankunState.aa(1) // // 调用子层方法 // if (window.qiankunState && window.qiankunState.getAnswers){ // window.qiankunState.getAnswers(localStorage.getItem("")) // } }) // watch( @@ -1355,24 +1402,30 @@ if (res) { let teacherRole = res.roleLinks.find((item) => item.role.refCode == 'teacher') let teacherInfos = res.infoList.find((item) => item.type == 'teacherInfo') let wechatInfo = res.infoList.find((item) => item.type == 'WeChat') let studentInfo = res.infoList.find((item) => item.type == 'Default') let phoneInfo = res.secretList.find((item) => item.type == 'MobilePhone') let nameAndPassword = res.secretList.find((item) => item.type == 'LoginNameAndPassword') if (nameAndPassword) { if (teacherRole && teacherInfos) { userInfo.value = { name: nameAndPassword.credential } localStorage.setItem('userInfo', JSON.stringify(userInfo.value)) } else if (teacherRole && teacherInfos) { userInfo.value = { ...teacherInfos, name: teacherInfos.name, role: 'Teacher', roleId: teacherRole.role.id } localStorage.setItem('userInfo', JSON.stringify(userInfo.value)) } else if (wechatInfo) { userInfo.value = { ...wechatInfo, phoneNumber: phoneInfo?.credential, Email: emailInfo?.credential, role: 'Student' } } else if (phoneInfo) { userInfo.value = { name: phoneInfo.credential } localStorage.setItem('userInfo', JSON.stringify(userInfo.value)) } @@ -1394,6 +1447,11 @@ name: '目录', icon: mulu, isShow: bookConfig.value.textbookComponents.indexOf('1E16353F') > -1 }, { name: '检索', icon: allSearch, isShow: bookConfig.value.textbookComponents.indexOf('B7C5B45D') > -1 }, { name: '笔记', @@ -1433,7 +1491,7 @@ isShow: bookConfig.value.textbookComponents.indexOf('83FB80FD') > -1 }, { name: '生字卡片', name: '生词卡片', icon: shengzikapian, isShow: bookConfig.value.textbookComponents.indexOf('38D32EE3') > -1 }, @@ -1564,10 +1622,12 @@ ] } const listLoading = ref(false) //获取目录 const catalogueData = ref([]) const getCatalogueData = () => { listLoading.value = true axios .get(bookConfig.value.resourceUrl + '/information.json') .then(function (response) { @@ -1576,6 +1636,7 @@ console.log(json, '目录') catalogueData.value = json.data headerData.totlePage = json.data[json.data.length - 1].end listLoading.value = false }) .catch(function (error) { console.log(error) @@ -1594,9 +1655,22 @@ const handleNodeClick = (data) => { console.log(data) headerData.process = data.start if (window.qiankunState && window.qiankunState.gotoPage) window.qiankunState.gotoPage(data.chapter, data.start) if (localStorage.getItem('tryPageCount')) { if (data.start < localStorage.getItem('tryPageCount')) { headerData.process = data.start if (window.qiankunState && window.qiankunState.gotoPage) window.qiankunState.gotoPage(data.chapter, data.start) } else { ElMessage({ message: '试读已结束!', type: 'warning' }) } } else { headerData.process = data.start if (window.qiankunState && window.qiankunState.gotoPage) window.qiankunState.gotoPage(data.chapter, data.start) } } //获取资源 @@ -1612,11 +1686,14 @@ const resourVisble = ref(false) const resourType = ref('') //资源类型图片/音频 const testVideo = ref('') //视频链接 const testAudio = ref('') //音频链接 const testWord = ref('') //word链接 const showHandle = ref(null) const playIndex = ref(null) //音频播放 const audioPlayer = ref(null) const getResourceData = () => { imgPreviewList.value = [] listLoading.value = true axios .get(bookConfig.value.resourceUrl + '/resource.json?t=12') .then(function (response) { @@ -1627,9 +1704,9 @@ teacherResourceList.value = [] // 获取数组中所有对象的age属性的数量 json.forEach((item) => { if(item.iconPath){ if (item.iconPath) { item.icon = bookConfig.value.resourceUrl + '/' + item.iconPath }else{ } else { item.icon = '' } if (resourceType.value == 'default') { @@ -1651,12 +1728,13 @@ resourceDataList.value = [] defaultResourceList.value.forEach((resItem) => { if (resItem.resourceType == '图片') { if(resItem.resourcePath){ if (resItem.resourcePath) { resItem.resourcePath = bookConfig.value.resourceUrl + '/' + resItem.resourcePath }else{ resItem.resourcePath = bookConfig.value.requestCtx + '/file/GetPreViewImage?md5=' + resItem.md5 } else { resItem.resourcePath = bookConfig.value.requestCtx + '/file/GetPreViewImage?md5=' + resItem.md5 } imgPreviewList.value.push(resItem.resourcePath) if (activeClassify.value == 'image') { resourceDataList.value.push(resItem) @@ -1691,7 +1769,6 @@ count: imgCount, key: 'image' }) } if (audioCount > 0) { classifySelectList.value.push({ @@ -1729,7 +1806,6 @@ } }) } console.log(resourceDataList.value,"图片") // if(activeClassify.value == "video"){ // resourceDataList.value.forEach((item,index)=>{ // console.log(item,"item") @@ -1758,10 +1834,11 @@ resourceDataList.value = [] teacherResourceList.value.forEach((resItem) => { if (resItem.resourceType == '图片') { if(resItem.resourcePath){ if (resItem.resourcePath) { resItem.resourcePath = bookConfig.value.resourceUrl + '/' + resItem.resourcePath }else{ resItem.resourcePath = bookConfig.value.requestCtx + '/file/GetPreViewImage?md5=' + resItem.md5 } else { resItem.resourcePath = bookConfig.value.requestCtx + '/file/GetPreViewImage?md5=' + resItem.md5 } imgPreviewList.value.push(resItem.resourcePath) if (activeClassify.value == 'image') { @@ -1835,22 +1912,32 @@ }) } } } listLoading.value = false }) .catch(function (error) { console.log(error) }) } //资源播放视频 const goPlay = (data) => { const goPlay = (data, index) => { resourType.value = data.resourceType if (data.resourceType == '视频' || data.resourceType == '音频') { resourVisble.value = true if (data.resourceType == '视频') { if (data.md5) { testVideo.value = bookConfig.value.requestCtx + '/file/api/ApiDownload?md5=' + data.md5 } else { testVideo.value = bookConfig.value.resourceUrl + '/' + data.resourcePath } resourVisble.value = true } else if (data.resourceType == '音频') { playIndex.value = index if (data.md5) { testAudio.value = bookConfig.value.requestCtx + '/file/api/ApiDownload?md5=' + data.md5 } else { testAudio.value = bookConfig.value.resourceUrl + '/' + data.resourcePath } autoPlay() } else { if (data.md5) { window.open(bookConfig.value.requestCtx + '/file/api/ApiDownload?md5=' + data.md5) @@ -1863,10 +1950,25 @@ // testWord.value = bookConfig.value.resourceUrl + '/' + data.resourcePath // } } // 关闭视频 const resourVisbleClose = () => { resourVisble.value = false testVideo.value = '' } const autoPlay = () => { if (audioPlayer.value) { audioPlayer.value.play() } } const goPause = () => { playIndex.value = null testAudio.value = '' } // //资源跳转到指定位置 const JumpPosition = (data) => { console.log(data,"跳转") console.log(data, '跳转') if (window.qiankunState && window.qiankunState.gotoPage) { window.qiankunState.gotoPage(Number(data.chapterNum), Number(data.pagination)) } @@ -1899,6 +2001,7 @@ // 菜单点击 const menuItemClick = (name) => { searchText.value = '' allSearchReault.value =[] if (menuState.open && activeMenu.value == name) { menuState.open = false } else { @@ -1912,7 +2015,7 @@ getNotesList() break case '资源': getResourceData() getResourceData() break case '知识图谱': break @@ -1921,6 +2024,8 @@ break case '书签': getReMarkList() break case '检索': break } } @@ -1967,6 +2072,10 @@ animation: false, bgColorList: [ { lable: '白', key: '#fff' }, { lable: '黄', key: '#FBF9F4' }, @@ -2004,6 +2113,9 @@ }) } const bgColorSelect = (item) => { window.qiankunActions.setGlobalState({ bgColor: item.key }) settingForm.bgColorActive = item.key } //笔记颜色筛选 @@ -2028,6 +2140,11 @@ } const catalogTree = ref() const allSearchReault = ref([]) const searchReaultData = reactive({ isShow: true, openIndex: 0, }) const searchBook = async () => { switch (activeMenu.value) { case '目录': @@ -2059,6 +2176,123 @@ case '书签': getReMarkList() break case '检索': listLoading.value = true allSearchReault.value = [] console.log(window.qiankunState.searchBookByKeyword) if (window.qiankunState && window.qiankunState.searchBookByKeyword) { let searchReault = window.qiankunState.searchBookByKeyword(searchText.value) if (searchReault.length > 0) { let catalogueList = sortArr(catalogueData.value, 'chapter') if (catalogueList.length > 0) { catalogueList.forEach((item) => { let labels = '' if (item.length > 1) { labels = item.map((i) => i.label).join('/') } let itemList = [] let text = searchText.value.replace(/^\s*|\s*$/g, '') searchReault.forEach((item1) => { if (item[0].chapter == item1.catalog) { item1.txt1 = item1.txt let replaceStr = "<span class='searchColor'>" + text + '</span>' let htmlStr = item1.txt1.split(text).join(replaceStr) item1.txt1 = '<p>' + htmlStr + '</p>' itemList.push(item1) } }) if (itemList.length > 0) { allSearchReault.value.push({ chapter: item[0].chapter, chapterName: labels ? labels : item[0].label, itemList: itemList }) } }) } console.log(allSearchReault.value, 444) listLoading.value = false } } break } } function sortArr(arr, str) { var _arr = [], _t = [], // 临时的变量 _tmp // 按照特定的参数将数组排序将具有相同值得排在一起 arr = arr.sort(function (a, b) { var s = a[str], t = b[str] return s < t ? -1 : 1 }) if (arr.length) { _tmp = arr[0][str] } // console.log( arr ); // 将相同类别的对象添加到统一个数组 for (var i in arr) { if (arr[i][str] === _tmp) { _t.push(arr[i]) } else { _tmp = arr[i][str] _arr.push(_t) _t = [arr[i]] } } // 将最后的内容推出新数组 _arr.push(_t) return _arr } //检索章节收起 const searchOpen = (index) => { searchReaultData.isShow = true searchReaultData.openIndex = index } //检索章节打开 const searchClose = (index) => { searchReaultData.isShow = false searchReaultData.openIndex = index } //检索跳转 const goSearchContent = (item) => { console.log(item,1111111111111111111111111111111111111111111111111111) if (window.qiankunState && window.qiankunState.gotoPage) { window.qiankunState.jumpSearchItem(item) } } const handleClear = () => { switch (activeMenu.value) { case '目录': if (catalogTree) catalogTree.value.filter(searchText.value) break case '笔记': getNotesList() break case '资源': getResourceData() break case '知识图谱': break case '截图': getScreenshotList() break case '书签': getReMarkList() break case '检索': allSearchReault.value = [] break } } @@ -2068,10 +2302,12 @@ const functionVisible = ref(false) const siweiVisble = ref(false) const modelToolVisble = ref(false) const shengciVisble = ref(false) const activeTool = ref(0) const toolState = reactive({ open: true }) const resourceUrl = ref('') const selectTeachTools = (item) => { activeTool.value = item.name @@ -2091,6 +2327,9 @@ case '词典': cidianVisible.value = true break case '生词卡片': resourceUrl.value = bookConfig.value.resourceUrl shengciVisble.value = true } } @@ -2123,6 +2362,7 @@ let canvas = null const floatingToolData = reactive({ activeToolData: '', //选中工具 activeBookmark: '', //选中书签 elLeft: 390, startclientx: 0, startclienty: 0 @@ -2152,10 +2392,7 @@ console.log(floatingToolData.activeToolData, 'floatingToolData.activeToolData') if (floatingToolData.activeToolData == '画笔') { floatingToolData.activeToolData = '' } else if (floatingToolData.activeToolData == '书签') { delReMark() floatingToolData.activeToolData = '' } else { }else { floatingToolData.activeToolData = item.name } @@ -2168,7 +2405,11 @@ case '标记': break case '书签': reMark() if(floatingToolData.activeBookmark != '书签' ){ floatingToolData.activeBookmark = '书签' reMark() } break case '截屏': jitT() @@ -2253,8 +2494,18 @@ console.log(str, '标记') if (str == 'hide') { if (window.qiankunState && window.qiankunState.delSign) { window.qiankunState.delSign({ ids: [] }) window.qiankunState.delSign({ type: 'Highlight' }) window.qiankunState.delSign({ type: 'Dashing' }) window.qiankunState.delSign({ type: 'Note' }) } window.qiankunActions.setGlobalState({ disableSign: true }) } else { window.qiankunActions.setGlobalState({ disableSign: false }) getSignData() } } @@ -2295,7 +2546,8 @@ ] }) .then((res) => { // floatingToolData.activeToolData = '' floatingToolData.activeToolData = '' ElMessage({ message: '添加书签成功!', type: 'success' @@ -2308,6 +2560,7 @@ const getReMarkList = () => { reMarkList.value = [] reMarkData.value = [] listLoading.value = true MG.identity .getUserKey({ domain: 'reMark', @@ -2320,7 +2573,7 @@ list.forEach((item) => { let text = searchText.value.replace(/^\s*|\s*$/g, '') if (searchText.value) { if (item.name.indexOf(text) > -1) { if (item.content.indexOf(text) > -1) { reMarkList.value.push(item) } } else { @@ -2328,7 +2581,16 @@ } }) } listLoading.value = false }) } //跳转 const goReMark = (item) => { console.log(item) if (window.qiankunState && window.qiankunState.gotoPage) { window.qiankunState.gotoPage(Number(item.chapterNum), Number(item.page)) } } // 内容页删除书签 const delReMark = () => { @@ -2378,7 +2640,7 @@ function jitT() { const screenShotHandler = new ScreenShort({ enableWebRtc: false, // 是否显示选项框 level: 99, // 层级级别 level: 999, // 层级级别 completeCallback: callback, //确认回调 closeCallback: closeFn //取消回调 } as any) @@ -2400,7 +2662,7 @@ if (isUpdateImg.value) { screenshotData.value = [] screenshotList.value.forEach((itemNote) => { if ((itemNote.id == screenshotId.value)) { if (itemNote.id == screenshotId.value) { itemNote.name = nameData.value } }) @@ -2449,6 +2711,7 @@ screenshotData.value = [] screenshotList.value = [] imgPreviewList.value = [] listLoading.value = true MG.identity .getUserKey({ domain: 'screenshot', @@ -2471,16 +2734,16 @@ } }) } listLoading.value = false }) } //查看截图 const getCapture = (item, index) => { imgUrl.value = item.imgUrl imgUrl.value = item.imgUrl || item.resourcePath previewIndex.value = index confirmDialog.value = true } const closePreview = () => { imgPreviewList.value = [] confirmDialog.value = false } // 编辑截图名称 @@ -2537,7 +2800,7 @@ ///内容选中 const getSelection = (data) => { if (data.txt) { if (data.txt && token) { toolActive.value = '' dialogToolData.txt = data.txt dialogToolData.page = data.page @@ -2545,6 +2808,9 @@ dialogToolData.left = data.x dialogToolData.top = data.y showToolBox.value = true dictionaryData.showContent = false lineDelete.showLineDelete = false dictionaryData.showContent = false } } @@ -2819,6 +3085,7 @@ //菜单笔记列表 const getNotesList = () => { let chapterList = [] listLoading.value = true catalogueData.value.forEach((item) => { chapterList.push(item.chapter + '') }) @@ -2855,14 +3122,14 @@ let text = searchText.value.replace(/^\s*|\s*$/g, '') if (item.note.indexOf(text) > -1 || item.txt.indexOf(text) > -1) { if (menuState.notesColor == 'all') { itemList = list itemList.push(item) } else if (item.color == menuState.notesColor) { itemList.push(item) } } } else { if (menuState.notesColor == 'all') { itemList = list itemList.push(item) } else if (item.color == menuState.notesColor) { itemList.push(item) } @@ -2879,6 +3146,7 @@ } console.log(scribeData.noteList, 'scribeData.noteList') } listLoading.value = false }) } @@ -2924,7 +3192,6 @@ color: '' }) window.noteHover = (type, id, chapterNum) => { console.log(type, 'typetype') let list = dialogToolData.notesList[chapterNum] let data = list.find((item) => item.id == id) noteContent.note = data.note @@ -2978,7 +3245,9 @@ if (window.qiankunState && window.qiankunState.delSign) { window.qiankunState.delSign({ ids: [ids] }) } getSignData() if (!window.qiankunState.disableSign) { getSignData() } if (type == 'Note') { getNotesList() } @@ -3006,7 +3275,7 @@ const updateUserKey = () => { let data = scribeData.noteList.find((item1) => item1.chapter == dialogToolData.chapter) data.noteList.forEach((itemNote) => { if ((itemNote.id = formData.id)) { if ((itemNote.id == formData.id)) { itemNote.note = formData.desc itemNote.color = noteColorActive.value } @@ -3025,7 +3294,9 @@ addNoteVisble.value = false isUpdate.value = false getNotesList() getSignData() if (!window.qiankunState.disableSign) { getSignData() } }) } @@ -3151,7 +3422,12 @@ } const layoutBtn = () => { localStorage.clear() localStorage.removeItem('token') localStorage.removeItem("userInfo") router.push('/login') } const goLogin = () => { localStorage.removeItem("userInfo") router.push('/login') } </script> @@ -3165,6 +3441,7 @@ display: flex; flex-direction: column; padding: 0 15px; .headerBox { height: 48px; // background-image: url('@/assets/images/header/top-bg.png'); @@ -3216,6 +3493,17 @@ box-shadow: 10px 0 10px -10px rgba(0, 0, 0, 0.07); background: #fff; border-radius: 16px 0px 0px 0px; -moz-user-select: none; -khtml-user-select: none; user-select: none; .notClick { pointer-events: none; .name{ color:#949494 !important; } } .menuItem { text-align: center; line-height: 1; @@ -3261,6 +3549,9 @@ height: calc(100vh - 48px); overflow-y: auto; background: #e0f2ff; -moz-user-select: none; -khtml-user-select: none; user-select: none; .searchBox { width: 100%; height: 60px; @@ -3270,7 +3561,7 @@ align-items: center; border-bottom: 1px solid #efefef; } .resourceBox { .resourceSearchBox { padding: 10px 0; margin: 0 20px; border-bottom: 1px solid rgba(204, 204, 204, 0.32); @@ -3363,6 +3654,7 @@ background: #fff; height: 34px; } .is-focus, .el-input__wrapper { box-shadow: none !important; @@ -3370,6 +3662,10 @@ border: none !important; height: 34px !important; } } .el-input-group__append { padding: 0 10px !important; background: none !important; } } .screenBox { @@ -3628,7 +3924,43 @@ align-items: center; } } .allSearchList { .searchItem { margin: 15px; background: #fff; border-radius: 10px; padding: 10px; display: flex; .index{ line-height: 24px; margin-right:10px; } .searchCon { flex:1; width: 240px; overflow: hidden; margin-right: 8px; height: 45px; line-height: 22px; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; text-overflow: ellipsis; .searchColor{ background:rgb(245, 225, 42, 0.5) } } } } } .allSearch,.notesBox,.resourceBox,.reMarkList,.screenshotList { height: calc(100% - 60px); overflow-y: auto; } } .menuStateBox { width: 25px; @@ -3691,6 +4023,8 @@ div { padding: 5px; display: flex; align-items: center; img { width: 18px; height: 18px; @@ -3745,6 +4079,13 @@ box-shadow: -3px 0px 6px 1px rgba(0, 0, 0, 0.07); background: #fff; border-radius: 0px 16px 0px 0px; -moz-user-select: none; -khtml-user-select: none; user-select: none; .notClick { pointer-events: none; color:#949494 !important; } .toolTitle { height: 57px; line-height: 53px; @@ -3873,6 +4214,9 @@ border-radius: 10px; padding: 6px; overflow: hidden; .notClick { pointer-events: none; } .floatToolItem { margin: 10px 0; height: 30px; @@ -3996,6 +4340,7 @@ width: 190px; display: flex; background: #ffffff; box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.16); border-radius: 5px; margin-bottom: 10px; @@ -4020,19 +4365,20 @@ z-index: 2; box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.3); padding: 5px 10px; background: #fff; // background: #fff; background-color: rgba(90, 90, 90, 0.9); border-radius: 5px; display: flex; .dialogToolItem { padding: 3px; margin: 0 10px; margin: 0 5px; border-radius: 5px; display: flex; justify-content: center; align-items: center; } .dialogToolItem:hover { background-color: rgba(44, 44, 44, 0.2); background-color: rgba(255, 255, 255, 0.3); } } .noteContentBox { @@ -4054,7 +4400,6 @@ } .phone_con { .per-phone { min-width: 150px; align-items: center; // justify-content: space-between; background: #f4f5f7; @@ -4092,7 +4437,8 @@ } .wendabox { width: 100%; height: 700px; height: 80vh; iframe { width: 100%; height: 100%; @@ -4132,8 +4478,12 @@ box-sizing: border-box; } } .myDialogs { height: 90vh; } .myAnserDialogs { width: 628px; width: 700px; height: 90vh; } .myNoteDialogs { width: 500px !important; @@ -4168,9 +4518,9 @@ video { width: 100%; } audio { width: 100%; } } .audioBox { // display:none; } .wordBox {