diff --git a/.env.development b/.env.development index b10b50c..df0b713 100644 --- a/.env.development +++ b/.env.development @@ -9,7 +9,8 @@ VITE_APP_BASE_API = '/dev-api' VITE_APP_DOMAIN = 'file.ysaix.com' -VITE_APP_UPLOAD_API = 'http://192.168.2.52:7863' +VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api' +#VITE_APP_UPLOAD_API = 'http://192.168.2.52:7863' VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/' diff --git a/.env.test b/.env.test index 993cace..3d671e1 100644 --- a/.env.test +++ b/.env.test @@ -1,5 +1,5 @@ # 页面标题 -VITE_APP_TITLE = AIx数字平台 +VITE_APP_TITLE = AIx数字平台(测试版) # 生产环境配置 VITE_APP_ENV = 'production' diff --git a/electron-builder-prod.yml b/electron-builder-prod.yml index 29c65e8..9dfb51e 100644 --- a/electron-builder-prod.yml +++ b/electron-builder-prod.yml @@ -1,7 +1,11 @@ appId: com.electron.app productName: AIx directories: + output: dist buildResources: build +win: + executableName: AIx + icon: resources/logo2.ico files: - '!**/.vscode/*' - '!src/*' @@ -10,9 +14,6 @@ files: - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' asarUnpack: - resources/** -win: - executableName: AIx - icon: resources/logo2.ico nsis: oneClick: false allowToChangeInstallationDirectory: true @@ -45,3 +46,9 @@ publish: url: https://prev.ysaix.com:7868/src/assets/smarttalk/ electronDownload: mirror: https://npmmirror.com/mirrors/electron/ +# 额外依赖打包到输出目录 +extraFiles: + - from: ./node_modules/im_electron_sdk/lib/ + to: ./resources + filter: + - '**/*' diff --git a/electron-builder-test.yml b/electron-builder-test.yml index ed4e1e2..6159547 100644 --- a/electron-builder-test.yml +++ b/electron-builder-test.yml @@ -13,7 +13,7 @@ asarUnpack: win: executableName: AIx icon: resources/logo2.ico -nsis: +nsis: oneClick: false allowToChangeInstallationDirectory: true artifactName: ${name}-${version}-setup.${ext} @@ -45,3 +45,9 @@ publish: url: http://localhost:3000 electronDownload: mirror: https://npmmirror.com/mirrors/electron/ +# 额外依赖打包到输出目录 +extraFiles: + - from: ./node_modules/im_electron_sdk/lib/ + to: ./resources + filter: + - '**/*' diff --git a/electron-builder.yml b/electron-builder.yml index 911fb1c..46e484d 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -13,7 +13,7 @@ asarUnpack: win: executableName: AIx icon: resources/logo2.ico -nsis: +nsis: oneClick: false allowToChangeInstallationDirectory: true artifactName: ${name}-${version}-setup.${ext} @@ -45,3 +45,9 @@ publish: url: https://file.ysaix.com:7868/src/assets/smarttalk/ 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 5c5797a..946990a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aix-win", - "version": "1.0.7", + "version": "1.1.8", "description": "An Electron application with Vue", "main": "./out/main/index.js", "author": "example.com", @@ -30,18 +30,21 @@ "crypto-js": "^4.2.0", "electron-dl-manager": "^3.0.0", "electron-log": "^5.1.7", + "electron-store": "8.0.0", "electron-updater": "^6.1.7", "element-plus": "^2.7.6", "fabric": "^5.3.0", "js-cookie": "^3.0.5", "jsencrypt": "^3.3.2", "jsondiffpatch": "0.6.0", + "im_electron_sdk": "^8.0.5904", "lodash": "^4.17.21", "pdfjs-dist": "4.4.168", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.1", "spark-md5": "^3.0.2", "vue-router": "^4.4.0", + "xgplayer": "^3.0.19", "xlsx": "^0.18.5" }, "devDependencies": { diff --git a/resources/logo.png b/resources/logo.png new file mode 100644 index 0000000..45f6e65 Binary files /dev/null and b/resources/logo.png differ diff --git a/src/main/chat.js b/src/main/chat.js new file mode 100644 index 0000000..b6700f2 --- /dev/null +++ b/src/main/chat.js @@ -0,0 +1,20 @@ +/** + * @description 腾讯云-即时通讯-sdkID + */ +// import { ipcMain } from 'electron' +// const TimMain = require('im_electron_sdk/dist/main') +import TimMain from 'im_electron_sdk/dist/main' +// import {TIMErrCode} from 'im_electron_sdk/dist/enumbers' +const sdkappidDef = 1600034736 // 可以去腾讯云即时通信IM控制台申请 + +// 初始化 +function init(sdkappid = sdkappidDef) { + return new TimMain({sdkappid}) +} +export function initialize(){ + // ipcMain.handle('im-chat:init', (event, sdkappid) => { + // return init(sdkappid) + // }) + return init() +} +export default { initialize, init } diff --git a/src/main/file.js b/src/main/file.js index d3eede2..ddabb2c 100644 --- a/src/main/file.js +++ b/src/main/file.js @@ -42,19 +42,23 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) { let filePath = appRootFilePath + fileNewName let uploadId = null let isOn = false + let lastMTime = fs.statSync(filePath).mtime.getTime() + console.log(lastMTime) setInterval(() => { - getFileMD5(filePath).then((md5New) => { - if (md5New !== md5) { - md5 = md5New + getFileMsg(filePath).then((msg) => { + if (msg !== lastMTime) { + lastMTime = msg if (uploadId) { clearTimeout(uploadId) } if (isOn === false) { + console.log(fileNewName) e.reply('listen-file-change-on' + fileNewName) isOn = true } //倒数十秒提交更改,十秒之内有继续修改则重置倒数 uploadId = setTimeout(() => { + console.log(223) //执行更新,上传文件 let formData = new FormData() formData.append('id', id) @@ -77,12 +81,19 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) { console.error('Error uploading file:', err) } }) - }, 20000) + }, 5000) } }) - }, 10000) + }, 1000) }) + function getFileMsg(path) { + return new Promise((resolve, reject) => { + const stats = fs.statSync(path) + return resolve(stats.mtime.getTime()) + }) + } + function getFileMD5(path) { return new Promise((resolve, reject) => { fs.readFile(path, (err, dataFile) => { @@ -121,13 +132,14 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) { e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' }) return } - getFileMD5(filePath).then((localMd5) => { - if (localMd5 === md5) { + getFileMsg(filePath).then((msg) => { + let time = new Date(lastModifyTime).getTime(); + msg = parseInt(msg/1000)*1000; + if (msg == time) { e.reply('is-async-local-file-reply' + fileNewName, { isAsync: false, type: '' }) } else { const stats = fs.statSync(filePath) //如果线上时间大于线下时间,就需要从线上下载,否则则需要上传 - let time = new Date(lastModifyTime) if (time > stats.mtime.getTime()) { e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' }) } else if (time < stats.mtime.getTime()) { @@ -146,7 +158,7 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) { //使用默认应用打开本地文件 ipcMain.on('open-path-app', (e, destination) => { let path = appRootFilePath + destination - shell.openExternal(path).catch((error) => { + shell.openPath(path).catch((error) => { console.log(error) }) }) diff --git a/src/main/index.js b/src/main/index.js index 441bf06..e4559b2 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -3,12 +3,19 @@ import { join } from 'path' import { electronApp, optimizer, is } from '@electron-toolkit/utils' import icon from '../../resources/icon.png?asset' import File from './file' +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' // 第二步: 初始化remote remote.initialize() -import updateInit from './update' +// 日志配置-初始化(日志直接绑定到console上) +if(!is.dev) Logger.initialize() +// 持久化数据-初始化 +Store.initialize() File({ app, shell, BrowserWindow, ipcMain }) process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' @@ -28,6 +35,7 @@ function createLoginWindow() { icon: join(__dirname, '../../resources/logo2.ico'), ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { + defaultEncoding: 'utf-8', preload: join(__dirname, '../preload/index.js'), sandbox: false, nodeIntegration: true, @@ -60,13 +68,16 @@ function createLoginWindow() { function createMainWindow() { mainWindow = new BrowserWindow({ width: 1200, + minWidth: 1200, height: 700, show: false, frame: false, // 无边框 autoHideMenuBar: true, + maximizable: false, icon: join(__dirname, '../../resources/logo2.ico'), ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { + defaultEncoding: 'utf-8', preload: join(__dirname, '../preload/index.js'), sandbox: false, // nodeIntegration: true, @@ -115,6 +126,7 @@ async function createLinkWin(data) { autoHideMenuBar: true, ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { + defaultEncoding: 'utf-8', sandbox: false, nodeIntegration: true, worldSafeExecuteJavaScript: true, @@ -126,13 +138,14 @@ async function createLinkWin(data) { let cookieDetails = { ...data.cookieData } await linkWin[data.key].webContents.session.cookies .set(cookieDetails) - .then(() => { - console.log('Cookie is successful') - }) - .catch((error) => { - console.error('Cookie is error', error) - }) + .then(() => {}) + .catch((error) => {}) data.fullPath = data.fullPath.replaceAll('//', '/') + if (data.fullPath.indexOf('?') !== -1) { + data.fullPath += '&urlSource=smarttalk' + }else { + data.fullPath += '?urlSource=smarttalk' + } linkWin[data.key].loadURL(data.fullPath) linkWin[data.key].once('ready-to-show', () => { @@ -147,6 +160,8 @@ async function createLinkWin(data) { // 初始化完成 app.on('ready', () => { + appWatchError() // 监听app错误 + process.env.LANG = 'en_US.UTF-8' // 设置应用程序用户模型标识符 electronApp.setAppUserModelId('com.electron') @@ -226,12 +241,14 @@ app.on('window-all-closed', () => { // 监听全局事件 function handleAll() { + const chatInstance = chat.initialize() // im-chat 实例 // 新窗口创建-监听 ipcMain.on('new-window', (e, data) => { const { id, type } = data const win = BrowserWindow.fromId(id) win.type = type // 绑定独立标识 - remote.enable(win.webContents) // 开启远程服务 + remote.enable(win.webContents) // 开启远程服务 + chatInstance.enable(win.webContents) // 开启im-chat }) // 用于监听-状态管理变化-同步所有窗口 ipcMain.handle('pinia-state-change', (e, storeName, jsonStr) => { @@ -243,4 +260,41 @@ function handleAll() { } } }) + // 用于监听-状态管理变化-初始同步 + ipcMain.handle('pinia-state-init', (e, wid, storeName, jsonStr) => { + // console.log('pinia-state-init', jsonStr) + const win = BrowserWindow.fromId(wid) + win.webContents.send('pinia-state-set', storeName, jsonStr) + }) } + +// app 崩溃监听器 +function appWatchError() { + // 渲染进程崩溃 + app.on('renderer-process-crashed', (event, webContents, killed) => { + console.error( + `APP-ERROR:renderer-process-crashed; event: ${JSON.stringify(event)}; webContents:${JSON.stringify( + webContents + )}; killed:${JSON.stringify(killed)}` + ) + }) + + // GPU进程崩溃 + app.on('gpu-process-crashed', (event, killed) => { + console.error(`APP-ERROR:gpu-process-crashed; event: ${JSON.stringify(event)}; killed: ${JSON.stringify(killed)}`) + }) + + // 渲染进程结束 + app.on('render-process-gone', async (event, webContents, details) => { + console.error( + `APP-ERROR:render-process-gone; event: ${JSON.stringify(event)}; webContents:${JSON.stringify( + webContents + )}; details:${JSON.stringify(details)}` + ) + }) + + // 子进程结束 + app.on('child-process-gone', async (event, details) => { + console.error(`APP-ERROR:child-process-gone; event: ${JSON.stringify(event)}; details:${JSON.stringify(details)}`) + }) +} \ No newline at end of file diff --git a/src/main/logger.js b/src/main/logger.js new file mode 100644 index 0000000..03b6db3 --- /dev/null +++ b/src/main/logger.js @@ -0,0 +1,52 @@ +/** + * @description 日志配置 + * @author zdg + * @date 2021-07-05 14:07:01 + */ +// import log from 'electron-log' +import log from 'electron-log/main' +import { app } from 'electron' +import path from 'path' + +// 关闭控制台打印 +// 日志控制台等级,默认值:false +log.transports.console.level = false +// log.transports.console.level = 'info' +// 日志文件等级,默认值:false +log.transports.file.level = 'info' +// 日志文件名,默认:main.log +// log.transports.file.fileName = 'main.log'; +// 日志大小,默认:1048576(1M),达到最大上限后,备份文件并重命名为:main.old.log,有且仅有一个备份文件 +log.transports.file.maxSize = 10 * 1024 * 1024; // 文件最大不超过 10M +// 自定义日志文件滚动策略 +log.transports.file.rollSize = 10 * 1024 * 1024; // 10MB +// 日志格式,默认:[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text} +log.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text}' +let date = new Date() +let dateStr = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() +// 文件位置及命名方式 +// 默认位置为:C:\Users\[user]\AppData\Roaming\[appname]\electron_log\ +// 文件名为:年-月-日.log +// 自定义文件保存位置为安装目录下 \log\年-月-日.log +// log.transports.file.resolvePathFn = () => 'logs\\' + dateStr+ '.log'; +log.transports.file.resolvePathFn = () => path.join(app.getPath('userData'), `logs/${dateStr}.log`) + +// 有六个日志级别error, warn, info, verbose, debug, silly。默认是silly +export const logger = { + error: (...args) => log.error(...args), + warn: (...args) => log.warn(...args), + info: (...args) => log.info(...args), + verbose: (...args) => log.verbose(...args), + debug: (...args) => log.debug(...args), + silly: (...args) => log.silly(...args) +} +export function initialize(bool = true, type = 'all') { + log.initialize() // 为渲染器进行初始化 + if (bool) { // 是否替换默认的console + if (type == 'all') Object.assign(console, log.functions) + else { // 替换指定类型 + console[type] = log[type] + } + } +} +export default { initialize } \ No newline at end of file diff --git a/src/main/store.js b/src/main/store.js new file mode 100644 index 0000000..c37f8f5 --- /dev/null +++ b/src/main/store.js @@ -0,0 +1,61 @@ +/** + * @description 解决 主进程|渲染进程 数据共享 + */ +import Store from 'electron-store' // 持久化存储 + +// 设置ipc与渲染器通信 +Store.initRenderer() + +// 默认共享数据 +const defaultData = { + session: { // 缓存(临时sessionStorage) + model: 'select', // 悬浮球-当前模式 + showBoardAll: false, // 全屏画板-是否显示 + isPdfWin: false, // pdf窗口是否打开 + isToolWin: false, // 工具窗口是否打开 + curSubjectNode: { + data: {}, // 当前教材节点 (包含当前教材 单元) + querySearch: {} // 查询资源所需参数 + } + }, + local: { // 本地(永久localStorage) + }, +} + +// 初始化 +export function initialize(){ + // 缓存数据-sessionStore + const sessionStore = new Store({ + name: 'session-store', // 存储文件名 + fileExtension: 'ini', // 文件后缀名 + encryptionKey: 'BvPLmgCC4DSIG0KkTec5', // 数据加密-防止用户直接改配置 + beforeEachMigration: (store, context) => { // 版本迁移回调 + console.log(`[session-store] 迁移从 ${context.fromVersion} → ${context.toVersion}`); + }, + migrations: { // 版本变化 + '0.0.0': store => { + // store.set('debugPhase', true); + } + } + }) + sessionStore.clear() // 先清除-所有缓存数据 + sessionStore.set(defaultData.session) // 初始化-默认数据 + + // 缓存数据-localStore + const localStore = new Store({ + name: 'local-store', // 存储文件名 + fileExtension: 'ini', // 文件后缀名 + encryptionKey: '6CyoHQmUaPmLzvVsh', // 数据加密-防止用户直接改配置 + beforeEachMigration: (store, context) => { // 版本迁移回调 + console.log(`[local-store] 迁移从 ${context.fromVersion} → ${context.toVersion}`); + }, + migrations: { // 版本变化 + '0.0.0': store => { + // store.set('debugPhase', true); + } + } + }) + localStore.set(defaultData.local) // 初始化-默认数据 + return {sessionStore, localStore} +} +export default { initialize } \ No newline at end of file diff --git a/src/main/tool.js b/src/main/tool.js deleted file mode 100644 index 8be3a66..0000000 --- a/src/main/tool.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @description: electron 封装的工具函数 - * 消息整理 - * tool-sphere:create 创建-悬浮球-窗口 - */ -import { app, shell, BrowserWindow, ipcMain } from 'electron' -import { is } from '@electron-toolkit/utils' - -// const baseUrl = 'http://localhost:5173/#' // 开发环境使用 -const baseUrl = process.env['ELECTRON_RENDERER_URL']+'/#' // 开发环境使用 -// 所有窗口 -let allWindow = {} -// 其他已有窗口 wins -export function init() { - // 创建工具-悬浮球 - ipcMain.on('tool-sphere:create', async(e, data) => { - // console.log('测试xxxx', data) - await createTools(data) // 执行逻辑 - e.reply('tool-sphere:create-reply', {code: 200, msg: 'success'}) // 返回结果 - }) -} -/** - * @description: 创建工具 - * @param {*} url 路由地址 - * @param {number} [width=800] 窗口宽度 - * @param {number} [height=600] 窗口高度 - * @param {{}} [option={}] 自定义选项 - * @author: zdg - * @date 2021-07-05 14:07:01 - */ - export function createTools({url, width = 800, height = 600, option={}}) { - const { mainWindow } = allWindow||{} // 获取主窗口 - const devUrl = `${baseUrl}${url}` - const buildUrl = `file://${__dirname}/index.html${url}` - const urlAll = is.dev ? devUrl : buildUrl - return new Promise((resolve) => { - let win = new BrowserWindow({ - width, height, - type: 'toolbar', // 创建的窗口类型为工具栏窗口 - frame: false, // 要创建无边框窗口 - resizable: false, // 禁止窗口大小缩放 - transparent: true, // 设置透明 - alwaysOnTop: true, // 窗口是否总是显示在其他窗口之前 - - parent: mainWindow, // 父窗口 - autoClose: true, // 关闭窗口后自动关闭 - webPreferences: { - nodeIntegration: true, // nodeApi调用 - contextIsolation: false, // 沙箱取消 - webSecurity: false // 跨域关闭 - }, - ...option - }) - // console.log(urlAll) - // url = 'https://www.baidu.com' - console.log(urlAll) - win.loadURL(urlAll) - win.setFullScreen(true) // 设置窗口为全屏 - win.setIgnoreMouseEvents(true) // 忽略鼠标事件|使窗口不可选中 - win.once('ready-to-show', () => { - win.show() - resolve(win) - }) - win.on('closed', () => { - win = null - }) - }) -} -// 保存窗口 -export function setWin(win = {}) { - if (win && Object.keys(win).length){ - Object.keys(win).forEach(key => { - if (!allWindow[key]) { // 不存在就保存 - allWindow[key] = win[key] - } - }) - } -} diff --git a/src/preload/index.js b/src/preload/index.js index 9da4869..ffdb7a0 100644 --- a/src/preload/index.js +++ b/src/preload/index.js @@ -1,8 +1,10 @@ import { contextBridge } from 'electron' import { electronAPI } from '@electron-toolkit/preload' - +import TimRender from 'im_electron_sdk/dist/renderer' // im渲染部分实例 // Custom APIs for renderer const api = { + preloadPath: __dirname, // 当前preload地址 + getTimRender: () => new TimRender(), // im渲染部分实例 } // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise diff --git a/src/renderer/index.html b/src/renderer/index.html index 3441296..1de810c 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -2,13 +2,13 @@ - AIx智慧教育 + %VITE_APP_TITLE% - + diff --git a/src/renderer/src/api/apiService.js b/src/renderer/src/api/apiService.js new file mode 100644 index 0000000..0b28e0d --- /dev/null +++ b/src/renderer/src/api/apiService.js @@ -0,0 +1,28 @@ +/** + * @description: 后端接口api + * @author zdg + * @date 2023-07-03 + */ +import request from '@/utils/request' +// /system/user/txCloudSign +export class ApiService { + // zdg: 公共请求-处理(可进行特殊处理) + static publicHttp(url, data, method, option = {}, type) { + method = method || 'get' // 默认GET + const config = { url, method } + if (!!data) config[method=='get'?'params':'data'] = data + if (!!option) Object.assign(config, option) + // 特殊格式处理 + let headers + if (type == 'file') headers = { 'Content-Type': 'multipart/form-data' } + else if (type == 'json') headers = { 'Content-Type': 'application/json' } + else if (type == 'form') headers = { 'Content-Type': 'application/x-www-form-urlencoded' } + headers && (config.headers = { ...config.headers, ...headers }) + return request(config) + } +} +// zdg: 腾讯云-即时通讯 +export class imChat { + // 获取腾讯im-chat appid 签名 + static getTxCloudSign = data => ApiService.publicHttp('/system/user/txCloudSign', data) +} diff --git a/src/renderer/src/api/classManage/index.js b/src/renderer/src/api/classManage/index.js index 6db5913..14471ee 100644 --- a/src/renderer/src/api/classManage/index.js +++ b/src/renderer/src/api/classManage/index.js @@ -50,7 +50,7 @@ export function getClassmain(id) { // 获取小组列表 export function listClassgroup(query) { return request({ - url: '/education/classgroup/list', + url: '/education/classgroup/new/list', method: 'get', params: query }) @@ -159,11 +159,13 @@ export function deleteSmartReserv(id) { method: 'delete' }) } -export function startClass(id) { +export function startClass(id, ex3) { + const params = {id} + !!ex3 && (params.ex3 = ex3) return request({ url: '/smarttalk/classReserv/startClass', method: 'get', - params: {id} + params }) } export function endClass(id) { @@ -173,3 +175,15 @@ export function endClass(id) { params: {id} }) } +/** + * @description 获取课堂信息 + * @param {*} id + * @returns + */ +export function getClassInfo(id) { + return request({ + url: '/smarttalk/classReserv/selectById', + method: 'get', + params: {id} + }) +} diff --git a/src/renderer/src/api/classTask/index.js b/src/renderer/src/api/classTask/index.js new file mode 100644 index 0000000..f441957 --- /dev/null +++ b/src/renderer/src/api/classTask/index.js @@ -0,0 +1,237 @@ +// 查询evaluation列表 +import request from '@/utils/request' + +// 查询反馈列表 + +// 查询classworkdata列表 班级作业列表 +export function listClassworkdata(query) { + return request({ + url: '/education/classworkdata/list', + method: 'get', + params: query + }) +} + +// 查询entpcoursework列表 课程作业列表 +export function listEntpcoursework(query) { + return request({ + url: '/education/entpcoursework/list', + method: 'get', + params: query + }) +} + +// 查询classworkeval列表 课堂作业列表 +export function listClassworkeval(query) { + return request({ + url: '/education/classworkeval/list', + method: 'get', + params: query + }) +} + +// 修改classworkeval +export function updateClassworkeval(data) { + return request({ + url: '/education/classworkeval', + method: 'put', + data: data + }) +} + + + + + + + + + +// 0------------------------ +// 查询班级列表 +export function listClassmain(query) { + return request({ + url: '/education/classmain/list', + method: 'get', + params: query + }) +} +// 查询学生列表 +export function listClassuser(query) { + return request({ + url: '/education/classuser/list', + method: 'get', + params: query + }) +} +// 新增班级 +export function addClassmain(data) { + return request({ + url: '/education/classmain', + method: 'post', + data: data + }) +} +// 查询所有学科的列表 +export function listEvaluation(query) { + return request({ + url: '/education/evaluation/list', + method: 'get', + params: query + }) +} +// 新增小组 +export function addClassgroup(data) { + return request({ + url: '/education/classgroup', + method: 'post', + data: data + }) +} +//班级详情 +export function getClassmain(id) { + return request({ + url: '/education/classmain/' + id, + method: 'get' + }) +} +// 获取小组列表 +export function listClassgroup(query) { + return request({ + url: '/education/classgroup/new/list', + method: 'get', + params: query + }) +} +//删除小组 +export function delClassgroup(id) { + return request({ + url: '/education/classgroup/' + id, + method: 'delete' + }) +} +//查询小组信息 +export function getClassgroup(id) { + return request({ + url: '/education/classgroup/' + id, + method: 'get' + }) +} +//修改小组信息 +export function updateClassgroup(data) { + return request({ + url: '/education/classgroup', + method: 'put', + data: data + }) +} +//新增学生 +export function addStudentmain(data) { + return request({ + url: '/education/studentmain', + method: 'post', + data: data + }) +} +//修改学生信息 +export function updateStudentmain(data) { + return request({ + url: '/education/studentmain', + method: 'put', + data: data + }) +} +//获取学生信息 +export function getStudentmain(id) { + return request({ + url: '/education/studentmain/' + id, + method: 'get' + }) +} +//删除学生 +export function leaveClass(data) { + return request({ + url: '/education/classuser/leaveClass', + method: 'post', + data: data + }) +} +//删除学生所有数据 +export function removeStudentDataAll(id) { + return request({ + url: '/education/studentmain/removeStudent/' + id, + method: 'post' + }) +} +//删除教室 +export function delClassroom(id) { + return request({ + url: '/education/classroom/' + id, + method: 'delete' + }) +} +//导入学生 +export function addStudentmainByNameArray(data) { + return request({ + url: '/education/studentmain/addByNameArray', + method: 'post', + data: data + }) +} +//新增课程预约 +export function addSmartClassReserv(data) { + return request({ + url: '/smarttalk/classReserv/addSmartClassReserv', + method: 'post', + data: data + }) +} +//修改课程预约 +export function updateSmartClassReserv(data) { + return request({ + url: '/smarttalk/classReserv/updateSmartClassReserv', + method: 'post', + data: data + }) +} +//查询课程预约 +export function getSelfReserv() { + return request({ + url: '/smarttalk/classReserv/getSelfReserv', + method: 'get' + }) +} +export function deleteSmartReserv(id) { + return request({ + url: '/smarttalk/classReserv/' + id, + method: 'delete' + }) +} +export function startClass(id, ex3) { + const params = {id} + !!ex3 && (params.ex3 = ex3) + return request({ + url: '/smarttalk/classReserv/startClass', + method: 'get', + params + }) +} +export function endClass(id) { + return request({ + url: '/smarttalk/classReserv/endClass', + method: 'get', + params: {id} + }) +} +/** + * @description 获取课堂信息 + * @param {*} id + * @returns + */ +export function getClassInfo(id) { + return request({ + url: '/smarttalk/classReserv/selectById', + method: 'get', + params: {id} + }) +} diff --git a/src/renderer/src/api/file/third.js b/src/renderer/src/api/file/third.js new file mode 100644 index 0000000..5019721 --- /dev/null +++ b/src/renderer/src/api/file/third.js @@ -0,0 +1,64 @@ +//查询第三方课件的接口 +import request from '@/utils/request' +//获取学科 +export const getSubjects = (params) => { + return request({ + url: '/smarttalk/cnjy/getSubjects', + method: 'get', + params + }) +} +//获取教材版本 +export const getTextbookVersion = (params) => { + return request({ + url: '/smarttalk/cnjy/getVersions', + method: 'get', + params + }) +} +//获得书籍 +export const getTextbook = (params) => { + return request({ + url: '/smarttalk/cnjy/getBooks', + method: 'get', + params + }) +} +//获取书籍章节 +export const getBook = (params) => { + return request({ + url: '/smarttalk/cnjy/getChapters', + method: 'get', + params + }) +} +//获取知识点信息 +export const getKnowledge = (params) => { + return request({ + url: '/smarttalk/cnjy/getKnowledgePoints', + method: 'get', + params + }) +} +//查询列表资源 +export const getBookList = (params) => { + return request({ + url: '/smarttalk/cnjy/getDocuments', + method: 'post', + params + }) +} +//获取图片路径 +export const getImgPath = (params) => { + return request({ + url: '/smarttalk/cnjy/getPreview', + method: 'get', + params + }) +} + + + + + + diff --git a/src/renderer/src/api/system/user.js b/src/renderer/src/api/system/user.js index b7e9e23..f8b74f0 100644 --- a/src/renderer/src/api/system/user.js +++ b/src/renderer/src/api/system/user.js @@ -50,3 +50,9 @@ export function updateUserInfo(data) { data: data }) } +export function getUserInfo(userId) { + return request({ + url: '/system/user/' + userId, + method: 'get' + }) +} diff --git a/src/renderer/src/api/teaching/classwork.js b/src/renderer/src/api/teaching/classwork.js index 57da30d..3173201 100644 --- a/src/renderer/src/api/teaching/classwork.js +++ b/src/renderer/src/api/teaching/classwork.js @@ -42,4 +42,13 @@ export function delClasswork(id) { url: '/education/classwork/' + id, method: 'delete' }) +} + +// 新增classwork +export function addClassworkReturnId(data) { + return request({ + url: '/education/classwork/saveAndReturnId', + method: 'post', + data: data + }) } \ No newline at end of file diff --git a/src/renderer/src/assets/iconfont/iconfont.css b/src/renderer/src/assets/iconfont/iconfont.css index fd25490..c1fab69 100644 --- a/src/renderer/src/assets/iconfont/iconfont.css +++ b/src/renderer/src/assets/iconfont/iconfont.css @@ -1,9 +1,9 @@ @font-face { font-family: "iconfont"; /* Project id 2794390 */ - src: url('iconfont.woff2?t=1723452423265') format('woff2'), - url('iconfont.woff?t=1723452423265') format('woff'), - url('iconfont.ttf?t=1723452423265') format('truetype'), - url('iconfont.svg?t=1723452423265#iconfont') format('svg'); + src: url('iconfont.woff2?t=1724212790213') format('woff2'), + url('iconfont.woff?t=1724212790213') format('woff'), + url('iconfont.ttf?t=1724212790213') format('truetype'), + url('iconfont.svg?t=1724212790213#iconfont') format('svg'); } .iconfont { @@ -14,6 +14,94 @@ -moz-osx-font-smoothing: grayscale; } +.icon-yiwen:before { + content: "\e687"; +} + +.icon-yiwen-01:before { + content: "\e688"; +} + +.icon-yihuo:before { + content: "\e689"; +} + +.icon-a-yiwen:before { + content: "\e6b1"; +} + +.icon-zan:before { + content: "\e658"; +} + +.icon-zan1:before { + content: "\e659"; +} + +.icon-zan2:before { + content: "\e65a"; +} + +.icon-zan3:before { + content: "\e65c"; +} + +.icon-zan4:before { + content: "\e67c"; +} + +.icon-yizan:before { + content: "\e67e"; +} + +.icon-zan5:before { + content: "\e67f"; +} + +.icon-zan-yizan:before { + content: "\e680"; +} + +.icon-zan6:before { + content: "\e681"; +} + +.icon-MBEfenggeduosetubiao-xihuan:before { + content: "\e682"; +} + +.icon-zan7:before { + content: "\e683"; +} + +.icon-zan11:before { + content: "\e6ff"; +} + +.icon-zan8:before { + content: "\e684"; +} + +.icon-dianzan-red:before { + content: "\e685"; +} + +.icon-zan9:before { + content: "\e69e"; +} + +.icon-zanping:before { + content: "\100ae"; +} + +.icon-zan10:before { + content: "\e686"; +} + +.icon-arrangement:before { + content: "\e656"; +} + .icon-zanwushuju:before { content: "\e655"; } diff --git a/src/renderer/src/assets/iconfont/iconfont.js b/src/renderer/src/assets/iconfont/iconfont.js index ed5d6de..2ad9df4 100644 --- a/src/renderer/src/assets/iconfont/iconfont.js +++ b/src/renderer/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_2794390='',function(l){var h=(h=document.getElementsByTagName("script"))[h.length-1],c=h.getAttribute("data-injectcss"),h=h.getAttribute("data-disable-injectsvg");if(!h){var a,v,t,z,i,p=function(h,c){c.parentNode.insertBefore(h,c)};if(c&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(h){console&&console.log(h)}}a=function(){var h,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_2794390,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(h=document.body).firstChild?p(c,h.firstChild):h.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),a()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=a,z=l.document,i=!1,o(),z.onreadystatechange=function(){"complete"==z.readyState&&(z.onreadystatechange=null,M())})}function M(){i||(i=!0,t())}function o(){try{z.documentElement.doScroll("left")}catch(h){return void setTimeout(o,50)}M()}}(window); \ No newline at end of file +window._iconfont_svg_string_2794390='',(c=>{var h=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var a,t,z,p,i,v=function(h,l){l.parentNode.insertBefore(h,l)};if(h&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(h){console&&console.log(h)}}a=function(){var h,l=document.createElement("div");l.innerHTML=c._iconfont_svg_string_2794390,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(h=document.body).firstChild?v(l,h.firstChild):h.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),a()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(z=a,p=c.document,i=!1,d(),p.onreadystatechange=function(){"complete"==p.readyState&&(p.onreadystatechange=null,M())})}function M(){i||(i=!0,z())}function d(){try{p.documentElement.doScroll("left")}catch(h){return void setTimeout(d,50)}M()}})(window); \ No newline at end of file diff --git a/src/renderer/src/assets/iconfont/iconfont.json b/src/renderer/src/assets/iconfont/iconfont.json index fd3dafa..30d5f43 100644 --- a/src/renderer/src/assets/iconfont/iconfont.json +++ b/src/renderer/src/assets/iconfont/iconfont.json @@ -5,6 +5,160 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "20574719", + "name": "疑问", + "font_class": "yiwen", + "unicode": "e687", + "unicode_decimal": 59015 + }, + { + "icon_id": "21052326", + "name": "yiwen-01", + "font_class": "yiwen-01", + "unicode": "e688", + "unicode_decimal": 59016 + }, + { + "icon_id": "30456317", + "name": "疑惑", + "font_class": "yihuo", + "unicode": "e689", + "unicode_decimal": 59017 + }, + { + "icon_id": "33439935", + "name": "[疑问]", + "font_class": "a-yiwen", + "unicode": "e6b1", + "unicode_decimal": 59057 + }, + { + "icon_id": "1242129", + "name": "赞", + "font_class": "zan", + "unicode": "e658", + "unicode_decimal": 58968 + }, + { + "icon_id": "1741390", + "name": "赞", + "font_class": "zan1", + "unicode": "e659", + "unicode_decimal": 58969 + }, + { + "icon_id": "3159200", + "name": "赞", + "font_class": "zan2", + "unicode": "e65a", + "unicode_decimal": 58970 + }, + { + "icon_id": "3402139", + "name": "赞", + "font_class": "zan3", + "unicode": "e65c", + "unicode_decimal": 58972 + }, + { + "icon_id": "4931286", + "name": "赞 (1)", + "font_class": "zan4", + "unicode": "e67c", + "unicode_decimal": 59004 + }, + { + "icon_id": "4942300", + "name": "已赞", + "font_class": "yizan", + "unicode": "e67e", + "unicode_decimal": 59006 + }, + { + "icon_id": "5806181", + "name": "赞", + "font_class": "zan5", + "unicode": "e67f", + "unicode_decimal": 59007 + }, + { + "icon_id": "7172310", + "name": "赞-已赞", + "font_class": "zan-yizan", + "unicode": "e680", + "unicode_decimal": 59008 + }, + { + "icon_id": "7293361", + "name": "赞2", + "font_class": "zan6", + "unicode": "e681", + "unicode_decimal": 59009 + }, + { + "icon_id": "8705087", + "name": "MBE风格多色图标-喜欢", + "font_class": "MBEfenggeduosetubiao-xihuan", + "unicode": "e682", + "unicode_decimal": 59010 + }, + { + "icon_id": "10024138", + "name": "赞", + "font_class": "zan7", + "unicode": "e683", + "unicode_decimal": 59011 + }, + { + "icon_id": "11055391", + "name": "赞", + "font_class": "zan11", + "unicode": "e6ff", + "unicode_decimal": 59135 + }, + { + "icon_id": "11086734", + "name": "赞", + "font_class": "zan8", + "unicode": "e684", + "unicode_decimal": 59012 + }, + { + "icon_id": "23592614", + "name": "点赞", + "font_class": "dianzan-red", + "unicode": "e685", + "unicode_decimal": 59013 + }, + { + "icon_id": "26327261", + "name": "赞", + "font_class": "zan9", + "unicode": "e69e", + "unicode_decimal": 59038 + }, + { + "icon_id": "27804883", + "name": "赞评", + "font_class": "zanping", + "unicode": "100ae", + "unicode_decimal": 65710 + }, + { + "icon_id": "29252894", + "name": "赞", + "font_class": "zan10", + "unicode": "e686", + "unicode_decimal": 59014 + }, + { + "icon_id": "4978988", + "name": "作业-布置作业", + "font_class": "arrangement", + "unicode": "e656", + "unicode_decimal": 58966 + }, { "icon_id": "9689424", "name": "暂无数据", diff --git a/src/renderer/src/assets/iconfont/iconfont.svg b/src/renderer/src/assets/iconfont/iconfont.svg index 99a0abf..8201a34 100644 --- a/src/renderer/src/assets/iconfont/iconfont.svg +++ b/src/renderer/src/assets/iconfont/iconfont.svg @@ -14,6 +14,50 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/src/assets/iconfont/iconfont.ttf b/src/renderer/src/assets/iconfont/iconfont.ttf index 477afd5..43f8dc3 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.ttf and b/src/renderer/src/assets/iconfont/iconfont.ttf differ diff --git a/src/renderer/src/assets/iconfont/iconfont.woff b/src/renderer/src/assets/iconfont/iconfont.woff index 397925d..9fc1445 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.woff and b/src/renderer/src/assets/iconfont/iconfont.woff differ diff --git a/src/renderer/src/assets/iconfont/iconfont.woff2 b/src/renderer/src/assets/iconfont/iconfont.woff2 index 7f2c06e..e1759ea 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.woff2 and b/src/renderer/src/assets/iconfont/iconfont.woff2 differ diff --git a/src/renderer/src/components/choose-textbook/index.vue b/src/renderer/src/components/choose-textbook/index.vue index bdd14b9..38fd5e1 100644 --- a/src/renderer/src/components/choose-textbook/index.vue +++ b/src/renderer/src/components/choose-textbook/index.vue @@ -42,11 +42,11 @@ + + diff --git a/src/renderer/src/components/choose-textbook/third/index.vue b/src/renderer/src/components/choose-textbook/third/index.vue new file mode 100644 index 0000000..2b7a6a2 --- /dev/null +++ b/src/renderer/src/components/choose-textbook/third/index.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/src/renderer/src/components/choose-textbook/third/selectSubject.vue b/src/renderer/src/components/choose-textbook/third/selectSubject.vue new file mode 100644 index 0000000..af4b57b --- /dev/null +++ b/src/renderer/src/components/choose-textbook/third/selectSubject.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/src/renderer/src/components/classManage/select/index.vue b/src/renderer/src/components/classManage/select/index.vue index b8959c3..6a11142 100644 --- a/src/renderer/src/components/classManage/select/index.vue +++ b/src/renderer/src/components/classManage/select/index.vue @@ -5,7 +5,7 @@ + + \ No newline at end of file diff --git a/src/renderer/src/components/move-file/index.vue b/src/renderer/src/components/move-file/index.vue index cb9d2b0..dfa7911 100644 --- a/src/renderer/src/components/move-file/index.vue +++ b/src/renderer/src/components/move-file/index.vue @@ -88,8 +88,6 @@ const curBookId = ref(-1) const curBookName = ref('') // 上册 const volumeOne = ref([]) -// 下册 -const volumeTwo = ref([]) // 当前节点 const currentNode = reactive({ data: {} @@ -115,7 +113,8 @@ const getSubjectContent = async () => { const params = { edusubject, edustage, - entpcourseedituserid: userId, + // entpcourseedituserid: userId, + itemgroup: 'textbook', pageSize: 500 } let data; @@ -127,9 +126,12 @@ const getSubjectContent = async () => { //获取教材版本 getSubject() //上册 - volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册') - //下册 - volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册') + /** + * 不区分上下册 + * 2024/08/20调整 + */ + // volumeOne.value = data.filter(item => item.level == 1) + getTreeData() } @@ -140,8 +142,8 @@ const getSubject = async () => { subjectList.value = JSON.parse(localStorage.getItem('subjectList')) } else { - const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 }) - subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id)) + const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 500 }) + subjectList.value = rows localStorage.setItem('subjectList', JSON.stringify(subjectList.value)) } @@ -159,14 +161,9 @@ const isHaveUnit = (id) => { const getTreeData = () => { //数据过滤 - let upData = transData(volumeOne.value) - let downData = transData(volumeTwo.value) - - if(upData.length && downData.length){ - treeData.value = [...upData,...downData] - } - else if(upData.length || downData.length){ - treeData.value = upData.length ? upData : downData + let upData = transData(evaluationList.value) + if(upData.length){ + treeData.value = [...upData] } else{ treeData.value = [] @@ -252,26 +249,35 @@ const handleNodeClick = (data, node) => { const transData = (data) => { let ary = [] - data.forEach(item => { let obj = {} - + // 根据当前教材ID 过滤出对应的单元、章节 if (item.rootid == curBookId.value) { - obj.label = item.itemtitle - obj.id = item.id - let ary2 = [] - evaluationList.value.forEach(el => { - let obj2 = {} - if (item.id == el.parentid) { - obj2 = { - label: el.itemtitle, - id: el.id + if(item.level == 1){ + obj.label = item.itemtitle + obj.id = item.id + obj.itemtitle = item.itemtitle + obj.edudegree = item.edudegree + obj.edustage = item.edustage + obj.edusubject = item.edusubject + let ary2 = [] + evaluationList.value.forEach(el => { + let obj2 = {} + if (item.id == el.parentid) { + obj2 = { + label: el.itemtitle, + id: el.id, + itemtitle : el.itemtitle, + edudegree : el.edudegree, + edustage : el.edustage, + edusubject : el.edusubject, + } + ary2.push(obj2) } - ary2.push(obj2) - } - obj.children = ary2 - }) - ary.push(obj) + obj.children = ary2 + }) + ary.push(obj) + } } }) return ary diff --git a/src/renderer/src/components/pdf/index copy.vue b/src/renderer/src/components/pdf/index copy.vue index 48d93e3..a1c3487 100644 --- a/src/renderer/src/components/pdf/index copy.vue +++ b/src/renderer/src/components/pdf/index copy.vue @@ -1,8 +1,12 @@ - @@ -180,4 +297,15 @@ watchEffect(() => { } } } +.a-fade-leave-active,.a-fade-enter-active{ + transition: all .3s; +} +.a-fade-enter-from,.a-fade-leave-to{ + width: 0; + opacity: 0; +} +.a-fade-enter-to,.a-fade-leave-from{ + width: 350px; + opacity: 1; +}