Compare commits

..

47 Commits

Author SHA1 Message Date
lyc 62a419356c Merge pull request 'edit' (#178) from lyc-dev into main 2024-12-24 17:32:25 +08:00
lyc 4b2d13db8e edit 2024-12-24 17:31:56 +08:00
zhengdegang f63bb69919 Merge pull request 'zdg_dev' (#177) from zdg_dev into main
Reviewed-on: #177
2024-12-24 16:08:38 +08:00
zdg b3ffa9bdaa Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zdg_dev 2024-12-24 16:07:00 +08:00
zdg 5510021566 上课允许不选班级开课 2024-12-24 16:06:53 +08:00
lyc 89e0b2f97c Merge pull request 'edit 框架设计' (#176) from lyc-dev into main 2024-12-24 14:48:25 +08:00
lyc 406e581182 edit 框架设计 2024-12-24 14:47:58 +08:00
lyc ea0e1c91cf Merge pull request 'lyc-dev' (#175) from lyc-dev into main 2024-12-24 14:38:20 +08:00
lyc d4dce61555 Merge branch 'main' into lyc-dev 2024-12-24 14:37:06 +08:00
lyc d6d5d13232 pptList 插入素材 增加loading 2024-12-24 14:32:59 +08:00
zhengdegang 33e38f8992 Merge pull request 'zdg_dev' (#174) from zdg_dev into main
Reviewed-on: #174
2024-12-24 12:05:02 +08:00
zdg 11968879d8 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zdg_dev
# Conflicts:
#	src/renderer/src/AixPPTist/src/views/Editor/EditorHeader/index.vue
2024-12-24 11:33:49 +08:00
zdg db86299523 ppt缩略图生成 2024-12-24 11:32:52 +08:00
zdg c152a3d3ee aippt生成-缩略图 2024-12-24 11:24:53 +08:00
lyc 9c8872579f Merge pull request 'edit 框架设计' (#173) from lyc-dev into main 2024-12-24 10:21:42 +08:00
lyc 4f32927b49 edit 框架设计 2024-12-24 10:21:13 +08:00
zhangxuelin e132f3927b Merge pull request 'zxl' (#172) from zxl into main
Reviewed-on: #172
2024-12-24 10:07:31 +08:00
朱浩 c290c170d3 报错后及时回转loading按钮 2024-12-24 10:01:34 +08:00
朱浩 3867a9603f 报错后及时回转loading按钮 2024-12-24 09:58:31 +08:00
zhangxuelin f732520d8d Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zxl 2024-12-24 09:51:31 +08:00
lyc 01fbb9d05c Merge pull request 'lyc-dev' (#171) from lyc-dev into main 2024-12-23 17:02:30 +08:00
lyc 1334becb54 Merge branch 'main' into lyc-dev 2024-12-23 17:01:53 +08:00
lyc c95508ed59 edit 模板 2024-12-23 17:01:40 +08:00
zhangxuelin cf540d73df Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zxl 2024-12-23 15:26:47 +08:00
baigl 35bf246975 Merge pull request 'baigl' (#170) from baigl into main
Reviewed-on: #170
2024-12-23 14:04:52 +08:00
白了个白 e3c4706880 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-12-23 14:02:24 +08:00
白了个白 c507331fb5 作业设计:作业类型 tag颜色样式修改 2024-12-23 14:01:04 +08:00
lyc abd7be0698 Merge pull request '模板edit' (#169) from lyc-dev into main 2024-12-23 10:45:55 +08:00
lyc 92e8aa64d7 模板edit 2024-12-23 10:45:27 +08:00
zhangxuelin 83d3cd5df8 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zxl 2024-12-23 09:41:19 +08:00
朱浩 b1407706a8 永川个性化打包 2024-12-22 12:15:01 +08:00
朱浩 996b4006a2 永川个性化打包 2024-12-22 11:46:10 +08:00
朱浩 4531533382 大模型顺序问题修改 2024-12-21 21:42:07 +08:00
朱浩 a601a9f8dd Merge remote-tracking branch 'origin/main' 2024-12-21 21:40:07 +08:00
zhengdegang 086836e016 Merge pull request '1.pptList 实验室功能' (#168) from zdg_dev into main
Reviewed-on: #168
2024-12-21 12:17:04 +08:00
zdg acb0231304 1.pptList 实验室功能
2.pplList 批量更新缩略图
2024-12-21 12:15:58 +08:00
baigl 94e23c3d69 Merge pull request 'baigl' (#167) from baigl into main
Reviewed-on: #167
2024-12-20 17:19:17 +08:00
白了个白 ea5ed4b6ac Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-12-20 17:18:32 +08:00
白了个白 6b45fa61dd 1 2024-12-20 17:17:35 +08:00
白了个白 7aa75794dc 科学实验:批改优化 2024-12-20 17:16:57 +08:00
朱浩 a2c2c4eec7 Merge remote-tracking branch 'origin/main' 2024-12-20 17:14:40 +08:00
CYS 4095390f45 Merge pull request '插入素材' (#166) from cys_dev into main
Reviewed-on: #166
2024-12-20 17:12:50 +08:00
cys d75af2636d 插入素材 2024-12-20 17:12:07 +08:00
朱浩 1983dafbba Merge remote-tracking branch 'origin/main' 2024-12-20 16:52:18 +08:00
朱浩 6c7284383a 宫格图片插件 2024-12-20 16:52:02 +08:00
baigl 28f79f7ab0 Merge pull request 'baigl' (#165) from baigl into main
Reviewed-on: #165
2024-12-20 16:07:29 +08:00
zhangxuelin 64272467a2 ppist里面不显示导入 2024-12-18 16:08:19 +08:00
47 changed files with 1478 additions and 623 deletions

View File

@ -17,6 +17,7 @@ VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktx
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/' VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'
# websocket 地址 # websocket 地址
# VITE_APP_WS_URL = 'wss://prev.ysaix.com:7868'
VITE_APP_WS_URL = 'wss://file.ysaix.com:7868' VITE_APP_WS_URL = 'wss://file.ysaix.com:7868'
# VITE_APP_WS_URL = 'ws://192.168.2.16:7865' # VITE_APP_WS_URL = 'ws://192.168.2.16:7865'

25
.env.yc Normal file
View File

@ -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'

25
.env.yc2 Normal file
View File

@ -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'

54
electron-builder-yc.yml Normal file
View File

@ -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:
- '**/*'

54
electron-builder-yc2.yml Normal file
View File

@ -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:
- '**/*'

View File

@ -1,6 +1,6 @@
{ {
"name": "aix-win-ws", "name": "aix-win-ws",
"version": "2.5.7", "version": "2.5.8",
"description": "", "description": "",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "上海交大重庆人工智能研究院", "author": "上海交大重庆人工智能研究院",
@ -16,6 +16,8 @@
"build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml", "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: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: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: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:mac": "electron-vite build --mode production && electron-builder --mac --config ./electron-builder-prod.yml",
"build:linux": "npm run build && electron-builder --linux" "build:linux": "npm run build && electron-builder --linux"

BIN
resources/yc-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -7,6 +7,7 @@ import Logger from './logger' // 日志封装
import chat from './chat' // chat封装 import chat from './chat' // chat封装
import Store from './store' // Store封装 import Store from './store' // Store封装
import updateInit from './update' import updateInit from './update'
// 代理 electron/remote // 代理 electron/remote
// 第一步引入remote // 第一步引入remote
import remote from '@electron/remote/main' 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() { function createLoginWindow() {
if (loginWindow) return if (loginWindow) return
loginWindow = new BrowserWindow({ loginWindow = new BrowserWindow({
width: 888, width: import.meta.env.MODE==='yc'||import.meta.env.MODE==='yc2'?1060:888,
height: 520, height: 520,
show: false, show: false,
frame: false, frame: false,
autoHideMenuBar: true, autoHideMenuBar: true,
maximizable: false, maximizable: false,
resizable: false, resizable: false,
icon: join(__dirname, '../../resources/logo2.ico'), icon: join(__dirname, logoIco),
...(process.platform === 'linux' ? { icon } : {}), ...(process.platform === 'linux' ? { icon } : {}),
webPreferences: { webPreferences: {
defaultEncoding: 'utf-8', defaultEncoding: 'utf-8',
@ -95,7 +96,7 @@ function createMainWindow() {
frame: false, // 无边框 frame: false, // 无边框
autoHideMenuBar: true, autoHideMenuBar: true,
maximizable: false, maximizable: false,
icon: join(__dirname, '../../resources/logo2.ico'), icon: join(__dirname, logoIco),
...(process.platform === 'linux' ? { icon } : {}), ...(process.platform === 'linux' ? { icon } : {}),
webPreferences: { webPreferences: {
defaultEncoding: 'utf-8', defaultEncoding: 'utf-8',

View File

@ -74,8 +74,6 @@ const initLoad: Function = () => {
!!(opt.ratio??null) && slidesStore.setViewportRatio(opt.ratio)// !!(opt.ratio??null) && slidesStore.setViewportRatio(opt.ratio)//
} }
return PPTApi.getSlideList(resource.id) return PPTApi.getSlideList(resource.id)
// PPTApi.updateWorkList()
// return Promise.resolve()
} }
return Promise.resolve() return Promise.resolve()
} }

View File

@ -14,7 +14,8 @@ export default () => {
const courseId = classcourse?.id // 课堂id const courseId = classcourse?.id // 课堂id
const timgroupid = classcourse?.timgroupid // 群组id const timgroupid = classcourse?.timgroupid // 群组id
const classcourseStore = useClasscourseStore() // 课堂信息-状态管理 const classcourseStore = useClasscourseStore() // 课堂信息-状态管理
if (!ChatWs.ws) ChatWs.init() // 上课状态才-初始化socket
if (!ChatWs.ws && !!courseId) ChatWs.init()
// 开课消息 // 开课消息
const startCourse = async() => { const startCourse = async() => {
// await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'open' }) // await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'open' })

View File

@ -50,6 +50,8 @@ export class Utils {
}, delay) }, delay)
} }
} }
// 延时
static sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
} }
/** ppt相关后端接口处理 */ /** ppt相关后端接口处理 */
@ -84,6 +86,12 @@ export class PPTApi {
slidesStore.setWorkItem(workItem) slidesStore.setWorkItem(workItem)
// 没有上课时调用-作业列表 // 没有上课时调用-作业列表
if(!classcourse) this.updateWorkList() if(!classcourse) this.updateWorkList()
// 没有上课时调用-批量更新缩略图
if(!classcourse) {
Utils.sleep(1500).then(() => {
this.batchUpdateThumUrl()
})
}
resolve(true) resolve(true)
} else msgUtils.msgError(res.msg || '获取数据失败');resolve(false) } else msgUtils.msgError(res.msg || '获取数据失败');resolve(false)
}) })
@ -233,13 +241,20 @@ export class PPTApi {
// 批量更新缩略图-异步 // 批量更新缩略图-异步
static batchUpdateThumUrl() { static batchUpdateThumUrl() {
return new Promise(async resolve => { return nextTick().then(async () => {
const list = slidesStore.workItem || [] const list = slidesStore.workItem || []
if (!list.length) return resolve() if (!list.length) return
const upList = [] const upList = []
for (const [ind,o] of list.entries()) { for (const [ind,o] of list.entries()) {
const isCreate = !o.fileurl // 是否创建
if (isCreate) {
const thumUrl = await this.getSlideThumUrl(ind) const thumUrl = await this.getSlideThumUrl(ind)
upList.push({ id: o.id, fileurl: thumUrl })
} }
}
if (!upList.length) return
// 批量更新
return await API_entpcoursefile.batchUpdateNew(upList)
}) })
} }

View File

@ -126,6 +126,8 @@ export class MsgEnum {
MSG_homework : 'HOMEWORK', MSG_homework : 'HOMEWORK',
/** @desc: 公屏 - 课堂作业|活动 */ /** @desc: 公屏 - 课堂作业|活动 */
MSG_pushSreen_work : 'pushSreen_work', MSG_pushSreen_work : 'pushSreen_work',
/** @desc: 公屏 - 实验 */
MSG_pushSreen_experiment : 'pushSreen_experiment',
/** @desc: 点赞 */ /** @desc: 点赞 */
MSG_dz : 'dz', MSG_dz : 'dz',
/** @desc: 疑惑 */ /** @desc: 疑惑 */

View File

@ -10,6 +10,7 @@ import { MsgEnum } from './types' // 消息枚举
import ChatWs from '@/plugins/socket' // 聊天socket import ChatWs from '@/plugins/socket' // 聊天socket
import Classcourse from './classcourse' // 课程相关 import Classcourse from './classcourse' // 课程相关
import msgUtils from '@/plugins/modal' // 消息工具 import msgUtils from '@/plugins/modal' // 消息工具
import * as dialogUtils from '@/utils/dialog' // 弹窗-函数
import { Homework } from './index' // api-作业相关 import { Homework } from './index' // api-作业相关
// import emitter from '@/utils/mitt' //mitt 事件总线 // import emitter from '@/utils/mitt' //mitt 事件总线
import useExecPlay from '../views/Screen/hooks/useExecPlay' // 播放控制 import useExecPlay from '../views/Screen/hooks/useExecPlay' // 播放控制
@ -114,6 +115,10 @@ export default () => {
if (!content.id) return if (!content.id) return
Homework.showHomework(content.id) Homework.showHomework(content.id)
break break
case MsgEnum.HEADS.MSG_pushSreen_experiment: // 打开实验:
if (!content.url) return
dialogUtils.openLink(content.url)
break
case MsgEnum.HEADS.MSG_closed: // 下课: case MsgEnum.HEADS.MSG_closed: // 下课:
close() close()
break break

View File

@ -1,5 +1,6 @@
import { useScreenStore, useSlidesStore, useClasscourseStore } from '../store' import { useScreenStore, useSlidesStore, useClasscourseStore } from '../store'
import { enterFullscreen, exitFullscreen, isFullscreen } from '../utils/fullscreen' import { enterFullscreen, exitFullscreen, isFullscreen } from '../utils/fullscreen'
import { sessionStore } from '@/utils/store' // electron-store 状态管理
import ChatWs from '@/plugins/socket' // 聊天socket import ChatWs from '@/plugins/socket' // 聊天socket
export default () => { export default () => {
@ -25,6 +26,9 @@ export default () => {
if (!!classcourse) { //DOTO 有课堂,执行退相关操作 if (!!classcourse) { //DOTO 有课堂,执行退相关操作
console.log('退出放映状态') console.log('退出放映状态')
ChatWs?.close() // 关闭ws ChatWs?.close() // 关闭ws
sessionStore.delete('curr.classcourse') // 清除课堂信息
sessionStore.delete('curr.resource') // 清除课件信息
sessionStore.delete('curr.isPublic') // 清除公屏状态
setTimeout(() => { setTimeout(() => {
window.close() // 关闭窗口 window.close() // 关闭窗口
}, 1000) }, 1000)

View File

@ -21,6 +21,7 @@ import { sessionStore } from '@/utils/store'
import { getSmarttalkPage } from '@/api/file' import { getSmarttalkPage } from '@/api/file'
import * as commUtils from '@/utils/comm.js' import * as commUtils from '@/utils/comm.js'
import { getFileSuffix } from '@/utils/ruoyi.js' import { getFileSuffix } from '@/utils/ruoyi.js'
import { PPTApi } from '../../../api'
const emit = defineEmits(['insertMaterial', 'close']) const emit = defineEmits(['insertMaterial', 'close'])
@ -60,73 +61,31 @@ const fileUrl = computed(() => (item) =>{
} }
}) })
// //
const onInsert = async (item) =>{ const onInsert = async (item) =>{
loading.value = true
const res = await fetch(item.fileFullPath) const res = await fetch(item.fileFullPath)
const bolb = await res.blob() const bolb = await res.blob()
const file = commUtils.blobToFile(bolb, item.fileShowName) const file = commUtils.blobToFile(bolb, item.fileShowName)
try {
const data = await PPTApi.toRousrceUrl(file)
if(videoSuffix.indexOf(getFileSuffix(item.fileShowName)) != -1){ if(videoSuffix.indexOf(getFileSuffix(item.fileShowName)) != -1){
emit('insertMaterial',{ type: 'video', file }) emit('insertMaterial',{ type: 'video', data })
} }
else{ else{
emit('insertMaterial',{ type: 'img', file }) emit('insertMaterial',{ type: 'img', data })
}
} finally {
loading.value = false
} }
} }
const GetUrlParameters = (parameters) => {
let resData = "";
let url = document.location.toString();
let arrUrl = url.split("?");
//
if (arrUrl.length > 1) {
//
let parametersArr = arrUrl[1].split("&");
//
for (let i = 0; i <= parametersArr.length; i++) {
if (parametersArr[i]) {
//
let parameterStr = parametersArr[i].split("=");
if (parameters == parameterStr[0]) {
resData = parameterStr[1];
break;
}
}
}
}
return resData;
}
const proxyToBase64 = (url)=> {
const dourl = GetUrlParameters(url)
console.log(dourl,'dourl')
return
axios({
url: "/api/logo.png",
method: "get",
responseType: "blob",
}).then((res) => {
const reader = new FileReader();
reader.readAsDataURL(res.data);
reader.onload = () => {
console.log(reader.result);
};
});
}
// //
const onClose = () =>{ const onClose = () =>{
emit('close') emit('close')
} }
onMounted(() => { onMounted(() => {
let data = sessionStore.get('subject.curNode') let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data); Object.assign(curNode, data);

View File

@ -281,11 +281,10 @@ const toggleNotesPanel = () => {
// //
interface MaterialParams { interface MaterialParams {
type: string, type: string,
file: any data: string
} }
const insertMaterial = (item: MaterialParams) =>{ const insertMaterial = async (item: MaterialParams) =>{
const { type, file } = item const { type, data } = item
PPTApi.toRousrceUrl(file).then(data=>{
if(type == 'video'){ if(type == 'video'){
createVideoElement(data) createVideoElement(data)
} }
@ -293,8 +292,6 @@ const insertMaterial = (item: MaterialParams) =>{
createImageElement(data) createImageElement(data)
} }
materiaVisible.value = false materiaVisible.value = false
})
} }
// //

View File

@ -3,7 +3,7 @@
<div class="left"> <div class="left">
<Popover trigger="click" placement="bottom-start" v-model:value="mainMenuVisible"> <Popover trigger="click" placement="bottom-start" v-model:value="mainMenuVisible">
<template #content> <template #content>
<FileInput accept=".pptist" @change="files => { <!-- <FileInput accept=".pptist" @change="files => {
importSpecificFile(files) importSpecificFile(files)
mainMenuVisible = false mainMenuVisible = false
}"> }">
@ -15,7 +15,7 @@
}"> }">
<PopoverMenuItem>导入 pptx 文件</PopoverMenuItem> <PopoverMenuItem>导入 pptx 文件</PopoverMenuItem>
</FileInput> </FileInput>
<PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem> <PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem> -->
<PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem> <PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem>
<!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem> --> <!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem> -->
<!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem> --> <!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem> -->
@ -54,9 +54,9 @@
<div class="arrow-btn"><IconDown class="arrow" /></div> <div class="arrow-btn"><IconDown class="arrow" /></div>
</Popover> </Popover>
</div> </div>
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')"> <!-- <div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
<IconDownload class="icon" /> <IconDownload class="icon" />
</div> </div> -->
<div class="menu-item" v-tooltip="`${userStore.user.parentDeptName}-${userStore.user.nickName}`"> <div class="menu-item" v-tooltip="`${userStore.user.parentDeptName}-${userStore.user.nickName}`">
<el-avatar size="small" :src="avatar" /> <el-avatar size="small" :src="avatar" />
</div> </div>

View File

@ -2,6 +2,8 @@ import axios from 'axios'
import request from '@/utils/request' import request from '@/utils/request'
import { getToken } from "@/utils/auth"; import { getToken } from "@/utils/auth";
let rootPath = import.meta.env.VITE_APP_ENV === 'production' ? 'https://ai.ysaix.com:7864' : ''
// 文生图片 // 文生图片
export function convertTextToPicture(data) { export function convertTextToPicture(data) {
return axios({ return axios({
@ -42,7 +44,7 @@ export function getPicture(data) {
// 大模型对话生成prompt模板 // 大模型对话生成prompt模板
export function chattoprompt(dataset_id,prompt) { export function chattoprompt(dataset_id,prompt) {
return axios({ return axios({
url: '/api/v1/parse/docs', url: rootPath + '/api/v1/parse/docs',
method: 'post', method: 'post',
headers: { headers: {
'Authorization': 'Bearer ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm', 'Authorization': 'Bearer ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm',

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

View File

@ -83,7 +83,7 @@
<!--AI 对话调整--> <!--AI 对话调整-->
<AdjustDialog v-model="isAdjust" :type="type" :item="editItem" :curMode="curMode" :conversation_id="conversation_id"/> <AdjustDialog v-model="isAdjust" :type="type" :item="editItem" :curMode="curMode" :conversation_id="conversation_id"/>
<!--添加编辑提示词--> <!--添加编辑提示词-->
<keywordDialog v-model="isWordDialog" :item="editItem" /> <keywordDialog v-model="isWordDialog" :item="editItem" :modeType="type" />
</template> </template>
<script setup> <script setup>
@ -104,7 +104,7 @@ import { cloneDeep } from 'lodash'
const props = defineProps(['type']) const props = defineProps(['type'])
const { user } = useUserStore() const { user } = useUserStore()
const curMode = ref(1) const curMode = ref(2)
const modeOptions = ref([ const modeOptions = ref([
{ {
label: '教学大模型', label: '教学大模型',
@ -415,7 +415,7 @@ const onEditSave = async (item) => {
} }
// //
const onSaveTemp = (item) => { const onSaveTemp = async (item) => {
if (item.answer == '') return if (item.answer == '') return
const data = { const data = {
@ -425,7 +425,10 @@ const onSaveTemp = (item) => {
content: item.answer, content: item.answer,
ex1: curNode.id ex1: curNode.id
} }
tempSave(data).then(res => { }) const res = await tempSave(data)
if(!item.resultId){
item.resultId = res.data
}
} }
// ### ** // ### **

View File

@ -38,6 +38,7 @@ const closeWindow = () => {
ElMessageBox.confirm('确认退出系统吗?', '提示', { ElMessageBox.confirm('确认退出系统吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
customClass: 'login-close-tool',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
userStore.logOut().then(() => { userStore.logOut().then(() => {
@ -54,7 +55,11 @@ onMounted(() =>{
}) })
</script> </script>
<style>
.login-close-tool {
-webkit-app-region: no-drag;
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
.header-tool { .header-tool {
width: 100%; width: 100%;

View File

@ -87,7 +87,7 @@ const getHomeWorkList = async () => {
// } else // } else
// 课标研读 目标设定 教材研读 框架梳理 学科定位 TODO 后续接入在添加 // 课标研读 目标设定 教材研读 框架梳理 学科定位 TODO 后续接入在添加
if (res.rows[i].worktype == '课堂展示') { if (res.rows[i].worktype == '课堂展示') {
res.rows[i].workclass = 'primary'; res.rows[i].workclass = 'success';
} else if (res.rows[i].worktype == '框架梳理') { } else if (res.rows[i].worktype == '框架梳理') {
res.rows[i].workclass = 'warning'; res.rows[i].workclass = 'warning';
} else if (res.rows[i].worktype == '常规作业') { } else if (res.rows[i].worktype == '常规作业') {
@ -95,7 +95,7 @@ const getHomeWorkList = async () => {
} else if (res.rows[i].worktype == '习题训练') { } else if (res.rows[i].worktype == '习题训练') {
res.rows[i].workclass = 'danger'; res.rows[i].workclass = 'danger';
} else if (res.rows[i].worktype == '科学实验') { } else if (res.rows[i].worktype == '科学实验') {
res.rows[i].workclass = 'danger'; res.rows[i].workclass = 'primary';
} else { } else {
res.rows[i].workclass = 'primary'; res.rows[i].workclass = 'primary';
} }

View File

@ -100,6 +100,8 @@ export class MsgEnum {
MSG_homework : 'HOMEWORK', MSG_homework : 'HOMEWORK',
/** @desc: 公屏 - 课堂作业|活动 */ /** @desc: 公屏 - 课堂作业|活动 */
MSG_pushSreen_work : 'pushSreen_work', MSG_pushSreen_work : 'pushSreen_work',
/** @desc: 公屏 - 实验 */
MSG_pushSreen_experiment : 'pushSreen_experiment',
/** @desc: 点赞 */ /** @desc: 点赞 */
MSG_dz : 'dz', MSG_dz : 'dz',
/** @desc: 疑惑 */ /** @desc: 疑惑 */

View File

@ -403,5 +403,11 @@ export const dataSetJson = {
"教材-高中-数学": "e03aa4fe9fd011ef91270242ac140006", "教材-高中-数学": "e03aa4fe9fd011ef91270242ac140006",
"教材-高中-地理": "270516829fd111efb13c0242ac140006", "教材-高中-地理": "270516829fd111efb13c0242ac140006",
"教材-高中-政治": "a2f0b247b85d11ef84290242ac140005", "教材-高中-政治": "a2f0b247b85d11ef84290242ac140005",
"课标-小学-科学": "935cfec8bf6a11ef98950242ac140006",
"课标-小学-数学": "3c4e298fbf7911ef8e8b0242ac140002",
"课标-小学-语文": "f76f1aa5bf7111ef90c80242ac140002",
"教材-小学-科学": "935cfec8bf6a11ef98950242ac140006",
"教材-小学-数学": "3c4e298fbf7911ef8e8b0242ac140002",
"教材-小学-语文": "f76f1aa5bf7111ef90c80242ac140002",
"鉴权": "ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm" "鉴权": "ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm"
} }

View File

@ -0,0 +1,49 @@
/**
* 弹窗-函数
*/
import { h, render } from 'vue'
import { ElDialog } from 'element-plus'
// 打开弹窗-函数
export const openDialog = (option, content) => {
let vNode
const body = document.body
const dOpts = {
modelValue: true,
width: 800,
height: 600,
title: '添加-超连接',
draggable: true,
'onUpdate:modelValue': val => {
if (vNode && !val) render(null, body)
},
...option
}
vNode = h(ElDialog, dOpts, {
default: typeof content == 'function' ? content(h) : content
})
render(vNode, body)
}
// 打开链接
export const openLink = (option, title) => {
// https://phet.colorado.edu/sims/html/number-play/latest/number-play_zh_CN.html
const isStr = typeof option == 'string'
const opt = isStr ? {} : option
const url = isStr ? option : option?.url || option?.src || option?.href
const titleNew = isStr? title||'实验室' : option?.title || '添加-超连接'
openDialog({
title: titleNew,
...opt
}, (h) => {
return h('iframe', {
src: url,
width: '100%',
style: {
height: 'calc(80vh - 75px)',
},
scrolling: 'no',
frameborder: '0',
})
})
}

View File

@ -0,0 +1,87 @@
/**
* ppt 转换为图片
*/
import { h, render, getCurrentInstance } from 'vue'
import { toPng, toJpeg } from 'html-to-image' // 引入html-to-image库
import { PPTXFileToJson } from "@/AixPPTist/src/hooks/useImport"
import ThumbnailSlide from '@/AixPPTist/src/views/components/ThumbnailSlide/index.vue'
import { useSlidesStore } from '@/AixPPTist/src/store'
// 延时
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
/**
* @description: 渲染组件
* @param {*} node 节点或属性
* @param {*} props 属性
* @param {*} children 子元素
* @param {*} container 容器
*/
const renderComponent = (node, props = {}, children, container) => {
let vNode, body
if (!node) throw new Error('vNode is required')
if (typeof container == 'string') {
if (node == 'slide') {
vNode = h(ThumbnailSlide, props, children)
} else throw new Error('vNode has no corresponding component')
} else {
vNode = h(node, props, children)
}
if (!container) body = document.body // 默认为body
else { // 判断是否为字符串
if (typeof container == 'string') {
body = document.querySelector(container)
} else {
body = container
}
}
return render(vNode, body)
}
/**
* @description: 幻灯片转换为图片
* 提示icon组件找不到是应为 h() 渲染是底层创建的虚拟节点找不到全局组件
* @param {*} slides 幻灯片数据
* @param {*} options 配置 number 为幻灯片宽度 | object 为配置项
* @returns
*/
export const slidesToImg = (slides = [], options) => {
let width, option, ispng = true
if (typeof options =='number'){width = options; option = {}}
else {const { width: w, isPng, ...opt } = options;width = w; ispng=isPng; option = opt}
const slidesStore = useSlidesStore()
!!width && slidesStore.setViewportSize(width) // 设置幻灯片宽度
return new Promise(async(resolve) => {
const instance = getCurrentInstance()
console.log('instance', instance)
const slidesDom = []
for(const slide of slides) {
const props = { class: 'c-thumbnail', slide, size: 120, ...option }
const node = h(ThumbnailSlide, props)
slidesDom.push(node)
}
// 渲染组件到body
const props = { class: 'c-thumbnails', style:{position:'absolute',top:0,left:'-200vw'}}
renderComponent('div', props, slidesDom)
let imgs = []
const toImag = ispng? toPng : toJpeg
for(const slide of slidesDom) {
const img = await toImag(slide.el)
imgs.push(img)
}
// console.log('ppt生成图片: ', imgs)
// console.log('图片已生成,正在卸载组件')
!!width && slidesStore.setViewportSize(1000) // 设置幻灯片宽度-恢复
render(null, document.body) // 卸载组件
resolve(imgs)
})
}
/**
* description: ppt 文件转换为图片
* @param {*} file file 为文件对象| arrayBuffer 为数组
* @param {*} options 配置 number 为幻灯片宽度 | object 为配置项
* @returns
*/
export const pptToImg = async(file, options) => {
const { slides } = await PPTXFileToJson(file)
return slidesToImg(slides, options)
}

View File

@ -9,7 +9,7 @@ export const asyncLocalFile = (item) => {
if (isAsync === true) { if (isAsync === true) {
item.async = 'on' item.async = 'on'
if (type === 'down') { if (type === 'down') {
console.log(item) // console.log(item)
ipcRenderer.send('download-file-default', { ipcRenderer.send('download-file-default', {
url: item.fileFullPath, url: item.fileFullPath,
fileName: item.fileNewName fileName: item.fileNewName

View File

@ -248,10 +248,11 @@ export function toolWindow(type, {url, isConsole, isWeb=true, option={}}) {
const devUrl = `${BaseUrl}${url}` const devUrl = `${BaseUrl}${url}`
const buildUrl = path.join(__dirname, 'index.html') const buildUrl = path.join(__dirname, 'index.html')
const urlAll = isDev ? devUrl : buildUrl const urlAll = isDev ? devUrl : buildUrl
let logoIco = import.meta.env.MODE==='yc'||import.meta.env.MODE==='yc2'?'/resources/yc-logo.png':'/resources/logo2.ico'
return new Promise(async(resolve) => { return new Promise(async(resolve) => {
const config = { const config = {
width, height, width, height,
icon: path.join(appPath, '/resources/logo2.ico'), icon: path.join(appPath, logoIco),
webPreferences: { webPreferences: {
preload: path.join(API.preloadPath, '/index.js'), preload: path.join(API.preloadPath, '/index.js'),
sandbox: false, sandbox: false,

View File

@ -154,14 +154,14 @@ const getClassWorkList = async () => {
// ---------------------------------------------- // ----------------------------------------------
// UI // UI
if (list[i].worktype == '学习目标定位') { if (list[i].worktype == '课堂展示') {
list[i].workclass = 'success' list[i].workclass = 'success'
list[i].workcodesList = JSON.parse(list[i].workcodes) list[i].workcodesList = JSON.parse(list[i].workcodes)
} else if (list[i].worktype == '教材研读') { } else if (list[i].worktype == '科学实验') {
list[i].workclass = 'primary' list[i].workclass = 'primary'
} else if (list[i].worktype == '框架梳理') { } else if (list[i].worktype == '框架梳理') {
list[i].workclass = 'warning' list[i].workclass = 'warning'
} else if (list[i].worktype == '学科定位') { } else if (list[i].worktype == '常规作业') {
list[i].workclass = 'info' list[i].workclass = 'info'
} else if (list[i].worktype == '习题训练') { } else if (list[i].worktype == '习题训练') {
list[i].workclass = 'danger' list[i].workclass = 'danger'

View File

@ -437,13 +437,15 @@ const queryPushRecords = (row) => {
// } else // } else
// TODO // TODO
if (res.rows[i].worktype == '课堂展示') { if (res.rows[i].worktype == '课堂展示') {
res.rows[i].workclass = 'primary'; res.rows[i].workclass = 'success';
} else if (res.rows[i].worktype == '框架梳理') { } else if (res.rows[i].worktype == '框架梳理') {
res.rows[i].workclass = 'warning'; res.rows[i].workclass = 'warning';
} else if (res.rows[i].worktype == '常规作业') { } else if (res.rows[i].worktype == '常规作业') {
res.rows[i].workclass = 'info'; res.rows[i].workclass = 'info';
} else if (res.rows[i].worktype == '习题训练') { } else if (res.rows[i].worktype == '习题训练') {
res.rows[i].workclass = 'danger'; res.rows[i].workclass = 'danger';
} else if (res.rows[i].worktype == '科学实验') {
res.rows[i].workclass = 'primary';
} else { } else {
res.rows[i].workclass = 'primary'; res.rows[i].workclass = 'primary';
} }

View File

@ -709,6 +709,7 @@ const acceptParams = async (params) => {
} else { } else {
// //
if (params.studentObj.worktype == '常规作业' || params.studentObj.worktype == '科学实验') { if (params.studentObj.worktype == '常规作业' || params.studentObj.worktype == '科学实验') {
if(params.studentObj.worktype == '常规作业'){
try { try {
// datacontent TODO // datacontent TODO
const res = await getClassworkdata(params.studentObj.id); const res = await getClassworkdata(params.studentObj.id);
@ -733,6 +734,9 @@ const acceptParams = async (params) => {
} catch (error) { } catch (error) {
console.error('Invalid JSON:', error) console.error('Invalid JSON:', error)
} }
}else{
// TODO 2024-12-20
}
params.studentQuizAllList.forEach((item) => { params.studentQuizAllList.forEach((item) => {
classWorkFormScore.teacherRating.push({ classWorkFormScore.teacherRating.push({

View File

@ -29,15 +29,15 @@ import { ElMessage } from 'element-plus'
const emit = defineEmits(['itemClick']) const emit = defineEmits(['itemClick'])
const items = shallowRef([ const items = shallowRef([
{ title: '自主搜题', description: '上千万高质量习题资源,历届考试真题,每道题均有习题解析', icon: '#icon-soutibao-',type:'primary' }, { title: '自主搜题', description: '上千万高质量习题资源,历届考试真题,每道题均有习题解析', icon: '#icon-soutibao-',type:'danger' },
// { title: '', description: '', icon: '#icon-soutibao-',type:'primary' }, // { title: '', description: '', icon: '#icon-soutibao-',type:'danger' },
{ title: '个人题库', description: '老师上传维护自己的个人题库。', icon: '#icon-soutibao-',type:'primary' }, { title: '个人题库', description: '老师上传维护自己的个人题库。', icon: '#icon-soutibao-',type:'danger' },
// { title: '', description: '', icon: '#icon-tubiao_wuxing-',type:'primary' }, // { title: '', description: '', icon: '#icon-tubiao_wuxing-',type:'primary' },
{ title: '课堂展示', description: '通过课堂白板绘制作业,提升学生的创作思维能力。', icon: '#icon-huaban',type:'danger' }, { title: '课堂展示', description: '通过课堂白板绘制作业,提升学生的创作思维能力。', icon: '#icon-huaban',type:'success' },
{ title: '常规作业', description: '推送pdf、视频、音频、图片学生可以拍照上传。', icon: '#icon-zhaoxiangji',type:'danger' }, { title: '常规作业', description: '推送pdf、视频、音频、图片学生可以拍照上传。', icon: '#icon-zhaoxiangji',type:'info' },
// { title: 'AI', description: 'AI', icon: '#icon-jiqiren_o',type:'danger' }, // { title: 'AI', description: 'AI', icon: '#icon-jiqiren_o',type:'danger' },
{ title: '习题上传', description: '自己上传个人题库。', icon: '#icon-shangchuan',type:'danger' }, { title: '习题上传', description: '自己上传个人题库。', icon: '#icon-shangchuan',type:'danger' },
{ title: '科学实验', description: '学生完成虚拟仿真实验,并提交实验结果。', icon: '#icon-shangchuan',type:'danger' }, { title: '科学实验', description: '学生完成虚拟仿真实验,并提交实验结果。', icon: '#icon-shangchuan',type:'primary' },
]); ]);
const handleClick = (item) => { const handleClick = (item) => {

View File

@ -734,6 +734,10 @@ const msgHandle = (msg) => {
Homework.win = null Homework.win = null
window.close() // window.close() //
break break
case MsgEnum.HEADS.MSG_pushSreen_experiment: // :
Homework.win = null
window.close() //
break
// case 'TIMAddRecvNewMsgCallback': // data=[] // case 'TIMAddRecvNewMsgCallback': // data=[]
// { // {
// (data||[]).forEach(o => { // (data||[]).forEach(o => {

View File

@ -59,10 +59,10 @@ const getHomework = async () => {
homeworkList.value = rows || []; homeworkList.value = rows || [];
homeworkList.value.forEach((item) => { homeworkList.value.forEach((item) => {
// UI // UI
if (item.worktype == '学习目标定位') { if (item.worktype == '课堂展示') {
item.workclass = 'success' item.workclass = 'success'
item.workcodesList = JSON.parse(item.workcodes) item.workcodesList = JSON.parse(item.workcodes)
} else if (item.worktype == '教材研读') { } else if (item.worktype == '科学实验') {
item.workclass = 'primary' item.workclass = 'primary'
} else if (item.worktype == '框架梳理') { } else if (item.worktype == '框架梳理') {
item.workclass = 'warning' item.workclass = 'warning'

View File

@ -0,0 +1,427 @@
<template>
<div class="login-container">
<div class="box-item desc">
<div class="welcome">
<p>欢迎登录 {{ homeTitle }}</p>
</div>
<img class="welcome-img" :src="leftBg2" />
</div>
<div class="box-item login" v-if="isRegister">
<WindowTools :is-has-max="false" />
<div class="login-title">账号登录</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item>
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="gotoreRegister">注册账号</a>
</div>
</el-form>
</div>
<div class="box-item login" v-else>
<WindowTools :is-has-max="false" />
<div class="login-title">账号注册</div>
<el-form ref="ruleFormRef" class="login-form" :model="ruleForm" label-width="auto" :rules="rules" size="large">
<el-form-item label="手机号" prop="username">
<el-input v-model="ruleForm.username" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="验证码" prop="smsCode" style="display: flex">
<el-input style="width:185px" v-model="ruleForm.smsCode" placeholder="请输入验证码" /><el-button style="margin-left:10px;width:100px" :disabled="codeName=='发送验证码'?false:true" type="primary" @click="sendyzm">{{ codeName }}</el-button>
</el-form-item>
<el-form-item label="密码" prop="password" >
<el-input autocomplete="on" type="password" v-model="ruleForm.password" placeholder="请输入密码" />
</el-form-item>
<el-form-item>
<el-button class="btn" type="primary" @click="RegisterForm(ruleFormRef)">立即注册</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="gotoLogin"> 返回登录 </a>
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<el-dialog
v-model="isImg"
title="人机验证"
width="500"
style=" -webkit-app-region: no-drag;"
>
<span>根据图片回答相关问题1</span>
<div style="display: flex;align-items: center;;margin-top:30px">
<img :src="isPeopleImg" style="width:200px;height:60px;cursor: pointer;" alt="" srcset="" @click="refreshImg">
<el-input v-model="ruleForm.imgCode" style="width: 250px;height:40px;margin-left:20px" placeholder="请根据图片填入答案" />
</div>
<div style="display: flex;justify-content: center;margin-top:30px">
<el-button type="primary" @click="sbmitImg">确定</el-button>
</div>
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import useUserStore from '@/store/modules/user'
import leftBg2 from '@/assets/images/login/left-bg2.png'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
import {sendcode,instructorregister,getCodeImg} from '@/api/login'
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
const isRegister = ref(true)
const ruleFormRef = ref(null)
const codeName=ref('发送验证码')
const timer=ref(null)
const isImg=ref(false)
const isPeopleImg=ref(null)
const type=ref(1) // 1 2
const resImg = reactive({ imgData: {} });
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const ruleForm = reactive({
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
smsCode: [{ required: true, trigger: 'blur', message: '请输入您的验证码' }],
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
const gotoreRegister=()=>{
codeName.value='发送验证码'
if(timer.value){
clearInterval(timer.value);
}
isRegister.value=false
}
//
const refreshImg=()=>{
getCodeImg().then(res=>{
isPeopleImg.value='data:image/jpg;base64,'+res.img
resImg.imgData=res
})
}
//
const sbmitImg=()=>{
if(ruleForm.imgCode){
// {mobile:ruleForm.phoneNumber,code:ruleForm.imgCode,uuid:resImg.imgData.uuid}
const { username:username,imgCode:code } = ruleForm
const params = {
username, code,
uuid: resImg.imgData.uuid,
source:4
}
sendcode(params).then(res=>{
if(res.code==200){
ElMessage.success('短信发送成功')
ruleForm.Code=res.data
isImg.value=false
codeName.value=60
timer.value=setInterval(()=>{
codeName.value--
if(codeName.value==0){
codeName.value='发送验证码'
clearInterval(timer.value);
}
},1000)
}
})
}else{
ElMessage.error('请根据图片输入验证码')
}
//
}
//
const sendyzm=()=>{
if(ruleForm.username){
const pattern = /^1[3-9]\d{9}$/;
if( pattern.test(ruleForm.username) ){
getCodeImg().then(res=>{
if(res.code==200){
ruleForm.imgCode=null
isPeopleImg.value='data:image/jpg;base64,'+res.img
isImg.value=true
resImg.imgData=res
// codeName.value=60
// timer.value=setInterval(()=>{
// codeName.value--
// if(codeName.value==0){
// codeName.value=''
// clearInterval(timer.value);
// }
// },1000)
}else{
ElMessage.error(res.msg)
}
})
}else{
ElMessage.error('请输入正确的手机号码')
}
// captchaImg({mobile:ruleForm.phoneNumber}).then(res=>{
// console.log('res->', res)
// })
}else{
ElMessage.error('请输入手机号码')
}
}
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject || isStadium(userStore.user)) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const isStadium = (user) => {
let roles = user.roles
return roles.some(item => item.roleKey === 'stadium')
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
const gotoLogin = () => {
codeName.value='发送验证码'
if (timer.value){
clearInterval(timer.value);
}
if (ruleFormRef.value) ruleFormRef.value.resetFields()
isRegister.value = true
}
//
const RegisterForm = async (formEl) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
instructorregister(ruleForm).then(res=>{
if(res.code==200){
ElMessage.success('您已注册成功')
if (ruleFormRef.value) ruleFormRef.value.resetFields()
gotoLogin()
}else{
ElMessage.error(res.msg)
}
})
console.log('submit!')
} else {
console.log('error submit!', fields)
}
})
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script>
<style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
padding: 23px 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 42px;
position: relative;
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 35px;
margin-top: 50px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style>

View File

@ -1,427 +1,13 @@
<template> <template>
<div class="login-container"> <ycLogin v-if="buildMode === 'yc'||buildMode === 'yc2'">
<div class="box-item desc"> </ycLogin>
<div class="welcome"> <defultLogin v-else>
<p>欢迎登录 {{ homeTitle }}</p> </defultLogin>
</div>
<img class="welcome-img" :src="leftBg2" />
</div>
<div class="box-item login" v-if="isRegister">
<WindowTools :is-has-max="false" />
<div class="login-title">账号登录</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item>
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="gotoreRegister">注册账号</a>
</div>
</el-form>
</div>
<div class="box-item login" v-else>
<WindowTools :is-has-max="false" />
<div class="login-title">账号注册</div>
<el-form ref="ruleFormRef" class="login-form" :model="ruleForm" label-width="auto" :rules="rules" size="large">
<el-form-item label="手机号" prop="username">
<el-input v-model="ruleForm.username" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="验证码" prop="smsCode" style="display: flex">
<el-input style="width:185px" v-model="ruleForm.smsCode" placeholder="请输入验证码" /><el-button style="margin-left:10px;width:100px" :disabled="codeName=='发送验证码'?false:true" type="primary" @click="sendyzm">{{ codeName }}</el-button>
</el-form-item>
<el-form-item label="密码" prop="password" >
<el-input autocomplete="on" type="password" v-model="ruleForm.password" placeholder="请输入密码" />
</el-form-item>
<el-form-item>
<el-button class="btn" type="primary" @click="RegisterForm(ruleFormRef)">立即注册</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="gotoLogin"> 返回登录 </a>
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<el-dialog
v-model="isImg"
title="人机验证"
width="500"
style=" -webkit-app-region: no-drag;"
>
<span>根据图片回答相关问题1</span>
<div style="display: flex;align-items: center;;margin-top:30px">
<img :src="isPeopleImg" style="width:200px;height:60px;cursor: pointer;" alt="" srcset="" @click="refreshImg">
<el-input v-model="ruleForm.imgCode" style="width: 250px;height:40px;margin-left:20px" placeholder="请根据图片填入答案" />
</div>
<div style="display: flex;justify-content: center;margin-top:30px">
<el-button type="primary" @click="sbmitImg">确定</el-button>
</div>
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template> </template>
<script setup> <script setup>
import { onMounted, reactive, ref } from 'vue' import ycLogin from './yc-login.vue'
import { ElMessage } from 'element-plus' import defultLogin from './defult-login.vue'
import { encrypt, decrypt } from '@/utils/jsencrypt' const buildMode = import.meta.env.MODE
import useUserStore from '@/store/modules/user'
import leftBg2 from '@/assets/images/login/left-bg2.png'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
import {sendcode,instructorregister,getCodeImg} from '@/api/login'
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
const isRegister = ref(true)
const ruleFormRef = ref(null)
const codeName=ref('发送验证码')
const timer=ref(null)
const isImg=ref(false)
const isPeopleImg=ref(null)
const type=ref(1) // 1 2
const resImg = reactive({ imgData: {} });
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const ruleForm = reactive({
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
smsCode: [{ required: true, trigger: 'blur', message: '请输入您的验证码' }],
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
const gotoreRegister=()=>{
codeName.value='发送验证码'
if(timer.value){
clearInterval(timer.value);
}
isRegister.value=false
}
//
const refreshImg=()=>{
getCodeImg().then(res=>{
isPeopleImg.value='data:image/jpg;base64,'+res.img
resImg.imgData=res
})
}
//
const sbmitImg=()=>{
if(ruleForm.imgCode){
// {mobile:ruleForm.phoneNumber,code:ruleForm.imgCode,uuid:resImg.imgData.uuid}
const { username:username,imgCode:code } = ruleForm
const params = {
username, code,
uuid: resImg.imgData.uuid,
source:4
}
sendcode(params).then(res=>{
if(res.code==200){
ElMessage.success('短信发送成功')
ruleForm.Code=res.data
isImg.value=false
codeName.value=60
timer.value=setInterval(()=>{
codeName.value--
if(codeName.value==0){
codeName.value='发送验证码'
clearInterval(timer.value);
}
},1000)
}
})
}else{
ElMessage.error('请根据图片输入验证码')
}
//
}
//
const sendyzm=()=>{
if(ruleForm.username){
const pattern = /^1[3-9]\d{9}$/;
if( pattern.test(ruleForm.username) ){
getCodeImg().then(res=>{
if(res.code==200){
ruleForm.imgCode=null
isPeopleImg.value='data:image/jpg;base64,'+res.img
isImg.value=true
resImg.imgData=res
// codeName.value=60
// timer.value=setInterval(()=>{
// codeName.value--
// if(codeName.value==0){
// codeName.value=''
// clearInterval(timer.value);
// }
// },1000)
}else{
ElMessage.error(res.msg)
}
})
}else{
ElMessage.error('请输入正确的手机号码')
}
// captchaImg({mobile:ruleForm.phoneNumber}).then(res=>{
// console.log('res->', res)
// })
}else{
ElMessage.error('请输入手机号码')
}
}
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject || isStadium(userStore.user)) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const isStadium = (user) => {
let roles = user.roles
return roles.some(item => item.roleKey === 'stadium')
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
const gotoLogin = () => {
codeName.value='发送验证码'
if (timer.value){
clearInterval(timer.value);
}
if (ruleFormRef.value) ruleFormRef.value.resetFields()
isRegister.value = true
}
//
const RegisterForm = async (formEl) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
instructorregister(ruleForm).then(res=>{
if(res.code==200){
ElMessage.success('您已注册成功')
if (ruleFormRef.value) ruleFormRef.value.resetFields()
gotoLogin()
}else{
ElMessage.error(res.msg)
}
})
console.log('submit!')
} else {
console.log('error submit!', fields)
}
})
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
padding: 23px 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 42px;
position: relative;
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 35px;
margin-top: 50px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style> </style>

View File

@ -0,0 +1,463 @@
<template>
<div class="login-container">
<div class="login-yc">
<img class="welcome-img" :src="buildMode === 'yc2'?leftBg2:leftBg1" />
</div>
<div class="box-item login" v-if="isRegister">
<WindowTools :is-has-max="false" />
<div style="display: flex;justify-content: center;"><img class="title-logo" :src="yclogo" /></div>
<div class="login-title">永川中小学</div>
<div class="login-title2">{{buildMode === 'yc2'?'虚拟仿真AI实训教学管理系统':'人工智能赋能科学素养与劳动技能系统'}}</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item style="margin-bottom: 20px">
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="gotoreRegister">注册账号</a>
</div>
<div class="title-bottom">
重庆市永川区教育委员会
</div>
</el-form>
</div>
<div class="box-item login" v-else>
<WindowTools :is-has-max="false" />
<div class="login-title">账号注册</div>
<el-form ref="ruleFormRef" class="login-form" :model="ruleForm" label-width="auto" :rules="rules" size="large">
<el-form-item label="手机号" prop="username">
<el-input v-model="ruleForm.username" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="验证码" prop="smsCode" style="display: flex">
<el-input style="width:185px" v-model="ruleForm.smsCode" placeholder="请输入验证码" /><el-button style="margin-left:10px;width:100px" :disabled="codeName=='发送验证码'?false:true" type="primary" @click="sendyzm">{{ codeName }}</el-button>
</el-form-item>
<el-form-item label="密码" prop="password" >
<el-input autocomplete="on" type="password" v-model="ruleForm.password" placeholder="请输入密码" />
</el-form-item>
<el-form-item>
<el-button class="btn" type="primary" @click="RegisterForm(ruleFormRef)">立即注册</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="gotoLogin"> 返回登录 </a>
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<el-dialog
v-model="isImg"
title="人机验证"
width="500"
style=" -webkit-app-region: no-drag;"
>
<span>根据图片回答相关问题1</span>
<div style="display: flex;align-items: center;;margin-top:30px">
<img :src="isPeopleImg" style="width:200px;height:60px;cursor: pointer;" alt="" srcset="" @click="refreshImg">
<el-input v-model="ruleForm.imgCode" style="width: 250px;height:40px;margin-left:20px" placeholder="请根据图片填入答案" />
</div>
<div style="display: flex;justify-content: center;margin-top:30px">
<el-button type="primary" @click="sbmitImg">确定</el-button>
</div>
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import useUserStore from '@/store/modules/user'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
import {sendcode,instructorregister,getCodeImg} from '@/api/login'
import yclogo from '@/assets/images/login/yc-logo.png'
import leftBg1 from '@/assets/images/login/ycpeitu.png'
import leftBg2 from '@/assets/images/login/ycpeitu2.jpg'
const { session } = require('@electron/remote')
const buildMode = import.meta.env.MODE
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
const isRegister = ref(true)
const ruleFormRef = ref(null)
const codeName=ref('发送验证码')
const timer=ref(null)
const isImg=ref(false)
const isPeopleImg=ref(null)
const type=ref(1) // 1 2
const resImg = reactive({ imgData: {} });
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const ruleForm = reactive({
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
smsCode: [{ required: true, trigger: 'blur', message: '请输入您的验证码' }],
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
const gotoreRegister=()=>{
codeName.value='发送验证码'
if(timer.value){
clearInterval(timer.value);
}
isRegister.value=false
}
//
const refreshImg=()=>{
getCodeImg().then(res=>{
isPeopleImg.value='data:image/jpg;base64,'+res.img
resImg.imgData=res
})
}
//
const sbmitImg=()=>{
if(ruleForm.imgCode){
// {mobile:ruleForm.phoneNumber,code:ruleForm.imgCode,uuid:resImg.imgData.uuid}
const { username:username,imgCode:code } = ruleForm
const params = {
username, code,
uuid: resImg.imgData.uuid,
source:4
}
sendcode(params).then(res=>{
if(res.code==200){
ElMessage.success('短信发送成功')
ruleForm.Code=res.data
isImg.value=false
codeName.value=60
timer.value=setInterval(()=>{
codeName.value--
if(codeName.value==0){
codeName.value='发送验证码'
clearInterval(timer.value);
}
},1000)
}
})
}else{
ElMessage.error('请根据图片输入验证码')
}
//
}
//
const sendyzm=()=>{
if(ruleForm.username){
const pattern = /^1[3-9]\d{9}$/;
if( pattern.test(ruleForm.username) ){
getCodeImg().then(res=>{
if(res.code==200){
ruleForm.imgCode=null
isPeopleImg.value='data:image/jpg;base64,'+res.img
isImg.value=true
resImg.imgData=res
// codeName.value=60
// timer.value=setInterval(()=>{
// codeName.value--
// if(codeName.value==0){
// codeName.value=''
// clearInterval(timer.value);
// }
// },1000)
}else{
ElMessage.error(res.msg)
}
})
}else{
ElMessage.error('请输入正确的手机号码')
}
// captchaImg({mobile:ruleForm.phoneNumber}).then(res=>{
// console.log('res->', res)
// })
}else{
ElMessage.error('请输入手机号码')
}
}
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject || isStadium(userStore.user)) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const isStadium = (user) => {
let roles = user.roles
return roles.some(item => item.roleKey === 'stadium')
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
const gotoLogin = () => {
codeName.value='发送验证码'
if (timer.value){
clearInterval(timer.value);
}
if (ruleFormRef.value) ruleFormRef.value.resetFields()
isRegister.value = true
}
//
const RegisterForm = async (formEl) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
instructorregister(ruleForm).then(res=>{
if(res.code==200){
ElMessage.success('您已注册成功')
if (ruleFormRef.value) ruleFormRef.value.resetFields()
gotoLogin()
}else{
ElMessage.error(res.msg)
}
})
console.log('submit!')
} else {
console.log('error submit!', fields)
}
})
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script>
<style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.login-yc{
width: 100%;
height: 100%;
img{
width: 100%;
height: 100%;
}
}
.box-item {
width: 370px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
padding: 23px 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 42px;
position: relative;
.title-logo{
width: 50px;
}
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 35px;
margin-top: 50px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 10px;
margin-top: 5px;
font-width: bold;
}
.login-title2 {
margin-bottom: 20px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
.title-bottom{
text-align: center;
width: 100%;
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style>

View File

@ -83,6 +83,8 @@ import useUserStore from '@/store/modules/user' // 用户信息
import ChooseTextbook from '@/components/choose-textbook/index.vue' import ChooseTextbook from '@/components/choose-textbook/index.vue'
import KjListItem from '@/views/prepare/container/kj-list-item.vue' import KjListItem from '@/views/prepare/container/kj-list-item.vue'
import FileImage from '@/components/file-image/index.vue' import FileImage from '@/components/file-image/index.vue'
import progressDialog from '@/views/teachingDesign/container/progress-dialog.vue'
import ThumbnailSlide from '@/AixPPTist/src/views/components/ThumbnailSlide/index.vue'
import {creatAPT, getSmarttalkPage, getModelInfo} from '@/api/file' import {creatAPT, getSmarttalkPage, getModelInfo} from '@/api/file'
import {ArrowDown, Flag, Position} from '@element-plus/icons-vue' import {ArrowDown, Flag, Position} from '@element-plus/icons-vue'
import {asyncLocalFile, parseCataByNode} from "@/utils/talkFile"; import {asyncLocalFile, parseCataByNode} from "@/utils/talkFile";
@ -94,11 +96,11 @@ import {createWindow, toLinkLeftWeb} from "@/utils/tool";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport"; import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport";
import * as API_entpcoursefile from "@/api/education/entpcoursefile"; import * as API_entpcoursefile from "@/api/education/entpcoursefile";
import progressDialog from '@/views/teachingDesign/container/progress-dialog.vue'
import msgUtils from "@/plugins/modal"; import msgUtils from "@/plugins/modal";
import * as commUtils from "@/utils/comm"; import * as commUtils from "@/utils/comm";
import * as Api_server from "@/api/apiService"; // import * as Api_server from "@/api/apiService"; //
import useClassTaskStore from '@/store/modules/classTask' import useClassTaskStore from '@/store/modules/classTask'
import { slidesToImg } from '@/utils/ppt' // ppt
const router = useRouter() const router = useRouter()
const userStore = useUserStore().user // const userStore = useUserStore().user //
@ -278,12 +280,14 @@ const handleFileChange = ()=> {
createAIPPTByFile(file) createAIPPTByFile(file)
} }
} }
// pptPPT线
const createAIPPTByFile = async (file)=> { const createAIPPTByFile = async (file)=> {
pgDialog.value.visible = true // pgDialog.value.visible = true
pgDialog.value.pg.percentage = 0 // pgDialog.value.pg.percentage = 0
const resPptJson = await PPTXFileToJson(file) const resPptJson = await PPTXFileToJson(file)
const { def, slides, ...content } = resPptJson const { def, slides, ...content } = resPptJson
//
const thumbnails = await slidesToImg(slides, content.width)
// || 线 // || 线
let completed = 0 let completed = 0
const total = slides.length const total = slides.length
@ -335,6 +339,7 @@ const createAIPPTByFile = async (file)=> {
entpcourseid: resCourse.id, entpcourseid: resCourse.id,
title: '', title: '',
filetype: 'slide', filetype: 'slide',
thumbnails, // -
slides: resSlides, slides: resSlides,
edituserid: userStore.userId edituserid: userStore.userId
} }

View File

@ -10,8 +10,8 @@
<div class="paragraphs"> <div class="paragraphs">
{{ outputText }} {{ outputText }}
</div> </div>
<el-button style="margin-bottom: 5px;" type="primary" @click="addMessage">从新生成</el-button> <el-button style="margin-bottom: 5px;" type="primary" @click="addMessage">{{ outputText ? '重新生成' : '生成大纲' }}</el-button>
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 1">下一步</el-button> <el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 1" :disabled="!outputText">下一步</el-button>
</el-card> </el-card>
<el-card v-if="activeStep === 1"> <el-card v-if="activeStep === 1">
<div style="padding-bottom: 10px">ppt模板选择</div> <div style="padding-bottom: 10px">ppt模板选择</div>
@ -57,7 +57,7 @@
</el-row> </el-row>
<div> <div>
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 0">上一步</el-button> <el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 0">上一步</el-button>
<el-button style="margin-bottom: 5px;" type="primary" v-loading="createPPTLoading" @click="outlineCreatePPT()">生成PPT</el-button> <el-button style="margin-bottom: 5px;" type="primary" :loading="createPPTLoading" @click="outlineCreatePPT()">生成PPT</el-button>
</div> </div>
</el-card> </el-card>
<el-card v-if="activeStep === 2"> <el-card v-if="activeStep === 2">
@ -177,6 +177,8 @@ const outlineCreatePPT = () => {
}; };
checkProgress(); checkProgress();
}).finally(()=>{
createPPTLoading.value = false
}) })
}; };

View File

@ -25,7 +25,7 @@
<c-form v-bind="classForm"> <c-form v-bind="classForm">
<template #item_classid="{prop, form}"> <template #item_classid="{prop, form}">
<span v-if="dt.ctCourse">{{ dt.ctCourse?.caption }}</span> <span v-if="dt.ctCourse">{{ dt.ctCourse?.caption }}</span>
<el-select v-else v-model="form[prop]" placeholder="请选择班级"> <el-select v-else v-model="form[prop]" placeholder="请选择班级" clearable>
<el-option v-for="item in listData.classList" :value="item.id" <el-option v-for="item in listData.classList" :value="item.id"
:label="`${item.caption} (${item.classstudentcount}人)`" /> :label="`${item.caption} (${item.classstudentcount}人)`" />
</el-select> </el-select>
@ -249,10 +249,10 @@ const getClasscourseList = async type => {
// isPublic // isPublic
const createClasscourse = async (isPublic = false) => { const createClasscourse = async (isPublic = false) => {
const { classid } = classForm.form const { classid } = classForm.form
if (!classid) { // if (!classid) {
ElMessage.warning('请选择班级') // ElMessage.warning('')
return // return
} // }
dt.loading = true dt.loading = true
const { entpcourseid, evalid, id, coursetitle } = myClassActive.value // const { entpcourseid, evalid, id, coursetitle } = myClassActive.value //
const curDate = commUtil.getDateNow('yyyy-MM-dd') const curDate = commUtil.getDateNow('yyyy-MM-dd')
@ -372,9 +372,9 @@ const openPublicScreen = (classcourse, isPublic) => {
createWindow('open-win', { createWindow('open-win', {
url: '/pptist', // url: '/pptist', //
close: () => { close: () => {
sessionStore.set('curr.resource', null) // sessionStore.delete('curr.resource') //
sessionStore.set('curr.classcourse', null) // sessionStore.delete('curr.classcourse') //
sessionStore.set('curr.isPublic', null) // sessionStore.delete('curr.isPublic') //
} }
}) })
visible.value = false // visible.value = false //

View File

@ -126,11 +126,17 @@
<span>下载</span> <span>下载</span>
</el-button> </el-button>
</div> </div>
<div v-if="item.fileSuffix === 'ppt' || item.fileSuffix === 'pptx'" class="item-popover-item"> <!-- <div v-if="item.fileSuffix === 'ppt' || item.fileSuffix === 'pptx'" class="item-popover-item">
<el-button text @click="adToKj(item)"> <el-button text @click="adToKj(item)">
<i class="iconfont icon-jiahao"></i> <i class="iconfont icon-jiahao"></i>
<span>加入课件</span> <span>加入课件</span>
</el-button> </el-button>
</div>-->
<div v-if="item.fileSuffix === 'ppt' || item.fileSuffix === 'pptx'" class="item-popover-item">
<el-button text @click="importPPT(item)">
<i class="iconfont icon-jiahao"></i>
<span>导入PPT</span>
</el-button>
</div> </div>
<div class="item-popover-item"> <div class="item-popover-item">
<el-button text @click="moveSmarttalkFun(item)"> <el-button text @click="moveSmarttalkFun(item)">
@ -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() { data() {
return { return {
listenList: [], listenList: [],
@ -217,6 +223,9 @@ export default {
}) })
.catch(() => {}) .catch(() => {})
}, },
importPPT(item) {
this.$emit('on-importPPT', item)
},
downloadFile(item) { downloadFile(item) {
ipcRenderer.send('save-as', item.fileFullPath, item.fileShowName) ipcRenderer.send('save-as', item.fileFullPath, item.fileShowName)
}, },

View File

@ -99,6 +99,7 @@
@on-delete="deleteTalk" @on-delete="deleteTalk"
@on-set="openSet" @on-set="openSet"
@on-delhomework="delhomework" @on-delhomework="delhomework"
@on-importPPT="importPPT"
@on-filearg="isOpenHomework = true" @on-filearg="isOpenHomework = true"
> >
<el-checkbox v-if="!item.uniquekey" label="" :value="item" /> <el-checkbox v-if="!item.uniquekey" label="" :value="item" />
@ -204,13 +205,12 @@ import TreeLog from '@/views/prepare/components/treeLog.vue'
import classStart from './container/class-start.vue' // import classStart from './container/class-start.vue' //
import MsgEnum from '@/plugins/imChat/msgEnum' // im import MsgEnum from '@/plugins/imChat/msgEnum' // im
import * as commUtils from "@/utils/comm"; import * as commUtils from "@/utils/comm";
import * as Api_server from "@/api/apiService";
import msgUtils from "@/plugins/modal"; import msgUtils from "@/plugins/modal";
import * as Api_server from "@/api/apiService";
import * as API_entpcoursefile from "@/api/education/entpcoursefile"; import * as API_entpcoursefile from "@/api/education/entpcoursefile";
import { slidesToImg } from '@/utils/ppt' // ppt
import ChatWs from '@/plugins/socket' // socket import ChatWs from '@/plugins/socket' // socket
if (!ChatWs.ws) ChatWs.init() if (!ChatWs.ws) ChatWs.init()
// import Chat from '@/utils/chat' // im
// if (!Chat.imChat) Chat.init()
const toolStore = useToolState() const toolStore = useToolState()
const fs = require('fs') const fs = require('fs')
@ -497,11 +497,11 @@ export default {
createWindow('open-win', { createWindow('open-win', {
url: '/pptist', // url: '/pptist', //
close: () => { close: () => {
sessionStore.set('curr.resource', null) // sessionStore.delete('curr.resource') //
if (type=='edit') { if (type=='edit') {
sessionStore.set('curr.smarttalk', null) // sessionStore.delete('curr.smarttalk') //
this.asyncAllFile() // this.asyncAllFile() //
} else sessionStore.set('curr.classcourse', null) // } else sessionStore.delete('curr.classcourse') //
} }
}) })
}, },
@ -555,6 +555,15 @@ export default {
progDownFile(e, num) { progDownFile(e, num) {
this.downloadNum = 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() { createFile() {
creatPPT(this.currentNode.itemtitle + '.pptx', this.uploadData).then((res) => { creatPPT(this.currentNode.itemtitle + '.pptx', this.uploadData).then((res) => {
this.currentFileList.unshift(res.resData) this.currentFileList.unshift(res.resData)
@ -563,8 +572,9 @@ export default {
},500) },500)
}) })
}, },
openFilePicker(){ async openFilePicker(){
this.$refs.fileInput.click(); this.$refs.fileInput.click();
// const files = await commUtils.getFiles()
}, },
handleFileChange(){ handleFileChange(){
const file = event.target.files[0]; const file = event.target.files[0];
@ -573,7 +583,7 @@ export default {
console.log('文件名:', file.name); console.log('文件名:', file.name);
console.log('文件类型:', file.type); console.log('文件类型:', file.type);
console.log('文件大小:', file.size); console.log('文件大小:', file.size);
this.createAIPPTByFile(file) this.createAIPPTByFile(file, this.currentNode.itemtitle + '.aippt')
} }
}, },
async toRousrceUrl(o) { async toRousrceUrl(o) {
@ -619,11 +629,13 @@ export default {
} }
} }
}, },
async createAIPPTByFile(file) { async createAIPPTByFile(file,fileShowName) {
this.pgDialog.visible = true this.pgDialog.visible = true
this.pgDialog.pg.percentage = 0 this.pgDialog.pg.percentage = 0
const resPptJson = await PPTXFileToJson(file) const resPptJson = await PPTXFileToJson(file)
const { def, slides, ...content } = resPptJson const { def, slides, ...content } = resPptJson
//
const thumbnails = await slidesToImg(slides, content.width)
// || 线 // || 线
let completed = 0 let completed = 0
const total = slides.length const total = slides.length
@ -665,7 +677,7 @@ export default {
...this.uploadData, ...this.uploadData,
fileId: slideid, fileId: slideid,
fileFlag: 'aippt', fileFlag: 'aippt',
fileShowName: this.currentNode.itemtitle + '.aippt' fileShowName: fileShowName
}).then(async (res) => { }).then(async (res) => {
const resSlides = slides.map(({id, ...slide}) => JSON.stringify(slide)) const resSlides = slides.map(({id, ...slide}) => JSON.stringify(slide))
@ -676,6 +688,7 @@ export default {
title: '', title: '',
filetype: 'slide', filetype: 'slide',
slides: resSlides, slides: resSlides,
thumbnails, //
edituserid: this.userStore.userId edituserid: this.userStore.userId
} }
const res_3 = await API_entpcoursefile.batchAddNew(params) const res_3 = await API_entpcoursefile.batchAddNew(params)

View File

@ -93,6 +93,9 @@ emitter.on('onGetMain', (item) => {
const emit = defineEmits(['']) const emit = defineEmits([''])
const onSelect = (item) =>{ const onSelect = (item) =>{
actId.value = item.id actId.value = item.id
item.child.forEach(el =>{
el.aiShow = false
})
emitter.emit('changeMode', item) emitter.emit('changeMode', item)
} }

View File

@ -14,8 +14,7 @@
<el-option v-for="item in modeOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in modeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
<el-button type="primary" :disabled="!(resultList.length)" @click="getCompletion">一键研读</el-button> <el-button type="primary" :disabled="!(resultList.length)" @click="getCompletion">一键研读</el-button>
<el-button type="primary">生成大纲</el-button> <el-button type="danger" @click="onCreate">生成PPT</el-button>
<el-button type="danger" @click="pptDialog = true">生成PPT</el-button>
</div> </div>
</div> </div>
<div class="right-con flex" ref="listRef"> <div class="right-con flex" ref="listRef">
@ -92,7 +91,10 @@ import * as API_entpcourse from '@/api/education/entpcourse' // 相关api
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // api import * as API_entpcoursefile from '@/api/education/entpcoursefile' // api
import * as Api_server from '@/api/apiService' // api import * as Api_server from '@/api/apiService' // api
import * as API_smarttalk from '@/api/file' // api import * as API_smarttalk from '@/api/file' // api
import msgUtils from '@/plugins/modal' // import msgUtils from '@/plugins/modal'
import { getEntpcoursefile } from '@/api/education/entpcoursefile'
import { createWindow } from '@/utils/tool' //
import { slidesToImg } from '@/utils/ppt' // ppt
const userStore = useUserStore() const userStore = useUserStore()
const pptDialog = ref(false) const pptDialog = ref(false)
@ -117,7 +119,7 @@ const pgDialog = reactive({ // 弹窗-进度条
} }
}) })
const curMode = ref(1) const curMode = ref(2)
const modeOptions = ref([ const modeOptions = ref([
{ {
label: '教学大模型', label: '教学大模型',
@ -134,6 +136,16 @@ emitter.on('changeMode', (item) => {
getTempResult(item.id) getTempResult(item.id)
}) })
const onCreate = () =>{
let isAnswer = resultList.value.every(item => !item.answer)
if(isAnswer){
ElMessage.warning('请先进行研读')
return
}
pptDialog.value = true
}
// //
const getCompletion = async () => { const getCompletion = async () => {
@ -194,7 +206,7 @@ const handleCompleteText = async (answer, index) => {
} }
// //
const onSaveTemp = (item) => { const onSaveTemp = async (item) => {
if (item.answer == '') return if (item.answer == '') return
const data = { const data = {
@ -204,7 +216,11 @@ const onSaveTemp = (item) => {
content: item.answer, content: item.answer,
ex1: curNode.id ex1: curNode.id
} }
tempSave(data).then(res => { }) const res = await tempSave(data)
if(!item.resultId){
item.resultId = res.data
}
} }
const isWordDialog = ref(false) const isWordDialog = ref(false)
@ -256,7 +272,7 @@ const listRef = ref()
// //
const isStarted = ref([]); const isStarted = ref([]);
const getTempResult = (id) => { const getTempResult = (id) => {
tempResult({ mainModelId: id }).then(res => { tempResult({ mainModelId: id, pageNum: 1, pageSize: 10000, ex1: curNode.id }).then(res => {
let rows = res.rows let rows = res.rows
if (rows.length > 0) { if (rows.length > 0) {
isStarted.value = new Array(rows.length).fill(true) isStarted.value = new Array(rows.length).fill(true)
@ -312,6 +328,7 @@ const prompt = ref('')
const addAiPPT = async (res) => { const addAiPPT = async (res) => {
// res = { url: 'https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx' }
let node = courseObj.node let node = courseObj.node
pptDialog.value = false; pptDialog.value = false;
if (!node) return msgUtils.msgWarning('请选择章节?') if (!node) return msgUtils.msgWarning('请选择章节?')
@ -330,6 +347,8 @@ const addAiPPT = async (res) => {
.then(async buffer => { .then(async buffer => {
const resPptJson = await PPTXFileToJson(buffer) const resPptJson = await PPTXFileToJson(buffer)
const { def, slides, ...content } = resPptJson const { def, slides, ...content } = resPptJson
//
const thumbnails = await slidesToImg(slides, content.width)
// || 线 // || 线
let completed = 0 let completed = 0
const total = slides.length const total = slides.length
@ -346,13 +365,20 @@ const addAiPPT = async (res) => {
const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params) const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params)
if (!!parentid ?? null) { // if (!!parentid ?? null) { //
// -Smarttalk // -Smarttalk
HTTP_SERVER_API('addSmarttalk', { fileId: parentid }) const smarttalk = await HTTP_SERVER_API('addSmarttalk', { fileId: parentid })
if (slides.length > 0) { if (slides.length > 0) {
const resSlides = slides.map(({ id, ...slide }) => JSON.stringify(slide)) const resSlides = slides.map(({ id, ...slide }) => JSON.stringify(slide))
const params = { parentid, filetype: 'slide', title: '', slides: resSlides } const params = { parentid, filetype: 'slide', title: '', thumbnails, slides: resSlides }
const res_3 = await HTTP_SERVER_API('batchAddNew', params) const res_3 = await HTTP_SERVER_API('batchAddNew', params)
if (res_3 && res_3.code == 200) { if (res_3 && res_3.code == 200) {
msgUtils.msgSuccess('生成PPT课件成功') msgUtils.msgSuccess('生成PPT课件成功')
//TODO
const res = await getEntpcoursefile(parentid)
if (res && res.code === 200) {
openPublicScreen('edit', res.data, smarttalk.resData) // -
} else {
ElMessage.warning(res.msg||'文件获取异常!')
}
} else { } else {
msgUtils.msgWarning('生成PPT课件失败') msgUtils.msgWarning('生成PPT课件失败')
} }
@ -361,7 +387,20 @@ const addAiPPT = async (res) => {
}) })
} }
const 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) //
} else sessionStore.set('curr.classcourse', null) //
}
})
}
const isEdit = ref(false) const isEdit = ref(false)
// //
const curIndex = ref(-1) const curIndex = ref(-1)

View File

@ -81,10 +81,10 @@ export const getClassWorkList = async (id) => {
// ---------------------------------------------- // ----------------------------------------------
// 处理任务类型的UI // 处理任务类型的UI
if (list[i].worktype == '学习目标定位') { if (list[i].worktype == '课堂展示') {
list[i].workclass = 'success' list[i].workclass = 'success'
list[i].workcodesList = JSON.parse(list[i].workcodes) list[i].workcodesList = JSON.parse(list[i].workcodes)
} else if (list[i].worktype == '教材研读') { } else if (list[i].worktype == '科学实验') {
list[i].workclass = 'primary' list[i].workclass = 'primary'
} else if (list[i].worktype == '框架梳理') { } else if (list[i].worktype == '框架梳理') {
list[i].workclass = 'warning' list[i].workclass = 'warning'