diff --git a/.env.yc b/.env.yc new file mode 100644 index 0000000..c3b1489 --- /dev/null +++ b/.env.yc @@ -0,0 +1,25 @@ +# 页面标题 +VITE_APP_TITLE = 文枢课堂 + +# 生产环境配置 +VITE_APP_ENV = 'production' + +# AIx融合数字管理系统/生产环境 +VITE_APP_BASE_API = 'https://prev.ysaix.com:7868/prod-api' + +VITE_APP_DOMAIN = 'prev.ysaix.com' + +VITE_APP_UPLOAD_API = 'https://prev.ysaix.com:7868/prod-api' + +# 是否在打包时开启压缩,支持 gzip 和 brotli +VITE_BUILD_COMPRESS = gzip + +VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/' + +VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/' + +# websocket 地址 +VITE_APP_WS_URL = 'wss://prev.ysaix.com:7868' + +# 是否显示开发工具 +VITE_SHOW_DEV_TOOLS = 'false' diff --git a/.env.yc2 b/.env.yc2 new file mode 100644 index 0000000..c4a88ef --- /dev/null +++ b/.env.yc2 @@ -0,0 +1,25 @@ +# 页面标题 +VITE_APP_TITLE = 实训教学 + +# 生产环境配置 +VITE_APP_ENV = 'production' + +# AIx融合数字管理系统/生产环境 +VITE_APP_BASE_API = 'https://prev.ysaix.com:7868/prod-api' + +VITE_APP_DOMAIN = 'prev.ysaix.com' + +VITE_APP_UPLOAD_API = 'https://prev.ysaix.com:7868/prod-api' + +# 是否在打包时开启压缩,支持 gzip 和 brotli +VITE_BUILD_COMPRESS = gzip + +VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/' + +VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/' + +# websocket 地址 +VITE_APP_WS_URL = 'wss://prev.ysaix.com:7868' + +# 是否显示开发工具 +VITE_SHOW_DEV_TOOLS = 'false' diff --git a/electron-builder-yc.yml b/electron-builder-yc.yml new file mode 100644 index 0000000..385b578 --- /dev/null +++ b/electron-builder-yc.yml @@ -0,0 +1,54 @@ +appId: com.electron.app.yc +productName: 文枢课堂 +directories: + output: dist + buildResources: build +win: + executableName: 文枢课堂 + icon: resources/yc-logo.png +files: + - '!**/.vscode/*' + - '!src/*' + - '!electron.vite.config.{js,ts,mjs,cjs}' + - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' + - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' +asarUnpack: + - resources/** +nsis: + oneClick: false + allowToChangeInstallationDirectory: true + artifactName: ${name}-yc-${version}-setup.${ext} + shortcutName: ${productName} + uninstallDisplayName: ${productName} + createDesktopShortcut: always +mac: + entitlementsInherit: build/entitlements.mac.plist + extendInfo: + - NSCameraUsageDescription: Application requests access to the device's camera. + - NSMicrophoneUsageDescription: Application requests access to the device's microphone. + - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. + - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. + notarize: false +dmg: + artifactName: ${name}-${version}.${ext} +linux: + target: + - AppImage + - snap + - deb + maintainer: electronjs.org + category: Utility +appImage: + artifactName: ${name}-${version}.${ext} +npmRebuild: false +publish: + provider: generic + url: https://prev.ysaix.com:7868/src/assets/smarttalkyc/ +electronDownload: + mirror: https://npmmirror.com/mirrors/electron/ +# 额外依赖打包到输出目录 +extraFiles: + - from: ./node_modules/im_electron_sdk/lib/ + to: ./resources + filter: + - '**/*' diff --git a/electron-builder-yc2.yml b/electron-builder-yc2.yml new file mode 100644 index 0000000..a7bfd2f --- /dev/null +++ b/electron-builder-yc2.yml @@ -0,0 +1,54 @@ +appId: com.electron.app.yc2 +productName: 实训教学 +directories: + output: dist + buildResources: build +win: + executableName: 实训教学 + icon: resources/yc-logo.png +files: + - '!**/.vscode/*' + - '!src/*' + - '!electron.vite.config.{js,ts,mjs,cjs}' + - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' + - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' +asarUnpack: + - resources/** +nsis: + oneClick: false + allowToChangeInstallationDirectory: true + artifactName: ${name}-ycsx-${version}-setup.${ext} + shortcutName: ${productName} + uninstallDisplayName: ${productName} + createDesktopShortcut: always +mac: + entitlementsInherit: build/entitlements.mac.plist + extendInfo: + - NSCameraUsageDescription: Application requests access to the device's camera. + - NSMicrophoneUsageDescription: Application requests access to the device's microphone. + - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. + - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. + notarize: false +dmg: + artifactName: ${name}-${version}.${ext} +linux: + target: + - AppImage + - snap + - deb + maintainer: electronjs.org + category: Utility +appImage: + artifactName: ${name}-${version}.${ext} +npmRebuild: false +publish: + provider: generic + url: https://prev.ysaix.com:7868/src/assets/smarttalkycsx/ +electronDownload: + mirror: https://npmmirror.com/mirrors/electron/ +# 额外依赖打包到输出目录 +extraFiles: + - from: ./node_modules/im_electron_sdk/lib/ + to: ./resources + filter: + - '**/*' diff --git a/package.json b/package.json index f861a30..9c9774e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aix-win-ws", - "version": "2.5.6", + "version": "2.5.8", "description": "", "main": "./out/main/index.js", "author": "上海交大重庆人工智能研究院", @@ -16,6 +16,8 @@ "build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml", "build:test": "electron-vite build --mode test && electron-builder --win --config ./electron-builder.yml", "build:prod": "electron-vite build --mode production && electron-builder --win --config ./electron-builder-prod.yml", + "build:yc": "electron-vite build --mode yc && electron-builder --win --config ./electron-builder-yc.yml", + "build:yc2": "electron-vite build --mode yc2 && electron-builder --win --config ./electron-builder-yc2.yml", "build:lt": "electron-vite build --mode lt && electron-builder --win --config ./electron-builder-lt.yml", "build:mac": "electron-vite build --mode production && electron-builder --mac --config ./electron-builder-prod.yml", "build:linux": "npm run build && electron-builder --linux" @@ -92,6 +94,8 @@ "tinycolor2": "^1.6.0", "tinymce": "6.8.3", "tippy.js": "^6.3.7", + "v-viewer": "^3.0.11", + "viewerjs": "^1.11.7", "vite-plugin-electron": "^0.28.8", "vue": "^3.4.34", "vue-cropper": "1.0.3", diff --git a/resources/yc-logo.png b/resources/yc-logo.png new file mode 100644 index 0000000..59db44e Binary files /dev/null and b/resources/yc-logo.png differ diff --git a/src/main/index.js b/src/main/index.js index c223604..f43c571 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -7,6 +7,7 @@ import Logger from './logger' // 日志封装 import chat from './chat' // chat封装 import Store from './store' // Store封装 import updateInit from './update' + // 代理 electron/remote // 第一步:引入remote import remote from '@electron/remote/main' @@ -41,19 +42,19 @@ if(!gotTheLock){ } }) } - +let logoIco = import.meta.env.MODE==='yc'||import.meta.env.MODE==='yc2'?'../../resources/yc-logo.png':'../../resources/logo2.ico' //登录窗口 function createLoginWindow() { if (loginWindow) return loginWindow = new BrowserWindow({ - width: 888, + width: import.meta.env.MODE==='yc'||import.meta.env.MODE==='yc2'?1060:888, height: 520, show: false, frame: false, autoHideMenuBar: true, maximizable: false, resizable: false, - icon: join(__dirname, '../../resources/logo2.ico'), + icon: join(__dirname, logoIco), ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { defaultEncoding: 'utf-8', @@ -95,7 +96,7 @@ function createMainWindow() { frame: false, // 无边框 autoHideMenuBar: true, maximizable: false, - icon: join(__dirname, '../../resources/logo2.ico'), + icon: join(__dirname, logoIco), ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { defaultEncoding: 'utf-8', diff --git a/src/renderer/index.html b/src/renderer/index.html index 8bb269e..6e072ed 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -8,7 +8,7 @@ http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" /> --> - + diff --git a/src/renderer/src/AixPPTist/src/App.vue b/src/renderer/src/AixPPTist/src/App.vue index 74c4b73..7fe9cd8 100644 --- a/src/renderer/src/AixPPTist/src/App.vue +++ b/src/renderer/src/AixPPTist/src/App.vue @@ -74,8 +74,6 @@ const initLoad: Function = () => { !!(opt.ratio??null) && slidesStore.setViewportRatio(opt.ratio)// 有比例配置项 } return PPTApi.getSlideList(resource.id) - // PPTApi.updateWorkList() - // return Promise.resolve() } return Promise.resolve() } diff --git a/src/renderer/src/AixPPTist/src/api/chat.ts b/src/renderer/src/AixPPTist/src/api/chat.ts index 6cf016e..70574e2 100644 --- a/src/renderer/src/AixPPTist/src/api/chat.ts +++ b/src/renderer/src/AixPPTist/src/api/chat.ts @@ -4,21 +4,51 @@ import ChatWs from '@/plugins/socket' // 聊天socket import { sessionStore } from '@/utils/store' // electron-store 状态管理 +import { useClasscourseStore } from '../store' import * as API_classcourse from '@/api/teaching/classcourse' // 后端api +import { MsgEnum } from './types' +// import msgUtils from '@/plugins/modal' // 消息工具 export default () => { const classcourse = sessionStore.get('curr.classcourse') // 课堂信息 + const courseId = classcourse?.id // 课堂id const timgroupid = classcourse?.timgroupid // 群组id + const classcourseStore = useClasscourseStore() // 课堂信息-状态管理 if (!ChatWs.ws) ChatWs.init() + // 开课消息 + const startCourse = async() => { + // await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'open' }) + ChatWs.sendMsg('open', {id: courseId}) + return Promise.resolve() + } // 下课消息 const exitCourse = async() => { if(!timgroupid) throw new Error('未获取到群组ID') - await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'closed' }) + await API_classcourse.updateClasscourse({ id: courseId, status: 'closed' }) return ChatWs.closedCourse(timgroupid) } + // 翻页消息 + const slideFlapping = (msg:object) => { + return new Promise(async (resolve, reject) => { + const isWs = !!ChatWs.ws && ChatWs.ws.readyState === 1 // 是否有socket连接 + if(!timgroupid) return reject('未获取到群组ID') + else if(!isWs) return reject('信异常,请重试!') + const {current: paging, animationSteps: cartoonTimes} = msg || {} + const head = MsgEnum.HEADS.MSG_slideFlapping + ChatWs.sendMsg(head, msg) // 发送消息 + API_classcourse.setPaging({ id: courseId, paging, cartoonTimes}) + // 更新本地缓存 + sessionStore.set('curr.classcourse.paging', paging) + sessionStore.set('curr.classcourse.cartoonTimes', cartoonTimes) + classcourseStore.classcourse.paging = paging + classcourseStore.classcourse.cartoonTimes = cartoonTimes + return resolve(true) + }) + } return { - exitCourse, - classcourse, groupid: timgroupid, + classcourse, + exitCourse, + slideFlapping, } } \ No newline at end of file diff --git a/src/renderer/src/AixPPTist/src/api/classcourse.ts b/src/renderer/src/AixPPTist/src/api/classcourse.ts index 21e7296..c92a6b6 100644 --- a/src/renderer/src/AixPPTist/src/api/classcourse.ts +++ b/src/renderer/src/AixPPTist/src/api/classcourse.ts @@ -14,6 +14,7 @@ const slidesStore = useStore.useSlidesStore() // 幻灯片-状态管理 const screenStore = useStore.useScreenStore() // 全屏-状态管理 const classcourseStore = useStore.useClasscourseStore() // 课堂信息-状态管理 const classcourse = sessionStore.get('curr.classcourse') // 课堂信息 +const isPublic = sessionStore.get('curr.isPublic') // 是否公屏开课 export class Classcourse { msgObj:ElMessageBox = null // 提示消息对象 @@ -36,8 +37,13 @@ export class Classcourse { // 如果课堂信息有值,则连接socket if (isCourse) { // 连接socket - if (!ChatWs.ws) ChatWs.init() ChatWs.id = classcourse.timgroupid // 群组id + if (!ChatWs.ws) { + ChatWs.init().then(_ => { + isPublic && ChatWs.sendMsg('open', {id: classcourse.id}) + // isPublic && console.log('socket-开课消息-已发送') + }) + } this.classcourse = classcourse // 课堂信息 this.id = classcourse.id // 课堂id // 如果课堂信息有paging,则更新当前页码 @@ -46,7 +52,7 @@ export class Classcourse { // 如果课堂信息有paging,则更新动画播放状态 const isAnim = !!cartoonTimes || cartoonTimes === 0 if (isPaging) slidesStore.updateSlideIndex(paging) - if (isAnim) slidesStore.updateAnimationIndex(cartoonTimes+1) + if (isAnim) slidesStore.updateAnimationIndex(cartoonTimes) // 课堂信息-状态管理 classcourseStore.setClasscourse(classcourse) // 待上课提示 diff --git a/src/renderer/src/AixPPTist/src/api/index.ts b/src/renderer/src/AixPPTist/src/api/index.ts index 203ba3e..5e5bcb6 100644 --- a/src/renderer/src/AixPPTist/src/api/index.ts +++ b/src/renderer/src/AixPPTist/src/api/index.ts @@ -50,6 +50,8 @@ export class Utils { }, delay) } } + // 延时 + static sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) } /** ppt相关后端接口处理 */ @@ -59,6 +61,7 @@ export class PPTApi { // 获取所有幻灯片列表 isUpdate为true不更新 static getSlideList(parentid: (Number | String),isUpdate?:Boolean): Promise { + const classcourse = sessionStore.get('curr.classcourse') // 课堂信息 return new Promise(async (resolve, reject) => { const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 } const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params) @@ -79,12 +82,16 @@ export class PPTApi { // 活动列表处理 // const workList = (res.rows || []).map(o => o.activityContent) const workItem = res.rows ? [...res.rows] : [] - // 写入作业列表数据 - // slidesStore.setWorkList(workList) // 获取所有的pptlist的数据 slidesStore.setWorkItem(workItem) - - this.updateWorkList() + // 没有上课时调用-作业列表 + if(!classcourse) this.updateWorkList() + // 没有上课时调用-批量更新缩略图 + if(!classcourse) { + Utils.sleep(1500).then(() => { + this.batchUpdateThumUrl() + }) + } resolve(true) } else msgUtils.msgError(res.msg || '获取数据失败');resolve(false) }) @@ -232,10 +239,29 @@ export class PPTApi { }) } + // 批量更新缩略图-异步 + static batchUpdateThumUrl() { + return nextTick().then(async () => { + const list = slidesStore.workItem || [] + if (!list.length) return + const upList = [] + for (const [ind,o] of list.entries()) { + const isCreate = !o.fileurl // 是否创建 + if (isCreate) { + const thumUrl = await this.getSlideThumUrl(ind) + upList.push({ id: o.id, fileurl: thumUrl }) + } + } + if (!upList.length) return + // 批量更新 + return await API_entpcoursefile.batchUpdateNew(upList) + }) + } + // thumbnail-slide thumbnail 缩略图 - static getSlideThumUrl(): Promise { + static getSlideThumUrl(index?:number): Promise { return nextTick().then(async() => { - const slideIndex = slidesStore.slideIndex + const slideIndex = index ?? slidesStore.slideIndex const elements = document.querySelectorAll('.thumbnail-slide') if (elements.length && slideIndex >= 0) { const element = elements[slideIndex] diff --git a/src/renderer/src/AixPPTist/src/api/types.ts b/src/renderer/src/AixPPTist/src/api/types.ts index e762748..0beb5ef 100644 --- a/src/renderer/src/AixPPTist/src/api/types.ts +++ b/src/renderer/src/AixPPTist/src/api/types.ts @@ -124,6 +124,10 @@ export class MsgEnum { MSG_classlecturePagesrc : 'classlecturePagesrc', /** @desc: 课堂作业|活动 */ MSG_homework : 'HOMEWORK', + /** @desc: 公屏 - 课堂作业|活动 */ + MSG_pushSreen_work : 'pushSreen_work', + /** @desc: 公屏 - 实验 */ + MSG_pushSreen_experiment : 'pushSreen_experiment', /** @desc: 点赞 */ MSG_dz : 'dz', /** @desc: 疑惑 */ diff --git a/src/renderer/src/AixPPTist/src/api/watcher.ts b/src/renderer/src/AixPPTist/src/api/watcher.ts index 220ed53..005aef2 100644 --- a/src/renderer/src/AixPPTist/src/api/watcher.ts +++ b/src/renderer/src/AixPPTist/src/api/watcher.ts @@ -10,6 +10,7 @@ import { MsgEnum } from './types' // 消息枚举 import ChatWs from '@/plugins/socket' // 聊天socket import Classcourse from './classcourse' // 课程相关 import msgUtils from '@/plugins/modal' // 消息工具 +import * as dialogUtils from '@/utils/dialog' // 弹窗-函数 import { Homework } from './index' // api-作业相关 // import emitter from '@/utils/mitt' //mitt 事件总线 import useExecPlay from '../views/Screen/hooks/useExecPlay' // 播放控制 @@ -23,7 +24,7 @@ export default () => { const classcourseStore = store.useClasscourseStore() // 课堂信息-状态管理 const resource = sessionStore.get('curr.resource') // apt 资源 const smarttalk = sessionStore.get('curr.smarttalk') // 备课资源 - const { execNext, turnPrevSlide } = useExecPlay() + const { execNext, turnPrevSlide } = useExecPlay(false) // 不加载钩子 // 监听幻灯片内容变化 watch(() => slidesStore.slides, (newVal, oldVal) => { PPTApi.updateSlides(newVal, oldVal) // 更新幻灯片内容 @@ -98,14 +99,25 @@ export default () => { break case MsgEnum.HEADS.MSG_slideFlapping: // 幻灯片翻页 const slideIndex = content?.current || 0 - const type = content?.animation + const type = content?.animation // 上下动作 + const steps = content?.animationSteps // 动画步骤 if (type === 'Nextsteps') execNext(true) // 下一步-异步动画 else if (type === 'Previoustep') turnPrevSlide() // 上一步清空-动画 else slidesStore.updateSlideIndex(slideIndex) // 更新幻灯片下标 + // 更新本地缓存 + sessionStore.set('curr.classcourse.paging', slideIndex) + sessionStore.set('curr.classcourse.cartoonTimes', steps) + classcourseStore.classcourse.paging = slideIndex + classcourseStore.classcourse.cartoonTimes = steps break - case MsgEnum.HEADS.MSG_homework: // 作业|活动-布置 - if (!content.classWorkId) return - Homework.showHomework(content.classWorkId) + // case MsgEnum.HEADS.MSG_homework: // 作业|活动-布置 不处理 + case MsgEnum.HEADS.MSG_pushSreen_work: // 打开-作业|活动 + if (!content.id) return + Homework.showHomework(content.id) + break + case MsgEnum.HEADS.MSG_pushSreen_experiment: // 打开实验: + if (!content.url) return + dialogUtils.openLink(content.url) break case MsgEnum.HEADS.MSG_closed: // 下课: close() @@ -130,4 +142,4 @@ export default () => { window.close() // 关闭窗口 }, 1000) } -} +} \ No newline at end of file diff --git a/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue b/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue index 0354fba..9f36ccc 100644 --- a/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue +++ b/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue @@ -25,6 +25,12 @@
常规作业
+ +
+ +
科学实验
+
+
@@ -157,6 +163,10 @@ const type = ref([ { label: '框架梳理', value: 'primary' + }, + { + label: '科学实验', + value: 'primary' } ]) diff --git a/src/renderer/src/AixPPTist/src/views/Screen/BaseView.vue b/src/renderer/src/AixPPTist/src/views/Screen/BaseView.vue index 8e7acf0..7d24e8c 100644 --- a/src/renderer/src/AixPPTist/src/views/Screen/BaseView.vue +++ b/src/renderer/src/AixPPTist/src/views/Screen/BaseView.vue @@ -36,8 +36,8 @@
幻灯片 {{slideIndex + 1}} / {{slides.length}}
@@ -50,6 +50,10 @@
+
+ + +
@@ -71,6 +75,7 @@ import WritingBoardTool from './WritingBoardTool.vue' import CountdownTimer from './CountdownTimer.vue' import emitter from '@/utils/mitt'; import Chat from '../../api/chat' // 聊天 +import { CircleDoubleDown, CircleDoubleUp } from '@icon-park/vue-next' // icon-park 图标库 const props = defineProps<{ changeViewMode: (mode: 'base' | 'presenter') => void @@ -103,12 +108,15 @@ const { exitScreening } = useScreening() const { fullscreenState, manualExitFullscreen } = useFullscreen() const chat:any = Chat() // 聊天室 +const screenStore =useScreenStore() const rightToolsVisible = ref(false) const writingBoardToolVisible = ref(false) const timerlVisible = ref(false) const slideThumbnailModelVisible = ref(false) const laserPen = ref(false) -const screenStore =useScreenStore() +const timer = ref(0) // 记录操作时间 +const iconHide = ref(false) // 工具栏图标是否显示 +const timerId = ref(null) // 定时器id const contextmenus = (): ContextmenuItem[] => { return [ { @@ -191,6 +199,30 @@ const contextmenus = (): ContextmenuItem[] => { ] } +const toolTrigger = (type:string) => { + const curT = Date.now() + if (curT - timer.value < 200) return + iconHide.value = false // 显示图标按钮 + if (timerId.value) clearTimeout(timerId.value) // 清除定时器 + switch (type) { + case 'icon': // 点击图标 + timer.value = curT + rightToolsVisible.value = !rightToolsVisible.value + break + case 'enter': // 移入 + timer.value = curT + rightToolsVisible.value = true + break + case 'leave': // 移出 + rightToolsVisible.value = false + break + default: + break + } + timerId.value = setTimeout(() => { // 定时器 + iconHide.value = true // 隐藏图标按钮 + }, 2000) +} // 下课 const exitCourse = async () => { // console.log('下课', chat) @@ -253,6 +285,18 @@ const exitCourse = async () => { top: -66px; } + .tools-icon{ + position: absolute; + right: 8px; + top: -35px; + z-index: 1; + cursor: pointer; + transition: opacity $transitionDelay; + &.opacity{ + opacity: .35; + } + } + .content { width: 100%; height: 100%; diff --git a/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts b/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts index 860e37a..cb59465 100644 --- a/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts +++ b/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts @@ -6,8 +6,13 @@ import { KEYS } from '../../../configs/hotkey' import { ANIMATION_CLASS_PREFIX } from '../../../configs/animation' import message from '../../../utils/message' import emitter from '@/utils/mitt'; +import Chat from '../../../api/chat' // 聊天封装 +// import ChatWs from '@/plugins/socket' // 聊天socket +// import { MsgEnum } from '../../../api/types' // 消息枚举 -export default () => { +export default (isLoader?: boolean = true) => { + // isLoader 是否执行 onMounted, onUnmounted + const chatApi = Chat() const slidesStore = useSlidesStore() const classcourseStore = useClasscourseStore() // 课堂信息-状态管理 const { slides, slideIndex, formatedAnimations, animationIndex } = storeToRefs(slidesStore) @@ -71,14 +76,15 @@ export default () => { elRef.addEventListener('animationend', handleAnimationEnd, { once: true }) } } - - onMounted(() => { - const firstAnimations = formatedAnimations.value[0] - if (firstAnimations && firstAnimations.animations.length) { - const autoExecFirstAnimations = firstAnimations.animations.every(item => item.trigger === 'auto' || item.trigger === 'meantime') - if (autoExecFirstAnimations) runAnimation() - } - }) + if (isLoader) { // 加载相关钩子 + onMounted(() => { + const firstAnimations = formatedAnimations.value[0] + if (firstAnimations && firstAnimations.animations.length) { + const autoExecFirstAnimations = firstAnimations.animations.every(item => item.trigger === 'auto' || item.trigger === 'meantime') + if (autoExecFirstAnimations) runAnimation() + } + }) + } // 撤销元素动画,除了将索引前移外,还需要清除动画状态 const revokeAnimation = () => { @@ -142,7 +148,6 @@ export default () => { inAnimation.value = false } const execNext = (isAsync: boolean) => { - console.log('execNext', isAsync) if (formatedAnimations.value.length && animationIndex.value < formatedAnimations.value.length) { runAnimation(isAsync) } @@ -178,8 +183,7 @@ export default () => { // 鼠标滚动翻页 const mousewheelListener = (e: WheelEvent) => { // console.log('mousewheel', e) - // 课堂信息存在时,不允许翻页 - if (!!classcourseStore.classcourse) e.preventDefault() + e.preventDefault() // 阻止默认事件 mousewheelListenerThrottle(e) } const mousewheelListenerThrottle = throttle(function(e: WheelEvent) { @@ -210,12 +214,17 @@ export default () => { } } // 向上翻页/向下翻页 - const turning = (e, type) => { + const turning = async (e, type) => { e.preventDefault() // 阻止默认事件 - // 课堂信息存在时,不允许翻页 - if (!!classcourseStore.classcourse) return if (type === 'prev') execPrev() else if (type === 'next') execNext() + if (classcourseStore.classcourse) { // 上课中 + const current = slideIndex.value + const animationSteps = animationIndex.value + const animation = type == 'next'?'Nextsteps':'Previoustep' + const msg = { current, animation, animationSteps} + chatApi.slideFlapping(msg) + } } // 快捷键翻页 const keydownListener = (e: KeyboardEvent) => { @@ -230,10 +239,11 @@ export default () => { key === KEYS.PAGEDOWN ) turning(e, 'next') } - - onMounted(() => {document.addEventListener('keydown', keydownListener)}) - onUnmounted(() => {document.removeEventListener('keydown', keydownListener)}) - + if (isLoader) { // 加载相关钩子 + onMounted(() => {document.addEventListener('keydown', keydownListener)}) + onUnmounted(() => {document.removeEventListener('keydown', keydownListener)}) + } + // 切换到上一张/上一张幻灯片(无视元素的入场动画) const turnPrevSlide = () => { slidesStore.updateSlideIndex(slideIndex.value - 1) diff --git a/src/renderer/src/api/aiGeneratedImage/index.js b/src/renderer/src/api/aiGeneratedImage/index.js index 9939c5d..eeb4211 100644 --- a/src/renderer/src/api/aiGeneratedImage/index.js +++ b/src/renderer/src/api/aiGeneratedImage/index.js @@ -2,6 +2,8 @@ import axios from 'axios' import request from '@/utils/request' import { getToken } from "@/utils/auth"; +let rootPath = import.meta.env.VITE_APP_ENV === 'production' ? 'https://ai.ysaix.com:7864' : '' + // 文生图片 export function convertTextToPicture(data) { return axios({ @@ -42,7 +44,7 @@ export function getPicture(data) { // 大模型对话生成prompt模板 export function chattoprompt(dataset_id,prompt) { return axios({ - url: '/api/v1/parse/docs', + url: rootPath + '/api/v1/parse/docs', method: 'post', headers: { 'Authorization': 'Bearer ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm', @@ -68,7 +70,7 @@ export function textSensitiveWord(data) { // 图片上传资源库 export function uploadPicture(data) { return axios({ - url: '/dev-api/smarttalk/file/upload', + url: import.meta.env.VITE_APP_BASE_API + '/smarttalk/file/upload', method: 'post', headers: { 'Accept': '*/*', diff --git a/src/renderer/src/api/teaching/classcourse.js b/src/renderer/src/api/teaching/classcourse.js index d312f85..d227b2d 100644 --- a/src/renderer/src/api/teaching/classcourse.js +++ b/src/renderer/src/api/teaching/classcourse.js @@ -95,3 +95,11 @@ export function getCourseTeachingMsg(id) { }) } +export function setPaging(data) { + return request({ + url: '/education/classcourse/record/paging', + method: 'post', + data + }) +} + diff --git a/src/renderer/src/assets/images/login/yc-logo.png b/src/renderer/src/assets/images/login/yc-logo.png new file mode 100644 index 0000000..59db44e Binary files /dev/null and b/src/renderer/src/assets/images/login/yc-logo.png differ diff --git a/src/renderer/src/assets/images/login/ycpeitu.png b/src/renderer/src/assets/images/login/ycpeitu.png new file mode 100644 index 0000000..433a296 Binary files /dev/null and b/src/renderer/src/assets/images/login/ycpeitu.png differ diff --git a/src/renderer/src/assets/images/login/ycpeitu2.jpg b/src/renderer/src/assets/images/login/ycpeitu2.jpg new file mode 100644 index 0000000..4cc2f71 Binary files /dev/null and b/src/renderer/src/assets/images/login/ycpeitu2.jpg differ diff --git a/src/renderer/src/components/choose-textbook/experimentBook.vue b/src/renderer/src/components/choose-textbook/experimentBook.vue new file mode 100644 index 0000000..cf14940 --- /dev/null +++ b/src/renderer/src/components/choose-textbook/experimentBook.vue @@ -0,0 +1,333 @@ + + + + + + \ No newline at end of file diff --git a/src/renderer/src/components/grid-pic/index.vue b/src/renderer/src/components/grid-pic/index.vue new file mode 100644 index 0000000..9725af6 --- /dev/null +++ b/src/renderer/src/components/grid-pic/index.vue @@ -0,0 +1,263 @@ + + + + + diff --git a/src/renderer/src/components/grid-pic/viewer-item.vue b/src/renderer/src/components/grid-pic/viewer-item.vue new file mode 100644 index 0000000..0a4d300 --- /dev/null +++ b/src/renderer/src/components/grid-pic/viewer-item.vue @@ -0,0 +1,144 @@ + + + diff --git a/src/renderer/src/components/template-study/container/adjust-dialog.vue b/src/renderer/src/components/template-study/container/adjust-dialog.vue index a5fa027..000f8e2 100644 --- a/src/renderer/src/components/template-study/container/adjust-dialog.vue +++ b/src/renderer/src/components/template-study/container/adjust-dialog.vue @@ -59,6 +59,7 @@ import { completion, docList } from '@/api/mode/index' import { sessionStore } from '@/utils/store' import { dataSetJson } from '@/utils/comm.js' import useUserStore from '@/store/modules/user' +import { sendChart } from '@/api/ai/index' import emitter from '@/utils/mitt'; const userInfo = useUserStore().user @@ -77,6 +78,14 @@ const props = defineProps({ type: { type: Number, default: 1 + }, + curMode:{ + type: Number, + default: 1 + }, + conversation_id: { + type: [Number, String], + default: '' } }) @@ -100,7 +109,8 @@ const curNode = reactive({}) const params = reactive( { prompt: '', - dataset_id: '' + dataset_id: '', + template: '' } ) @@ -108,7 +118,24 @@ const params = reactive( const getCompletion = async (val) => { try { params.prompt = `按照${val}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value} 对${curNode.itemtitle}进行教学分析` - const { data } = await completion(params) + params.template = props.item.prompt + + let data = null; + // 教学大模型 + if(props.curMode == 1){ + const res = await sendChart({ + content: params.prompt, + conversationId: props.conversation_id, + stream: false + }) + data = res.data + } + else{ + // 知识库模型 + const res = await completion(params) + data = res.data + } + let answer = data.answer msgList.value.push({ type: 'robot', diff --git a/src/renderer/src/components/template-study/container/right.vue b/src/renderer/src/components/template-study/container/right.vue index 74a87c3..9343418 100644 --- a/src/renderer/src/components/template-study/container/right.vue +++ b/src/renderer/src/components/template-study/container/right.vue @@ -81,7 +81,7 @@ - + @@ -104,7 +104,7 @@ import { cloneDeep } from 'lodash' const props = defineProps(['type']) const { user } = useUserStore() -const curMode = ref(1) +const curMode = ref(2) const modeOptions = ref([ { label: '教学大模型', @@ -205,7 +205,7 @@ const scrollToBottom = (height, index) => { let listDom = listRef.value.children if (index == 0) { - // 220 去掉头部 + // 220 去掉头部 let screenHeight = window.innerHeight - 220 if (height > screenHeight) { listRef.value.scrollTop = (height - screenHeight + 50) @@ -240,7 +240,7 @@ const changeTemplate = (val) => { const removeItem = async (item, isChild) => { /** * item: 当前操作的模板 - * isChild: 子模板中的移除为 true + * isChild: 子模板中的移除为 true */ if (item.ex3 != '1') { ElMessageBox.confirm( @@ -320,10 +320,10 @@ const againResult = async (index, item) => { str = str.replace('{模板内容}',item.prompt) params.prompt = str params.template = item.prompt - + let data = null; // 教学大模型 - if (mode.value == 1) { + if (curMode.value == 1) { const res = await sendChart({ content: params.prompt, conversationId: conversation_id.value, @@ -606,4 +606,4 @@ onUnmounted(() => { width: 110px !important; min-width: 110px !important; } - \ No newline at end of file + diff --git a/src/renderer/src/components/whiteboard/whiteboard.vue b/src/renderer/src/components/whiteboard/whiteboard.vue index 88c9860..c6eb9a2 100644 --- a/src/renderer/src/components/whiteboard/whiteboard.vue +++ b/src/renderer/src/components/whiteboard/whiteboard.vue @@ -15,7 +15,7 @@
-
@@ -146,7 +147,7 @@ const open = async (id, classObj) => { await getAptInfo(id) // 获取班级列表 getClassList() - console.log('classObj', classObj) + // console.log('classObj', classObj) // 继续上课 if (!!classObj) { dt.ctCourse = classObj @@ -245,8 +246,8 @@ const getClasscourseList = async type => { } } } -// 创建课程 -const createClasscourse = async () => { +// 创建课程 isPublic 公屏上课 +const createClasscourse = async (isPublic = false) => { const { classid } = classForm.form if (!classid) { ElMessage.warning('请选择班级') @@ -255,8 +256,8 @@ const createClasscourse = async () => { dt.loading = true const { entpcourseid, evalid, id, coursetitle } = myClassActive.value // 课件对象 const curDate = commUtil.getDateNow('yyyy-MM-dd') - const params = { - id: 0, coursetype: '', courseverid: 0, coursedesc: '', status: '', + const params = { // 公屏上课直接 status = open + id: 0, coursetype: '', courseverid: 0, coursedesc: '', status: isPublic?'open':'', teacherid: userStore.id, entpcoursefileid: id, classid, entpcourseid, evalid, coursetitle, plandate: curDate, opendate: curDate @@ -274,7 +275,7 @@ const createClasscourse = async () => { setTimeout(async() => { msgEl.close() const res = await Http_Classcourse.getClasscourse(teacherForm.form.classcourseid) - openPublicScreen(res.data) + openPublicScreen(res.data, isPublic) }, 2000); }, 1000); } @@ -355,7 +356,7 @@ const getQrUrl = async() => { } // 打开公屏 -const openPublicScreen = (classcourse) => { +const openPublicScreen = (classcourse, isPublic) => { console.log('打开公屏', classcourse) if (!dt.ctCourse) { // 新开课需要发送消息-继续上课不需要直接打开 // 发送app端待开课消息 @@ -366,11 +367,14 @@ const openPublicScreen = (classcourse) => { const resource = toRaw(myClassActive.value) sessionStore.set('curr.resource', resource) // 缓存当前资源信息 sessionStore.set('curr.classcourse', classcourse) // 缓存当前当前上课 + // 公屏开课 + sessionStore.set('curr.isPublic', isPublic) // 缓存是否公屏开课 createWindow('open-win', { url: '/pptist', // 窗口关闭时,清除缓存 close: () => { sessionStore.set('curr.resource', null) // 清除缓存 sessionStore.set('curr.classcourse', null) // 清除缓存 + sessionStore.set('curr.isPublic', null) // 清除缓存 } }) visible.value = false // 关闭弹窗 diff --git a/src/renderer/src/views/prepare/container/file-list-item.vue b/src/renderer/src/views/prepare/container/file-list-item.vue index c2e3e90..7f28cdb 100644 --- a/src/renderer/src/views/prepare/container/file-list-item.vue +++ b/src/renderer/src/views/prepare/container/file-list-item.vue @@ -126,11 +126,17 @@ 下载 -
+ +
+ + + 导入PPT +
@@ -181,7 +187,7 @@ export default { } } }, - emits: { 'on-move': null, 'on-delete': null, 'on-set': null, 'on-reSet': null, 'on-delhomework': null,'on-filearg': null }, + emits: { 'on-move': null, 'on-delete': null, 'on-set': null, 'on-reSet': null, 'on-delhomework': null,'on-filearg': null,'on-importPPT': null }, data() { return { listenList: [], @@ -217,6 +223,9 @@ export default { }) .catch(() => {}) }, + importPPT(item) { + this.$emit('on-importPPT', item) + }, downloadFile(item) { ipcRenderer.send('save-as', item.fileFullPath, item.fileShowName) }, diff --git a/src/renderer/src/views/prepare/index.vue b/src/renderer/src/views/prepare/index.vue index c593d06..bd0b57b 100644 --- a/src/renderer/src/views/prepare/index.vue +++ b/src/renderer/src/views/prepare/index.vue @@ -10,6 +10,7 @@ 新建文枢课件 AI一键生成 + 打开宫格 导入PPT @@ -98,6 +99,7 @@ @on-delete="deleteTalk" @on-set="openSet" @on-delhomework="delhomework" + @on-importPPT="importPPT" @on-filearg="isOpenHomework = true" > @@ -193,7 +195,7 @@ import outLink from '@/utils/linkConfig' import { createWindow, sessionStore, getAppInstallUrl, ipcMsgSend } from '@/utils/tool' import { cloneDeep } from 'lodash' import { delClasswork, listEntpcourse } from '@/api/teaching/classwork' -import { updateClasscourse } from '@/api/teaching/classcourse' +import { updateClasscourse, getClasscourse } from '@/api/teaching/classcourse' import { getClassInfo, getSelfReserv, endClass } from '@/api/classManage' import { useGetHomework } from '@/hooks/useGetHomework' import { editListItem } from '@/hooks/useClassTask' @@ -296,7 +298,7 @@ export default { }, currentKJFileList() { // return this.currentFileList.filter((item) => item.fileFlag === 'apt' || item.fileFlag === '课件') - return this.currentFileList.filter((item) => ['apt','aippt','课件'].includes(item.fileFlag)) + return this.currentFileList.filter((item) => ['aippt'].includes(item.fileFlag)) }, currentSCFileList() { // return this.currentFileList.filter((item) => item.fileFlag !== 'apt' && item.fileFlag !== '课件') @@ -341,6 +343,14 @@ export default { // } // }, methods: { + openGridPic() { + createWindow('open-win', { + url: '/gridPic', // 窗口关闭时,清除缓存 + option: { + maximizable: true + } + }) + }, // 延时 sleep(ms){return new Promise(resolve => setTimeout(resolve, ms))}, addAiPPT(item) { @@ -355,8 +365,8 @@ export default { // 开始上课 startClass(item, classObj) { // 关闭状态,打开上课相关功能(已打开,忽略) - // const id = sessionStore.has('activeClass.id') ? sessionStore.get('activeClass.id') : null - // if (id && id == item.id) return ElMessage.warning('当前正在上课,请勿重复操作') + const iscourse = !!sessionStore.get('curr.classcourse') + if (iscourse) return ElMessage.warning('公屏已打开,请勿重复操作') // 当前上课-store sessionStore.set('activeClass', item) this.activeClass = item @@ -367,7 +377,8 @@ export default { this.$refs.calssRef.open(item.fileId, classObj) } if(item.fileFlag === 'aippt') { - this.$refs.calssRef.open(item.fileId, classObj) + if (!!classObj) this.changeClass('continue', classObj) // 继续上课 + else this.$refs.calssRef.open(item.fileId, classObj) // 新开课 } }, // 继续上课-apt @@ -375,7 +386,20 @@ export default { switch(type) { case 'continue': { // 继续上课 const aptFileId = row.entpcoursefileid - this.$refs.calssRef.open(aptFileId, row) + const res = await getEntpcoursefile(aptFileId) + if (res.code == 200) { + const resource = res.data + if (resource.filetype != 'aippt') this.$refs.calssRef.open(aptFileId, row) + else { // aippt 继续上课 + if (!!sessionStore.get('curr.classcourse')) return ElMessage.warning('公屏已打开,请勿重复操作') + const { data:classcourse } = await getClasscourse(row.id) // 获取最新课堂信息 + const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0}) + setTimeout(()=>{ + msgEl.close() + this.openPublicScreen('class', resource, classcourse||row) // 打开公屏-窗口 + }, 2000) + } + } else ElMessage.error(res.msg||'获取课件信息失败') break } case 'close': { // 关闭上课 @@ -428,16 +452,7 @@ export default { if (row.fileFlag === 'aippt' && !!row.fileId) { const res = await getEntpcoursefile(row.fileId) if (res && res.code === 200) { - sessionStore.set('curr.resource', res.data) // 缓存当前资源信息 - sessionStore.set('curr.smarttalk', row) // 缓存当前文件smarttalk - createWindow('open-win', { - url: '/pptist', // 窗口关闭时,清除缓存 - close: () => { - sessionStore.set('curr.resource', null) // 清除缓存 - sessionStore.set('curr.smarttalk', null) // 清除缓存 - this.asyncAllFile() // 刷新资源列表 - } - }) + this.openPublicScreen('edit', res.data, row) // 打开公屏-窗口 } else { ElMessage.warning(res.msg||'文件获取异常!') } @@ -448,6 +463,7 @@ export default { } case 'wsApp': { // 发送app端待开课消息 // console.log('wsApp', row) + if (!!sessionStore.get('curr.classcourse')) return ElMessage.warning('公屏已打开,请勿重复操作') const head = MsgEnum.HEADS.MSG_0000 const data = { id: row.id } const type = ChatWs.TYPES.single @@ -462,24 +478,38 @@ export default { msgEl.close() // 关闭提示 const resource = res?.data||{} const classcourse = row - sessionStore.set('curr.resource', resource) // 缓存当前资源信息 - sessionStore.set('curr.classcourse', classcourse) // 缓存当前当前上课 - createWindow('open-win', { - url: '/pptist', // 窗口关闭时,清除缓存 - close: () => { - sessionStore.set('curr.resource', null) // 清除缓存 - sessionStore.set('curr.classcourse', null) // 清除缓存 - } - }) + this.openPublicScreen('class',resource, classcourse) // 打开公屏-窗口 break } default: break } }, + /** + * description 打开公屏 + * @param {string} type 类型 edit 打开 class 上课 + * @param {object} resource 资源信息 + * @param {object} currData 当前数据 type: edit/class 备课信息 | 课堂信息 + */ + openPublicScreen(type, resource, currData) { + sessionStore.set('curr.resource', resource) // 缓存当前资源信息 + if (type=='edit') sessionStore.set('curr.smarttalk', currData) // 缓存当前文件smarttalk + else sessionStore.set('curr.classcourse', currData) // 缓存当前当前上课 + createWindow('open-win', { + url: '/pptist', // 窗口关闭时,清除缓存 + close: () => { + sessionStore.set('curr.resource', null) // 清除缓存 + if (type=='edit') { + sessionStore.set('curr.smarttalk', null) // 清除缓存 + this.asyncAllFile() // 刷新资源列表 + } else sessionStore.set('curr.classcourse', null) // 清除缓存 + } + }) + }, + closeChange() { // 上课弹窗被关闭-触发 // console.log('关闭上课弹窗') - // this.activeClass = null + this.activeClass = null sessionStore.delete('activeClass') }, initReserv(id) { @@ -526,6 +556,15 @@ export default { progDownFile(e, num) { this.downloadNum = num }, + importPPT(item) { + let _this = this; + fetch(item.fileFullPath) + .then(res => res.arrayBuffer()) + .then(buffer => { + let name = item.fileShowName.substring(0, item.fileShowName.lastIndexOf('.')) + '.aippt' + _this.createAIPPTByFile(buffer, name) + }) + }, createFile() { creatPPT(this.currentNode.itemtitle + '.pptx', this.uploadData).then((res) => { this.currentFileList.unshift(res.resData) @@ -544,7 +583,7 @@ export default { console.log('文件名:', file.name); console.log('文件类型:', file.type); console.log('文件大小:', file.size); - this.createAIPPTByFile(file) + this.createAIPPTByFile(file, this.currentNode.itemtitle + '.aippt') } }, async toRousrceUrl(o) { @@ -590,7 +629,7 @@ export default { } } }, - async createAIPPTByFile(file) { + async createAIPPTByFile(file,fileShowName) { this.pgDialog.visible = true this.pgDialog.pg.percentage = 0 const resPptJson = await PPTXFileToJson(file) @@ -636,7 +675,7 @@ export default { ...this.uploadData, fileId: slideid, fileFlag: 'aippt', - fileShowName: this.currentNode.itemtitle + '.aippt' + fileShowName: fileShowName }).then(async (res) => { const resSlides = slides.map(({id, ...slide}) => JSON.stringify(slide)) diff --git a/src/renderer/src/views/resource/container/components/VideoLog.vue b/src/renderer/src/views/resource/container/components/VideoLog.vue new file mode 100644 index 0000000..3842f41 --- /dev/null +++ b/src/renderer/src/views/resource/container/components/VideoLog.vue @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/src/renderer/src/views/resource/container/exper-list.vue b/src/renderer/src/views/resource/container/exper-list.vue new file mode 100644 index 0000000..96ce1ca --- /dev/null +++ b/src/renderer/src/views/resource/container/exper-list.vue @@ -0,0 +1,214 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/src/views/resource/container/resoure-search.vue b/src/renderer/src/views/resource/container/resoure-search.vue index 97c3bf2..a74fbbb 100644 --- a/src/renderer/src/views/resource/container/resoure-search.vue +++ b/src/renderer/src/views/resource/container/resoure-search.vue @@ -3,19 +3,21 @@ {{ item.label + :type="sourceStore.activeIndex == item.id ? 'primary' : ''" + @click="sourceStore.changeType(item)" :disabled="item.disabled">{{ item.label }} - - - - - - + @@ -34,22 +36,24 @@
- - - +
@@ -63,7 +67,10 @@ import {watch,ref,onMounted} from 'vue' import useResoureStore from '../store' import {coursewareTypeList} from '@/utils/resourceDict' +// 是否是第三方资源 const isThird = ref(false) +//判断是不是进入实验室 +const isExper = ref(false) const sourceStore = useResoureStore() // 防抖函数 const debounce = (fn, t) => { @@ -85,6 +92,9 @@ onMounted(() => { watch(() => sourceStore.query.fileSource,() => { sourceStore.query.fileSource === '第三方'?isThird.value = true:isThird.value = false }) +watch(() => sourceStore.query.orderByColumn,() => { + sourceStore.query.orderByColumn === 'uploadTime'?isExper.value = true:isExper.value = false +}) \ No newline at end of file +