Compare commits
130 Commits
370396e2b8
...
c4c4bca54a
Author | SHA1 | Date |
---|---|---|
yangws | c4c4bca54a | |
lyc | cc1faa7f88 | |
lyc | 0b67ca30f8 | |
朱浩 | d690a4ae70 | |
朱浩 | 974aaf3522 | |
lyc | 524eedc451 | |
lyc | 9d38f715a1 | |
朱浩 | b4c9db619e | |
朱浩 | 7cf3487bfb | |
朱浩 | 74275ac327 | |
朱浩 | c29a3d7a77 | |
朱浩 | b8325d336b | |
朱浩 | c2876ff983 | |
zhengdegang | 6035803db1 | |
zdg | f9de8514ed | |
zdg | cc8ea2b043 | |
lyc | 9e0e2a41f2 | |
lyc | 3fcd0cdb21 | |
lyc | 815b2b15cb | |
lyc | eb17167feb | |
朱浩 | ae500c94ce | |
lyc | 258a99bc36 | |
朱浩 | e5845a6714 | |
朱浩 | 1c1ec2be71 | |
朱浩 | 572bdb5fec | |
朱浩 | 7974aeabe4 | |
zhangxuelin | c11b9765e6 | |
lyc | 0e34b6a1e9 | |
lyc | b10be40ddc | |
zhangxuelin | 964e99c186 | |
zhangxuelin | e1e9f171e0 | |
zhangxuelin | a8f9c828d7 | |
朱浩 | c56039270c | |
朱浩 | 32c64d073b | |
lyc | 1498b9bc9f | |
lyc | 9a8d0e61d5 | |
lyc | d4b24f8d71 | |
lyc | ccf4211f44 | |
朱浩 | aaa75df73b | |
lyc | 6ddd172bb9 | |
lyc | 00d23f4b31 | |
朱浩 | 54d9f6d782 | |
朱浩 | 5db876ebfa | |
zhengdegang | 6b1ec46360 | |
zdg | 8fc1339419 | |
zdg | 0c8af75e2a | |
zhengdegang | b8f2eb24e2 | |
zdg | 618618f736 | |
zdg | 43afd575de | |
zhengdegang | 8c2362ff4f | |
zhangxuelin | 4687d62b90 | |
zhangxuelin | 2a78cb40dc | |
zdg | d12b5bb29f | |
zdg | 5430cb8c50 | |
zdg | 6f890843ab | |
朱浩 | bd2024eed2 | |
朱浩 | 9f510187c0 | |
zhangxuelin | dca4eaae91 | |
zhangxuelin | d64122775a | |
zhangxuelin | 29ad68e5ab | |
zdg | 4e2512ed77 | |
zdg | c9d377aa74 | |
zdg | 7221a203e9 | |
朱浩 | 3bcb0a2ef3 | |
朱浩 | 9018abb673 | |
朱浩 | a55a662ce9 | |
朱浩 | 528e7876a9 | |
朱浩 | 82456a7f96 | |
zdg | 7dfa51f5dc | |
lyc | 74b0cf5fdb | |
lyc | df7658a17d | |
zhangxuelin | db496ee154 | |
lyc | 8a9fa83c04 | |
lyc | 9a6f37528c | |
zhangxuelin | a6fff59601 | |
zhangxuelin | 2a670c1447 | |
zhengdegang | d64b469ac8 | |
zdg | 19901d802f | |
zdg | 22a92ee84b | |
zdg | 8d558c47f5 | |
lyc | 9ab5f65409 | |
lyc | 95e9fc528a | |
lyc | e38235fd6b | |
lyc | 26c46e33c7 | |
lyc | ce274237dc | |
lyc | d46d312b37 | |
lyc | fb84977321 | |
zhangxuelin | 1d4cad94c1 | |
zhangxuelin | da78c7e1e8 | |
朱浩 | 2ef129d5c1 | |
朱浩 | 6b8c6c8c7f | |
zhengdegang | fa5e132c2d | |
zdg | 99c6fe85d4 | |
zdg | 05074f3145 | |
lyc | 34b35d8beb | |
lyc | 921ce6ecb0 | |
lyc | 1be862a0f2 | |
lyc | 9ef553f8eb | |
lyc | e53eab0c31 | |
朱浩 | 1a51af93f6 | |
朱浩 | ae4f7d3b49 | |
lyc | 0ba717de6e | |
lyc | 6e29229e95 | |
lyc | df84893509 | |
zhengdegang | f32dd848cc | |
zdg | bb02f92bb2 | |
zdg | dbf6b665a8 | |
朱浩 | 43d75f94dd | |
朱浩 | 62a5339d04 | |
朱浩 | 191ead45cb | |
朱浩 | cac9490256 | |
lyc | d46b01cca2 | |
lyc | 57aa718711 | |
lyc | 0d41a5a62f | |
lyc | 510258361f | |
zdg | ee53207d31 | |
zdg | 24c2540436 | |
zdg | e788174a3a | |
zdg | 89ef820366 | |
朱浩 | 4248fd4504 | |
朱浩 | 241b0d3f24 | |
朱浩 | ede8dfd533 | |
朱浩 | 8f54d17439 | |
朱浩 | 15f6b5dcbd | |
zdg | b4e7867922 | |
zdg | ba64ff931b | |
朱浩 | 955d3a6d7d | |
朱浩 | b7f45bbd33 | |
yangws | 8f3925f57a | |
lyc | 0eb047b3f2 |
|
@ -7,6 +7,8 @@ VITE_APP_ENV = 'development'
|
||||||
# AIx融合数字管理系统/开发环境
|
# AIx融合数字管理系统/开发环境
|
||||||
VITE_APP_BASE_API = '/dev-api'
|
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 = 'http://192.168.2.52:7863'
|
||||||
|
|
||||||
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
|
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
|
||||||
|
|
|
@ -5,13 +5,15 @@ VITE_APP_TITLE = AIx数字平台
|
||||||
VITE_APP_ENV = 'production'
|
VITE_APP_ENV = 'production'
|
||||||
|
|
||||||
# AIx融合数字管理系统/生产环境
|
# AIx融合数字管理系统/生产环境
|
||||||
VITE_APP_BASE_API = 'https://file.ysaix.com:7868/prod-api'
|
VITE_APP_BASE_API = 'https://prev.ysaix.com:7868/prod-api'
|
||||||
|
|
||||||
VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api'
|
VITE_APP_DOMAIN = 'prev.ysaix.com'
|
||||||
|
|
||||||
|
VITE_APP_UPLOAD_API = 'https://prev.ysaix.com:7868/prod-api'
|
||||||
|
|
||||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
VITE_BUILD_COMPRESS = gzip
|
VITE_BUILD_COMPRESS = gzip
|
||||||
|
|
||||||
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
|
VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/'
|
||||||
|
|
||||||
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'
|
VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# 页面标题
|
||||||
|
VITE_APP_TITLE = AIx数字平台
|
||||||
|
|
||||||
|
# 生产环境配置
|
||||||
|
VITE_APP_ENV = 'production'
|
||||||
|
|
||||||
|
# AIx融合数字管理系统/生产环境
|
||||||
|
VITE_APP_BASE_API = 'https://file.ysaix.com:7868/prod-api'
|
||||||
|
|
||||||
|
VITE_APP_DOMAIN = 'file.ysaix.com'
|
||||||
|
|
||||||
|
VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api'
|
||||||
|
|
||||||
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
|
VITE_BUILD_COMPRESS = gzip
|
||||||
|
|
||||||
|
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
|
||||||
|
|
||||||
|
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'
|
|
@ -0,0 +1,47 @@
|
||||||
|
appId: com.electron.app
|
||||||
|
productName: AIx
|
||||||
|
directories:
|
||||||
|
buildResources: build
|
||||||
|
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/**
|
||||||
|
win:
|
||||||
|
executableName: AIx
|
||||||
|
icon: resources/logo2.ico
|
||||||
|
nsis:
|
||||||
|
oneClick: false
|
||||||
|
allowToChangeInstallationDirectory: true
|
||||||
|
artifactName: ${name}-${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/smarttalk/
|
||||||
|
electronDownload:
|
||||||
|
mirror: https://npmmirror.com/mirrors/electron/
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "electron-app",
|
"name": "aix-win",
|
||||||
"version": "1.0.1",
|
"version": "1.0.6",
|
||||||
"description": "An Electron application with Vue",
|
"description": "An Electron application with Vue",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "example.com",
|
"author": "example.com",
|
||||||
|
@ -14,7 +14,8 @@
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"build:unpack": "npm run build && electron-builder --dir",
|
"build:unpack": "npm run build && electron-builder --dir",
|
||||||
"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": "npm run build && 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:mac": "npm run build && electron-builder --mac",
|
"build:mac": "npm run build && electron-builder --mac",
|
||||||
"build:linux": "npm run build && electron-builder --linux"
|
"build:linux": "npm run build && electron-builder --linux"
|
||||||
},
|
},
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
"electron-log": "^5.1.7",
|
"electron-log": "^5.1.7",
|
||||||
"electron-updater": "^6.1.7",
|
"electron-updater": "^6.1.7",
|
||||||
"element-plus": "^2.7.6",
|
"element-plus": "^2.7.6",
|
||||||
"fabric": "5.3.0",
|
"fabric": "^5.3.0",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"jsondiffpatch": "0.6.0",
|
"jsondiffpatch": "0.6.0",
|
||||||
|
|
|
@ -77,10 +77,10 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
||||||
console.error('Error uploading file:', err)
|
console.error('Error uploading file:', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 5000)
|
}, 20000)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 10000)
|
||||||
})
|
})
|
||||||
|
|
||||||
function getFileMD5(path) {
|
function getFileMD5(path) {
|
||||||
|
@ -201,7 +201,8 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
||||||
/*创建新的ppt文件*/
|
/*创建新的ppt文件*/
|
||||||
ipcMain.on('creat-file-default', (e, { name, uploadData, cookie }) => {
|
ipcMain.on('creat-file-default', (e, { name, uploadData, cookie }) => {
|
||||||
createFolder('tempFile').then(() => {
|
createFolder('tempFile').then(() => {
|
||||||
let path = appTempFilePath + name
|
let path = appTempFilePath + name.replace(/[\\/:*?"<>|]/, '')
|
||||||
|
console.log(path)
|
||||||
fs.writeFileSync(path, '', 'utf-8')
|
fs.writeFileSync(path, '', 'utf-8')
|
||||||
let fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
let fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
|
@ -237,7 +238,8 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
||||||
|
|
||||||
//下载文件
|
//下载文件
|
||||||
ipcMain.on('download-file-default', (e, { url, fileName }) => {
|
ipcMain.on('download-file-default', (e, { url, fileName }) => {
|
||||||
createFolder('selfFile').then(async () => {
|
createFolder('selfFile')
|
||||||
|
.then(async () => {
|
||||||
const browserWindow = BrowserWindow.getFocusedWindow()
|
const browserWindow = BrowserWindow.getFocusedWindow()
|
||||||
const id = await manager.download({
|
const id = await manager.download({
|
||||||
window: browserWindow,
|
window: browserWindow,
|
||||||
|
@ -248,7 +250,9 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
||||||
onDownloadStarted: async ({ id, item, webContents }) => {
|
onDownloadStarted: async ({ id, item, webContents }) => {
|
||||||
// Do something with the download id
|
// Do something with the download id
|
||||||
},
|
},
|
||||||
onDownloadProgress: async ({ id, item, percentCompleted }) => {},
|
onDownloadProgress: async ({ id, item, percentCompleted }) => {
|
||||||
|
e.reply('download-file-default-prog' + fileName, percentCompleted)
|
||||||
|
},
|
||||||
onDownloadCompleted: async ({ id, item }) => {
|
onDownloadCompleted: async ({ id, item }) => {
|
||||||
console.log('完成')
|
console.log('完成')
|
||||||
e.reply('download-file-default' + fileName, true)
|
e.reply('download-file-default' + fileName, true)
|
||||||
|
@ -268,6 +272,9 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
e.reply('download-file-default' + fileName, false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/**另存为...
|
/**另存为...
|
||||||
|
|
|
@ -31,7 +31,7 @@ function createLoginWindow() {
|
||||||
preload: join(__dirname, '../preload/index.js'),
|
preload: join(__dirname, '../preload/index.js'),
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false, // 沙箱取消
|
contextIsolation: false // 沙箱取消
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
loginWindow.type = 'login' // 唯一标识
|
loginWindow.type = 'login' // 唯一标识
|
||||||
|
@ -41,7 +41,7 @@ function createLoginWindow() {
|
||||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||||
loginWindow.loadURL('http://localhost:5173/#/login')
|
loginWindow.loadURL('http://localhost:5173/#/login')
|
||||||
} else {
|
} else {
|
||||||
loginWindow.loadFile(join(__dirname, '../renderer/index.html'), {hash: 'login'})
|
loginWindow.loadFile(join(__dirname, '../renderer/index.html'), { hash: 'login' })
|
||||||
updateInit(loginWindow)
|
updateInit(loginWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ function createMainWindow() {
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
// nodeIntegration: true,
|
// nodeIntegration: true,
|
||||||
nodeIntegration: true, // nodeApi调用
|
nodeIntegration: true, // nodeApi调用
|
||||||
contextIsolation: false, // 沙箱取消
|
contextIsolation: false // 沙箱取消
|
||||||
// webSecurity: false // 跨域关闭
|
// webSecurity: false // 跨域关闭
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -80,7 +80,8 @@ function createMainWindow() {
|
||||||
mainWindow.show()
|
mainWindow.show()
|
||||||
})
|
})
|
||||||
mainWindow.on('closed', () => {
|
mainWindow.on('closed', () => {
|
||||||
setTimeout(() => { // 延迟销毁
|
setTimeout(() => {
|
||||||
|
// 延迟销毁
|
||||||
mainWindow = null
|
mainWindow = null
|
||||||
}, 1000)
|
}, 1000)
|
||||||
// app.quit() // 主窗口关闭-结束所有进程
|
// app.quit() // 主窗口关闭-结束所有进程
|
||||||
|
@ -92,7 +93,7 @@ function createMainWindow() {
|
||||||
mainWindow.webContents.openDevTools()
|
mainWindow.webContents.openDevTools()
|
||||||
|
|
||||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] )
|
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||||
} else {
|
} else {
|
||||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||||
}
|
}
|
||||||
|
@ -102,14 +103,12 @@ function createMainWindow() {
|
||||||
remote.enable(mainWindow.webContents)
|
remote.enable(mainWindow.webContents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 打开外部链接窗口
|
||||||
// 作业窗口相关-开发中
|
let linkWin = {}
|
||||||
let linkWindow
|
|
||||||
async function createLinkWin(data) {
|
async function createLinkWin(data) {
|
||||||
if (linkWindow) return
|
if (linkWin[data.key]) return
|
||||||
linkWindow = new BrowserWindow({
|
|
||||||
width: 650,
|
linkWin[data.key] = new BrowserWindow({
|
||||||
height: 500,
|
|
||||||
show: false,
|
show: false,
|
||||||
frame: true,
|
frame: true,
|
||||||
maximizable: true,
|
maximizable: true,
|
||||||
|
@ -122,22 +121,27 @@ async function createLinkWin(data) {
|
||||||
contextIsolation: true
|
contextIsolation: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
linkWindow.type = 'link' // 唯一标识
|
linkWin[data.key].type = 'link' // 唯一标识
|
||||||
|
|
||||||
let cookieDetails = { ...data.cookieData }
|
let cookieDetails = { ...data.cookieData }
|
||||||
await linkWindow.webContents.session.cookies.set(cookieDetails).then(()=>{
|
await linkWin[data.key].webContents.session.cookies
|
||||||
console.log('Cookie is successful');
|
.set(cookieDetails)
|
||||||
}).catch( error =>{
|
.then(() => {
|
||||||
console.error('Cookie is error', error);
|
console.log('Cookie is successful')
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
linkWindow.loadURL(data.fullPath)
|
console.error('Cookie is error', error)
|
||||||
|
|
||||||
linkWindow.once('ready-to-show', () => {
|
|
||||||
linkWindow.show()
|
|
||||||
linkWindow.maximize()
|
|
||||||
})
|
})
|
||||||
linkWindow.on('closed', () => {
|
data.fullPath = data.fullPath.replaceAll('//', '/')
|
||||||
linkWindow = null
|
linkWin[data.key].loadURL(data.fullPath)
|
||||||
|
|
||||||
|
linkWin[data.key].once('ready-to-show', () => {
|
||||||
|
linkWin[data.key].show()
|
||||||
|
linkWin[data.key].maximize()
|
||||||
|
})
|
||||||
|
linkWin[data.key].on('closed', () => {
|
||||||
|
linkWin[data.key] = null
|
||||||
|
delete linkWin[data.key]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,9 +175,10 @@ app.on('ready', () => {
|
||||||
}
|
}
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
mainWindow.close() // 先发出这个关闭指令
|
mainWindow.close() // 先发出这个关闭指令
|
||||||
setTimeout(() => { //
|
setTimeout(() => {
|
||||||
|
//
|
||||||
mainWindow.destroy()
|
mainWindow.destroy()
|
||||||
}, 200);
|
}, 200)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -200,22 +205,14 @@ app.on('ready', () => {
|
||||||
ipcMain.on('openWindow', (e, data) => {
|
ipcMain.on('openWindow', (e, data) => {
|
||||||
createLinkWin(data)
|
createLinkWin(data)
|
||||||
})
|
})
|
||||||
// 新窗口创建-监听
|
// zdg: 消息监听
|
||||||
ipcMain.on('new-window', (e,data) => {
|
handleAll()
|
||||||
const { id, type } = data
|
|
||||||
const win = BrowserWindow.fromId(id)
|
|
||||||
win.type = type // 绑定独立标识
|
|
||||||
remote.enable(win.webContents) // 开启远程服务
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// 打开-登录窗口
|
// 打开-登录窗口
|
||||||
createLoginWindow()
|
createLoginWindow()
|
||||||
|
|
||||||
app.on('activate', function () {
|
app.on('activate', function () {
|
||||||
if (BrowserWindow.getAllWindows().length === 0) createLoginWindow()
|
if (BrowserWindow.getAllWindows().length === 0) createLoginWindow()
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Quit when all windows are closed, except on macOS. There, it's common
|
// Quit when all windows are closed, except on macOS. There, it's common
|
||||||
|
@ -226,3 +223,24 @@ app.on('window-all-closed', () => {
|
||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 监听全局事件
|
||||||
|
function handleAll() {
|
||||||
|
// 新窗口创建-监听
|
||||||
|
ipcMain.on('new-window', (e, data) => {
|
||||||
|
const { id, type } = data
|
||||||
|
const win = BrowserWindow.fromId(id)
|
||||||
|
win.type = type // 绑定独立标识
|
||||||
|
remote.enable(win.webContents) // 开启远程服务
|
||||||
|
})
|
||||||
|
// 用于监听-状态管理变化-同步所有窗口
|
||||||
|
ipcMain.handle('pinia-state-change', (e, storeName, jsonStr) => {
|
||||||
|
for(const curWin of BrowserWindow.getAllWindows()){
|
||||||
|
const id = curWin.webContents.id
|
||||||
|
const bool = id !== e.sender.id && !curWin.isDestroyed()
|
||||||
|
if (bool) { // 除了消息发送窗口和销毁的窗口 其他都发送
|
||||||
|
curWin.webContents.send('pinia-state-set', storeName, jsonStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Electron</title>
|
<title>AIx智慧教育</title>
|
||||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||||
<!-- <meta
|
<!-- <meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
||||||
/> -->
|
/> -->
|
||||||
<meta http-equiv="Content-Security-Policy" content="connect-src *; default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src * 'self' data: blob:" />
|
<meta http-equiv="Content-Security-Policy" content="connect-src *; default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * 'self' data: blob:" />
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
@ -130,3 +130,46 @@ export function addStudentmainByNameArray(data) {
|
||||||
data: data
|
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) {
|
||||||
|
return request({
|
||||||
|
url: '/smarttalk/classReserv/startClass',
|
||||||
|
method: 'get',
|
||||||
|
params: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function endClass(id) {
|
||||||
|
return request({
|
||||||
|
url: '/smarttalk/classReserv/endClass',
|
||||||
|
method: 'get',
|
||||||
|
params: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 新增pdf圈点勾画
|
||||||
|
export const addsmartBookMark = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/smarttalk/bookMark/addSmartBookMark',
|
||||||
|
method: 'post',
|
||||||
|
data:params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改pdf圈点勾画
|
||||||
|
export const updateSmartBookMarkContent = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/smarttalk/bookMark/updateSmartBookMarkContent',
|
||||||
|
method: 'post',
|
||||||
|
data:params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据书id获取pdf圈点勾画
|
||||||
|
export const getBookMarkById = (bookId) => {
|
||||||
|
return request({
|
||||||
|
url: '/smarttalk/bookMark/' + bookId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据id删除对应页数
|
||||||
|
export function deleteBookMark(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/smarttalk/bookMark/' + ids,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
|
@ -17,3 +17,29 @@ export function listEntpcourse(query) {
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增entpcourse
|
||||||
|
export function addEntpcourse(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcourse',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 布置作业
|
||||||
|
export function saveByClassWorkArray(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classwork/saveByClassWorkArray',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除classwork 作业
|
||||||
|
export function delClasswork(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classwork/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 2794390 */
|
font-family: "iconfont"; /* Project id 2794390 */
|
||||||
src: url('iconfont.woff2?t=1722323882803') format('woff2'),
|
src: url('iconfont.woff2?t=1723186593107') format('woff2'),
|
||||||
url('iconfont.woff?t=1722323882803') format('woff'),
|
url('iconfont.woff?t=1723186593107') format('woff'),
|
||||||
url('iconfont.ttf?t=1722323882803') format('truetype'),
|
url('iconfont.ttf?t=1723186593107') format('truetype'),
|
||||||
url('iconfont.svg?t=1722323882803#iconfont') format('svg');
|
url('iconfont.svg?t=1723186593107#iconfont') format('svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
|
@ -14,6 +14,486 @@
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-xiangzuo:before {
|
||||||
|
content: "\e64d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kechengziyuan1:before {
|
||||||
|
content: "\e647";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tubiaozhizuomobanyihuifu-:before {
|
||||||
|
content: "\e69d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-window-01:before {
|
||||||
|
content: "\e70b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-chuangkou-chuangkouhua:before {
|
||||||
|
content: "\e64b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-a-lujing13357:before {
|
||||||
|
content: "\e64c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-icon:before {
|
||||||
|
content: "\e640";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ke:before {
|
||||||
|
content: "\e641";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiaocaixuanze:before {
|
||||||
|
content: "\e642";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-organization-framework-line:before {
|
||||||
|
content: "\e9fe";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiaocai:before {
|
||||||
|
content: "\e67b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zaixiankaoshi:before {
|
||||||
|
content: "\e643";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-mubiaohuizhi:before {
|
||||||
|
content: "\e652";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tupushujuyuan:before {
|
||||||
|
content: "\e653";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-mubiao:before {
|
||||||
|
content: "\e723";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhishitupu:before {
|
||||||
|
content: "\e644";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tupu:before {
|
||||||
|
content: "\f48c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhongwenwenxian:before {
|
||||||
|
content: "\e645";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tupu1:before {
|
||||||
|
content: "\e952";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kuangjia:before {
|
||||||
|
content: "\e6ea";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wenxian:before {
|
||||||
|
content: "\e7b6";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tupu-01:before {
|
||||||
|
content: "\e679";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tupu2:before {
|
||||||
|
content: "\e69c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tupu3:before {
|
||||||
|
content: "\e6a7";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuzhikuangjia:before {
|
||||||
|
content: "\e646";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tupu4:before {
|
||||||
|
content: "\e6d5";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-a-kaoshi1:before {
|
||||||
|
content: "\eb13";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fankui:before {
|
||||||
|
content: "\e738";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tiku:before {
|
||||||
|
content: "\e621";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ldc-position:before {
|
||||||
|
content: "\e63a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-sucai:before {
|
||||||
|
content: "\e620";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-pengyou:before {
|
||||||
|
content: "\e61a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuoye:before {
|
||||||
|
content: "\e61c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiaoxuefenxi:before {
|
||||||
|
content: "\e605";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wenjianjia:before {
|
||||||
|
content: "\ec17";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiaoxueyanxiu:before {
|
||||||
|
content: "\e60d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiaoxuesheji:before {
|
||||||
|
content: "\e606";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhuanyeziyuanku:before {
|
||||||
|
content: "\e651";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-pengyouquan:before {
|
||||||
|
content: "\e616";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dangqianhuihua:before {
|
||||||
|
content: "\e675";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-yanjiushi:before {
|
||||||
|
content: "\e607";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-gongzuotai:before {
|
||||||
|
content: "\e676";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-lunwen:before {
|
||||||
|
content: "\e60e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-decheng_xianshangxuexi:before {
|
||||||
|
content: "\e624";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jitibeike-:before {
|
||||||
|
content: "\e65b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-keti:before {
|
||||||
|
content: "\e6fe";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-pengyouquan1:before {
|
||||||
|
content: "\e635";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-beike1:before {
|
||||||
|
content: "\e61b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fenxiang:before {
|
||||||
|
content: "\e611";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhucetianjiahaoyou:before {
|
||||||
|
content: "\e8ca";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-duoqudaojicheng:before {
|
||||||
|
content: "\e696";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-yewukaizhan:before {
|
||||||
|
content: "\e612";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fankui1:before {
|
||||||
|
content: "\e6fa";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shezhi:before {
|
||||||
|
content: "\e614";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-chayue:before {
|
||||||
|
content: "\e617";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-baogao:before {
|
||||||
|
content: "\e630";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xuekezuhe:before {
|
||||||
|
content: "\e625";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fenxiang1:before {
|
||||||
|
content: "\eb24";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tongzhizhongxin:before {
|
||||||
|
content: "\eb43";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiajia:before {
|
||||||
|
content: "\e618";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shengchanguochengguanli:before {
|
||||||
|
content: "\e62a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kejian:before {
|
||||||
|
content: "\e64a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-liulan:before {
|
||||||
|
content: "\e648";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fabiao:before {
|
||||||
|
content: "\e654";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kecheng:before {
|
||||||
|
content: "\e619";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-bianji1:before {
|
||||||
|
content: "\e61d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-pingjia:before {
|
||||||
|
content: "\e628";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zonghezhenduan:before {
|
||||||
|
content: "\e6a0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-banji:before {
|
||||||
|
content: "\e71e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tousuyujianyi:before {
|
||||||
|
content: "\e729";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiaoliu:before {
|
||||||
|
content: "\e6b8";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-pingyi:before {
|
||||||
|
content: "\e79a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xueqingfenxi:before {
|
||||||
|
content: "\e67a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-check:before {
|
||||||
|
content: "\e622";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-upload:before {
|
||||||
|
content: "\e634";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-taolun:before {
|
||||||
|
content: "\e61e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tujing:before {
|
||||||
|
content: "\e947";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zixun:before {
|
||||||
|
content: "\e6b3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-liulan1:before {
|
||||||
|
content: "\e6b4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-yaosu:before {
|
||||||
|
content: "\e68c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-baogao1:before {
|
||||||
|
content: "\e62e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shoucang:before {
|
||||||
|
content: "\e61f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shenhe:before {
|
||||||
|
content: "\e623";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-w_duoxuanti:before {
|
||||||
|
content: "\e677";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fenxi:before {
|
||||||
|
content: "\e76d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kanshu:before {
|
||||||
|
content: "\e626";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-qwe:before {
|
||||||
|
content: "\e627";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiancha:before {
|
||||||
|
content: "\e6c1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-qunzu:before {
|
||||||
|
content: "\e62b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuopin:before {
|
||||||
|
content: "\e6a9";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shuyi_jiaoxueguanli:before {
|
||||||
|
content: "\e678";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-liangsuan:before {
|
||||||
|
content: "\e657";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-business-report:before {
|
||||||
|
content: "\e880";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiangmupingshen:before {
|
||||||
|
content: "\e742";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiangmushenbao:before {
|
||||||
|
content: "\e743";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiajia1:before {
|
||||||
|
content: "\e62c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-a-fenxiang2:before {
|
||||||
|
content: "\e62f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-icon_kaoshifenxi:before {
|
||||||
|
content: "\e6d3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-a-ketangshilu1:before {
|
||||||
|
content: "\e631";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tubiao_moshileixingpeizhi:before {
|
||||||
|
content: "\e632";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiezuo:before {
|
||||||
|
content: "\e633";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-buzhi:before {
|
||||||
|
content: "\e636";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ziyuanfenxi:before {
|
||||||
|
content: "\e637";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shoucang1:before {
|
||||||
|
content: "\e638";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-iconku-zhuanqu-:before {
|
||||||
|
content: "\e649";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-PPT:before {
|
||||||
|
content: "\e639";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fabiaolunwen:before {
|
||||||
|
content: "\e772";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiezuo1:before {
|
||||||
|
content: "\e63b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fenxi1:before {
|
||||||
|
content: "\e63c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kechengziyuan:before {
|
||||||
|
content: "\e6e9";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-36zuoyepingtai:before {
|
||||||
|
content: "\e699";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiekebiaozhunbijishu:before {
|
||||||
|
content: "\e63d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xunzhang:before {
|
||||||
|
content: "\e63e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiaocaizhengding:before {
|
||||||
|
content: "\e6a4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xinzengmoxing:before {
|
||||||
|
content: "\e7b8";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-a-biaozhangxunzhangyingxiong:before {
|
||||||
|
content: "\e79d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xunzhang1:before {
|
||||||
|
content: "\e63f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-paizhao-xianxing:before {
|
||||||
|
content: "\e8d1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhuye2:before {
|
||||||
|
content: "\e604";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuoye2:before {
|
||||||
|
content: "\f48d";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-zuoye1:before {
|
.icon-zuoye1:before {
|
||||||
content: "\e610";
|
content: "\e610";
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,846 @@
|
||||||
"css_prefix_text": "icon-",
|
"css_prefix_text": "icon-",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "6176588",
|
||||||
|
"name": "向左",
|
||||||
|
"font_class": "xiangzuo",
|
||||||
|
"unicode": "e64d",
|
||||||
|
"unicode_decimal": 58957
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "3053509",
|
||||||
|
"name": "课程资源",
|
||||||
|
"font_class": "kechengziyuan1",
|
||||||
|
"unicode": "e647",
|
||||||
|
"unicode_decimal": 58951
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4427402",
|
||||||
|
"name": "窗口",
|
||||||
|
"font_class": "tubiaozhizuomobanyihuifu-",
|
||||||
|
"unicode": "e69d",
|
||||||
|
"unicode_decimal": 59037
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "18418603",
|
||||||
|
"name": "窗口",
|
||||||
|
"font_class": "window-01",
|
||||||
|
"unicode": "e70b",
|
||||||
|
"unicode_decimal": 59147
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "30970631",
|
||||||
|
"name": "窗口-窗口化",
|
||||||
|
"font_class": "chuangkou-chuangkouhua",
|
||||||
|
"unicode": "e64b",
|
||||||
|
"unicode_decimal": 58955
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41196567",
|
||||||
|
"name": "下课",
|
||||||
|
"font_class": "a-lujing13357",
|
||||||
|
"unicode": "e64c",
|
||||||
|
"unicode_decimal": 58956
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "680440",
|
||||||
|
"name": "教材",
|
||||||
|
"font_class": "icon",
|
||||||
|
"unicode": "e640",
|
||||||
|
"unicode_decimal": 58944
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "3069674",
|
||||||
|
"name": "课",
|
||||||
|
"font_class": "ke",
|
||||||
|
"unicode": "e641",
|
||||||
|
"unicode_decimal": 58945
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4287869",
|
||||||
|
"name": "教材选择",
|
||||||
|
"font_class": "jiaocaixuanze",
|
||||||
|
"unicode": "e642",
|
||||||
|
"unicode_decimal": 58946
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6235034",
|
||||||
|
"name": "组织框架",
|
||||||
|
"font_class": "organization-framework-line",
|
||||||
|
"unicode": "e9fe",
|
||||||
|
"unicode_decimal": 59902
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "10900222",
|
||||||
|
"name": "教材",
|
||||||
|
"font_class": "jiaocai",
|
||||||
|
"unicode": "e67b",
|
||||||
|
"unicode_decimal": 59003
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "12820192",
|
||||||
|
"name": "在线考试",
|
||||||
|
"font_class": "zaixiankaoshi",
|
||||||
|
"unicode": "e643",
|
||||||
|
"unicode_decimal": 58947
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "12975221",
|
||||||
|
"name": "目标绘制",
|
||||||
|
"font_class": "mubiaohuizhi",
|
||||||
|
"unicode": "e652",
|
||||||
|
"unicode_decimal": 58962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "15222476",
|
||||||
|
"name": "图谱数据源",
|
||||||
|
"font_class": "tupushujuyuan",
|
||||||
|
"unicode": "e653",
|
||||||
|
"unicode_decimal": 58963
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "18455976",
|
||||||
|
"name": "目标",
|
||||||
|
"font_class": "mubiao",
|
||||||
|
"unicode": "e723",
|
||||||
|
"unicode_decimal": 59171
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "22387410",
|
||||||
|
"name": "知识图谱",
|
||||||
|
"font_class": "zhishitupu",
|
||||||
|
"unicode": "e644",
|
||||||
|
"unicode_decimal": 58948
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "23514020",
|
||||||
|
"name": "图谱",
|
||||||
|
"font_class": "tupu",
|
||||||
|
"unicode": "f48c",
|
||||||
|
"unicode_decimal": 62604
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "24204139",
|
||||||
|
"name": "中文文献",
|
||||||
|
"font_class": "zhongwenwenxian",
|
||||||
|
"unicode": "e645",
|
||||||
|
"unicode_decimal": 58949
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "25426189",
|
||||||
|
"name": "图谱",
|
||||||
|
"font_class": "tupu1",
|
||||||
|
"unicode": "e952",
|
||||||
|
"unicode_decimal": 59730
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "25597826",
|
||||||
|
"name": "框架",
|
||||||
|
"font_class": "kuangjia",
|
||||||
|
"unicode": "e6ea",
|
||||||
|
"unicode_decimal": 59114
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "25843549",
|
||||||
|
"name": "文献",
|
||||||
|
"font_class": "wenxian",
|
||||||
|
"unicode": "e7b6",
|
||||||
|
"unicode_decimal": 59318
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "26904934",
|
||||||
|
"name": "122-图谱",
|
||||||
|
"font_class": "tupu-01",
|
||||||
|
"unicode": "e679",
|
||||||
|
"unicode_decimal": 59001
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "28551525",
|
||||||
|
"name": "图谱",
|
||||||
|
"font_class": "tupu2",
|
||||||
|
"unicode": "e69c",
|
||||||
|
"unicode_decimal": 59036
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "29570352",
|
||||||
|
"name": "图谱",
|
||||||
|
"font_class": "tupu3",
|
||||||
|
"unicode": "e6a7",
|
||||||
|
"unicode_decimal": 59047
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "31509204",
|
||||||
|
"name": "组织框架",
|
||||||
|
"font_class": "zuzhikuangjia",
|
||||||
|
"unicode": "e646",
|
||||||
|
"unicode_decimal": 58950
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "34748859",
|
||||||
|
"name": "图谱",
|
||||||
|
"font_class": "tupu4",
|
||||||
|
"unicode": "e6d5",
|
||||||
|
"unicode_decimal": 59093
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "35203463",
|
||||||
|
"name": "考试 (1)",
|
||||||
|
"font_class": "a-kaoshi1",
|
||||||
|
"unicode": "eb13",
|
||||||
|
"unicode_decimal": 60179
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "577336",
|
||||||
|
"name": "反馈",
|
||||||
|
"font_class": "fankui",
|
||||||
|
"unicode": "e738",
|
||||||
|
"unicode_decimal": 59192
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1447760",
|
||||||
|
"name": "题库",
|
||||||
|
"font_class": "tiku",
|
||||||
|
"unicode": "e621",
|
||||||
|
"unicode_decimal": 58913
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1837440",
|
||||||
|
"name": "ldc-position",
|
||||||
|
"font_class": "ldc-position",
|
||||||
|
"unicode": "e63a",
|
||||||
|
"unicode_decimal": 58938
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4138813",
|
||||||
|
"name": "素材",
|
||||||
|
"font_class": "sucai",
|
||||||
|
"unicode": "e620",
|
||||||
|
"unicode_decimal": 58912
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4166140",
|
||||||
|
"name": "朋友",
|
||||||
|
"font_class": "pengyou",
|
||||||
|
"unicode": "e61a",
|
||||||
|
"unicode_decimal": 58906
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4310077",
|
||||||
|
"name": "作业",
|
||||||
|
"font_class": "zuoye",
|
||||||
|
"unicode": "e61c",
|
||||||
|
"unicode_decimal": 58908
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4686996",
|
||||||
|
"name": "教学分析",
|
||||||
|
"font_class": "jiaoxuefenxi",
|
||||||
|
"unicode": "e605",
|
||||||
|
"unicode_decimal": 58885
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4893191",
|
||||||
|
"name": "文件夹",
|
||||||
|
"font_class": "wenjianjia",
|
||||||
|
"unicode": "ec17",
|
||||||
|
"unicode_decimal": 60439
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "5838892",
|
||||||
|
"name": "教学研修",
|
||||||
|
"font_class": "jiaoxueyanxiu",
|
||||||
|
"unicode": "e60d",
|
||||||
|
"unicode_decimal": 58893
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "5931005",
|
||||||
|
"name": "教学设计",
|
||||||
|
"font_class": "jiaoxuesheji",
|
||||||
|
"unicode": "e606",
|
||||||
|
"unicode_decimal": 58886
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6360457",
|
||||||
|
"name": "专业资源库",
|
||||||
|
"font_class": "zhuanyeziyuanku",
|
||||||
|
"unicode": "e651",
|
||||||
|
"unicode_decimal": 58961
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6556747",
|
||||||
|
"name": "朋友圈",
|
||||||
|
"font_class": "pengyouquan",
|
||||||
|
"unicode": "e616",
|
||||||
|
"unicode_decimal": 58902
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6992598",
|
||||||
|
"name": "当前会话",
|
||||||
|
"font_class": "dangqianhuihua",
|
||||||
|
"unicode": "e675",
|
||||||
|
"unicode_decimal": 58997
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7327280",
|
||||||
|
"name": "研究室",
|
||||||
|
"font_class": "yanjiushi",
|
||||||
|
"unicode": "e607",
|
||||||
|
"unicode_decimal": 58887
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7905547",
|
||||||
|
"name": "工作台",
|
||||||
|
"font_class": "gongzuotai",
|
||||||
|
"unicode": "e676",
|
||||||
|
"unicode_decimal": 58998
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "9023128",
|
||||||
|
"name": "论文",
|
||||||
|
"font_class": "lunwen",
|
||||||
|
"unicode": "e60e",
|
||||||
|
"unicode_decimal": 58894
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "10130330",
|
||||||
|
"name": "德诚_线上学习001",
|
||||||
|
"font_class": "decheng_xianshangxuexi",
|
||||||
|
"unicode": "e624",
|
||||||
|
"unicode_decimal": 58916
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "10493421",
|
||||||
|
"name": "集体备课",
|
||||||
|
"font_class": "jitibeike-",
|
||||||
|
"unicode": "e65b",
|
||||||
|
"unicode_decimal": 58971
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "13228302",
|
||||||
|
"name": "课题",
|
||||||
|
"font_class": "keti",
|
||||||
|
"unicode": "e6fe",
|
||||||
|
"unicode_decimal": 59134
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "16364339",
|
||||||
|
"name": "朋友圈",
|
||||||
|
"font_class": "pengyouquan1",
|
||||||
|
"unicode": "e635",
|
||||||
|
"unicode_decimal": 58933
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37075078",
|
||||||
|
"name": "备课",
|
||||||
|
"font_class": "beike1",
|
||||||
|
"unicode": "e61b",
|
||||||
|
"unicode_decimal": 58907
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1425581",
|
||||||
|
"name": "share",
|
||||||
|
"font_class": "fenxiang",
|
||||||
|
"unicode": "e611",
|
||||||
|
"unicode_decimal": 58897
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1727462",
|
||||||
|
"name": "227注册、添加好友",
|
||||||
|
"font_class": "zhucetianjiahaoyou",
|
||||||
|
"unicode": "e8ca",
|
||||||
|
"unicode_decimal": 59594
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "2071695",
|
||||||
|
"name": "场景灵活",
|
||||||
|
"font_class": "duoqudaojicheng",
|
||||||
|
"unicode": "e696",
|
||||||
|
"unicode_decimal": 59030
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "2077372",
|
||||||
|
"name": "业务开展",
|
||||||
|
"font_class": "yewukaizhan",
|
||||||
|
"unicode": "e612",
|
||||||
|
"unicode_decimal": 58898
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "2680657",
|
||||||
|
"name": "反馈",
|
||||||
|
"font_class": "fankui1",
|
||||||
|
"unicode": "e6fa",
|
||||||
|
"unicode_decimal": 59130
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4520331",
|
||||||
|
"name": "设置",
|
||||||
|
"font_class": "shezhi",
|
||||||
|
"unicode": "e614",
|
||||||
|
"unicode_decimal": 58900
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4572456",
|
||||||
|
"name": "查阅",
|
||||||
|
"font_class": "chayue",
|
||||||
|
"unicode": "e617",
|
||||||
|
"unicode_decimal": 58903
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4662757",
|
||||||
|
"name": "报告",
|
||||||
|
"font_class": "baogao",
|
||||||
|
"unicode": "e630",
|
||||||
|
"unicode_decimal": 58928
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "5062867",
|
||||||
|
"name": "学科组合",
|
||||||
|
"font_class": "xuekezuhe",
|
||||||
|
"unicode": "e625",
|
||||||
|
"unicode_decimal": 58917
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "5387651",
|
||||||
|
"name": "分享",
|
||||||
|
"font_class": "fenxiang1",
|
||||||
|
"unicode": "eb24",
|
||||||
|
"unicode_decimal": 60196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "5387730",
|
||||||
|
"name": "通知中心",
|
||||||
|
"font_class": "tongzhizhongxin",
|
||||||
|
"unicode": "eb43",
|
||||||
|
"unicode_decimal": 60227
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "5643535",
|
||||||
|
"name": "下架",
|
||||||
|
"font_class": "xiajia",
|
||||||
|
"unicode": "e618",
|
||||||
|
"unicode_decimal": 58904
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6241672",
|
||||||
|
"name": "生产过程管理",
|
||||||
|
"font_class": "shengchanguochengguanli",
|
||||||
|
"unicode": "e62a",
|
||||||
|
"unicode_decimal": 58922
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6286025",
|
||||||
|
"name": "课件",
|
||||||
|
"font_class": "kejian",
|
||||||
|
"unicode": "e64a",
|
||||||
|
"unicode_decimal": 58954
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6447187",
|
||||||
|
"name": "浏览",
|
||||||
|
"font_class": "liulan",
|
||||||
|
"unicode": "e648",
|
||||||
|
"unicode_decimal": 58952
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6548533",
|
||||||
|
"name": "发表",
|
||||||
|
"font_class": "fabiao",
|
||||||
|
"unicode": "e654",
|
||||||
|
"unicode_decimal": 58964
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6560965",
|
||||||
|
"name": "课程",
|
||||||
|
"font_class": "kecheng",
|
||||||
|
"unicode": "e619",
|
||||||
|
"unicode_decimal": 58905
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6682548",
|
||||||
|
"name": "编辑",
|
||||||
|
"font_class": "bianji1",
|
||||||
|
"unicode": "e61d",
|
||||||
|
"unicode_decimal": 58909
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7291078",
|
||||||
|
"name": "评价",
|
||||||
|
"font_class": "pingjia",
|
||||||
|
"unicode": "e628",
|
||||||
|
"unicode_decimal": 58920
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7339813",
|
||||||
|
"name": "综合诊断",
|
||||||
|
"font_class": "zonghezhenduan",
|
||||||
|
"unicode": "e6a0",
|
||||||
|
"unicode_decimal": 59040
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7712190",
|
||||||
|
"name": "班级",
|
||||||
|
"font_class": "banji",
|
||||||
|
"unicode": "e71e",
|
||||||
|
"unicode_decimal": 59166
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7712222",
|
||||||
|
"name": "投诉与建议",
|
||||||
|
"font_class": "tousuyujianyi",
|
||||||
|
"unicode": "e729",
|
||||||
|
"unicode_decimal": 59177
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "8136353",
|
||||||
|
"name": "交流",
|
||||||
|
"font_class": "jiaoliu",
|
||||||
|
"unicode": "e6b8",
|
||||||
|
"unicode_decimal": 59064
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "8198365",
|
||||||
|
"name": "评议",
|
||||||
|
"font_class": "pingyi",
|
||||||
|
"unicode": "e79a",
|
||||||
|
"unicode_decimal": 59290
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "8225912",
|
||||||
|
"name": "学情分析",
|
||||||
|
"font_class": "xueqingfenxi",
|
||||||
|
"unicode": "e67a",
|
||||||
|
"unicode_decimal": 59002
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "9922803",
|
||||||
|
"name": "check",
|
||||||
|
"font_class": "check",
|
||||||
|
"unicode": "e622",
|
||||||
|
"unicode_decimal": 58914
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "9927174",
|
||||||
|
"name": "upload",
|
||||||
|
"font_class": "upload",
|
||||||
|
"unicode": "e634",
|
||||||
|
"unicode_decimal": 58932
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "10360972",
|
||||||
|
"name": "讨论",
|
||||||
|
"font_class": "taolun",
|
||||||
|
"unicode": "e61e",
|
||||||
|
"unicode_decimal": 58910
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "10373863",
|
||||||
|
"name": "途径",
|
||||||
|
"font_class": "tujing",
|
||||||
|
"unicode": "e947",
|
||||||
|
"unicode_decimal": 59719
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "11594397",
|
||||||
|
"name": "咨询",
|
||||||
|
"font_class": "zixun",
|
||||||
|
"unicode": "e6b3",
|
||||||
|
"unicode_decimal": 59059
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "11594406",
|
||||||
|
"name": "浏览",
|
||||||
|
"font_class": "liulan1",
|
||||||
|
"unicode": "e6b4",
|
||||||
|
"unicode_decimal": 59060
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "11677828",
|
||||||
|
"name": "要素",
|
||||||
|
"font_class": "yaosu",
|
||||||
|
"unicode": "e68c",
|
||||||
|
"unicode_decimal": 59020
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "11810655",
|
||||||
|
"name": "报告",
|
||||||
|
"font_class": "baogao1",
|
||||||
|
"unicode": "e62e",
|
||||||
|
"unicode_decimal": 58926
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "12476912",
|
||||||
|
"name": "收藏",
|
||||||
|
"font_class": "shoucang",
|
||||||
|
"unicode": "e61f",
|
||||||
|
"unicode_decimal": 58911
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "12771482",
|
||||||
|
"name": "审核",
|
||||||
|
"font_class": "shenhe",
|
||||||
|
"unicode": "e623",
|
||||||
|
"unicode_decimal": 58915
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "12842682",
|
||||||
|
"name": "w_多选题",
|
||||||
|
"font_class": "w_duoxuanti",
|
||||||
|
"unicode": "e677",
|
||||||
|
"unicode_decimal": 58999
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "14095222",
|
||||||
|
"name": "分析",
|
||||||
|
"font_class": "fenxi",
|
||||||
|
"unicode": "e76d",
|
||||||
|
"unicode_decimal": 59245
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "14898108",
|
||||||
|
"name": "kanshu",
|
||||||
|
"font_class": "kanshu",
|
||||||
|
"unicode": "e626",
|
||||||
|
"unicode_decimal": 58918
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "15053387",
|
||||||
|
"name": "上传",
|
||||||
|
"font_class": "qwe",
|
||||||
|
"unicode": "e627",
|
||||||
|
"unicode_decimal": 58919
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "15644340",
|
||||||
|
"name": "检查",
|
||||||
|
"font_class": "jiancha",
|
||||||
|
"unicode": "e6c1",
|
||||||
|
"unicode_decimal": 59073
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "17334540",
|
||||||
|
"name": "群组",
|
||||||
|
"font_class": "qunzu",
|
||||||
|
"unicode": "e62b",
|
||||||
|
"unicode_decimal": 58923
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "17335274",
|
||||||
|
"name": "作品",
|
||||||
|
"font_class": "zuopin",
|
||||||
|
"unicode": "e6a9",
|
||||||
|
"unicode_decimal": 59049
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "22779610",
|
||||||
|
"name": "教学管理",
|
||||||
|
"font_class": "shuyi_jiaoxueguanli",
|
||||||
|
"unicode": "e678",
|
||||||
|
"unicode_decimal": 59000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "27538561",
|
||||||
|
"name": "liangsuan",
|
||||||
|
"font_class": "liangsuan",
|
||||||
|
"unicode": "e657",
|
||||||
|
"unicode_decimal": 58967
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "27787858",
|
||||||
|
"name": "business-report",
|
||||||
|
"font_class": "business-report",
|
||||||
|
"unicode": "e880",
|
||||||
|
"unicode_decimal": 59520
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "28627465",
|
||||||
|
"name": "项目评审",
|
||||||
|
"font_class": "xiangmupingshen",
|
||||||
|
"unicode": "e742",
|
||||||
|
"unicode_decimal": 59202
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "28627468",
|
||||||
|
"name": "项目申报",
|
||||||
|
"font_class": "xiangmushenbao",
|
||||||
|
"unicode": "e743",
|
||||||
|
"unicode_decimal": 59203
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "33848036",
|
||||||
|
"name": "下架",
|
||||||
|
"font_class": "xiajia1",
|
||||||
|
"unicode": "e62c",
|
||||||
|
"unicode_decimal": 58924
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "33987020",
|
||||||
|
"name": "分享 2",
|
||||||
|
"font_class": "a-fenxiang2",
|
||||||
|
"unicode": "e62f",
|
||||||
|
"unicode_decimal": 58927
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37863983",
|
||||||
|
"name": "考试分析",
|
||||||
|
"font_class": "icon_kaoshifenxi",
|
||||||
|
"unicode": "e6d3",
|
||||||
|
"unicode_decimal": 59091
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37966293",
|
||||||
|
"name": "课堂实录 1",
|
||||||
|
"font_class": "a-ketangshilu1",
|
||||||
|
"unicode": "e631",
|
||||||
|
"unicode_decimal": 58929
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "38963985",
|
||||||
|
"name": "图标_模式类型配置",
|
||||||
|
"font_class": "tubiao_moshileixingpeizhi",
|
||||||
|
"unicode": "e632",
|
||||||
|
"unicode_decimal": 58930
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "39005401",
|
||||||
|
"name": "写作-copy",
|
||||||
|
"font_class": "xiezuo",
|
||||||
|
"unicode": "e633",
|
||||||
|
"unicode_decimal": 58931
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "39313770",
|
||||||
|
"name": "布置-copy",
|
||||||
|
"font_class": "buzhi",
|
||||||
|
"unicode": "e636",
|
||||||
|
"unicode_decimal": 58934
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "39506557",
|
||||||
|
"name": "资源分析",
|
||||||
|
"font_class": "ziyuanfenxi",
|
||||||
|
"unicode": "e637",
|
||||||
|
"unicode_decimal": 58935
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "3267408",
|
||||||
|
"name": "收藏",
|
||||||
|
"font_class": "shoucang1",
|
||||||
|
"unicode": "e638",
|
||||||
|
"unicode_decimal": 58936
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "3161194",
|
||||||
|
"name": "题目数量",
|
||||||
|
"font_class": "iconku-zhuanqu-",
|
||||||
|
"unicode": "e649",
|
||||||
|
"unicode_decimal": 58953
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "4349654",
|
||||||
|
"name": "PPT",
|
||||||
|
"font_class": "PPT",
|
||||||
|
"unicode": "e639",
|
||||||
|
"unicode_decimal": 58937
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "5708257",
|
||||||
|
"name": "发表论文",
|
||||||
|
"font_class": "fabiaolunwen",
|
||||||
|
"unicode": "e772",
|
||||||
|
"unicode_decimal": 59250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6837777",
|
||||||
|
"name": "写作",
|
||||||
|
"font_class": "xiezuo1",
|
||||||
|
"unicode": "e63b",
|
||||||
|
"unicode_decimal": 58939
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7171145",
|
||||||
|
"name": "粮食动态决策分析系统",
|
||||||
|
"font_class": "fenxi1",
|
||||||
|
"unicode": "e63c",
|
||||||
|
"unicode_decimal": 58940
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "12252416",
|
||||||
|
"name": "课程资源",
|
||||||
|
"font_class": "kechengziyuan",
|
||||||
|
"unicode": "e6e9",
|
||||||
|
"unicode_decimal": 59113
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "19567416",
|
||||||
|
"name": "作业平台",
|
||||||
|
"font_class": "36zuoyepingtai",
|
||||||
|
"unicode": "e699",
|
||||||
|
"unicode_decimal": 59033
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "975544",
|
||||||
|
"name": "结课标准-笔记数",
|
||||||
|
"font_class": "jiekebiaozhunbijishu",
|
||||||
|
"unicode": "e63d",
|
||||||
|
"unicode_decimal": 58941
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "7086494",
|
||||||
|
"name": "勋章",
|
||||||
|
"font_class": "xunzhang",
|
||||||
|
"unicode": "e63e",
|
||||||
|
"unicode_decimal": 58942
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "12573415",
|
||||||
|
"name": "教材征订",
|
||||||
|
"font_class": "jiaocaizhengding",
|
||||||
|
"unicode": "e6a4",
|
||||||
|
"unicode_decimal": 59044
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "35789485",
|
||||||
|
"name": "新增模型",
|
||||||
|
"font_class": "xinzengmoxing",
|
||||||
|
"unicode": "e7b8",
|
||||||
|
"unicode_decimal": 59320
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "39690914",
|
||||||
|
"name": "表彰勋章英雄",
|
||||||
|
"font_class": "a-biaozhangxunzhangyingxiong",
|
||||||
|
"unicode": "e79d",
|
||||||
|
"unicode_decimal": 59293
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "39798902",
|
||||||
|
"name": "勋章",
|
||||||
|
"font_class": "xunzhang1",
|
||||||
|
"unicode": "e63f",
|
||||||
|
"unicode_decimal": 58943
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1727478",
|
||||||
|
"name": "237拍照-线性",
|
||||||
|
"font_class": "paizhao-xianxing",
|
||||||
|
"unicode": "e8d1",
|
||||||
|
"unicode_decimal": 59601
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "1123930",
|
||||||
|
"name": "主页未选",
|
||||||
|
"font_class": "zhuye2",
|
||||||
|
"unicode": "e604",
|
||||||
|
"unicode_decimal": 58884
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41372607",
|
||||||
|
"name": "作业-2",
|
||||||
|
"font_class": "zuoye2",
|
||||||
|
"unicode": "f48d",
|
||||||
|
"unicode_decimal": 62605
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "8023423",
|
"icon_id": "8023423",
|
||||||
"name": "作业",
|
"name": "作业",
|
||||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 262 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,353 +0,0 @@
|
||||||
@font-face {
|
|
||||||
font-family: "iconfont"; /* Project id 4581405 */
|
|
||||||
/* Color fonts */
|
|
||||||
src:
|
|
||||||
url('iconfont.woff2?t=1718185631014') format('woff2'),
|
|
||||||
url('iconfont.woff?t=1718185631014') format('woff'),
|
|
||||||
url('iconfont.ttf?t=1718185631014') format('truetype');
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
font-family: "iconfont" !important;
|
|
||||||
font-size: 16px;
|
|
||||||
font-style: normal;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-iconku-zhuanqu-:before {
|
|
||||||
content: "\e649";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-PPT:before {
|
|
||||||
content: "\e62b";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fabiaolunwen:before {
|
|
||||||
content: "\e772";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiezuo1:before {
|
|
||||||
content: "\e612";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fenxi1:before {
|
|
||||||
content: "\e627";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-kechengziyuan:before {
|
|
||||||
content: "\e6e9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-36zuoyepingtai:before {
|
|
||||||
content: "\e699";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shoucang1:before {
|
|
||||||
content: "\e617";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fenxiang:before {
|
|
||||||
content: "\e60a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-zhucetianjiahaoyou:before {
|
|
||||||
content: "\e8ca";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-duoqudaojicheng:before {
|
|
||||||
content: "\e695";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-yewukaizhan:before {
|
|
||||||
content: "\e602";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fankui1:before {
|
|
||||||
content: "\e6fa";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shezhi:before {
|
|
||||||
content: "\e60c";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-chayue:before {
|
|
||||||
content: "\e605";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-baogao:before {
|
|
||||||
content: "\e630";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xuekezuhe:before {
|
|
||||||
content: "\e625";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fenxiang1:before {
|
|
||||||
content: "\eb24";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-tongzhizhongxin:before {
|
|
||||||
content: "\eb43";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiajia:before {
|
|
||||||
content: "\e60f";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shengchanguochengguanli:before {
|
|
||||||
content: "\e629";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-kejian:before {
|
|
||||||
content: "\e64a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-liulan:before {
|
|
||||||
content: "\e648";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fabiao:before {
|
|
||||||
content: "\e654";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-kecheng:before {
|
|
||||||
content: "\e609";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-bianji:before {
|
|
||||||
content: "\e608";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-pingjia:before {
|
|
||||||
content: "\e628";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-zonghezhenduan:before {
|
|
||||||
content: "\e6a0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-banji:before {
|
|
||||||
content: "\e71e";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-tousuyujianyi:before {
|
|
||||||
content: "\e729";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-jiaoliu:before {
|
|
||||||
content: "\e6b8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-pingyi:before {
|
|
||||||
content: "\e79a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xueqingfenxi:before {
|
|
||||||
content: "\e67a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-check:before {
|
|
||||||
content: "\e622";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-upload:before {
|
|
||||||
content: "\e634";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-taolun:before {
|
|
||||||
content: "\e61e";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-tujing:before {
|
|
||||||
content: "\e947";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-zixun:before {
|
|
||||||
content: "\e6b3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-liulan1:before {
|
|
||||||
content: "\e6b4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-yaosu:before {
|
|
||||||
content: "\e68c";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-baogao1:before {
|
|
||||||
content: "\e62d";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shoucang:before {
|
|
||||||
content: "\e60d";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shenhe:before {
|
|
||||||
content: "\e616";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-w_duoxuanti:before {
|
|
||||||
content: "\e669";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fenxi:before {
|
|
||||||
content: "\e76d";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-kanshu:before {
|
|
||||||
content: "\e614";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-qwe:before {
|
|
||||||
content: "\e603";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-jiancha:before {
|
|
||||||
content: "\e6c1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-qunzu:before {
|
|
||||||
content: "\e606";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-zuopin:before {
|
|
||||||
content: "\e6a9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shuyi_jiaoxueguanli:before {
|
|
||||||
content: "\e65f";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-liangsuan:before {
|
|
||||||
content: "\e657";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-business-report:before {
|
|
||||||
content: "\e880";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiangmupingshen:before {
|
|
||||||
content: "\e742";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiangmushenbao:before {
|
|
||||||
content: "\e743";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiajia1:before {
|
|
||||||
content: "\e623";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-a-fenxiang2:before {
|
|
||||||
content: "\e626";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-icon_kaoshifenxi:before {
|
|
||||||
content: "\e6d3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-a-ketangshilu1:before {
|
|
||||||
content: "\e610";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-tubiao_moshileixingpeizhi:before {
|
|
||||||
content: "\e62a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiezuo:before {
|
|
||||||
content: "\e611";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-buzhi:before {
|
|
||||||
content: "\e607";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-ziyuanfenxi:before {
|
|
||||||
content: "\e631";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fankui:before {
|
|
||||||
content: "\e738";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-tiku:before {
|
|
||||||
content: "\e621";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-ldc-position:before {
|
|
||||||
content: "\e63a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-sucai:before {
|
|
||||||
content: "\e620";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-pengyou:before {
|
|
||||||
content: "\e61a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-zuoye:before {
|
|
||||||
content: "\e61c";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-jiaoxuefenxi:before {
|
|
||||||
content: "\e600";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-wenjianjia:before {
|
|
||||||
content: "\ec17";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-jiaoxueyanxiu:before {
|
|
||||||
content: "\e60b";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-jiaoxuesheji:before {
|
|
||||||
content: "\e604";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-zhuanyeziyuanku:before {
|
|
||||||
content: "\e64f";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-pengyouquan:before {
|
|
||||||
content: "\e615";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-dangqianhuihua:before {
|
|
||||||
content: "\e663";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-yanjiushi:before {
|
|
||||||
content: "\e601";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-gongzuotai:before {
|
|
||||||
content: "\e66b";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-lunwen:before {
|
|
||||||
content: "\e60e";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-decheng_xianshangxuexi:before {
|
|
||||||
content: "\e624";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-jitibeike-:before {
|
|
||||||
content: "\e65b";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-keti:before {
|
|
||||||
content: "\e6fe";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-pengyouquan1:before {
|
|
||||||
content: "\e635";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-beike:before {
|
|
||||||
content: "\e61b";
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,156 +0,0 @@
|
||||||
{
|
|
||||||
"id": "4581405",
|
|
||||||
"name": "AIX教师端",
|
|
||||||
"font_family": "iconfont",
|
|
||||||
"css_prefix_text": "icon-",
|
|
||||||
"description": "",
|
|
||||||
"glyphs": [
|
|
||||||
{
|
|
||||||
"icon_id": "577336",
|
|
||||||
"name": "反馈",
|
|
||||||
"font_class": "fankui",
|
|
||||||
"unicode": "e738",
|
|
||||||
"unicode_decimal": 59192
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "1447760",
|
|
||||||
"name": "题库",
|
|
||||||
"font_class": "tiku",
|
|
||||||
"unicode": "e621",
|
|
||||||
"unicode_decimal": 58913
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "1837440",
|
|
||||||
"name": "ldc-position",
|
|
||||||
"font_class": "ldc-position",
|
|
||||||
"unicode": "e63a",
|
|
||||||
"unicode_decimal": 58938
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "4138813",
|
|
||||||
"name": "素材",
|
|
||||||
"font_class": "sucai",
|
|
||||||
"unicode": "e620",
|
|
||||||
"unicode_decimal": 58912
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "4166140",
|
|
||||||
"name": "朋友",
|
|
||||||
"font_class": "pengyou",
|
|
||||||
"unicode": "e61a",
|
|
||||||
"unicode_decimal": 58906
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "4310077",
|
|
||||||
"name": "作业",
|
|
||||||
"font_class": "zuoye",
|
|
||||||
"unicode": "e61c",
|
|
||||||
"unicode_decimal": 58908
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "4686996",
|
|
||||||
"name": "教学分析",
|
|
||||||
"font_class": "jiaoxuefenxi",
|
|
||||||
"unicode": "e600",
|
|
||||||
"unicode_decimal": 58880
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "4893191",
|
|
||||||
"name": "文件夹",
|
|
||||||
"font_class": "wenjianjia",
|
|
||||||
"unicode": "ec17",
|
|
||||||
"unicode_decimal": 60439
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "5838892",
|
|
||||||
"name": "教学研修",
|
|
||||||
"font_class": "jiaoxueyanxiu",
|
|
||||||
"unicode": "e60b",
|
|
||||||
"unicode_decimal": 58891
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "5931005",
|
|
||||||
"name": "教学设计",
|
|
||||||
"font_class": "jiaoxuesheji",
|
|
||||||
"unicode": "e604",
|
|
||||||
"unicode_decimal": 58884
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "6360457",
|
|
||||||
"name": "专业资源库",
|
|
||||||
"font_class": "zhuanyeziyuanku",
|
|
||||||
"unicode": "e64f",
|
|
||||||
"unicode_decimal": 58959
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "6556747",
|
|
||||||
"name": "朋友圈",
|
|
||||||
"font_class": "pengyouquan",
|
|
||||||
"unicode": "e615",
|
|
||||||
"unicode_decimal": 58901
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "6992598",
|
|
||||||
"name": "当前会话",
|
|
||||||
"font_class": "dangqianhuihua",
|
|
||||||
"unicode": "e663",
|
|
||||||
"unicode_decimal": 58979
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "7327280",
|
|
||||||
"name": "研究室",
|
|
||||||
"font_class": "yanjiushi",
|
|
||||||
"unicode": "e601",
|
|
||||||
"unicode_decimal": 58881
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "7905547",
|
|
||||||
"name": "工作台",
|
|
||||||
"font_class": "gongzuotai",
|
|
||||||
"unicode": "e66b",
|
|
||||||
"unicode_decimal": 58987
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "9023128",
|
|
||||||
"name": "论文",
|
|
||||||
"font_class": "lunwen",
|
|
||||||
"unicode": "e60e",
|
|
||||||
"unicode_decimal": 58894
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "10130330",
|
|
||||||
"name": "德诚_线上学习001",
|
|
||||||
"font_class": "decheng_xianshangxuexi",
|
|
||||||
"unicode": "e624",
|
|
||||||
"unicode_decimal": 58916
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "10493421",
|
|
||||||
"name": "集体备课",
|
|
||||||
"font_class": "jitibeike-",
|
|
||||||
"unicode": "e65b",
|
|
||||||
"unicode_decimal": 58971
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "13228302",
|
|
||||||
"name": "课题",
|
|
||||||
"font_class": "keti",
|
|
||||||
"unicode": "e6fe",
|
|
||||||
"unicode_decimal": 59134
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "16364339",
|
|
||||||
"name": "朋友圈",
|
|
||||||
"font_class": "pengyouquan1",
|
|
||||||
"unicode": "e635",
|
|
||||||
"unicode_decimal": 58933
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37075078",
|
|
||||||
"name": "备课",
|
|
||||||
"font_class": "beike",
|
|
||||||
"unicode": "e61b",
|
|
||||||
"unicode_decimal": 58907
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -5,9 +5,9 @@
|
||||||
<span>{{ curBookName }}</span>
|
<span>{{ curBookName }}</span>
|
||||||
<i class="iconfont icon-xiangyou"></i>
|
<i class="iconfont icon-xiangyou"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="book-list">
|
<div class="book-list" v-loading="treeLoading">
|
||||||
<el-tree ref="refTree" :data="treeData" :props="defaultProps" node-key="id"
|
<el-tree ref="refTree" :data="treeData" :props="defaultProps" node-key="id"
|
||||||
:default-expanded-keys="defaultExpandedKeys" :current-node-key="currentNode" highlight-current
|
:default-expanded-keys="defaultExpandedKeys" :current-node-key="currentNodeId" highlight-current
|
||||||
@node-click="handleNodeClick">
|
@node-click="handleNodeClick">
|
||||||
<template #default="{ node }">
|
<template #default="{ node }">
|
||||||
<span :title="node.label" class="tree-label">{{ node.label }}</span>
|
<span :title="node.label" class="tree-label">{{ node.label }}</span>
|
||||||
|
@ -30,7 +30,10 @@
|
||||||
<el-scrollbar height="450px">
|
<el-scrollbar height="450px">
|
||||||
<div class="textbook-item flex" v-for="item in subjectList" :class="curBookId == item.id ? 'active-item' : ''"
|
<div class="textbook-item flex" v-for="item in subjectList" :class="curBookId == item.id ? 'active-item' : ''"
|
||||||
:key="item.id" @click="changeBook(item)">
|
:key="item.id" @click="changeBook(item)">
|
||||||
<img :src="BaseUrl + item.avartar" class="textbook-img" alt="">
|
<img v-if="item.avartar" :src="BaseUrl + item.avartar" class="textbook-img" alt="">
|
||||||
|
<div v-else class="textbook-img">
|
||||||
|
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
|
||||||
|
</div>
|
||||||
<span class="book-name">{{ item.itemtitle }}</span>
|
<span class="book-name">{{ item.itemtitle }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
@ -39,7 +42,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, nextTick, toRaw } from 'vue';
|
import { onMounted, ref, nextTick, toRaw, reactive,watch } from 'vue';
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { listEvaluation } from '@/api/subject'
|
import { listEvaluation } from '@/api/subject'
|
||||||
|
|
||||||
|
@ -60,18 +63,26 @@ const defaultProps = {
|
||||||
label: 'label',
|
label: 'label',
|
||||||
class: 'textbook-tree'
|
class: 'textbook-tree'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const treeLoading = ref(false)
|
||||||
//当前教材ID
|
//当前教材ID
|
||||||
const curBookId = ref(-1)
|
const curBookId = ref(-1)
|
||||||
//当前教材名称
|
//当前教材名称
|
||||||
const curBookName = ref('')
|
const curBookName = ref('')
|
||||||
//当前教材封面图
|
//当前教材封面图
|
||||||
const curBookImg = ref('')
|
const curBookImg = ref('')
|
||||||
|
//当前教材文件路径
|
||||||
|
const curBookPath = ref('')
|
||||||
// 上册
|
// 上册
|
||||||
const volumeOne = ref([])
|
const volumeOne = ref([])
|
||||||
// 下册
|
// 下册
|
||||||
const volumeTwo = ref([])
|
const volumeTwo = ref([])
|
||||||
|
// 当前节点
|
||||||
|
const currentNode = reactive({
|
||||||
|
data:{}
|
||||||
|
})
|
||||||
// 当前选中的节点ID
|
// 当前选中的节点ID
|
||||||
const currentNode = ref(0)
|
const currentNodeId = ref(0)
|
||||||
// 当前选中的节点名称
|
// 当前选中的节点名称
|
||||||
const currentNodeName = ref('')
|
const currentNodeName = ref('')
|
||||||
// 默认展开的节点
|
// 默认展开的节点
|
||||||
|
@ -82,6 +93,7 @@ const refTree = ref(null)
|
||||||
|
|
||||||
//获取教材下面的单元内容
|
//获取教材下面的单元内容
|
||||||
const getSubjectContent = async () => {
|
const getSubjectContent = async () => {
|
||||||
|
treeLoading.value = true
|
||||||
const params = {
|
const params = {
|
||||||
edusubject,
|
edusubject,
|
||||||
edustage,
|
edustage,
|
||||||
|
@ -90,16 +102,11 @@ const getSubjectContent = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let data;
|
let data;
|
||||||
if (localStorage.getItem('evaluationList')) {
|
|
||||||
evaluationList.value = JSON.parse(localStorage.getItem('evaluationList'))
|
|
||||||
data = evaluationList.value
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const { rows } = await listEvaluation(params)
|
const { rows } = await listEvaluation(params)
|
||||||
localStorage.setItem('evaluationList', JSON.stringify(rows))
|
localStorage.setItem('evaluationList', JSON.stringify(rows))
|
||||||
evaluationList.value = rows
|
evaluationList.value = rows
|
||||||
data = rows
|
data = rows
|
||||||
}
|
treeLoading.value = false
|
||||||
|
|
||||||
//获取教材版本
|
//获取教材版本
|
||||||
await getSubject()
|
await getSubject()
|
||||||
|
@ -107,15 +114,15 @@ const getSubjectContent = async () => {
|
||||||
volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
|
volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
|
||||||
//下册
|
//下册
|
||||||
volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
|
volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
|
||||||
|
|
||||||
getTreeData()
|
getTreeData()
|
||||||
}
|
}
|
||||||
|
|
||||||
//选择教材
|
//选择教材
|
||||||
const changeBook = ({ id, itemtitle, avartar }) => {
|
const changeBook = ({ id, itemtitle, avartar, fileurl }) => {
|
||||||
curBookId.value = id
|
curBookId.value = id
|
||||||
curBookName.value = itemtitle
|
curBookName.value = itemtitle
|
||||||
curBookImg.value = BaseUrl + avartar
|
curBookImg.value = BaseUrl + avartar
|
||||||
|
curBookPath.value = fileurl
|
||||||
getTreeData()
|
getTreeData()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
|
@ -126,10 +133,20 @@ const getTreeData = () => {
|
||||||
//数据过滤
|
//数据过滤
|
||||||
let upData = transData(volumeOne.value)
|
let upData = transData(volumeOne.value)
|
||||||
let downData = transData(volumeTwo.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
|
treeData.value = upData.length ? upData : downData
|
||||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
}
|
||||||
|
else{
|
||||||
|
treeData.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
currentNode.value = getLastLevelData(treeData.value)[0].id
|
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||||
|
currentNode.data = getLastLevelData(treeData.value)[0]
|
||||||
|
currentNodeId.value = getLastLevelData(treeData.value)[0].id
|
||||||
currentNodeName.value = getLastLevelData(treeData.value)[0].label
|
currentNodeName.value = getLastLevelData(treeData.value)[0].label
|
||||||
emitChangeBook()
|
emitChangeBook()
|
||||||
})
|
})
|
||||||
|
@ -138,17 +155,22 @@ const getTreeData = () => {
|
||||||
|
|
||||||
const emitChangeBook = () => {
|
const emitChangeBook = () => {
|
||||||
let curNode = {
|
let curNode = {
|
||||||
id: currentNode.value,
|
id: currentNodeId.value,
|
||||||
label: currentNodeName.value
|
label: currentNodeName.value,
|
||||||
|
itemtitle: currentNode.data.itemtitle,
|
||||||
|
edudegree: currentNode.data.edudegree,
|
||||||
|
edustage: currentNode.data.edustage,
|
||||||
|
edusubject: currentNode.data.edusubject,
|
||||||
}
|
}
|
||||||
let parentNode = findParentByChildId(treeData.value, currentNode.value)
|
let parentNode = findParentByChildId(treeData.value, currentNodeId.value)
|
||||||
curNode.parentNode = toRaw(parentNode)
|
curNode.parentNode = toRaw(parentNode)
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
textBook: {
|
textBook: {
|
||||||
curBookId: curBookId.value,
|
curBookId: curBookId.value,
|
||||||
curBookName: curBookName.value,
|
curBookName: curBookName.value,
|
||||||
curBookImg: curBookImg.value
|
curBookImg: curBookImg.value,
|
||||||
|
curBookPath: curBookPath.value
|
||||||
},
|
},
|
||||||
node: curNode
|
node: curNode
|
||||||
}
|
}
|
||||||
|
@ -201,20 +223,26 @@ const findParentByChildId = (treeData, targetNodeId) => {
|
||||||
|
|
||||||
const transData = (data) => {
|
const transData = (data) => {
|
||||||
let ary = []
|
let ary = []
|
||||||
|
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
let obj = {}
|
let obj = {}
|
||||||
|
|
||||||
if (item.rootid == curBookId.value) {
|
if (item.rootid == curBookId.value) {
|
||||||
obj.label = item.itemtitle
|
obj.label = item.itemtitle
|
||||||
obj.id = item.id
|
obj.id = item.id
|
||||||
|
obj.itemtitle = item.itemtitle
|
||||||
|
obj.edudegree = item.edudegree
|
||||||
|
obj.edustage = item.edustage
|
||||||
|
obj.edusubject = item.edusubject
|
||||||
let ary2 = []
|
let ary2 = []
|
||||||
evaluationList.value.forEach(el => {
|
evaluationList.value.forEach(el => {
|
||||||
let obj2 = {}
|
let obj2 = {}
|
||||||
if (item.id == el.parentid) {
|
if (item.id == el.parentid) {
|
||||||
obj2 = {
|
obj2 = {
|
||||||
label: el.itemtitle,
|
label: el.itemtitle,
|
||||||
id: el.id
|
id: el.id,
|
||||||
|
itemtitle : el.itemtitle,
|
||||||
|
edudegree : el.edudegree,
|
||||||
|
edustage : el.edustage,
|
||||||
|
edusubject : el.edusubject,
|
||||||
}
|
}
|
||||||
ary2.push(obj2)
|
ary2.push(obj2)
|
||||||
}
|
}
|
||||||
|
@ -228,27 +256,25 @@ const transData = (data) => {
|
||||||
|
|
||||||
//获取教材
|
//获取教材
|
||||||
const getSubject = async () => {
|
const getSubject = async () => {
|
||||||
if (localStorage.getItem('subjectList')) {
|
|
||||||
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
|
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
|
||||||
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id))
|
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject)
|
||||||
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
|
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
|
||||||
}
|
|
||||||
|
|
||||||
// 默认第一个
|
// 默认第一个
|
||||||
|
if(!subjectList.value.length) return
|
||||||
curBookName.value = subjectList.value[0].itemtitle
|
curBookName.value = subjectList.value[0].itemtitle
|
||||||
curBookId.value = subjectList.value[0].id
|
curBookId.value = subjectList.value[0].id
|
||||||
curBookImg.value = BaseUrl + subjectList.value[0].avartar
|
curBookImg.value = BaseUrl + subjectList.value[0].avartar
|
||||||
|
curBookPath.value = subjectList.value[0].fileurl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const isHaveUnit = (id) => {
|
// const isHaveUnit = (id) => {
|
||||||
return evaluationList.value.some(item => {
|
// return evaluationList.value.some(item => {
|
||||||
return item.rootid == id
|
// return item.rootid == id
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
const handleNodeClick = (data, node) => {
|
const handleNodeClick = (data, node) => {
|
||||||
|
@ -256,28 +282,32 @@ const handleNodeClick = (data, node) => {
|
||||||
* data : 当前节点数据
|
* data : 当前节点数据
|
||||||
* node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象
|
* node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象
|
||||||
*/
|
*/
|
||||||
const currentNode = data;
|
|
||||||
|
const nodeData = data;
|
||||||
const parentNode = node.parent.data;
|
const parentNode = node.parent.data;
|
||||||
|
|
||||||
if (Array.isArray(parentNode)) {
|
if (Array.isArray(parentNode)) {
|
||||||
currentNode.parentNode = null
|
nodeData.parentNode = null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
currentNode.parentNode = parentNode
|
nodeData.parentNode = parentNode
|
||||||
}
|
}
|
||||||
|
|
||||||
let curData = {
|
let curData = {
|
||||||
textBook: {
|
textBook: {
|
||||||
curBookId: curBookId.value,
|
curBookId: curBookId.value,
|
||||||
curBookName: curBookName.value,
|
curBookName: curBookName.value,
|
||||||
curBookImg: curBookImg.value
|
curBookImg: curBookImg.value,
|
||||||
|
curBookPath: curBookPath.value
|
||||||
},
|
},
|
||||||
node: toRaw(currentNode)
|
node: toRaw(nodeData)
|
||||||
}
|
}
|
||||||
|
currentNode.data = curData
|
||||||
emit('nodeClick', curData)
|
emit('nodeClick', curData)
|
||||||
}
|
}
|
||||||
|
watch(() => userStore.edusubject, () => {
|
||||||
|
console.log(userStore.edusubject,'userStore.edusubject')
|
||||||
|
})
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSubjectContent()
|
getSubjectContent()
|
||||||
})
|
})
|
||||||
|
@ -365,6 +395,9 @@ onMounted(() => {
|
||||||
.textbook-img {
|
.textbook-img {
|
||||||
width: 55px;
|
width: 55px;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, toRaw, onMounted, nextTick, watch } from 'vue'
|
import { ref, reactive, toRaw, onMounted, nextTick, watch } from 'vue'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { listEvaluation } from '@/api/subject'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -66,6 +68,8 @@ const props = defineProps({
|
||||||
default: '移动至'
|
default: '移动至'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const { edustage, edusubject, userId } = userStore.user
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const bookVisible = ref(false)
|
const bookVisible = ref(false)
|
||||||
|
@ -106,10 +110,19 @@ watch(() => props.modelValue, (newVal) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const getSubjectContent = () => {
|
const getSubjectContent = async () => {
|
||||||
|
|
||||||
evaluationList.value = JSON.parse(localStorage.getItem('evaluationList'))
|
const params = {
|
||||||
let data = evaluationList.value
|
edusubject,
|
||||||
|
edustage,
|
||||||
|
entpcourseedituserid: userId,
|
||||||
|
pageSize: 500
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
const { rows } = await listEvaluation(params)
|
||||||
|
localStorage.setItem('evaluationList', JSON.stringify(rows))
|
||||||
|
evaluationList.value = rows
|
||||||
|
data = rows
|
||||||
|
|
||||||
//获取教材版本
|
//获取教材版本
|
||||||
getSubject()
|
getSubject()
|
||||||
|
@ -117,24 +130,50 @@ const getSubjectContent = () => {
|
||||||
volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
|
volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
|
||||||
//下册
|
//下册
|
||||||
volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
|
volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
|
||||||
|
|
||||||
getTreeData()
|
getTreeData()
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSubject = () => {
|
const getSubject = async () => {
|
||||||
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
|
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
|
||||||
|
|
||||||
|
if (localStorage.getItem('subjectList')) {
|
||||||
|
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))
|
||||||
|
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
|
||||||
|
}
|
||||||
|
|
||||||
// 默认第一个
|
// 默认第一个
|
||||||
|
if(!subjectList.value.length) return
|
||||||
curBookName.value = subjectList.value[0].itemtitle
|
curBookName.value = subjectList.value[0].itemtitle
|
||||||
curBookId.value = subjectList.value[0].id
|
curBookId.value = subjectList.value[0].id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isHaveUnit = (id) => {
|
||||||
|
return evaluationList.value.some(item => {
|
||||||
|
return item.rootid == id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const getTreeData = () => {
|
const getTreeData = () => {
|
||||||
//数据过滤
|
//数据过滤
|
||||||
let upData = transData(volumeOne.value)
|
let upData = transData(volumeOne.value)
|
||||||
let downData = transData(volumeTwo.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
|
treeData.value = upData.length ? upData : downData
|
||||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
}
|
||||||
|
else{
|
||||||
|
treeData.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||||
currentNodeId.value = getLastLevelData(treeData.value)[0].id
|
currentNodeId.value = getLastLevelData(treeData.value)[0].id
|
||||||
currentNodeName.value = getLastLevelData(treeData.value)[0].label
|
currentNodeName.value = getLastLevelData(treeData.value)[0].label
|
||||||
emitChangeBook()
|
emitChangeBook()
|
||||||
|
@ -297,7 +336,8 @@ onMounted(() => {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #000;
|
color: #000;
|
||||||
.icon-close{
|
|
||||||
|
.icon-close {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
<template>
|
||||||
|
<div class="canvasitem">
|
||||||
|
<div class="pdfAdnFabric" id="pdfAdnFabric">
|
||||||
|
<canvas id="pdf-fabric"></canvas>
|
||||||
|
<canvas id="pdf-fabric1" v-if="props.pdfObj.numberOfPdf == 2"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup >
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
onMounted,
|
||||||
|
watch,
|
||||||
|
reactive,
|
||||||
|
nextTick,
|
||||||
|
} from 'vue'
|
||||||
|
import { fabric } from 'fabric'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { handleevent, savecanvsStore, initcanvasdata, displayData } from '@/utils/pdfAndFabric'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
pdfObj: {
|
||||||
|
type: Object,
|
||||||
|
default: {
|
||||||
|
numberOfPdf: 2, //pdf数量 只有 1 2
|
||||||
|
pdfUrl: null,
|
||||||
|
numPages: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// canvas的所有数据
|
||||||
|
const canvsStore = reactive({
|
||||||
|
id: 'xxxx',
|
||||||
|
pageArr: []
|
||||||
|
})
|
||||||
|
const fabriccanvas = ref(null)
|
||||||
|
const fabriccanvas1 = ref(null)
|
||||||
|
// 页面总数
|
||||||
|
const numPagesTotal = ref(0)
|
||||||
|
const imgarr = ref([])
|
||||||
|
// 多个pdf的对象
|
||||||
|
const canvasNumbsValue = ref([])
|
||||||
|
const emit = defineEmits(['update:numPagesTotal'])
|
||||||
|
const renderPage = async (canvasobj) => {
|
||||||
|
if (canvasobj.page > numPagesTotal.value) return
|
||||||
|
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
|
||||||
|
// 渲染当前页
|
||||||
|
const page = await pdf.getPage(canvasobj.page)
|
||||||
|
var screenWidth = window.innerWidth/2-100;
|
||||||
|
var screenHeight = window.innerHeight;
|
||||||
|
const viewport = page.getViewport({ scale:2})
|
||||||
|
|
||||||
|
const canvasElement = canvasobj.canvas
|
||||||
|
canvasElement.width = viewport.width
|
||||||
|
canvasElement.height = viewport.height
|
||||||
|
const renderContext = {
|
||||||
|
canvasContext: canvasobj.context,
|
||||||
|
viewport: viewport
|
||||||
|
}
|
||||||
|
page.render(renderContext).promise.then((res) => {
|
||||||
|
const img = document.createElement('img')
|
||||||
|
img.src = canvasobj.canvas.toDataURL('image/png')
|
||||||
|
canvasobj.canvas.remove()
|
||||||
|
imgarr.value.push({ src: img.src, page: canvasobj.page, JSONdata: {}, index: canvasobj.index })
|
||||||
|
img.onload = () => {
|
||||||
|
// 在这里执行图像加载完成后的操作
|
||||||
|
// 根据传过来的pdf对象 判断改渲染哪一个fabric
|
||||||
|
if (props.pdfObj.numberOfPdf == 2) {
|
||||||
|
if (canvasobj.index == 0) {
|
||||||
|
|
||||||
|
fabriccanvas.value.setWidth(screenWidth)
|
||||||
|
fabriccanvas.value.setHeight(screenHeight)
|
||||||
|
displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
|
||||||
|
} else {
|
||||||
|
fabriccanvas1.value.setWidth(screenWidth)
|
||||||
|
fabriccanvas1.value.setHeight(screenHeight)
|
||||||
|
displayData(fabriccanvas1, canvsStore, canvasobj, fabric, img)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fabriccanvas.value.setWidth(screenWidth)
|
||||||
|
fabriccanvas.value.setHeight(screenHeight)
|
||||||
|
displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
|
||||||
|
}
|
||||||
|
// console.log(imgarr.value)
|
||||||
|
img.remove()
|
||||||
|
}
|
||||||
|
// 判断imgarr的JSONdata在canvsStore.pageArr有没有
|
||||||
|
canvsStore.pageArr.forEach((item) => {
|
||||||
|
if (item.page == canvasobj.page) {
|
||||||
|
imgarr.value.forEach((img) => {
|
||||||
|
if (img.page == canvasobj.page) {
|
||||||
|
img.JSONdata = item.JSONdata
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const updatePage = (canvasobj) => {
|
||||||
|
renderPage(canvasobj)
|
||||||
|
}
|
||||||
|
const loadPdf = async (canvasobj) => {
|
||||||
|
updatePage(canvasobj)
|
||||||
|
}
|
||||||
|
|
||||||
|
const initPdf = async (type = 'default') => {
|
||||||
|
// 保存数据
|
||||||
|
savecanvsStore(imgarr, canvsStore)
|
||||||
|
// initcanvasdata(fabriccanvas)
|
||||||
|
// initcanvasdata(fabriccanvas1)
|
||||||
|
// 单页模式
|
||||||
|
if (type == 'restone') {
|
||||||
|
// 清除 canvas 上的所有对象
|
||||||
|
fabriccanvas1.value.clear()
|
||||||
|
// 释放 canvas 的资源
|
||||||
|
fabriccanvas1.value.dispose()
|
||||||
|
}
|
||||||
|
// 清除现有 canvas 元素的内容
|
||||||
|
canvasNumbsValue.value.forEach((canvasObj) => {
|
||||||
|
const context = canvasObj.context
|
||||||
|
context.clearRect(0, 0, canvasObj.canvas.width, canvasObj.canvas.height)
|
||||||
|
})
|
||||||
|
// 清除所有对象
|
||||||
|
imgarr.value = []
|
||||||
|
canvasNumbsValue.value = []
|
||||||
|
if (props.pdfObj.pdfUrl) {
|
||||||
|
await nextTick() // 确保 DOM 渲染完成
|
||||||
|
if (props.pdfObj.numberOfPdf == 1) {
|
||||||
|
canvasNumbsValue.value = [{}]
|
||||||
|
const canvasElement = document.createElement('canvas')
|
||||||
|
canvasNumbsValue.value[0].canvas = canvasElement
|
||||||
|
canvasNumbsValue.value[0].context = canvasNumbsValue.value[0].canvas.getContext('2d')
|
||||||
|
canvasNumbsValue.value[0].page = props.pdfObj.numPages
|
||||||
|
canvasNumbsValue.value[0].index = 0
|
||||||
|
await loadPdf(canvasNumbsValue.value[0])
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < props.pdfObj.numberOfPdf; i++) {
|
||||||
|
canvasNumbsValue.value[i] = {}
|
||||||
|
const canvasElement = document.createElement('canvas')
|
||||||
|
canvasNumbsValue.value[i].canvas = canvasElement
|
||||||
|
canvasNumbsValue.value[i].context = canvasNumbsValue.value[i].canvas.getContext('2d')
|
||||||
|
// 设置页数
|
||||||
|
if (i == 0) {
|
||||||
|
canvasNumbsValue.value[i].page = props.pdfObj.numPages
|
||||||
|
} else {
|
||||||
|
canvasNumbsValue.value[i].page = props.pdfObj.numPages + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasNumbsValue.value[i].index = i
|
||||||
|
// 加载FabricVue
|
||||||
|
await loadPdf(canvasNumbsValue.value[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const initPdfone = async () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
fabriccanvas1.value = new fabric.Canvas('pdf-fabric1')
|
||||||
|
fabriccanvas1.value.isDrawingMode = true
|
||||||
|
fabriccanvas1.value.freeDrawingBrush.color = '#A33AFE'
|
||||||
|
fabriccanvas1.value.freeDrawingCursor = 'default'
|
||||||
|
fabriccanvas1.value.setWidth(860)
|
||||||
|
handleevent(fabriccanvas1.value, imgarr, 'two')
|
||||||
|
}, 0)
|
||||||
|
initPdf('addOnePage')
|
||||||
|
}
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
|
||||||
|
numPagesTotal.value = pdf.numPages
|
||||||
|
// console.log(pdf)
|
||||||
|
// 初始化fabriccanvas
|
||||||
|
fabriccanvas.value = new fabric.Canvas('pdf-fabric')
|
||||||
|
fabriccanvas.value.setWidth(860)
|
||||||
|
fabriccanvas.value.isDrawingMode = true
|
||||||
|
fabriccanvas.value.freeDrawingBrush.color = '#A33AFE'
|
||||||
|
fabriccanvas.value.freeDrawingCursor = 'default'
|
||||||
|
|
||||||
|
fabriccanvas1.value = new fabric.Canvas('pdf-fabric1')
|
||||||
|
fabriccanvas1.value.isDrawingMode = true
|
||||||
|
fabriccanvas1.value.freeDrawingBrush.color = '#A33AFE'
|
||||||
|
fabriccanvas1.value.freeDrawingCursor = 'default'
|
||||||
|
fabriccanvas1.value.setWidth(860)
|
||||||
|
emit('update:numPagesTotal', pdf.numPages)
|
||||||
|
initPdf()
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
ElMessage.error('pdf文件错误')
|
||||||
|
}
|
||||||
|
// 监听2个canvas事件
|
||||||
|
handleevent(fabriccanvas.value, imgarr)
|
||||||
|
handleevent(fabriccanvas1.value, imgarr, 'two')
|
||||||
|
})
|
||||||
|
defineExpose({
|
||||||
|
initPdf,
|
||||||
|
initPdfone
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.canvasitem {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.pdfAdnFabric {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
align-items: center;
|
||||||
|
:deep(> div:nth-of-type(1)) {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,8 +1,18 @@
|
||||||
<template>
|
pdfAdnFabric<template>
|
||||||
<div class="canvasitem">
|
<div class="canvasitem">
|
||||||
<div class="pdfAdnFabric" id="pdfAdnFabric">
|
<div class="pdfAdnFabric" id="pdfAdnFabric" >
|
||||||
<canvas id="pdf-fabric"></canvas>
|
<!-- @touchmove="handleTouchMove"
|
||||||
<canvas id="pdf-fabric1" v-if="props.pdfObj.numberOfPdf == 2"></canvas>
|
@touchend="handleTouchEnd"
|
||||||
|
@mousedown="handleMouseDown"
|
||||||
|
@mousemove="handleMouseMove"
|
||||||
|
@mouseup="handleMouseUp" -->
|
||||||
|
<div :class="ispointer ? 'ispointer' : ''">
|
||||||
|
<canvas ref="fabriccanvas" />
|
||||||
|
</div>
|
||||||
|
<!-- style="pointer-events: none;" -->
|
||||||
|
<div v-if="props.pdfObj.numberOfPdf === 2" :class="ispointer ? 'ispointer' : ''">
|
||||||
|
<canvas ref="fabriccanvas1" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -16,12 +26,17 @@ import {
|
||||||
defineProps,
|
defineProps,
|
||||||
defineExpose,
|
defineExpose,
|
||||||
nextTick,
|
nextTick,
|
||||||
defineEmits
|
defineEmits,watchEffect
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { fabric } from 'fabric'
|
// import { fabric } from 'fabric'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { handleevent, savecanvsStore, initcanvasdata, displayData } from '@/utils/pdfAndFabric'
|
import { handleevent, savecanvsStore, initcanvasdata, displayData } from '@/utils/pdfAndFabric'
|
||||||
|
import { fabricVue, TYPES } from '@/plugins/fabric'
|
||||||
|
import { updateSmartBookMarkContent, addsmartBookMark,getBookMarkById } from '@/api/eTextbook/index'
|
||||||
|
import {useToolState} from '@/store/modules/tool'
|
||||||
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
|
const toolState = useToolState();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
pdfObj: {
|
pdfObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -32,13 +47,16 @@ const props = defineProps({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const ispointer = ref(false) //是否画笔模式
|
||||||
// canvas的所有数据
|
// canvas的所有数据
|
||||||
const canvsStore = reactive({
|
const canvsStore = reactive({
|
||||||
id: 'xxxx',
|
|
||||||
pageArr: []
|
pageArr: []
|
||||||
})
|
})
|
||||||
const fabriccanvas = ref(null)
|
const fabriccanvas = ref(null)
|
||||||
const fabriccanvas1 = ref(null)
|
const fabriccanvas1 = ref(null)
|
||||||
|
|
||||||
|
const canvasFabricVue = ref(null)
|
||||||
|
const canvas1FabricVue = ref(null)
|
||||||
// 页面总数
|
// 页面总数
|
||||||
const numPagesTotal = ref(0)
|
const numPagesTotal = ref(0)
|
||||||
const imgarr = ref([])
|
const imgarr = ref([])
|
||||||
|
@ -50,9 +68,9 @@ const renderPage = async (canvasobj) => {
|
||||||
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
|
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
|
||||||
// 渲染当前页
|
// 渲染当前页
|
||||||
const page = await pdf.getPage(canvasobj.page)
|
const page = await pdf.getPage(canvasobj.page)
|
||||||
var screenWidth = window.innerWidth/2-100;
|
var screenWidth = window.innerWidth / 2 - 100
|
||||||
var screenHeight = window.innerHeight;
|
var screenHeight = window.innerHeight
|
||||||
const viewport = page.getViewport({ scale:2})
|
const viewport = page.getViewport({ scale: 2 })
|
||||||
|
|
||||||
const canvasElement = canvasobj.canvas
|
const canvasElement = canvasobj.canvas
|
||||||
canvasElement.width = viewport.width
|
canvasElement.width = viewport.width
|
||||||
|
@ -61,6 +79,8 @@ const renderPage = async (canvasobj) => {
|
||||||
canvasContext: canvasobj.context,
|
canvasContext: canvasobj.context,
|
||||||
viewport: viewport
|
viewport: viewport
|
||||||
}
|
}
|
||||||
|
// console.log(renderContext,22222222222222222222)
|
||||||
|
|
||||||
page.render(renderContext).promise.then((res) => {
|
page.render(renderContext).promise.then((res) => {
|
||||||
const img = document.createElement('img')
|
const img = document.createElement('img')
|
||||||
img.src = canvasobj.canvas.toDataURL('image/png')
|
img.src = canvasobj.canvas.toDataURL('image/png')
|
||||||
|
@ -71,33 +91,75 @@ const renderPage = async (canvasobj) => {
|
||||||
// 根据传过来的pdf对象 判断改渲染哪一个fabric
|
// 根据传过来的pdf对象 判断改渲染哪一个fabric
|
||||||
if (props.pdfObj.numberOfPdf == 2) {
|
if (props.pdfObj.numberOfPdf == 2) {
|
||||||
if (canvasobj.index == 0) {
|
if (canvasobj.index == 0) {
|
||||||
|
canvasFabricVue.value.canvas.setWidth(screenWidth)
|
||||||
fabriccanvas.value.setWidth(screenWidth)
|
canvasFabricVue.value.canvas.setHeight(screenHeight)
|
||||||
fabriccanvas.value.setHeight(screenHeight)
|
// updateCanvasBackgroundImage(canvasFabricVue,img)
|
||||||
displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
|
displayData(canvasFabricVue, canvsStore, canvasobj, fabric, img)
|
||||||
} else {
|
} else {
|
||||||
fabriccanvas1.value.setWidth(screenWidth)
|
canvas1FabricVue.value.canvas.setWidth(screenWidth)
|
||||||
fabriccanvas1.value.setHeight(screenHeight)
|
canvas1FabricVue.value.canvas.setHeight(screenHeight)
|
||||||
displayData(fabriccanvas1, canvsStore, canvasobj, fabric, img)
|
displayData(canvas1FabricVue, canvsStore, canvasobj, fabric, img)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fabriccanvas.value.setWidth(screenWidth)
|
canvasFabricVue.value.canvas.setHeight(screenHeight)
|
||||||
fabriccanvas.value.setHeight(screenHeight)
|
displayData(canvasFabricVue, canvsStore, canvasobj, fabric, img)
|
||||||
displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
|
|
||||||
}
|
}
|
||||||
// console.log(imgarr.value)
|
|
||||||
img.remove()
|
img.remove()
|
||||||
}
|
}
|
||||||
// 判断imgarr的JSONdata在canvsStore.pageArr有没有
|
// 判断imgarr的JSONdata在canvsStore.pageArr有没有
|
||||||
canvsStore.pageArr.forEach((item) => {
|
// canvsStore.pageArr.forEach((item) => {
|
||||||
if (item.page == canvasobj.page) {
|
// if (item.page == canvasobj.page) {
|
||||||
imgarr.value.forEach((img) => {
|
// imgarr.value.forEach((img) => {
|
||||||
if (img.page == canvasobj.page) {
|
// if (img.page == canvasobj.page) {
|
||||||
img.JSONdata = item.JSONdata
|
// img.JSONdata = item.JSONdata
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 保存数据
|
||||||
|
const savaDataStore = () => {
|
||||||
|
if(!toolState.isToolWin){
|
||||||
|
toolState.isPdfWin=false
|
||||||
|
toolState.showBoardAll=true //恢复默认值
|
||||||
|
ipcRenderer.invoke('tool-sphere:reset') //重置tool状态
|
||||||
|
ipcRenderer.send('open-PDF:minimize')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
imgarr.value.forEach((a) => {
|
||||||
|
if (a.index == 0) {
|
||||||
|
a.JSONdata = canvasFabricVue.value.canvas.toJSON()
|
||||||
|
} else {
|
||||||
|
a.JSONdata = canvas1FabricVue.value.canvas.toJSON()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const nameMap = new Map(canvsStore.pageArr.map((item) => [item.page, item.id]))
|
||||||
|
// 创建一个用于存储所有异步操作的数组
|
||||||
|
let promises = []
|
||||||
|
imgarr.value.forEach((item) => {
|
||||||
|
if (nameMap.has(item.page)) {
|
||||||
|
const params = {
|
||||||
|
id: nameMap.get(item.page),
|
||||||
|
contentData: JSON.stringify(item.JSONdata.objects)
|
||||||
|
}
|
||||||
|
promises.push(updateSmartBookMarkContent([params]))
|
||||||
|
} else {
|
||||||
|
promises.push(addsmartBookMark({
|
||||||
|
pageNum: item.page,
|
||||||
|
contentData: JSON.stringify(item.JSONdata.objects),
|
||||||
|
bookId: props.pdfObj.bookId,
|
||||||
|
type: '教材',
|
||||||
|
source: 'smarttalk'
|
||||||
|
}))
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Promise.all(promises).then(res=>{
|
||||||
|
toolState.isPdfWin=false
|
||||||
|
toolState.showBoardAll=true //恢复默认值
|
||||||
|
ipcRenderer.invoke('tool-sphere:reset') //重置tool状态
|
||||||
|
ipcRenderer.send('open-PDF:minimize')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const updatePage = (canvasobj) => {
|
const updatePage = (canvasobj) => {
|
||||||
|
@ -108,16 +170,55 @@ const loadPdf = async (canvasobj) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const initPdf = async (type = 'default') => {
|
const initPdf = async (type = 'default') => {
|
||||||
|
imgarr.value.forEach((a) => {
|
||||||
|
if (a.index == 0) {
|
||||||
|
a.JSONdata = canvasFabricVue.value.canvas.toJSON()
|
||||||
|
} else {
|
||||||
|
a.JSONdata = canvas1FabricVue.value.canvas.toJSON()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 判断是否翻页以及工具窗口是否打开 满足 翻页+打开工具才能保存数据
|
||||||
|
if (type != 'default' && toolState.isToolWin) {
|
||||||
|
const nameMap = new Map(canvsStore.pageArr.map((item) => [item.page, item.id]))
|
||||||
|
// 创建一个用于存储所有异步操作的数组
|
||||||
|
let promises = []
|
||||||
|
imgarr.value.forEach((item) => {
|
||||||
|
if (nameMap.has(item.page)) {
|
||||||
|
const params = {
|
||||||
|
id: nameMap.get(item.page),
|
||||||
|
contentData: JSON.stringify(item.JSONdata.objects)
|
||||||
|
}
|
||||||
|
promises.push(updateSmartBookMarkContent([params]))
|
||||||
|
} else {
|
||||||
|
promises.push(addsmartBookMark({
|
||||||
|
pageNum: item.page,
|
||||||
|
contentData: JSON.stringify(item.JSONdata.objects),
|
||||||
|
bookId: props.pdfObj.bookId,
|
||||||
|
type: '教材',
|
||||||
|
source: 'smarttalk'
|
||||||
|
}))
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Promise.all(promises).then(res=>{
|
||||||
|
getBookMarkById(props.pdfObj.bookId).then(res=>{
|
||||||
|
const pageArr=getUniqueArrayByLastOccurrence(res.data)
|
||||||
|
canvsStore.pageArr=[]
|
||||||
|
pageArr.forEach((a) => {
|
||||||
|
canvsStore.pageArr.push({ page: a.pageNum, id: a.id, JSONdata: a.contentData })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
// 保存数据
|
// 保存数据
|
||||||
savecanvsStore(imgarr, canvsStore)
|
// savecanvsStore(imgarr, canvsStore)
|
||||||
// initcanvasdata(fabriccanvas)
|
|
||||||
// initcanvasdata(fabriccanvas1)
|
if (props.pdfObj.numberOfPdf == 1) {
|
||||||
// 单页模式
|
// imgarr.value[0]
|
||||||
if (type == 'restone') {
|
canvasFabricVue.value.history.clean()
|
||||||
// 清除 canvas 上的所有对象
|
} else {
|
||||||
fabriccanvas1.value.clear()
|
canvasFabricVue.value.history.clean()
|
||||||
// 释放 canvas 的资源
|
canvas1FabricVue.value.history.clean()
|
||||||
fabriccanvas1.value.dispose()
|
|
||||||
}
|
}
|
||||||
// 清除现有 canvas 元素的内容
|
// 清除现有 canvas 元素的内容
|
||||||
canvasNumbsValue.value.forEach((canvasObj) => {
|
canvasNumbsValue.value.forEach((canvasObj) => {
|
||||||
|
@ -149,7 +250,6 @@ const initPdf = async (type = 'default') => {
|
||||||
} else {
|
} else {
|
||||||
canvasNumbsValue.value[i].page = props.pdfObj.numPages + 1
|
canvasNumbsValue.value[i].page = props.pdfObj.numPages + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
canvasNumbsValue.value[i].index = i
|
canvasNumbsValue.value[i].index = i
|
||||||
// 加载FabricVue
|
// 加载FabricVue
|
||||||
await loadPdf(canvasNumbsValue.value[i])
|
await loadPdf(canvasNumbsValue.value[i])
|
||||||
|
@ -157,47 +257,144 @@ const initPdf = async (type = 'default') => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//根据page去重
|
||||||
|
const getUniqueArrayByLastOccurrence=(array)=> {
|
||||||
|
const uniqueItems = array.reduce((acc, current) => {
|
||||||
|
// 使用 Map 来跟踪最后一个出现的 pageNum 和它的对象
|
||||||
|
acc.set(current.pageNum, current);
|
||||||
|
return acc;
|
||||||
|
}, new Map());
|
||||||
|
|
||||||
|
// 将 Map 的值转换回数组
|
||||||
|
const resultArray = Array.from(uniqueItems.values());
|
||||||
|
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
const initPdfone = async () => {
|
const initPdfone = async () => {
|
||||||
setTimeout(() => {
|
setTimeout(async () => {
|
||||||
fabriccanvas1.value = new fabric.Canvas('pdf-fabric1')
|
const option = { freeDrawingCursor: 'default' }
|
||||||
fabriccanvas1.value.isDrawingMode = true
|
const canvas2 = new fabricVue()
|
||||||
fabriccanvas1.value.freeDrawingBrush.color = '#A33AFE'
|
await canvas2.initCanvas(fabriccanvas1.value, option)
|
||||||
fabriccanvas1.value.freeDrawingCursor = 'default'
|
canvas2.canvas.setWidth(window.innerWidth / 2 - 100)
|
||||||
fabriccanvas1.value.setWidth(595)
|
canvas1FabricVue.value = canvas2
|
||||||
handleevent(fabriccanvas1.value, imgarr, 'two')
|
await initPdf('addOnePage')
|
||||||
}, 0)
|
}, 0)
|
||||||
initPdf('addOnePage')
|
|
||||||
}
|
}
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
|
// 创建canvas转化成图片
|
||||||
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
|
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
|
||||||
numPagesTotal.value = pdf.numPages
|
numPagesTotal.value = pdf.numPages
|
||||||
// console.log(pdf)
|
|
||||||
// 初始化fabriccanvas
|
// 初始化fabriccanvas
|
||||||
fabriccanvas.value = new fabric.Canvas('pdf-fabric')
|
const option = { freeDrawingCursor: 'default' }
|
||||||
fabriccanvas.value.setWidth(595)
|
const canvas1 = new fabricVue()
|
||||||
fabriccanvas.value.isDrawingMode = true
|
// canvas1.boardConfig.mode= TYPES.ActionMode.OTHER
|
||||||
fabriccanvas.value.freeDrawingBrush.color = '#A33AFE'
|
// canvas1.boardConfig.mode= TYPES.ActionMode.ERASE
|
||||||
fabriccanvas.value.freeDrawingCursor = 'default'
|
await canvas1.initCanvas(fabriccanvas.value, option)
|
||||||
|
canvas1.canvas.setWidth(window.innerWidth / 2 - 100)
|
||||||
fabriccanvas1.value = new fabric.Canvas('pdf-fabric1')
|
canvasFabricVue.value = canvas1
|
||||||
fabriccanvas1.value.isDrawingMode = true
|
const canvas2 = new fabricVue()
|
||||||
fabriccanvas1.value.freeDrawingBrush.color = '#A33AFE'
|
await canvas2.initCanvas(fabriccanvas1.value, option)
|
||||||
fabriccanvas1.value.freeDrawingCursor = 'default'
|
canvas2.canvas.setWidth(window.innerWidth / 2 - 100)
|
||||||
fabriccanvas1.value.setWidth(595)
|
// canvas2.canvas.isDrawingMode=false
|
||||||
|
canvas1FabricVue.value = canvas2
|
||||||
|
window.test = { canvas1, canvas2 }
|
||||||
emit('update:numPagesTotal', pdf.numPages)
|
emit('update:numPagesTotal', pdf.numPages)
|
||||||
initPdf()
|
|
||||||
|
if (props.pdfObj.allPageData.length) {
|
||||||
|
props.pdfObj.allPageData.forEach((a) => {
|
||||||
|
if (a.pageNum == 1 || a.pageNum == 2) {
|
||||||
|
canvsStore.pageArr.push({ page: a.pageNum, id: a.id, JSONdata: a.contentData })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await initPdf()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
ElMessage.error('pdf文件错误')
|
ElMessage.error('pdf文件错误')
|
||||||
}
|
}
|
||||||
|
setToolStatus()
|
||||||
// 监听2个canvas事件
|
// 监听2个canvas事件
|
||||||
handleevent(fabriccanvas.value, imgarr)
|
// handleevent(fabriccanvas.value, imgarr)
|
||||||
handleevent(fabriccanvas1.value, imgarr, 'two')
|
// handleevent(fabriccanvas1.value, imgarr, 'two')
|
||||||
})
|
})
|
||||||
|
// zdg: 设置-底部工具栏-状态
|
||||||
|
const setToolStatus = () => {
|
||||||
|
toolState.showBoardAll = false
|
||||||
|
}
|
||||||
|
// 判断元素是否加载完成
|
||||||
|
const handleMode = (vale,type)=>{
|
||||||
|
if(vale=='select'){
|
||||||
|
ispointer.value=true
|
||||||
|
}else{
|
||||||
|
ispointer.value=false
|
||||||
|
}
|
||||||
|
switch(vale) {
|
||||||
|
case 'select': // 选择模式
|
||||||
|
canvasFabricVue.value?.handleMode(TYPES.ActionMode.OTHER)
|
||||||
|
canvas1FabricVue.value?.handleMode(TYPES.ActionMode.OTHER)
|
||||||
|
break
|
||||||
|
case 'brush': // 画笔模式
|
||||||
|
canvasFabricVue.value?.handleMode(TYPES.ActionMode.DRAW)
|
||||||
|
canvasFabricVue.value.canvas.freeDrawingCursor = 'default'
|
||||||
|
canvas1FabricVue.value?.handleMode(TYPES.ActionMode.DRAW)
|
||||||
|
canvas1FabricVue.value.canvas.freeDrawingCursor = 'default'
|
||||||
|
break
|
||||||
|
case 'erase': // 板擦模式
|
||||||
|
canvasFabricVue.value?.handleMode(TYPES.ActionMode.ERASE)
|
||||||
|
canvas1FabricVue.value?.handleMode(TYPES.ActionMode.ERASE)
|
||||||
|
break
|
||||||
|
case 'clear': // 清空画布
|
||||||
|
clearCanvas()
|
||||||
|
// canvas1FabricVue.value.history?.clean()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 清空canvas
|
||||||
|
const clearCanvas=()=>{
|
||||||
|
if(canvasFabricVue.value){
|
||||||
|
const objects = canvasFabricVue.value.canvas.getObjects();
|
||||||
|
objects.forEach((obj) => {
|
||||||
|
// 检查对象是否是背景
|
||||||
|
if (obj !== canvasFabricVue.value.canvas.backgroundImage) {
|
||||||
|
// 删除背景之外的对象
|
||||||
|
canvasFabricVue.value.canvas.remove(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvasFabricVue.value.canvas.renderAll();
|
||||||
|
}
|
||||||
|
if(canvas1FabricVue.value){
|
||||||
|
const objects = canvas1FabricVue.value.canvas.getObjects();
|
||||||
|
objects.forEach((obj) => {
|
||||||
|
// 检查对象是否是背景
|
||||||
|
if (obj !== canvas1FabricVue.value.canvas.backgroundImage) {
|
||||||
|
// 删除背景之外的对象
|
||||||
|
canvas1FabricVue.value.canvas.remove(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas1FabricVue.value.canvas.renderAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const watchToolState=()=>{
|
||||||
|
if(toolState.showBoardAll){
|
||||||
|
setTimeout(() => {
|
||||||
|
toolState.showBoardAll=false
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
// 加载工具
|
||||||
|
handleMode(toolState.model)
|
||||||
|
|
||||||
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
initPdf,
|
initPdf,
|
||||||
initPdfone
|
initPdfone,
|
||||||
|
savaDataStore
|
||||||
|
})
|
||||||
|
watchEffect(() => {
|
||||||
|
console.log(toolState.model,'监听')
|
||||||
|
watchToolState() //监听工具栏
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -207,6 +404,8 @@ defineExpose({
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 100vh;
|
||||||
}
|
}
|
||||||
.pdfAdnFabric {
|
.pdfAdnFabric {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -217,4 +416,7 @@ defineExpose({
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ispointer {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -23,7 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button type="primary" @click="editUserInfo">
|
<el-button type="primary" @click="editUserInfo" :loading="subjectLoading">
|
||||||
确定
|
确定
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { listEvaluation } from '@/api/subject'
|
import { listEvaluation } from '@/api/subject'
|
||||||
import { updateUserInfo } from '@/api/system/user'
|
import { updateUserInfo } from '@/api/system/user'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
@ -42,12 +42,20 @@ const userStore = useUserStore()
|
||||||
const { userId, userName } = userStore.user
|
const { userId, userName } = userStore.user
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
loginData: {
|
||||||
|
type: Object,
|
||||||
|
default(){
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const subjectLoading = ref(false)
|
||||||
|
|
||||||
// 定义要发送的emit事件
|
// 定义要发送的emit事件
|
||||||
const emit = defineEmits(['update:modelValue', 'onSuccess'])
|
const emit = defineEmits(['update:modelValue', 'onSuccess'])
|
||||||
|
|
||||||
|
@ -111,8 +119,18 @@ const editUserInfo = async () =>{
|
||||||
edustage: gradeVal.value,
|
edustage: gradeVal.value,
|
||||||
edusubject: subjectVal.value
|
edusubject: subjectVal.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改之后需要重新登录 查询用户信息,否则不登录 查询的用户信息是未修改之前的
|
||||||
|
// 接口如此,我也不知道为啥要这样
|
||||||
|
subjectLoading.value = true
|
||||||
|
try {
|
||||||
|
//修改用户信息
|
||||||
await updateUserInfo(data)
|
await updateUserInfo(data)
|
||||||
|
await userStore.login(props.loginData)
|
||||||
await userStore.getInfo()
|
await userStore.getInfo()
|
||||||
|
} finally {
|
||||||
|
subjectLoading.value = false
|
||||||
|
}
|
||||||
emit('onSuccess')
|
emit('onSuccess')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,5 +166,7 @@ watch(() => props.modelValue, (newVal) => {
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-select-dropdown__item{
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -24,8 +24,7 @@
|
||||||
<div class="flex-type flex">
|
<div class="flex-type flex">
|
||||||
<span class="name">类别:</span>
|
<span class="name">类别:</span>
|
||||||
<el-select v-model="item.fileData.fileFlag" placeholder="Select" style="width: 100px">
|
<el-select v-model="item.fileData.fileFlag" placeholder="Select" style="width: 100px">
|
||||||
<el-option v-for="el in resourceType" :key="el.alue" :label="el.label" :value="el.value"
|
<el-option v-for="el in resourceType" :key="el.alue" :label="el.label" :value="el.value" />
|
||||||
:disabled="checkFile(el, item)" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,7 +97,7 @@ const hanleFileChange = (file) => {
|
||||||
// 验证文件大小
|
// 验证文件大小
|
||||||
// B < KB < MB < GB
|
// B < KB < MB < GB
|
||||||
// file.raw.size 单位是B
|
// file.raw.size 单位是B
|
||||||
const fileSize = file.raw.size / 1024 / 1024 > 100
|
const fileSize = file.raw.size / 1024 / 1024 > 500
|
||||||
if (fileSize) {
|
if (fileSize) {
|
||||||
ElMessage.error('文件大小错误! 请上传小于100M的文件!')
|
ElMessage.error('文件大小错误! 请上传小于100M的文件!')
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* @description: v-drag
|
||||||
|
* @author zdg
|
||||||
|
* @date 2023-06-07
|
||||||
|
*/
|
||||||
|
// 工具包
|
||||||
|
const utils = {
|
||||||
|
// Creates an event handler that can be used in Vue code
|
||||||
|
// setup start moving end
|
||||||
|
vueDragEvent: (el, action) => {
|
||||||
|
el.dispatchEvent(new Event(`drag-${action}`))
|
||||||
|
},
|
||||||
|
dragStart: (el, target, axis, snap, e) => {
|
||||||
|
// el.style.cursor = 'move'
|
||||||
|
// el.onmousedown = function (e) {
|
||||||
|
// const disX = e.clientX - el.offsetLeft
|
||||||
|
// const disY = e.clientY - el.offsetTop
|
||||||
|
// document.onmousemove = function (e) {
|
||||||
|
// const left = e.clientX - disX
|
||||||
|
// const top= e.clientY - disY
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// 首次向元素添加可拖动配置 | Add draggable configuration to element for the first time
|
||||||
|
const mountedHook = (el, binding) => {
|
||||||
|
console.log(el, binding)
|
||||||
|
const value = binding.value || {}
|
||||||
|
const handleSelector = value instanceof Object ? value.el : value // 获取元素
|
||||||
|
const isOpen = value instanceof Object ? value.open || true : true // 是否开启拖拽 默认:开启
|
||||||
|
const handleArray = [] // 拖拽元素
|
||||||
|
if (!isOpen) return false // 没有开启不加载后面的代码
|
||||||
|
let axis
|
||||||
|
// Store all the DOM elements that will be used as handles.
|
||||||
|
// They can be declared using a string with a CSS tag, class or id, or using Vue refs.
|
||||||
|
if (!!handleSelector) {
|
||||||
|
if (handleSelector instanceof HTMLElement) {
|
||||||
|
handleArray.push(handleSelector);
|
||||||
|
} else {
|
||||||
|
// handleArray.push(document.querySelectorAll(handleSelector));
|
||||||
|
document.querySelectorAll(handleSelector).forEach((child) => {
|
||||||
|
handleArray.push(child);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handleArray.length !== 0) {
|
||||||
|
// Define move element and apply CSS class
|
||||||
|
// el.classList.add(window.data.class.usesHandle);
|
||||||
|
|
||||||
|
handleArray.forEach((grabElement) => {
|
||||||
|
// Apply CSS class to each grab element
|
||||||
|
// grabElement.classList.add(window.data.class.handle);
|
||||||
|
|
||||||
|
// Add events to start drag with handle
|
||||||
|
grabElement.onmousedown = (e) => utils.dragStart(grabElement, el, axis, e);
|
||||||
|
grabElement.ontouchstart = (e) => utils.dragStart(grabElement, el, axis, e);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Add events to start drag without handle
|
||||||
|
el.onmousedown = (e) => utils.dragStart(el, el, axis, e);
|
||||||
|
el.ontouchstart = (e) => utils.dragStart(el, el, axis, e);
|
||||||
|
}
|
||||||
|
// Vue event on setup
|
||||||
|
utils.vueDragEvent(el, 'setup')
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
// Hooks for Vue3
|
||||||
|
mounted(el, binding) {
|
||||||
|
mountedHook(el, binding)
|
||||||
|
},
|
||||||
|
// Hooks for Vue2
|
||||||
|
inserted(el, binding) {
|
||||||
|
mountedHook(el, binding)
|
||||||
|
},
|
||||||
|
|
||||||
|
update(el, binding){
|
||||||
|
},
|
||||||
|
updated(el, binding){
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { homeworklist, listEntpcourse } from '@/api/teaching/classwork'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
// 当前选中教材章节节点
|
||||||
|
let curNode = null
|
||||||
|
// 查询作业列表所需要的ID
|
||||||
|
let chapterId = null
|
||||||
|
|
||||||
|
export const useGetHomework = async (node) => {
|
||||||
|
/**
|
||||||
|
* node
|
||||||
|
*/
|
||||||
|
if (!node) return
|
||||||
|
curNode = node
|
||||||
|
|
||||||
|
// 获取查询作业列表所需ID
|
||||||
|
let { rows } = await getChapterId()
|
||||||
|
if (rows.length > 0) {
|
||||||
|
chapterId = rows[0].id
|
||||||
|
} else {
|
||||||
|
// 没有则 传教新的entpcourse 再次查询
|
||||||
|
await createEntpcourse()
|
||||||
|
let { rows } = await getChapterId()
|
||||||
|
chapterId = rows[0].id
|
||||||
|
}
|
||||||
|
|
||||||
|
return await getHomeWorkList()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据教材章节ID 查询作业列表所需ID
|
||||||
|
const getChapterId = () => {
|
||||||
|
return listEntpcourse({
|
||||||
|
evalid: curNode.id ? curNode.id : curNode.parentNode.id,
|
||||||
|
edituserid: userStore.user.userId,
|
||||||
|
pageSize: 500
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的entpcourse
|
||||||
|
const createEntpcourse = () => {
|
||||||
|
// 参照 web AIx 传入参数
|
||||||
|
var cform = {}
|
||||||
|
cform.entpid = userStore.deptId
|
||||||
|
cform.level = 1
|
||||||
|
cform.parentid = 0
|
||||||
|
cform.dictid = 0
|
||||||
|
cform.evalid = curNode.id
|
||||||
|
cform.evalparentid = 0
|
||||||
|
cform.edusubject = curNode.edusubject
|
||||||
|
cform.edudegree = curNode.edudegree
|
||||||
|
cform.edustage = curNode.edustage
|
||||||
|
cform.coursetype = '课标学科'
|
||||||
|
cform.coursetitle = curNode.itemtitle
|
||||||
|
cform.coursedesc = ''
|
||||||
|
cform.status = ''
|
||||||
|
cform.dflag = 0
|
||||||
|
cform.edituserid = userStore.userId
|
||||||
|
cform.createblankfile = 'yes'
|
||||||
|
return addEntpcourse(cform)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getHomeWorkList = async () => {
|
||||||
|
return await homeworklist({
|
||||||
|
entpcourseid: chapterId,
|
||||||
|
edituserid: userStore.user.userId,
|
||||||
|
pageSize: 100
|
||||||
|
}).then((res) => {
|
||||||
|
//以下代码 参照AIx web端 作业布置
|
||||||
|
let list = []
|
||||||
|
for (var i = 0; i < res.rows.length; i++) {
|
||||||
|
res.rows[i].taskconfig = []
|
||||||
|
|
||||||
|
// 找child
|
||||||
|
for (var j = 0; j < res.rows.length; j++) {
|
||||||
|
if (res.rows[j].parentid == res.rows[i].id) {
|
||||||
|
var ss = []
|
||||||
|
if (res.rows[j].classworkdatastudentids != null) {
|
||||||
|
ss = JSON.parse('[' + res.rows[j].classworkdatastudentids + ']')
|
||||||
|
}
|
||||||
|
var js = {
|
||||||
|
id: res.rows[j].id,
|
||||||
|
classid: res.rows[j].classid,
|
||||||
|
classcaption: res.rows[j].classcaption,
|
||||||
|
parentid: 0,
|
||||||
|
worktype: '',
|
||||||
|
workkey: res.rows[j].workkey,
|
||||||
|
worktag: '',
|
||||||
|
entpcourseid: 0,
|
||||||
|
evalid: 0,
|
||||||
|
edusubject: '',
|
||||||
|
edudegree: '',
|
||||||
|
workdate: '',
|
||||||
|
title: '',
|
||||||
|
workcodes: '',
|
||||||
|
studentlist: ss,
|
||||||
|
deaddate: res.rows[j].deaddate,
|
||||||
|
timelength: res.rows[j].timelength,
|
||||||
|
weights: res.rows[j].weights,
|
||||||
|
feedtype: res.rows[j].feedtype
|
||||||
|
}
|
||||||
|
res.rows[i].taskconfig.push(js)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.rows[i].fileShowName = res.rows[i].uniquekey
|
||||||
|
|
||||||
|
// 注意slideid>0的,这一些作业是添加到PPT页面的,所以在作业管理中不能出现
|
||||||
|
// 2024-05-15,酉阳,jackyshen
|
||||||
|
if (res.rows[i].classid == 0 && res.rows[i].slideid == 0) {
|
||||||
|
list.push(res.rows[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是习题训练任务,则检查一共有多少道
|
||||||
|
if (res.rows[i].entpcourseworklist != '') {
|
||||||
|
res.rows[i].entpcourseworklistarray = JSON.parse('[' + res.rows[i].entpcourseworklist + ']')
|
||||||
|
} else {
|
||||||
|
res.rows[i].entpcourseworklistarray = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="app-main">
|
<section class="app-main">
|
||||||
|
<div class="app-main-left no-select">
|
||||||
|
<div v-for="(item, index) in title" :key="index" :class="item.active?'active':''" class="app-main-left-item" @click="active(index)">
|
||||||
|
<div class="app-main-left-item-icon">
|
||||||
|
<i :class="item.img"></i>
|
||||||
|
</div>
|
||||||
|
<div class="app-main-left-item-text">{{item.name}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<transition mode="out-in" name="fade-transform">
|
<transition mode="out-in" name="fade-transform">
|
||||||
<div v-show="$route != null" style="height: 100%">
|
<div v-show="$route != null" style="height: 100%; flex: 1">
|
||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="Component" v-if="route.meta.keepAlive" :key="route.name" />
|
<component :is="Component" v-if="route.meta.keepAlive" :key="route.name" />
|
||||||
|
@ -13,10 +21,170 @@
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup></script>
|
<script setup>
|
||||||
|
import { reactive, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import routerStore from '@/store/modules/route'
|
||||||
|
const router = useRouter()
|
||||||
|
const title = reactive([
|
||||||
|
{
|
||||||
|
name: '教学工作台',
|
||||||
|
img: 'iconfont icon-gongzuotai',
|
||||||
|
id: 1,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
name: '课程教学',
|
||||||
|
img: 'iconfont icon-PPT',
|
||||||
|
type: 'hash',
|
||||||
|
url: '/prepare',
|
||||||
|
child1: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '作业管理',
|
||||||
|
img: 'iconfont icon-36zuoyepingtai',
|
||||||
|
url: '/teaching/classtaskassign?titleName=作业布置',
|
||||||
|
child1: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '教学研究室',
|
||||||
|
img: 'iconfont icon-yanjiushi',
|
||||||
|
id: 2,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
name: '资源研究',
|
||||||
|
url: '/resource',
|
||||||
|
type: 'hash',
|
||||||
|
img: 'iconfont icon-business-report',
|
||||||
|
child1: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '课标分析',
|
||||||
|
url: '/teaching/chatwithstandard',
|
||||||
|
img: 'iconfont icon-kecheng',
|
||||||
|
child1: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '教材分析',
|
||||||
|
url: '/teaching/chatwithtextbook',
|
||||||
|
img: 'iconfont icon-yanjiushi',
|
||||||
|
child1: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '高考研究',
|
||||||
|
url: '/education/colentrance',
|
||||||
|
img: 'iconfont icon-icon_kaoshifenxi',
|
||||||
|
child1: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '教学资源库',
|
||||||
|
img: 'iconfont icon-zhuanyeziyuanku',
|
||||||
|
id: 3,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
name: '教学素材',
|
||||||
|
img: 'iconfont icon-sucai',
|
||||||
|
url: '/teaching/materialbank',
|
||||||
|
child1: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '课程资源',
|
||||||
|
img: 'iconfont icon-kechengziyuan',
|
||||||
|
url: '/teaching/coursewareresource',
|
||||||
|
child1: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '习题资源',
|
||||||
|
img: 'iconfont icon-iconku-zhuanqu-',
|
||||||
|
url: '/teaching/quesbank',
|
||||||
|
child1: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const active = (index) => {
|
||||||
|
const route = routerStore()
|
||||||
|
title.forEach((item, i) => {
|
||||||
|
if (i === index) {
|
||||||
|
item.active = true
|
||||||
|
route.setNowRouter(item.child)
|
||||||
|
if (item.id !== 3) {
|
||||||
|
router.push(item.child[0].url)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.active = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMounted(()=>{
|
||||||
|
active(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.no-select {
|
||||||
|
-webkit-user-select: none; /* Safari */
|
||||||
|
-moz-user-select: none; /* Firefox */
|
||||||
|
-ms-user-select: none; /* IE10+ */
|
||||||
|
user-select: none; /* Standard syntax */
|
||||||
|
}
|
||||||
.app-main {
|
.app-main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.app-main-left {
|
||||||
|
width: 80px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
.app-main-left-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 65px;
|
||||||
|
height: 65px;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: #839ce0;
|
||||||
|
border-radius: 10px;
|
||||||
|
.app-main-left-item-icon {
|
||||||
|
background: #fff;
|
||||||
|
color: #758fd3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
background: #839ce0;
|
||||||
|
border-radius: 10px;
|
||||||
|
.app-main-left-item-icon {
|
||||||
|
background: #fff;
|
||||||
|
color: #758fd3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.app-main-left-item-text {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.app-main-left-item-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #7c97e1;
|
||||||
|
color: #fff;
|
||||||
|
i {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -4,9 +4,15 @@
|
||||||
<h3 class="title" @click="changeTab">AIX智慧课堂</h3>
|
<h3 class="title" @click="changeTab">AIX智慧课堂</h3>
|
||||||
<div class="change-tab">
|
<div class="change-tab">
|
||||||
<ul class="flex">
|
<ul class="flex">
|
||||||
<li v-for="item in menus" :key="item.path" class="flex"
|
<li
|
||||||
:class="currentRoute == item.path ? 'active-li' : ''" @click="changePage(item.path)">
|
v-for="(item, index) in routeHeader.nowRouter"
|
||||||
<i class="iconfont" :class="item.icon"></i>
|
:key="index"
|
||||||
|
class="flex"
|
||||||
|
:style="{'color' : item.color}"
|
||||||
|
:class="currentRoute === item.url ? 'active-li' : ''"
|
||||||
|
@click="handleOutLink(item.url,item.type)"
|
||||||
|
>
|
||||||
|
<i :class="item.img"></i>
|
||||||
<span class="text">{{ item.name }}</span>
|
<span class="text">{{ item.name }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -14,17 +20,22 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-section flex">
|
<div class="right-section flex">
|
||||||
<WindowTools/>
|
<WindowTools />
|
||||||
<div class="user flex">
|
<div class="user flex">
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
<el-dropdown
|
||||||
|
class="right-menu-item hover-effect"
|
||||||
|
trigger="click"
|
||||||
|
@command="handleCommand"
|
||||||
|
>
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper">
|
||||||
<img :src="userStore.user.avatar" class="user-avatar" style="float: left;" />
|
<img :src="userStore.user.avatar" class="user-avatar" style="float: left" />
|
||||||
<div style="margin-top: 18px; font-size: 0.8em;"> {{ userStore.user.nickName }}</div>
|
<div style="margin-top: 18px; font-size: 0.8em">{{ userStore.user.nickName }}</div>
|
||||||
</div>
|
</div>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
|
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="changePage('/classReserv')">课程预约</el-dropdown-item>
|
||||||
<el-dropdown-item @click="changePage('/class')">班级中心</el-dropdown-item>
|
<el-dropdown-item @click="changePage('/class')">班级中心</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="logout">
|
<el-dropdown-item divided command="logout">
|
||||||
<span>退出登录</span>
|
<span>退出登录</span>
|
||||||
|
@ -44,15 +55,34 @@ import { useRouter } from 'vue-router'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
import WindowTools from '@/components/window-tools/index.vue'
|
import WindowTools from '@/components/window-tools/index.vue'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import routerStore from '@/store/modules/route'
|
||||||
|
import outLink from '@/utils/linkConfig'
|
||||||
|
const routeHeader = routerStore()
|
||||||
const { ipcRenderer } = window.electron || {}
|
const { ipcRenderer } = window.electron || {}
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const currentRoute = ref('')
|
const currentRoute = ref('')
|
||||||
|
|
||||||
const menus = ref([
|
const handleOutLink = (path, type) => {
|
||||||
|
if (!path) return
|
||||||
|
if (type === 'hash') {
|
||||||
|
router.push(path)
|
||||||
|
} else {
|
||||||
|
// key 对应的 linkConfig.js 外部链接配置
|
||||||
|
let configObj = outLink().getBaseData()
|
||||||
|
let fullPath = configObj.fullPath + path
|
||||||
|
fullPath = fullPath.replaceAll('//', '/')
|
||||||
|
// 通知主进程
|
||||||
|
ipcRenderer.send('openWindow', {
|
||||||
|
key: path,
|
||||||
|
fullPath: fullPath,
|
||||||
|
cookieData: { ...configObj.data }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*const menus = ref([
|
||||||
{
|
{
|
||||||
icon: 'icon-jiaoxueziyuan icon-resource',
|
icon: 'icon-zhuye2 icon-homepage',
|
||||||
name: '主页',
|
name: '主页',
|
||||||
path: '/homepage'
|
path: '/homepage'
|
||||||
},
|
},
|
||||||
|
@ -66,12 +96,12 @@ const menus = ref([
|
||||||
name: '备课',
|
name: '备课',
|
||||||
path: '/prepare'
|
path: '/prepare'
|
||||||
},
|
},
|
||||||
/*{
|
{
|
||||||
icon: 'icon-jiangke1 icon-teach',
|
icon: 'icon-jiangke1 icon-teach',
|
||||||
name: '授课',
|
name: '授课',
|
||||||
path: '/teach'
|
path: '/teach'
|
||||||
}*/
|
}
|
||||||
])
|
])*/
|
||||||
|
|
||||||
// 监听当前路由
|
// 监听当前路由
|
||||||
watch(
|
watch(
|
||||||
|
@ -82,20 +112,19 @@ watch(
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
const changePage = (url) => {
|
const changePage = (url) => {
|
||||||
router.push(url)
|
router.push(url)
|
||||||
}
|
}
|
||||||
function handleCommand(command) {
|
function handleCommand(command) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case "setLayout":
|
case 'setLayout':
|
||||||
setLayout();
|
setLayout()
|
||||||
break;
|
break
|
||||||
case "logout":
|
case 'logout':
|
||||||
logout();
|
logout()
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,20 +133,25 @@ function logout() {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(() => {
|
})
|
||||||
userStore.logOut().then(() => {
|
.then(() => {
|
||||||
// router.replace('/login')
|
userStore
|
||||||
ipcRenderer && ipcRenderer.send('openLoginWindow')
|
.logOut()
|
||||||
}).catch(()=>{
|
.then(() => {
|
||||||
// router.replace('/login')
|
// router.replace('/login')
|
||||||
ipcRenderer && ipcRenderer.send('openLoginWindow')
|
ipcRenderer && ipcRenderer.send('openLoginWindow')
|
||||||
})
|
})
|
||||||
}).catch(() => { });
|
.catch(() => {
|
||||||
|
// router.replace('/login')
|
||||||
|
ipcRenderer && ipcRenderer.send('openLoginWindow')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits(['setLayout'])
|
const emits = defineEmits(['setLayout'])
|
||||||
function setLayout() {
|
function setLayout() {
|
||||||
emits('setLayout');
|
emits('setLayout')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -160,6 +194,10 @@ function setLayout() {
|
||||||
color: #f99b53;
|
color: #f99b53;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-homepage {
|
||||||
|
color: #0a84ff;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-prepare {
|
.icon-prepare {
|
||||||
color: #b088e8;
|
color: #b088e8;
|
||||||
}
|
}
|
||||||
|
@ -175,7 +213,7 @@ function setLayout() {
|
||||||
|
|
||||||
.active-li {
|
.active-li {
|
||||||
background: #d3e3fb;
|
background: #d3e3fb;
|
||||||
color: #409EFF;
|
color: #409eff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +237,6 @@ function setLayout() {
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
|
||||||
.user {
|
.user {
|
||||||
.user-info {
|
.user-info {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
|
|
|
@ -118,7 +118,7 @@ export default {
|
||||||
file.callback(res)
|
file.callback(res)
|
||||||
},
|
},
|
||||||
beforeUpload(file) {
|
beforeUpload(file) {
|
||||||
const MAX_SIZE = 100 * 1024 * 1024 // 2MB
|
const MAX_SIZE = 500 * 1024 * 1024 // 2MB
|
||||||
if (file.size > MAX_SIZE) {
|
if (file.size > MAX_SIZE) {
|
||||||
this.$message.error('文件大小不能超过 100MB!')
|
this.$message.error('文件大小不能超过 100MB!')
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -28,6 +28,10 @@ let uploaderStore = ref(uploaderState())
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-header {
|
.el-header {
|
||||||
padding: 0
|
padding: 0;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
.el-main {
|
||||||
|
--el-main-padding: 0 20px 0 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,236 +0,0 @@
|
||||||
/**
|
|
||||||
* @description: 点击事件
|
|
||||||
*/
|
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
|
||||||
import * as TYPES from '@/constants/draw'
|
|
||||||
import * as fabricStore from '@/store/modules/draw'
|
|
||||||
|
|
||||||
export let updateInkHook = null // ((ink: IInk[]) => void) | null
|
|
||||||
|
|
||||||
export class CanvasClickEvent {
|
|
||||||
isMouseDown = false
|
|
||||||
isSpaceKeyDown = false
|
|
||||||
startPoint // fabric.Point | undefined
|
|
||||||
currentElement = null // The current mouse move draws the element|当前鼠标移动会绘制元素
|
|
||||||
|
|
||||||
mouseDownTime = 0
|
|
||||||
autoDrawInk = [[], [], []] // google auto draw ink Array<Array<number>>
|
|
||||||
isDrawBasic = false // 是否默认绘制
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.initClickEvent()
|
|
||||||
}
|
|
||||||
|
|
||||||
initClickEvent() {
|
|
||||||
const canvas = FabricVue.canvas
|
|
||||||
const useDrawStore = fabricStore.useDrawStore()
|
|
||||||
const useBoardStore = fabricStore.useBoardStore()
|
|
||||||
|
|
||||||
// 事件:按下鼠标
|
|
||||||
canvas?.on('mouse:down', (e) => {
|
|
||||||
console.log(222222222222222222222222222)
|
|
||||||
|
|
||||||
this.isMouseDown = true
|
|
||||||
if (this.isSpaceKeyDown) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.startPoint = e.absolutePointer
|
|
||||||
let currentElement = null
|
|
||||||
|
|
||||||
if (useBoardStore.mode === TYPES.ActionMode.DRAW) {
|
|
||||||
if (useBoardStore.drawType === TYPES.DrawType.Shape) {
|
|
||||||
// switch (useShapeStore.shapeStyle) {
|
|
||||||
// case ShapeStyle.Rect:
|
|
||||||
// currentElement = new RectShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Circle:
|
|
||||||
// currentElement = new CircleShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Line:
|
|
||||||
// currentElement = new LineShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Ellipse:
|
|
||||||
// currentElement = new EllipseShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Triangle:
|
|
||||||
// currentElement = new TriangleShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.ArrowLine:
|
|
||||||
// currentElement = new ArrowLineShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.ArrowOutline:
|
|
||||||
// currentElement = new ArrowOutlineShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Cloud:
|
|
||||||
// currentElement = new CloudShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Tooltips:
|
|
||||||
// currentElement = new TooltipsShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Lightning:
|
|
||||||
// currentElement = new LightningShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Close:
|
|
||||||
// currentElement = new CloseShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Check:
|
|
||||||
// currentElement = new CheckShap(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Info:
|
|
||||||
// currentElement = new InfoShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Backspace:
|
|
||||||
// currentElement = new BackspaceShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Block:
|
|
||||||
// currentElement = new BlockShap(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Speaker:
|
|
||||||
// currentElement = new SpeakerShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Search:
|
|
||||||
// currentElement = new SearchShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.InfoOutline:
|
|
||||||
// currentElement = new InfoOutlineShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Heart:
|
|
||||||
// currentElement = new HeartShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// case ShapeStyle.Alert:
|
|
||||||
// currentElement = new AlertShape(e.absolutePointer)
|
|
||||||
// break
|
|
||||||
// default:
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
} else if (useBoardStore.drawType === TYPES.DrawType.FreeStyle) {
|
|
||||||
switch (useDrawStore.drawStyle) {
|
|
||||||
// case TYPES.DrawStyle.Shape:
|
|
||||||
// currentElement = new ShapeElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.Pixels:
|
|
||||||
// currentElement = new PixelsElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.Text:
|
|
||||||
// currentElement = new DrawTextElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.MultiLine:
|
|
||||||
// currentElement = new MultiLineElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.Reticulate:
|
|
||||||
// currentElement = new ReticulateElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.Rainbow:
|
|
||||||
// currentElement = new RainbowElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.Thorn:
|
|
||||||
// currentElement = new ThornElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.MultiPoint:
|
|
||||||
// currentElement = new MultiPointElement()
|
|
||||||
// break
|
|
||||||
// case TYPES.DrawStyle.Wiggle:
|
|
||||||
// currentElement = new WiggleElement()
|
|
||||||
// break
|
|
||||||
case TYPES.DrawStyle.Basic:
|
|
||||||
// if (useDrawStore.openAutoDraw) {
|
|
||||||
// autoDrawData.resetLoadedSVG()
|
|
||||||
// this.mouseDownTime = new Date().getTime()
|
|
||||||
// }
|
|
||||||
this.isDrawBasic = true
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.currentElement = currentElement
|
|
||||||
})
|
|
||||||
|
|
||||||
// 事件:移动鼠标
|
|
||||||
canvas?.on('mouse:move', (e) => {
|
|
||||||
console.log(222222222222222222222222222)
|
|
||||||
|
|
||||||
if (this.isMouseDown) {
|
|
||||||
// Press space, drag the canvas, stop drawing.
|
|
||||||
if (this.isSpaceKeyDown) {
|
|
||||||
canvas.relativePan(new fabric.Point(e.e.movementX, e.e.movementY))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// two touch disabled drawing on mobile
|
|
||||||
if (FabricVue.evnet?.touchEvent?.isTwoTouch) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.mouseDownTime &&
|
|
||||||
e.absolutePointer?.x &&
|
|
||||||
e.absolutePointer?.y
|
|
||||||
) {
|
|
||||||
this.autoDrawInk[0].push(e.absolutePointer?.x)
|
|
||||||
this.autoDrawInk[1].push(e.absolutePointer?.y)
|
|
||||||
this.autoDrawInk[2].push(new Date().getTime() - this.mouseDownTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
useBoardStore.mode === TYPES.ActionMode.DRAW && this.currentElement
|
|
||||||
) {
|
|
||||||
this.currentElement.addPosition(e.absolutePointer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 事件:松开鼠标
|
|
||||||
canvas?.on('mouse:up', (e) => {
|
|
||||||
console.log(222222222222222222222222222)
|
|
||||||
|
|
||||||
this.isMouseDown = false
|
|
||||||
if (this.autoDrawInk?.[0]?.length > 3) {
|
|
||||||
autoDrawData.addInk([...this.autoDrawInk])
|
|
||||||
updateInkHook?.([...autoDrawData.inks])
|
|
||||||
this.autoDrawInk = [[], [], []]
|
|
||||||
this.mouseDownTime = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.currentElement) {
|
|
||||||
let isDestroy = false
|
|
||||||
if (this.startPoint && e.absolutePointer) {
|
|
||||||
const { x: startX, y: startY } = this.startPoint
|
|
||||||
const { x: endX, y: endY } = e.absolutePointer
|
|
||||||
if (startX === endX && startY === endY) {
|
|
||||||
this.currentElement.destroy()
|
|
||||||
isDestroy = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isDestroy) {
|
|
||||||
if (
|
|
||||||
this.currentElement instanceof LineShape ||
|
|
||||||
this.currentElement instanceof ArrowLineShape
|
|
||||||
) {
|
|
||||||
this.currentElement?.mouseUp()
|
|
||||||
}
|
|
||||||
FabricVue.history?.saveState()
|
|
||||||
}
|
|
||||||
this.currentElement = null
|
|
||||||
}
|
|
||||||
// zdg: 基础画笔 保存数据
|
|
||||||
if (this.isDrawBasic) FabricVue.history?.saveState()
|
|
||||||
})
|
|
||||||
|
|
||||||
canvas?.on('mouse:dblclick', (e) => {
|
|
||||||
if (e?.absolutePointer) {
|
|
||||||
const { x, y } = e.absolutePointer
|
|
||||||
FabricVue.textElement?.loadText(x, y)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setSpaceKeyDownState(isSpaceKeyDown) {
|
|
||||||
this.isSpaceKeyDown = isSpaceKeyDown
|
|
||||||
}
|
|
||||||
|
|
||||||
changeInkHookFn(fn) {
|
|
||||||
updateInkHook = fn
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
import { CanvasClickEvent } from './clickEvent'
|
|
||||||
// import { ObjectEvent } from './objectEvent'
|
|
||||||
// import { CanvasTouchEvent } from './touchEvent'
|
|
||||||
// import { CanvasZoomEvent } from './zoomEvent'
|
|
||||||
// import { WindowEvent } from './windowEvent'
|
|
||||||
|
|
||||||
export class CanvasEvent {
|
|
||||||
clickEvent // CanvasClickEvent
|
|
||||||
zoomEvent // CanvasZoomEvent
|
|
||||||
objectEvent // ObjectEvent
|
|
||||||
windowEvent // WindowEvent
|
|
||||||
touchEvent // CanvasTouchEvent
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
const clickEvent = new CanvasClickEvent()
|
|
||||||
this.clickEvent = clickEvent
|
|
||||||
|
|
||||||
// const zoomEvent = new CanvasZoomEvent()
|
|
||||||
// this.zoomEvent = zoomEvent
|
|
||||||
|
|
||||||
// const objectEvent = new ObjectEvent()
|
|
||||||
// this.objectEvent = objectEvent
|
|
||||||
|
|
||||||
// const windowEvent = new WindowEvent()
|
|
||||||
// this.windowEvent = windowEvent
|
|
||||||
|
|
||||||
// const touchEvent = new CanvasTouchEvent()
|
|
||||||
// this.touchEvent = touchEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEvent() {
|
|
||||||
this.windowEvent.removeWindowEvent()
|
|
||||||
this.touchEvent.removeTouchEvent()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
/**
|
|
||||||
* 历史记录
|
|
||||||
*/
|
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
|
||||||
import * as commUtils from '@/plugins/fabric/utils'
|
|
||||||
import * as bgUtils from '@/plugins/fabric/utils/bg'
|
|
||||||
import { diff, unpatch, patch } from 'jsondiffpatch'
|
|
||||||
import * as fabricStore from '@/store/modules/draw'
|
|
||||||
|
|
||||||
const initState = {}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Operation History
|
|
||||||
*/
|
|
||||||
export class History {
|
|
||||||
diffs = []
|
|
||||||
canvasData = {}
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
const canvas = FabricVue.canvas
|
|
||||||
if (canvas) {
|
|
||||||
const canvasJson = commUtils.getCanvasJSON()
|
|
||||||
this.canvasData = cloneDeep(canvasJson ?? {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 缓存相关数据json
|
|
||||||
saveState() {
|
|
||||||
const canvas = FabricVue?.canvas
|
|
||||||
if (canvas) {
|
|
||||||
const useFileStore = fabricStore.useFileStore()
|
|
||||||
this.diffs = this.diffs.slice(0, this.index)
|
|
||||||
const canvasJson = commUtils.getCanvasJSON()
|
|
||||||
const delta = diff(canvasJson, this.canvasData)
|
|
||||||
this.diffs.push(delta)
|
|
||||||
|
|
||||||
// More than 50 operations, remove initial state
|
|
||||||
if (this.diffs.length > 50) {
|
|
||||||
this.diffs.shift()
|
|
||||||
} else {
|
|
||||||
this.index++
|
|
||||||
}
|
|
||||||
this.canvasData = cloneDeep(canvasJson ?? {})
|
|
||||||
useFileStore.updateBoardData(canvasJson)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 撤销
|
|
||||||
undo() {
|
|
||||||
const canvas = FabricVue?.canvas
|
|
||||||
if (canvas && this.index > 0) {
|
|
||||||
const useFileStore = fabricStore.useFileStore()
|
|
||||||
const delta = this.diffs[this.index - 1]
|
|
||||||
this.index--
|
|
||||||
const canvasJson = patch(this.canvasData, delta)
|
|
||||||
canvas.loadFromJSON(canvasJson, () => {
|
|
||||||
commUtils.handleCanvasJSONLoaded(canvas)
|
|
||||||
|
|
||||||
canvas.requestRenderAll()
|
|
||||||
useFileStore.updateBoardData(canvasJson)
|
|
||||||
this.canvasData = cloneDeep(canvasJson ?? {})
|
|
||||||
FabricVue.triggerHook()
|
|
||||||
|
|
||||||
if ((delta)?.backgroundImage) {
|
|
||||||
handleBackgroundImageWhenCanvasSizeChange()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 退回|重做
|
|
||||||
redo() {
|
|
||||||
const canvas = FabricVue?.canvas
|
|
||||||
if (this.index < this.diffs.length && canvas) {
|
|
||||||
const useFileStore = fabricStore.useFileStore()
|
|
||||||
const delta = this.diffs[this.index]
|
|
||||||
this.index++
|
|
||||||
const canvasJson = unpatch(this.canvasData, delta)
|
|
||||||
canvas.loadFromJSON(canvasJson, () => {
|
|
||||||
handleCanvasJSONLoaded(canvas)
|
|
||||||
canvas.requestRenderAll()
|
|
||||||
|
|
||||||
useFileStore.updateBoardData(canvasJson)
|
|
||||||
this.canvasData = cloneDeep(canvasJson ?? {})
|
|
||||||
FabricVue.triggerHook()
|
|
||||||
|
|
||||||
if ((delta)?.backgroundImage) {
|
|
||||||
bgUtils.handleBackgroundImageWhenCanvasSizeChange()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clean() {
|
|
||||||
const useFileStore = fabricStore.useFileStore()
|
|
||||||
const useBoardStore = fabricStore.useBoardStore()
|
|
||||||
FabricVue?.canvas?.clear()
|
|
||||||
this.index = 0
|
|
||||||
this.diffs = []
|
|
||||||
this.canvasData = {}
|
|
||||||
useFileStore.updateBoardData(initState)
|
|
||||||
useBoardStore.updateBackgroundColor('#ffffff')
|
|
||||||
useBoardStore.cleanBackgroundImage()
|
|
||||||
}
|
|
||||||
|
|
||||||
initHistory() {
|
|
||||||
const canvas = FabricVue.canvas
|
|
||||||
if (canvas) {
|
|
||||||
const canvasJson = commUtils.getCanvasJSON()
|
|
||||||
this.canvasData = canvasJson
|
|
||||||
this.index = 0
|
|
||||||
this.diffs = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对象克隆
|
|
||||||
function cloneDeep(obj = {}) {
|
|
||||||
return JSON.parse(JSON.stringify(obj))
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,27 +0,0 @@
|
||||||
import { fabric } from 'fabric'
|
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
|
||||||
import * as fabricUtils from '@/plugins/fabric/utils/draw'
|
|
||||||
import * as fabricStore from '@/store/modules/draw'
|
|
||||||
const useDrawStore = fabricStore.useDrawStore()
|
|
||||||
/**
|
|
||||||
* 基础 画笔
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
export const renderPencilBrush = () => {
|
|
||||||
const canvas = FabricVue.canvas
|
|
||||||
if (!canvas) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const pencilBrush = new fabric.PencilBrush(canvas)
|
|
||||||
canvas.isDrawingMode = true
|
|
||||||
canvas.freeDrawingBrush = pencilBrush
|
|
||||||
canvas.freeDrawingBrush.width = fabricUtils.getDrawWidth()
|
|
||||||
canvas.freeDrawingBrush.color = useDrawStore.drawColors[0]
|
|
||||||
canvas.freeDrawingBrush.shadow = new fabric.Shadow({
|
|
||||||
blur: fabricUtils.getShadowWidth(),
|
|
||||||
offsetX: 0,
|
|
||||||
offsetY: 0,
|
|
||||||
color: useDrawStore.shadowColor
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: 材质
|
|
||||||
*/
|
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
|
||||||
import * as TYPES from '@/constants/draw'
|
|
||||||
import * as fabricStore from '@/store/modules/draw'
|
|
||||||
|
|
||||||
export class Material {
|
|
||||||
initPromise = null // Initialize promise
|
|
||||||
crayonImage = null
|
|
||||||
carbonImage = null
|
|
||||||
clothImage = null
|
|
||||||
oilImage = null
|
|
||||||
crayonDarkImage = null
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.initMaterial()
|
|
||||||
}
|
|
||||||
|
|
||||||
async initMaterial() {
|
|
||||||
|
|
||||||
this.initPromise = Promise.all([
|
|
||||||
this.loadImage(TYPES.MATERIAL_TYPE.CRAYON),
|
|
||||||
this.loadImage(TYPES.MATERIAL_TYPE.CARBON),
|
|
||||||
this.loadImage(TYPES.MATERIAL_TYPE.CLOTH),
|
|
||||||
this.loadImage(TYPES.MATERIAL_TYPE.OIL),
|
|
||||||
this.loadImage(TYPES.MATERIAL_TYPE.CRAYON_DARK)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
render({materialType, color}) {
|
|
||||||
const useDrawStore = fabricStore.useDrawStore()
|
|
||||||
materialType = materialType || useDrawStore.materialType
|
|
||||||
color = color || useDrawStore.drawColors[0]
|
|
||||||
this.initPromise?.then(() => {
|
|
||||||
switch (materialType) {
|
|
||||||
case TYPES.MATERIAL_TYPE.CRAYON:
|
|
||||||
this.renderMaterial(this.crayonImage, color)
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.CARBON:
|
|
||||||
this.renderMaterial(this.carbonImage, color)
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.CLOTH:
|
|
||||||
this.renderMaterial(this.clothImage, color)
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.OIL:
|
|
||||||
this.renderMaterial(this.oilImage, color)
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.CRAYON_DARK:
|
|
||||||
this.renderMaterial(this.crayonDarkImage, color)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMaterial(materialImg, color, opacity = 1) {
|
|
||||||
if (FabricVue.canvas) {
|
|
||||||
const useDrawStore = fabricStore.useDrawStore()
|
|
||||||
const patternBrush = new fabric.PatternBrush(FabricVue.canvas)
|
|
||||||
const patternCanvas = document.createElement('canvas')
|
|
||||||
patternCanvas.width = patternCanvas.height = 100
|
|
||||||
const context = patternCanvas.getContext('2d')
|
|
||||||
if (context) {
|
|
||||||
context.fillStyle = color
|
|
||||||
context.fillRect(0, 0, 100, 100)
|
|
||||||
if (materialImg) {
|
|
||||||
context.globalAlpha = opacity
|
|
||||||
context.drawImage(materialImg, 0, 0, 100, 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
patternBrush.getPatternSrc = () => {
|
|
||||||
return patternCanvas
|
|
||||||
}
|
|
||||||
patternBrush.getPatternSrcFunction = () => {
|
|
||||||
return patternCanvas
|
|
||||||
}
|
|
||||||
FabricVue.canvas.freeDrawingBrush = patternBrush
|
|
||||||
FabricVue.canvas.freeDrawingBrush.width = getDrawWidth()
|
|
||||||
FabricVue.canvas.freeDrawingBrush.shadow = new fabric.Shadow({
|
|
||||||
blur: getShadowWidth(),
|
|
||||||
offsetX: 0,
|
|
||||||
offsetY: 0,
|
|
||||||
color: useDrawStore.shadowColor
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadImage(imageName) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const img = new Image()
|
|
||||||
// img.src = formatPublicUrl(`pattern/${imageName}.png`)
|
|
||||||
img.src = `pattern/${imageName}.png`
|
|
||||||
img.onload = () => {
|
|
||||||
switch (imageName) {
|
|
||||||
case TYPES.MATERIAL_TYPE.CARBON:
|
|
||||||
this.carbonImage = img
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.CRAYON:
|
|
||||||
this.crayonImage = img
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.CLOTH:
|
|
||||||
this.clothImage = img
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.OIL:
|
|
||||||
this.oilImage = img
|
|
||||||
break
|
|
||||||
case TYPES.MATERIAL_TYPE.CRAYON_DARK:
|
|
||||||
this.crayonDarkImage = img
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
resolve(true)
|
|
||||||
}
|
|
||||||
img.onerror = () => {
|
|
||||||
resolve(false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const material = new Material()
|
|
|
@ -1,98 +0,0 @@
|
||||||
/**
|
|
||||||
* 多个材质画刷
|
|
||||||
* @param {*} params
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
|
||||||
import * as TYPES from '@/constants/draw'
|
|
||||||
import * as fabricUtils from '@/plugins/fabric/utils/draw'
|
|
||||||
import * as fabricStore from '@/store/modules/draw'
|
|
||||||
|
|
||||||
const COLOR_WIDTH = 5
|
|
||||||
|
|
||||||
export const renderMultiColor = (params={}) => {
|
|
||||||
const canvas = FabricVue.canvas
|
|
||||||
if (!canvas) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const useDrawStore = fabricStore.useDrawStore()
|
|
||||||
const colors = params?.colors ?? useDrawStore.drawColors
|
|
||||||
const type = params?.type ?? useDrawStore.multiColorType
|
|
||||||
|
|
||||||
const patternBrush = new fabric.PatternBrush(canvas)
|
|
||||||
const patternCanvas = document.createElement('canvas')
|
|
||||||
const context = patternCanvas.getContext('2d')
|
|
||||||
if (context) {
|
|
||||||
switch (type) {
|
|
||||||
case TYPES.MultiColorType.COL:
|
|
||||||
renderCol(patternCanvas, context, colors)
|
|
||||||
break
|
|
||||||
case TYPES.MultiColorType.ROW:
|
|
||||||
renderRow(patternCanvas, context, colors)
|
|
||||||
break
|
|
||||||
case TYPES.MultiColorType.CIRCLE:
|
|
||||||
renderCircle(patternCanvas, context, colors)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
patternBrush.getPatternSrc = () => {
|
|
||||||
return patternCanvas
|
|
||||||
}
|
|
||||||
patternBrush.getPatternSrcFunction = () => {
|
|
||||||
return patternCanvas
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.isDrawingMode = true
|
|
||||||
canvas.freeDrawingBrush = patternBrush
|
|
||||||
canvas.freeDrawingBrush.width = fabricUtils.getDrawWidth()
|
|
||||||
canvas.freeDrawingBrush.shadow = new fabric.Shadow({
|
|
||||||
blur: fabricUtils.getShadowWidth(),
|
|
||||||
offsetX: 0,
|
|
||||||
offsetY: 0,
|
|
||||||
color: useDrawStore.shadowColor
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderCol(canvas, context, colors=[]) {
|
|
||||||
canvas.width = COLOR_WIDTH * colors.length
|
|
||||||
canvas.height = 20
|
|
||||||
colors.forEach((color, i) => {
|
|
||||||
context.fillStyle = color
|
|
||||||
context.fillRect(COLOR_WIDTH * i, 0, COLOR_WIDTH, 20)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderRow(canvas, context, colors=[]) {
|
|
||||||
canvas.width = 20
|
|
||||||
canvas.height = COLOR_WIDTH * colors.length
|
|
||||||
colors.forEach((color, i) => {
|
|
||||||
context.fillStyle = color
|
|
||||||
context.fillRect(0, COLOR_WIDTH * i, 20, COLOR_WIDTH)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderCircle(canvas, context, colors=[]) {
|
|
||||||
const radius = 10
|
|
||||||
const padding = 5
|
|
||||||
const n = colors.length
|
|
||||||
|
|
||||||
const canvasWidth = 2 * padding + n * radius * 2 + (n - 1) * padding
|
|
||||||
canvas.width = canvasWidth
|
|
||||||
canvas.height = radius * 2 + 2 * padding
|
|
||||||
|
|
||||||
let x = padding + radius
|
|
||||||
const y = padding + radius
|
|
||||||
|
|
||||||
// render multi circle
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
context.beginPath()
|
|
||||||
context.fillStyle = colors[i]
|
|
||||||
context.arc(x, y, radius, 0, Math.PI * 2)
|
|
||||||
context.closePath()
|
|
||||||
context.fill()
|
|
||||||
x += 2 * radius + padding
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
/**
|
|
||||||
* @description 封装fabric js
|
|
||||||
*/
|
|
||||||
import { fabric } from 'fabric'
|
|
||||||
|
|
||||||
// 当前使用到的常量|类型(枚举)
|
|
||||||
export class TYPES {
|
|
||||||
static ActionMode = {
|
|
||||||
DRAW: 'draw', // 画笔模式
|
|
||||||
ERASE: 'erase', // 橡皮擦模式
|
|
||||||
SELECT: 'select', // 选择模式
|
|
||||||
Board: 'board' // 画板模式
|
|
||||||
}
|
|
||||||
// 画笔类型
|
|
||||||
static DrawType = {
|
|
||||||
FreeStyle: 'freeStyle',
|
|
||||||
Shape: 'shape'
|
|
||||||
}
|
|
||||||
// 画笔样式
|
|
||||||
static DrawStyle = {
|
|
||||||
Basic: 'basic',
|
|
||||||
Rainbow: 'rainbow',
|
|
||||||
Shape: 'shape',
|
|
||||||
Material: 'material',
|
|
||||||
Pixels: 'pixels',
|
|
||||||
MultiColor: 'multiColor',
|
|
||||||
Text: 'text',
|
|
||||||
MultiLine: 'multiLine',
|
|
||||||
Reticulate: 'reticulate',
|
|
||||||
MultiPoint: 'multiPoint',
|
|
||||||
Wiggle: 'wiggle',
|
|
||||||
Thorn: 'thorn'
|
|
||||||
}
|
|
||||||
// 各种形状
|
|
||||||
static DrawShape = {
|
|
||||||
Bubble: 'bubble',
|
|
||||||
Star: 'star',
|
|
||||||
Love: 'love',
|
|
||||||
Butterfly: 'butterfly',
|
|
||||||
Snow: 'snow',
|
|
||||||
Music: 'music',
|
|
||||||
Sun: 'sun',
|
|
||||||
Moon: 'moon',
|
|
||||||
Leaf: 'leaf',
|
|
||||||
Flower: 'flower'
|
|
||||||
}
|
|
||||||
// 材质类型
|
|
||||||
static MATERIAL_TYPE = {
|
|
||||||
CRAYON: 'crayon',
|
|
||||||
CARBON: 'carbon',
|
|
||||||
CLOTH: 'cloth',
|
|
||||||
OIL: 'oil',
|
|
||||||
CRAYON_DARK: 'crayonDark'
|
|
||||||
}
|
|
||||||
// 多颜色类型
|
|
||||||
static MultiColorType = {
|
|
||||||
COL: 'col',
|
|
||||||
ROW: 'row',
|
|
||||||
CIRCLE: 'circle'
|
|
||||||
}
|
|
||||||
// 画笔元素
|
|
||||||
static FREESTYLE_ELEMENT_CUSTOM_TYPE = {
|
|
||||||
IMAGE: 'image',
|
|
||||||
I_TEXT: 'itext',
|
|
||||||
RAINBOW: 'rainbow',
|
|
||||||
SHAPE: 'shape',
|
|
||||||
PIXELS: 'pixels',
|
|
||||||
DRAW_TEXT: 'drawText',
|
|
||||||
MULTI_LINE: 'multiLine',
|
|
||||||
RETICULATE: 'reticulate',
|
|
||||||
MULTI_POINT: 'multiPoint',
|
|
||||||
WIGGLE: 'wiggle',
|
|
||||||
THORN: 'thorn'
|
|
||||||
}
|
|
||||||
// 形状元素
|
|
||||||
static SHAPE_ELEMENT_CUSTOM_TYPE = {
|
|
||||||
SHAPE_LINE: 'shapeLine',
|
|
||||||
SHAPE_RECT: 'shapeRect',
|
|
||||||
SHAPE_CIRCLE: 'shapeCircle',
|
|
||||||
SHAPE_ELLIPSE: 'shapeEllipse',
|
|
||||||
SHAPE_TRIANGLE: 'shapeTriangle',
|
|
||||||
SHAPE_ARROW_LINE: 'shapeArrowLine',
|
|
||||||
SHAPE_ARROW_OUTLINE: 'shapeArrowOutline',
|
|
||||||
SHAPE_CLOUD: 'shapeCloud',
|
|
||||||
SHAPE_TOOLTIPS: 'shapeTooltips',
|
|
||||||
SHAPE_LIGHTNING: 'shapeLightning',
|
|
||||||
SHAPE_CLOSE: 'shapeClose',
|
|
||||||
SHAPE_CHECK: 'shapeCheck',
|
|
||||||
SHAPE_INFO: 'shapeInfo',
|
|
||||||
SHAPE_BACKSPACE: 'shapeBackspace',
|
|
||||||
SHAPE_BLOCK: 'shapeBlock',
|
|
||||||
SHAPE_SPEAKER: 'shapeSpeaker',
|
|
||||||
SHAPE_SEARCH: 'shapeSearch',
|
|
||||||
SHAPE_INFO_OUTLINE: 'shapeInfoOutline',
|
|
||||||
SHAPE_HEART: 'shapeHeart',
|
|
||||||
SHAPE_ALERT: 'shapeAlert'
|
|
||||||
}
|
|
||||||
// 元素类型
|
|
||||||
static ELEMENT_CUSTOM_TYPE = {
|
|
||||||
...this.FREESTYLE_ELEMENT_CUSTOM_TYPE,
|
|
||||||
...this.SHAPE_ELEMENT_CUSTOM_TYPE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 自由绘画 FreeStyle
|
|
||||||
// export class FreeStyle {
|
|
||||||
// }
|
|
||||||
export const FreeStyle = {
|
|
||||||
renderPencilBrush: (canvas) => {
|
|
||||||
const pencilBrush = new fabric.PencilBrush(canvas)
|
|
||||||
canvas.isDrawingMode = true
|
|
||||||
canvas.freeDrawingBrush = pencilBrush
|
|
||||||
canvas.freeDrawingBrush.width = fabricUtils.getDrawWidth()
|
|
||||||
canvas.freeDrawingBrush.color = useDrawStore.drawColors[0]
|
|
||||||
canvas.freeDrawingBrush.shadow = new fabric.Shadow({
|
|
||||||
blur: fabricUtils.getShadowWidth(),
|
|
||||||
offsetX: 0,
|
|
||||||
offsetY: 0,
|
|
||||||
color: useDrawStore.shadowColor
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 封装fabric-canvas
|
|
||||||
export class fabricVue {
|
|
||||||
canvas = null // fabric-canvas 对象
|
|
||||||
evnet = null // 事件对象
|
|
||||||
history = null // 历史记录
|
|
||||||
textElement // 文本节点
|
|
||||||
hookFn = [] // 钩子
|
|
||||||
defConfig // 默认配置-canvas
|
|
||||||
drawConfig // 默认配置-画笔
|
|
||||||
boardConfig // 默认配置-画板
|
|
||||||
publicConfig // 公共属性-配置
|
|
||||||
|
|
||||||
/** 构造函数 */
|
|
||||||
constructor() {
|
|
||||||
// this.textElement = new TextElement() // 创建文本节点
|
|
||||||
const initLanguage = ['en', 'en-US', 'en-us'].includes(navigator.language) ? 'en' : 'zh'
|
|
||||||
this.drawConfig = { // 默认配置
|
|
||||||
drawWidth: 1,
|
|
||||||
drawColors: ['#000000'],
|
|
||||||
shadowWidth: 0,
|
|
||||||
shadowColor: '#000000',
|
|
||||||
drawTextValue: 'draw',
|
|
||||||
drawStyle: TYPES.DrawStyle.Basic,
|
|
||||||
drawShapeCount: 2,
|
|
||||||
materialType: TYPES.MATERIAL_TYPE.CRAYON,
|
|
||||||
drawShape: TYPES.DrawShape.Bubble,
|
|
||||||
eraserWidth: 20,
|
|
||||||
multiColorType: TYPES.MultiColorType.COL,
|
|
||||||
textFontFamily: 'Georgia',
|
|
||||||
openAutoDraw: false,
|
|
||||||
fontStyles: [],
|
|
||||||
}
|
|
||||||
this.boardConfig = { // 默认配置
|
|
||||||
mode: TYPES.ActionMode.DRAW,
|
|
||||||
drawType: TYPES.DrawType.FreeStyle,
|
|
||||||
language: initLanguage,
|
|
||||||
canvasWidth: 1,
|
|
||||||
canvasHeight: 1,
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 1)',
|
|
||||||
backgroundOpacity: 1,
|
|
||||||
hasBackgroundImage: false,
|
|
||||||
backgroundImageOpacity: 1,
|
|
||||||
isObjectCaching: true,
|
|
||||||
openGuideLine: false,
|
|
||||||
}
|
|
||||||
this.defConfig = { // 默认配置-canvas
|
|
||||||
// 它用于指定选中对象时显示的选择框的颜色
|
|
||||||
selectionColor: 'rgba(101, 204, 138, 0.3)',
|
|
||||||
// 用于控制在组合(group)对象时是否保留每个对象的堆叠顺序
|
|
||||||
preserveObjectStacking: true,
|
|
||||||
// 它用于控制是否在高分辨率显示器(例如 Retina 显示器)上启用图像的像素级缩放
|
|
||||||
enableRetinaScaling: true,
|
|
||||||
// 锁定背景,不受缩放影响 false
|
|
||||||
backgroundVpt: false,
|
|
||||||
// 默认全屏模式
|
|
||||||
width: window.innerWidth,
|
|
||||||
height: window.innerHeight,
|
|
||||||
}
|
|
||||||
this.publicConfig = { // 公共属性配置
|
|
||||||
Object: { // 默认配置-对象上
|
|
||||||
// 设置对象边框的颜色(Rect、Circle、Ellipse、Path)
|
|
||||||
borderColor: '#65CC8A',
|
|
||||||
// 设置对象角落的颜色(Rect、RoundRect、Circle 和 Ellipse 类型的对象)
|
|
||||||
cornerColor: '#65CC8A',
|
|
||||||
// 设置角落的形状(Rect、RoundRect、Circle 和 Ellipse 类型的对象)
|
|
||||||
// circle rect round [圆形,直角(默认),圆角]
|
|
||||||
cornerStyle: 'circle',
|
|
||||||
// 设置边框虚线的样式(Rect、Circle、Ellipse、Path)
|
|
||||||
borderDashArray: [3, 3],
|
|
||||||
// 矩形(Rect)、圆角矩形(RoundRect)、圆形(Circle)和椭圆形(Ellipse)对象的角落是否透明
|
|
||||||
transparentCorners: false
|
|
||||||
},
|
|
||||||
Line: {
|
|
||||||
// 设置边缘连接方式为 miter bevel round|尖角(默认) 斜角切割线 圆形连接
|
|
||||||
strokeLineJoin: 'round',
|
|
||||||
// 设置结束连接方式为 butt round square|直角(默认) 圆形 正方形
|
|
||||||
strokeLineCap: 'round'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 初始化canvas
|
|
||||||
* @param {*} canvasEl canvas元素
|
|
||||||
* @returns boolean 是否初始化成功
|
|
||||||
*/
|
|
||||||
initCanvas(canvasEl, option = {}) {
|
|
||||||
return new Promise(async(resolve) => {
|
|
||||||
if (!canvasEl) resolve(false)
|
|
||||||
this.canvas = new fabric.Canvas(canvasEl, {
|
|
||||||
...this.defConfig, ...option
|
|
||||||
})
|
|
||||||
// this.evnet = new CanvasEvent() // 创建相关事件
|
|
||||||
// this.history = new History() // 创建历史记录
|
|
||||||
// await this.initCanvasStorage()
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
export const FabricVue = new fabricVue()
|
|
||||||
export default FabricVue
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
|
|
||||||
/**
|
|
||||||
* 工具:背景相关的颜色、背景图片
|
|
||||||
*/
|
|
||||||
import { fabric } from 'fabric'
|
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
|
||||||
import * as fabricStore from '@/store/modules/draw'
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事件: 缩放变化-更新背景图片
|
|
||||||
*/
|
|
||||||
export const handleBackgroundImageWhenCanvasSizeChange = (isRender = true) => {
|
|
||||||
const backgroundImage = FabricVue?.canvas?.backgroundImage
|
|
||||||
if (backgroundImage) {
|
|
||||||
setBackgroundImage(backgroundImage)
|
|
||||||
if (isRender) {
|
|
||||||
FabricVue.canvas?.requestRenderAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 更新 fabric 加载画布背景图片
|
|
||||||
* @param {*} data string
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const updateCanvasBackgroundImage = (data) => {
|
|
||||||
const canvas = FabricVue.canvas
|
|
||||||
if (!canvas) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fabric.Image.fromURL(
|
|
||||||
data,
|
|
||||||
(image) => {
|
|
||||||
setBackgroundImage(image)
|
|
||||||
|
|
||||||
canvas.setBackgroundImage(image, () => {
|
|
||||||
FabricVue.render()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{crossOrigin: 'anonymous'}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 设置-更新背景图片
|
|
||||||
* @param {*} image fabric.Image
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const setBackgroundImage = (image) => {
|
|
||||||
const canvas = FabricVue?.canvas
|
|
||||||
if (!canvas) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const useBoardStore = fabricStore.useBoardStore()
|
|
||||||
const canvasWidth = canvas.getWidth()
|
|
||||||
const canvasHeight = canvas.getHeight()
|
|
||||||
|
|
||||||
const imgWidth = image.width
|
|
||||||
const imgHeight = image.height
|
|
||||||
|
|
||||||
const scaleWidth = canvasWidth / imgWidth
|
|
||||||
const scaleHeight = canvasHeight / imgHeight
|
|
||||||
|
|
||||||
const scale = Math.min(scaleWidth, scaleHeight)
|
|
||||||
image.scale(scale)
|
|
||||||
|
|
||||||
const imgLeft = canvasWidth / 2 - (imgWidth * scale) / 2
|
|
||||||
const imgTop = canvasHeight / 2 - (imgHeight * scale) / 2
|
|
||||||
|
|
||||||
image.set({
|
|
||||||
left: imgLeft,
|
|
||||||
top: imgTop,
|
|
||||||
originX: 'left',
|
|
||||||
originY: 'top',
|
|
||||||
opacity: useBoardStore.backgroundImageOpacity
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/**
|
|
||||||
* hexToRgba
|
|
||||||
* @param hex hexadecimal color
|
|
||||||
* @param alpha
|
|
||||||
* @returns rgba
|
|
||||||
*/
|
|
||||||
export function hexToRgba(hex, alpha = 1) {
|
|
||||||
const bigint = parseInt(hex.slice(1), 16)
|
|
||||||
const r = (bigint >> 16) & 255
|
|
||||||
const g = (bigint >> 8) & 255
|
|
||||||
const b = bigint & 255
|
|
||||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function rgbaToHex(rgba) {
|
|
||||||
if (!rgba) {
|
|
||||||
return rgba
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = rgba.match(/\d+/g)
|
|
||||||
const hex = `#${(
|
|
||||||
(1 << 24) +
|
|
||||||
(parseInt(values[0]) << 16) +
|
|
||||||
(parseInt(values[1]) << 8) +
|
|
||||||
parseInt(values[2])
|
|
||||||
)
|
|
||||||
.toString(16)
|
|
||||||
.slice(1)}`
|
|
||||||
return hex
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get rgba alpha
|
|
||||||
* @param rgbaString rgba color
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function getAlphaFromRgba(rgbaString) {
|
|
||||||
const match = rgbaString.match(/rgba?\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/)
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
return parseFloat(match[4])
|
|
||||||
} else {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the rgba of the new alpha
|
|
||||||
* @param rgbaColor
|
|
||||||
* @param newAlpha
|
|
||||||
* @returns newRgbaColor
|
|
||||||
*/
|
|
||||||
export function changeAlpha(rgbaColor, newAlpha) {
|
|
||||||
const match = rgbaColor.match(/rgba?\((.*?)\)/)
|
|
||||||
if (!match) {
|
|
||||||
return rgbaColor
|
|
||||||
}
|
|
||||||
|
|
||||||
const rgbPart = match[1].split(',').map((i) => i.trim())
|
|
||||||
newAlpha = Math.min(1, Math.max(0, newAlpha))
|
|
||||||
|
|
||||||
const newRgbaColor = `rgba(${rgbPart[0]}, ${rgbPart[1]}, ${rgbPart[2]}, ${newAlpha})`
|
|
||||||
return newRgbaColor
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isHexColor(color) {
|
|
||||||
return /^#([0-9A-Fa-f]{3}){1,2}$/.test(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isRgbaColor(color) {
|
|
||||||
return /^rgba?\((\d+,\s*){2}\d+,\s*(0(\.\d+)?|1(\.0)?)\)$/.test(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getColorFormat(color) {
|
|
||||||
if (isHexColor(color)) {
|
|
||||||
return 'hex'
|
|
||||||
} else if (isRgbaColor(color)) {
|
|
||||||
return 'rgba'
|
|
||||||
} else {
|
|
||||||
return 'unknown'
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
import * as fabricStore from '@/store/modules/draw'
|
|
||||||
import { FabricVue } from "@/plugins/fabric";
|
|
||||||
// import { v4 as uuidv4 } from 'uuid'
|
|
||||||
const useDrawStore = fabricStore.useDrawStore()
|
|
||||||
|
|
||||||
export const getDrawWidth = (width) => {
|
|
||||||
return (
|
|
||||||
(width ?? useDrawStore.drawWidth) /
|
|
||||||
(FabricVue.canvas?.getZoom() ?? 1)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getEraserWidth = (width) => {
|
|
||||||
return (
|
|
||||||
(width ?? useDrawStore.eraserWidth) /
|
|
||||||
(FabricVue.canvas?.getZoom() ?? 1)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getShadowWidth = (width) => {
|
|
||||||
return (
|
|
||||||
(width ?? useDrawStore.shadowWidth) /
|
|
||||||
(FabricVue.canvas?.getZoom() ?? 1)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// export const setObjectAttr = (obj, type) => {
|
|
||||||
// const id = uuidv4()
|
|
||||||
// obj.set({
|
|
||||||
// id,
|
|
||||||
// _customType: type
|
|
||||||
// })
|
|
||||||
// }
|
|
|
@ -1,165 +0,0 @@
|
||||||
/**
|
|
||||||
* 工具类
|
|
||||||
*/
|
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
|
||||||
import * as TYPES from '@/constants/draw'
|
|
||||||
import * as lineUtils from '@/plugins/fabric/utils/shape/line'
|
|
||||||
import * as arrowLineUtils from '@/plugins/fabric/utils/shape/arrowLine'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* compare version
|
|
||||||
* @param v1
|
|
||||||
* @param v2
|
|
||||||
* @returns 0: v1 === v2; 1: v1 > v2; -1: v1 < v2
|
|
||||||
*/
|
|
||||||
export const compareVersion = (v1, v2) => {
|
|
||||||
const v1s = v1.split('.')
|
|
||||||
const v2s = v2.split('.')
|
|
||||||
const len = Math.max(v1s.length, v2s.length)
|
|
||||||
|
|
||||||
while (v1s.length < len) {
|
|
||||||
v1s.push('0')
|
|
||||||
}
|
|
||||||
while (v2s.length < len) {
|
|
||||||
v1s.push('0')
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
const num1 = parseInt(v1s[i])
|
|
||||||
const num2 = parseInt(v2s[i])
|
|
||||||
|
|
||||||
if (num1 > num2) {
|
|
||||||
return 1
|
|
||||||
} else if (num1 < num2) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get random integer
|
|
||||||
* @param min min range
|
|
||||||
* @param max max range
|
|
||||||
*/
|
|
||||||
export const getRandomInt = (min, max) => {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get random float
|
|
||||||
* @param min min range
|
|
||||||
* @param max max range
|
|
||||||
*/
|
|
||||||
export const getRandomFloat = (min, max) => {
|
|
||||||
return Math.random() * (max - min) + min
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* handle static file paths in the public folder
|
|
||||||
* @param originUrl
|
|
||||||
* @returns publicDir + originUrl
|
|
||||||
*/
|
|
||||||
export const formatPublicUrl = (originUrl) => {
|
|
||||||
if (originUrl && typeof originUrl === 'string') {
|
|
||||||
return `${import.meta.env.BASE_URL}${originUrl}`
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the distance between two points
|
|
||||||
* @param start
|
|
||||||
* @param end
|
|
||||||
* @returns distance
|
|
||||||
*/
|
|
||||||
export const getDistance = (start, end) => {
|
|
||||||
return Math.sqrt(Math.pow(start.x - end.x, 2) + Math.pow(start.y - end.y, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* is it mobile
|
|
||||||
*/
|
|
||||||
export const isMobile = () => {
|
|
||||||
return /phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone|Mobi|Android|iPhone|iPad/i.test(
|
|
||||||
navigator.userAgent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// 生成UUID
|
|
||||||
export const getUUID = () => {
|
|
||||||
const timestamp = Date.now();
|
|
||||||
// 时间戳部分
|
|
||||||
const timestampPart = timestamp.toString(16).padStart(12, '0');
|
|
||||||
|
|
||||||
// 生成随机数
|
|
||||||
const rnd = crypto.getRandomValues(new Uint8Array(4));
|
|
||||||
const randomPart = rnd.map(byte => byte.toString(16).padStart(2, '0')).join('');
|
|
||||||
|
|
||||||
// 应用变体和版本标识符
|
|
||||||
const version = (timestamp >= 0x0100000000000000) ? (timestamp & 0x00000000FFFF0000) : 0x4000;
|
|
||||||
const variant = (rnd[0] & 0x0F) | 0x80;
|
|
||||||
|
|
||||||
// 合并所有部分并返回
|
|
||||||
return `${timestampPart}-${randomPart}-${version.toString(16).padStart(4, '0')}-${variant.toString(16).padStart(4, '0')}-${rnd[3].toString(16).padStart(4, '0')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** loadCanvas */
|
|
||||||
export const getCanvasJSON = () => {
|
|
||||||
const canvas = FabricVue?.canvas
|
|
||||||
if (canvas) {
|
|
||||||
return (
|
|
||||||
// 返回额外属性
|
|
||||||
canvas.toDatalessJSON([
|
|
||||||
'id',
|
|
||||||
'_customType',
|
|
||||||
'perPixelTargetFind',
|
|
||||||
'objectCaching'
|
|
||||||
]) ?? {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handling canvas json loaded data
|
|
||||||
* Used to initialize undo redo
|
|
||||||
* @param canvas fabric.Canvas
|
|
||||||
*/
|
|
||||||
export const handleCanvasJSONLoaded = (canvas) => {
|
|
||||||
canvas.getObjects().forEach((obj) => {
|
|
||||||
if (obj._customType === TYPES.ELEMENT_CUSTOM_TYPE.SHAPE_LINE) {
|
|
||||||
const points = obj.points
|
|
||||||
const lastControl = points.length - 1
|
|
||||||
obj.controls = points.reduce(function (acc, point, index) {
|
|
||||||
acc['p' + index] = new fabric.Control({
|
|
||||||
positionHandler: lineUtils.polygonPositionHandler,
|
|
||||||
actionHandler: lineUtils.anchorWrapper(
|
|
||||||
index > 0 ? index - 1 : lastControl,
|
|
||||||
lineUtils.actionHandler
|
|
||||||
),
|
|
||||||
actionName: 'polylineEndPoint',
|
|
||||||
pointIndex: index
|
|
||||||
})
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj._customType === TYPES.ELEMENT_CUSTOM_TYPE.SHAPE_ARROW_LINE) {
|
|
||||||
const paths = (obj).path
|
|
||||||
obj.controls = paths
|
|
||||||
.slice(0, paths.length - 4)
|
|
||||||
.reduce(function (acc, point, index) {
|
|
||||||
acc['p' + index] = new fabric.Control({
|
|
||||||
positionHandler: arrowLineUtils.pathPositionHandler,
|
|
||||||
actionHandler: arrowLineUtils.anchorWrapper(
|
|
||||||
index > 0 ? index - 1 : paths.length - 5,
|
|
||||||
arrowLineUtils.actionHandler
|
|
||||||
),
|
|
||||||
actionName: 'pathEndPoint',
|
|
||||||
pointIndex: index
|
|
||||||
})
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,164 +0,0 @@
|
||||||
import { fabric } from 'fabric'
|
|
||||||
|
|
||||||
export function calculateArrowSlidePath(
|
|
||||||
paths,
|
|
||||||
startX,
|
|
||||||
startY,
|
|
||||||
endX,
|
|
||||||
endY
|
|
||||||
) {
|
|
||||||
const angleRad = Math.atan2(endY - startY, endX - startX)
|
|
||||||
const arrowWidthHalf = 10
|
|
||||||
|
|
||||||
const arrowLeftX = endX - arrowWidthHalf * Math.cos(angleRad - Math.PI / 6)
|
|
||||||
const arrowLeftY = endY - arrowWidthHalf * Math.sin(angleRad - Math.PI / 6)
|
|
||||||
const arrowRightX = endX - arrowWidthHalf * Math.cos(angleRad + Math.PI / 6)
|
|
||||||
const arrowRightY = endY - arrowWidthHalf * Math.sin(angleRad + Math.PI / 6)
|
|
||||||
|
|
||||||
paths[paths.length - 4][1] = endX
|
|
||||||
paths[paths.length - 4][2] = endY
|
|
||||||
paths[paths.length - 3][1] = arrowLeftX
|
|
||||||
paths[paths.length - 3][2] = arrowLeftY
|
|
||||||
paths[paths.length - 2][1] = endX
|
|
||||||
paths[paths.length - 2][2] = endY
|
|
||||||
paths[paths.length - 1][1] = arrowRightX
|
|
||||||
paths[paths.length - 1][2] = arrowRightY
|
|
||||||
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} object fabric.Path
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function getObjectSizeWithStroke(object) {
|
|
||||||
const stroke = new fabric.Point(
|
|
||||||
object.strokeUniform ? 1 / (object.scaleX) : 1,
|
|
||||||
object.strokeUniform ? 1 / (object.scaleY) : 1
|
|
||||||
).multiply(object.strokeWidth)
|
|
||||||
return new fabric.Point(
|
|
||||||
(object.width) + stroke.x,
|
|
||||||
(object.height) + stroke.y
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} this fabric.Control
|
|
||||||
* @param {*} dim
|
|
||||||
* @param {*} finalMatrix
|
|
||||||
* @param {*} fabricObject fabric.Path
|
|
||||||
* @returns fabric.Control['positionHandler']
|
|
||||||
*/
|
|
||||||
export const pathPositionHandler = function (
|
|
||||||
fabricControl,
|
|
||||||
dim,
|
|
||||||
finalMatrix,
|
|
||||||
fabricObject
|
|
||||||
) {
|
|
||||||
const paths = fabricObject.path
|
|
||||||
const x = paths[fabricControl.pointIndex][1] - fabricObject.pathOffset.x
|
|
||||||
const y = paths[fabricControl.pointIndex][2] - fabricObject.pathOffset.y
|
|
||||||
return fabric.util.transformPoint(
|
|
||||||
{ x, y },
|
|
||||||
fabric.util.multiplyTransformMatrices(
|
|
||||||
fabricObject.canvas?.viewportTransform,
|
|
||||||
fabricObject.calcTransformMatrix()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} eventData
|
|
||||||
* @param {*} transform
|
|
||||||
* @param {*} x
|
|
||||||
* @param {*} y
|
|
||||||
* @returns fabric.Control['actionHandler']
|
|
||||||
*/
|
|
||||||
export const actionHandler = function (
|
|
||||||
eventData,
|
|
||||||
transform,
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
) {
|
|
||||||
const pathObj = transform.target,
|
|
||||||
currentControl = pathObj.controls[pathObj.__corner],
|
|
||||||
mouseLocalPosition = pathObj.toLocalPoint(
|
|
||||||
new fabric.Point(x, y),
|
|
||||||
'center',
|
|
||||||
'center'
|
|
||||||
),
|
|
||||||
pathBaseSize = getObjectSizeWithStroke(pathObj),
|
|
||||||
size = pathObj._getTransformedDimensions(0, 0),
|
|
||||||
finalPointPosition = {
|
|
||||||
x:
|
|
||||||
(mouseLocalPosition.x * pathBaseSize.x) / size.x + pathObj.pathOffset.x,
|
|
||||||
y: (mouseLocalPosition.y * pathBaseSize.y) / size.y + pathObj.pathOffset.y
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = pathObj.path
|
|
||||||
path[currentControl.pointIndex][1] = finalPointPosition.x
|
|
||||||
path[currentControl.pointIndex][2] = finalPointPosition.y
|
|
||||||
|
|
||||||
if (
|
|
||||||
currentControl.pointIndex === path?.length - 5 ||
|
|
||||||
currentControl.pointIndex === path?.length - 6
|
|
||||||
) {
|
|
||||||
const point1 = path[path.length - 6]
|
|
||||||
const point2 = path[path.length - 5]
|
|
||||||
const newPath = calculateArrowSlidePath(
|
|
||||||
path,
|
|
||||||
point1[1],
|
|
||||||
point1[2],
|
|
||||||
point2[1],
|
|
||||||
point2[2]
|
|
||||||
)
|
|
||||||
pathObj._setPath(newPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} anchorIndex
|
|
||||||
* @param {*} fn fabric.Control['actionHandler']
|
|
||||||
* @returns MouseEvent fabric.Transform
|
|
||||||
*/
|
|
||||||
export function anchorWrapper(
|
|
||||||
anchorIndex,
|
|
||||||
fn
|
|
||||||
) {
|
|
||||||
return function (
|
|
||||||
eventData,
|
|
||||||
transform,
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
) {
|
|
||||||
const fabricObject = transform.target
|
|
||||||
const paths = fabricObject.path
|
|
||||||
const absolutePoint = fabric.util.transformPoint(
|
|
||||||
{
|
|
||||||
x: paths[anchorIndex][1] - fabricObject.pathOffset.x,
|
|
||||||
y: paths[anchorIndex][2] - fabricObject.pathOffset.y
|
|
||||||
},
|
|
||||||
fabricObject.calcTransformMatrix()
|
|
||||||
)
|
|
||||||
const actionPerformed = fn(eventData, transform, x, y)
|
|
||||||
fabricObject._setPath(fabricObject.path)
|
|
||||||
const pathBaseSize = getObjectSizeWithStroke(fabricObject)
|
|
||||||
|
|
||||||
const newX =
|
|
||||||
(paths[anchorIndex][1] - fabricObject.pathOffset.x) / pathBaseSize.x
|
|
||||||
const newY =
|
|
||||||
(paths[anchorIndex][2] - fabricObject.pathOffset.y) / pathBaseSize.y
|
|
||||||
fabricObject.setPositionByOrigin(
|
|
||||||
absolutePoint,
|
|
||||||
(newX + 0.5),
|
|
||||||
(newY + 0.5)
|
|
||||||
)
|
|
||||||
return actionPerformed
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
/**
|
|
||||||
* 线
|
|
||||||
*/
|
|
||||||
import { fabric } from 'fabric'
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} object fabric.Polyline
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function getObjectSizeWithStroke(object) {
|
|
||||||
const stroke = new fabric.Point(
|
|
||||||
object.strokeUniform ? 1 / (object.scaleX) : 1,
|
|
||||||
object.strokeUniform ? 1 / (object.scaleY) : 1
|
|
||||||
).multiply(object.strokeWidth)
|
|
||||||
return new fabric.Point(
|
|
||||||
(object.width) + stroke.x,
|
|
||||||
(object.height) + stroke.y
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} this fabric.Control
|
|
||||||
* @param {*} dim
|
|
||||||
* @param {*} finalMatrix
|
|
||||||
* @param {*} fabricObject fabric.Polyline
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const polygonPositionHandler = function (
|
|
||||||
fabricControl, dim, finalMatrix,fabricObject) {
|
|
||||||
const points = fabricObject.points
|
|
||||||
const x = points[fabricControl.pointIndex].x - fabricObject.pathOffset.x
|
|
||||||
const y = points[fabricControl.pointIndex].y - fabricObject.pathOffset.y
|
|
||||||
return fabric.util.transformPoint(
|
|
||||||
{ x, y },
|
|
||||||
fabric.util.multiplyTransformMatrices(
|
|
||||||
fabricObject.canvas?.viewportTransform,
|
|
||||||
fabricObject.calcTransformMatrix()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} eventData
|
|
||||||
* @param {*} transform
|
|
||||||
* @param {*} x
|
|
||||||
* @param {*} y
|
|
||||||
* @returns fabric.Control['actionHandler']
|
|
||||||
*/
|
|
||||||
export const actionHandler = function (
|
|
||||||
eventData, transform,x, y) {
|
|
||||||
const polygon = transform.target,
|
|
||||||
currentControl = polygon.controls[polygon.__corner],
|
|
||||||
mouseLocalPosition = polygon.toLocalPoint(
|
|
||||||
new fabric.Point(x, y),
|
|
||||||
'center',
|
|
||||||
'center'
|
|
||||||
),
|
|
||||||
polygonBaseSize = getObjectSizeWithStroke(polygon),
|
|
||||||
size = polygon._getTransformedDimensions(0, 0),
|
|
||||||
finalPointPosition = {
|
|
||||||
x:
|
|
||||||
(mouseLocalPosition.x * polygonBaseSize.x) / size.x +
|
|
||||||
polygon.pathOffset.x,
|
|
||||||
y:
|
|
||||||
(mouseLocalPosition.y * polygonBaseSize.y) / size.y +
|
|
||||||
polygon.pathOffset.y
|
|
||||||
}
|
|
||||||
|
|
||||||
const points = polygon.points
|
|
||||||
points[currentControl.pointIndex] = finalPointPosition
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} anchorIndex number
|
|
||||||
* @param {*} fn fabric.Control['actionHandler']
|
|
||||||
* @returns function (eventData, transform, x, y ) | MouseEvent fabric.Transform number number
|
|
||||||
*/
|
|
||||||
export function anchorWrapper(anchorIndex, fn) {
|
|
||||||
return function (eventData, transform, x, y ) {
|
|
||||||
const fabricObject = transform.target
|
|
||||||
const points = fabricObject.points
|
|
||||||
const absolutePoint = fabric.util.transformPoint(
|
|
||||||
{
|
|
||||||
x: points[anchorIndex].x - fabricObject.pathOffset.x,
|
|
||||||
y: points[anchorIndex].y - fabricObject.pathOffset.y
|
|
||||||
},
|
|
||||||
fabricObject.calcTransformMatrix()
|
|
||||||
)
|
|
||||||
const actionPerformed = fn(eventData, transform, x, y)
|
|
||||||
fabricObject._setPositionDimensions({})
|
|
||||||
const polygonBaseSize = getObjectSizeWithStroke(fabricObject)
|
|
||||||
|
|
||||||
const newX =
|
|
||||||
(points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x
|
|
||||||
const newY =
|
|
||||||
(points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y
|
|
||||||
fabricObject.setPositionByOrigin(
|
|
||||||
absolutePoint,
|
|
||||||
(newX + 0.5),
|
|
||||||
(newY + 0.5)
|
|
||||||
)
|
|
||||||
return actionPerformed
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* 共享数据状态-多窗口
|
||||||
|
*/
|
||||||
|
const isNode = typeof require !== 'undefined' // 是否支持node函数
|
||||||
|
const { ipcRenderer } = isNode?require('electron'):{} // app使用
|
||||||
|
export function shareStorePlugin({store}) {
|
||||||
|
store.$subscribe(() => { // 自动同步
|
||||||
|
// 在存储变化的时候执行
|
||||||
|
const storeName = store.$id
|
||||||
|
// 用于多窗口共享(需要共享的状态名称)
|
||||||
|
const names = ['tool']
|
||||||
|
if (names.includes(storeName)) stateSync(store) // 需要同步
|
||||||
|
})
|
||||||
|
// 暴露方法-手动同步
|
||||||
|
store.stateSync = () => stateSync(store)
|
||||||
|
// 监听主线程消息-同步数据
|
||||||
|
stateChange(store)
|
||||||
|
}
|
||||||
|
// 同步数据-发送给主线程
|
||||||
|
function stateSync(store) {
|
||||||
|
const storeName = store.$id
|
||||||
|
const jsonStr = JSON.stringify(store.$state)
|
||||||
|
console.log('state-change', jsonStr, storeName)
|
||||||
|
// 通知主线程更新
|
||||||
|
ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
|
||||||
|
}
|
||||||
|
// 同步数据-接收主线程消息
|
||||||
|
function stateChange(store) {
|
||||||
|
const storeName = store.$id
|
||||||
|
ipcRenderer?.on('pinia-state-set', (e, sName, jsonStr) => {
|
||||||
|
if (sName == storeName) { // 更新对应数据
|
||||||
|
// console.log('state-set', jsonStr, sName)
|
||||||
|
const curJson = JSON.stringify(store.$state) // 当前数据
|
||||||
|
const isUp = curJson != jsonStr // 不同的时候才写入,不然会导致触发数据变化监听,导致死循环
|
||||||
|
if (!isUp) return
|
||||||
|
const stateJson = JSON.parse(jsonStr) // 新数据
|
||||||
|
// 更新状态
|
||||||
|
store.$patch(stateJson)
|
||||||
|
// 您可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态
|
||||||
|
// store.$state = stateJson
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ export const constantRoutes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/resource',
|
redirect: '/homepage',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/homepage',
|
path: '/homepage',
|
||||||
|
@ -31,7 +31,7 @@ export const constantRoutes = [
|
||||||
path: '/prepare',
|
path: '/prepare',
|
||||||
component: () => import('@/views/prepare/index.vue'),
|
component: () => import('@/views/prepare/index.vue'),
|
||||||
name: 'prepare',
|
name: 'prepare',
|
||||||
meta: {title: '备课',keepAlive:true}
|
meta: {title: '备课'}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/teach',
|
path: '/teach',
|
||||||
|
@ -45,6 +45,12 @@ export const constantRoutes = [
|
||||||
name: 'profile',
|
name: 'profile',
|
||||||
meta: {title: '个人中心'}
|
meta: {title: '个人中心'}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/classReserv',
|
||||||
|
component: () => import('@/views/classManage/classReserv.vue'),
|
||||||
|
name: 'classReserv',
|
||||||
|
meta: {title: '课程预约'}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/class',
|
path: '/class',
|
||||||
component: () => import('@/views/classManage/index.vue'),
|
component: () => import('@/views/classManage/index.vue'),
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
|
import { shareStorePlugin } from '@/plugins/shareStore'
|
||||||
|
|
||||||
const pinia = createPinia() //创建pinia实例
|
const pinia = createPinia() //创建pinia实例
|
||||||
pinia.use(piniaPluginPersistedstate) //将插件添加到 pinia 实例上
|
pinia.use(piniaPluginPersistedstate) //将插件添加到 pinia 实例上
|
||||||
|
pinia.use(shareStorePlugin) // 多窗口数据状态共享
|
||||||
|
|
||||||
export const store = pinia
|
export const store = pinia
|
|
@ -1,295 +0,0 @@
|
||||||
/**
|
|
||||||
* @description fabric.js 缓存画板
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { defineStore } from "pinia";
|
|
||||||
import * as TYPES from "@/constants/draw";
|
|
||||||
import { FabricVue } from "@/plugins/fabric";
|
|
||||||
import * as fabricUtils from '@/plugins/fabric/utils/draw'
|
|
||||||
import * as colorUtils from '@/plugins/fabric/utils/color'
|
|
||||||
import * as bgUtils from '@/plugins/fabric/utils/bg'
|
|
||||||
import * as commUtils from '@/plugins/fabric/utils'
|
|
||||||
import { material } from '@/plugins/fabric/nodes/draw/material'
|
|
||||||
import { renderMultiColor } from '@/plugins/fabric/nodes/draw/multiColor'
|
|
||||||
|
|
||||||
// 画板模块-缓存 json相关
|
|
||||||
export const BOARD_VERSION = '1.0.0'
|
|
||||||
|
|
||||||
const getFileObj = (data) => ({
|
|
||||||
id: commUtils.getUUID(),
|
|
||||||
title: 'paint-board',
|
|
||||||
boardVersion: BOARD_VERSION,
|
|
||||||
boardData: {},
|
|
||||||
zoom: 1,
|
|
||||||
canvasWidth: 1,
|
|
||||||
canvasHeight: 1
|
|
||||||
, ...data
|
|
||||||
})
|
|
||||||
const initFileObj = getFileObj()
|
|
||||||
export const useFileStore = defineStore(
|
|
||||||
'file',
|
|
||||||
{
|
|
||||||
state() {
|
|
||||||
return {
|
|
||||||
currentId: initFileObj.id,
|
|
||||||
currentIndex: 0,
|
|
||||||
currentFile: initFileObj,
|
|
||||||
files: [ initFileObj ],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
// update file | 更新文件
|
|
||||||
updateFile(file = {}) {
|
|
||||||
if (file?.id != this.currentId) return
|
|
||||||
this.currentFile = file
|
|
||||||
this.files[this.currentIndex] = data
|
|
||||||
},
|
|
||||||
// update board data | 更新画板数据
|
|
||||||
updateBoardData(data) {
|
|
||||||
this.currentFile.boardData = data
|
|
||||||
this.files[this.currentIndex].boardData = data
|
|
||||||
},
|
|
||||||
// update board zoom | 更新画板缩放
|
|
||||||
updateBoardZoom(zoom){
|
|
||||||
this.currentFile.zoom = zoom
|
|
||||||
this.files[this.currentIndex].zoom = zoom
|
|
||||||
},
|
|
||||||
// update board field | 更新画板字段
|
|
||||||
updateBoardField(key, value) {
|
|
||||||
if(key == 'id') return
|
|
||||||
this.currentFile[key] = value
|
|
||||||
this.files[this.currentIndex][key] = value
|
|
||||||
},
|
|
||||||
// 创建新画板
|
|
||||||
createBoard(option = {}) {
|
|
||||||
delete option?.id
|
|
||||||
let fileObj = getFileObj(option)
|
|
||||||
this.currentFile = fileObj
|
|
||||||
this.currentId = fileObj.id
|
|
||||||
this.files.push(fileObj)
|
|
||||||
this.currentIndex = this.files.length - 1
|
|
||||||
return fileObj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 主模块-缓存
|
|
||||||
const initLanguage = ['en', 'en-US', 'en-us'].includes(navigator.language) ? 'en' : 'zh'
|
|
||||||
export const useBoardStore = defineStore(
|
|
||||||
'board',
|
|
||||||
{
|
|
||||||
state() {
|
|
||||||
return {
|
|
||||||
mode: TYPES.ActionMode.DRAW,
|
|
||||||
drawType: TYPES.DrawType.FreeStyle,
|
|
||||||
language: initLanguage,
|
|
||||||
canvasWidth: 1,
|
|
||||||
canvasHeight: 1,
|
|
||||||
backgroundColor: 'rgba(255, 255, 255, 1)',
|
|
||||||
backgroundOpacity: 1,
|
|
||||||
hasBackgroundImage: false,
|
|
||||||
backgroundImageOpacity: 1,
|
|
||||||
isObjectCaching: true,
|
|
||||||
openGuideLine: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
// update mode | 更新模式
|
|
||||||
updateMode(mode) {
|
|
||||||
if (this.mode != mode) FabricVue.handleMode(mode)
|
|
||||||
this.mode = mode
|
|
||||||
},
|
|
||||||
// update draw type | 更新画笔类型
|
|
||||||
updateDrawType(drawType) {
|
|
||||||
if (this.drawType != drawType){
|
|
||||||
this.drawType = drawType
|
|
||||||
FabricVue.handleMode()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// update language | 更新语言
|
|
||||||
updateLanguage(language) {this.language = language},
|
|
||||||
updateCanvasWidth(width) {
|
|
||||||
if (this.canvasWidth !== width){
|
|
||||||
this.canvasHeight = width
|
|
||||||
FabricVue.updateCanvasWidth(width)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// init background | 初始化背景
|
|
||||||
initBackground() {
|
|
||||||
const backgroundColor = FabricVue?.canvas?.backgroundColor
|
|
||||||
console.log('xxxx', backgroundColor, FabricVue?.canvas)
|
|
||||||
if (backgroundColor && typeof backgroundColor === 'string') {
|
|
||||||
const type = colorUtils.getColorFormat(backgroundColor)
|
|
||||||
if (type === 'hex') {
|
|
||||||
const color = colorUtils.hexToRgba(backgroundColor)
|
|
||||||
const opacity = colorUtils.getAlphaFromRgba(color)
|
|
||||||
this.backgroundColor = color
|
|
||||||
this.backgroundOpacity = opacity
|
|
||||||
} else if (type === 'rgba') {
|
|
||||||
const opacity = colorUtils.getAlphaFromRgba(backgroundColor)
|
|
||||||
this.backgroundColor = backgroundColor
|
|
||||||
this.backgroundOpacity = opacity
|
|
||||||
}
|
|
||||||
} else if (FabricVue?.canvas) {
|
|
||||||
if (this.backgroundColor) {
|
|
||||||
FabricVue.canvas.backgroundColor = this.backgroundColor
|
|
||||||
} else {
|
|
||||||
FabricVue.canvas.backgroundColor = 'rgba(255, 255, 255, 1)'
|
|
||||||
this.backgroundColor = 'rgba(255, 255, 255, 1)'
|
|
||||||
this.backgroundOpacity = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const backgroundImage = FabricVue?.canvas?.backgroundImage
|
|
||||||
if (backgroundImage) {
|
|
||||||
bgUtils.handleBackgroundImageWhenCanvasSizeChange()
|
|
||||||
this.hasBackgroundImage = true
|
|
||||||
this.backgroundOpacity = backgroundImage.opacity
|
|
||||||
} else {
|
|
||||||
this.hasBackgroundImage = true
|
|
||||||
this.backgroundOpacity = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 画笔模块-缓存
|
|
||||||
export const useDrawStore = defineStore(
|
|
||||||
'draw',
|
|
||||||
{
|
|
||||||
state() {
|
|
||||||
return {
|
|
||||||
drawWidth: 1,
|
|
||||||
drawColors: ['#000000'],
|
|
||||||
shadowWidth: 0,
|
|
||||||
shadowColor: '#000000',
|
|
||||||
drawTextValue: 'draw',
|
|
||||||
drawStyle: TYPES.DrawStyle.Basic,
|
|
||||||
drawShapeCount: 2,
|
|
||||||
materialType: TYPES.MATERIAL_TYPE.CRAYON,
|
|
||||||
drawShape: TYPES.DrawShape.Bubble,
|
|
||||||
eraserWidth: 20,
|
|
||||||
multiColorType: TYPES.MultiColorType.COL,
|
|
||||||
textFontFamily: 'Georgia',
|
|
||||||
openAutoDraw: false,
|
|
||||||
fontStyles: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
// update draw width | 更新画笔宽度
|
|
||||||
updateDrawWidth(w) {
|
|
||||||
const oldW = this.drawWidth
|
|
||||||
if (oldW != w && FabricVue.canvas) {
|
|
||||||
FabricVue.canvas.freeDrawingBrush.width = fabricUtils.getDrawWidth(w)
|
|
||||||
this.drawWidth = w
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// update draw colors | 更新画笔颜色
|
|
||||||
updateDrawColors(drawColors = []) {
|
|
||||||
const drawStyle = this.drawStyle
|
|
||||||
switch(drawStyle) {
|
|
||||||
case TYPES.DrawStyle.Basic:
|
|
||||||
if (FabricVue.canvas) {
|
|
||||||
FabricVue.canvas.freeDrawingBrush.color = drawColors[0]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case TYPES.DrawStyle.Material:
|
|
||||||
if (this.drawColors[0] != drawColors[0]) {
|
|
||||||
material.render({})
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case TYPES.DrawStyle.MultiColor:
|
|
||||||
renderMultiColor({colors: drawColors})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
this.drawColors = drawColors
|
|
||||||
},
|
|
||||||
// update shadow width | 更新画笔阴影宽度
|
|
||||||
updateShadowWidth(shadowWidth) {
|
|
||||||
if (FabricVue.canvas) {
|
|
||||||
(FabricVue.canvas.freeDrawingBrush.shadow).blur = fabricUtils.getShadowWidth(shadowWidth)
|
|
||||||
}
|
|
||||||
this.shadowWidth = shadowWidth
|
|
||||||
},
|
|
||||||
// update shadow color | 更新画笔阴影颜色
|
|
||||||
updateShadowColor(shadowColor) {
|
|
||||||
if (FabricVue.canvas) {
|
|
||||||
(FabricVue.canvas.freeDrawingBrush.shadow).color = shadowColor
|
|
||||||
}
|
|
||||||
this.shadowColor = shadowColor
|
|
||||||
},
|
|
||||||
// update shadow Shape | 更新画笔阴影形状
|
|
||||||
updateDrawShape(drawShape) {
|
|
||||||
this.drawShape = drawShape
|
|
||||||
},
|
|
||||||
// update draw style | 更新画笔阴影样式
|
|
||||||
updateDrawStyle(drawStyle) {
|
|
||||||
this.drawStyle = drawStyle
|
|
||||||
FabricVue.handleDrawStyle()
|
|
||||||
},
|
|
||||||
// update draw shape count | 更新画笔形状数量
|
|
||||||
updateDrawShapeCount(drawShapeCount){
|
|
||||||
this.drawShapeCount = drawShapeCount
|
|
||||||
},
|
|
||||||
// update draw text value | 更新画笔文字
|
|
||||||
updateDrawTextValue(drawTextValue){
|
|
||||||
this.drawTextValue = drawTextValue
|
|
||||||
},
|
|
||||||
// update material type | 更新材质类型
|
|
||||||
updateMaterialType(materialType) {
|
|
||||||
if(this.materialType != materialType) {
|
|
||||||
material.render({materialType})
|
|
||||||
this.materialType = materialType
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// update eraser width | 更新橡皮擦宽度
|
|
||||||
updateEraserWidth(eraserWidth) {
|
|
||||||
const oldW = this.drawWidth
|
|
||||||
if (oldW != eraserWidth && FabricVue.canvas) {
|
|
||||||
FabricVue.canvas.freeDrawingBrush.width = fabricUtils.getEraserWidth(eraserWidth)
|
|
||||||
this.eraserWidth = eraserWidth
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// update multi color type | 更新多色材质类型
|
|
||||||
updateMultiColorType(multiColorType) {
|
|
||||||
if(this.multiColorType != multiColorType) {
|
|
||||||
renderMultiColor({type: multiColorType})
|
|
||||||
this.multiColorType = multiColorType
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// update text font family | 更新文字字体
|
|
||||||
updateTextFontFamily(fontFamily){
|
|
||||||
this.textFontFamily = fontFamily
|
|
||||||
},
|
|
||||||
// update auto draw state | 更新自动绘图状态
|
|
||||||
updateAutoDrawState() {
|
|
||||||
this.openAutoDraw = !this.openAutoDraw
|
|
||||||
},
|
|
||||||
// update font styles | 更新字体样式
|
|
||||||
updateFontStyles(type) {
|
|
||||||
const ind = this.fontStyles.findIndex(item => item === type)
|
|
||||||
if (ind >= 0) this.fontStyles.splice(ind, 1)
|
|
||||||
else this.fontStyles.push(type)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// interface BoardState {
|
|
||||||
// mode: string // operating mode
|
|
||||||
// drawType: string // draw type
|
|
||||||
// language: string // i18n language 'zh' 'en'
|
|
||||||
// canvasWidth: number // canvas width 0.1 ~ 1
|
|
||||||
// canvasHeight: number // canvas height 0.1 ~ 1
|
|
||||||
// backgroundColor: string // canvas background color
|
|
||||||
// backgroundOpacity: number // canvas background color opacity
|
|
||||||
// hasBackgroundImage: boolean // canvas background image
|
|
||||||
// backgroundImageOpacity: number // canvas background Image opacity
|
|
||||||
// isObjectCaching: boolean // fabric objectCaching
|
|
||||||
// openGuideLine: boolean // does the guide line show
|
|
||||||
// }
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
const routerStore = defineStore('router', {
|
||||||
|
state: () => ({
|
||||||
|
nowRouter: []
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
setNowRouter(payload) {
|
||||||
|
this.nowRouter = payload
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {},
|
||||||
|
persist: true
|
||||||
|
})
|
||||||
|
export default routerStore
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* 工具类-窗口-状态管理
|
||||||
|
*/
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useToolState = defineStore('tool', {
|
||||||
|
state: () => ({
|
||||||
|
model: 'select', // 悬浮球-当前模式
|
||||||
|
showBoardAll: false, // 全屏画板-是否显示
|
||||||
|
isPdfWin: false, // pdf窗口是否打开
|
||||||
|
isToolWin: false, // 工具窗口是否打开
|
||||||
|
curSubjectNode: {
|
||||||
|
data: {}, // 当前教材节点 (包含当前教材 单元)
|
||||||
|
querySearch: {} // 查询资源所需参数
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
}
|
||||||
|
})
|
|
@ -17,7 +17,7 @@ const uploaderStore = defineStore('uploader', {
|
||||||
pushFile(payload) {
|
pushFile(payload) {
|
||||||
let _this = this
|
let _this = this
|
||||||
let arr = payload.filter((item) => {
|
let arr = payload.filter((item) => {
|
||||||
const MAX_SIZE = 100 * 1024 * 1024; // 2MB
|
const MAX_SIZE = 500 * 1024 * 1024; // 2MB
|
||||||
if (item.size > MAX_SIZE) {
|
if (item.size > MAX_SIZE) {
|
||||||
ElMessage.error('文件大小不能超过 100MB!');
|
ElMessage.error('文件大小不能超过 100MB!');
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { defineStore } from "pinia"
|
import { defineStore } from 'pinia'
|
||||||
import { login, logout, getInfo } from '@/api/login'
|
import { login, logout, getInfo } from '@/api/login'
|
||||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||||
import defAva from '@/assets/images/user.png'
|
import defAva from '@/assets/images/user.png'
|
||||||
|
|
||||||
const useUserStore = defineStore(
|
const useUserStore = defineStore('user', {
|
||||||
'user',
|
|
||||||
{
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
token: getToken(),
|
token: getToken(),
|
||||||
id: '',
|
id: '',
|
||||||
|
@ -23,11 +21,13 @@ const useUserStore = defineStore(
|
||||||
const code = userInfo.code
|
const code = userInfo.code
|
||||||
const uuid = userInfo.uuid
|
const uuid = userInfo.uuid
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
login(username, password, code, uuid).then(res => {
|
login(username, password, code, uuid)
|
||||||
|
.then((res) => {
|
||||||
setToken(res.token)
|
setToken(res.token)
|
||||||
this.token = res.token
|
this.token = res.token
|
||||||
resolve(res)
|
resolve(res)
|
||||||
}).catch(error => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -35,45 +35,47 @@ const useUserStore = defineStore(
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
getInfo() {
|
getInfo() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getInfo().then(res => {
|
getInfo()
|
||||||
|
.then((res) => {
|
||||||
res.user.avatar = import.meta.env.VITE_APP_BASE_API + res.user.avatar
|
res.user.avatar = import.meta.env.VITE_APP_BASE_API + res.user.avatar
|
||||||
const user = res.user
|
const user = res.user
|
||||||
this.user = user
|
this.user = user
|
||||||
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : user.avatar;
|
const avatar = user.avatar == '' || user.avatar == null ? defAva : user.avatar
|
||||||
|
|
||||||
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
if (res.roles && res.roles.length > 0) {
|
||||||
|
// 验证返回的roles是否是一个非空数组
|
||||||
this.roles = res.roles
|
this.roles = res.roles
|
||||||
this.permissions = res.permissions
|
this.permissions = res.permissions
|
||||||
} else {
|
} else {
|
||||||
this.roles = ['ROLE_DEFAULT']
|
this.roles = ['ROLE_DEFAULT']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.id = user.userId
|
this.id = user.userId
|
||||||
this.userName = user.userName
|
this.userName = user.userName
|
||||||
this.nickName = user.nickName;
|
this.nickName = user.nickName
|
||||||
this.avatar = avatar;
|
this.avatar = avatar
|
||||||
this.userType = user.userType;
|
this.userType = user.userType
|
||||||
this.deptId = user.deptId;
|
this.deptId = user.deptId
|
||||||
this.deptName = user.deptName;
|
this.deptName = user.deptName
|
||||||
this.deptLogo = user.deptLogo;
|
this.deptLogo = user.deptLogo
|
||||||
this.deptSlogan = user.deptSlogan;
|
this.deptSlogan = user.deptSlogan
|
||||||
this.activeDeptId = user.activeDeptId;
|
this.activeDeptId = user.activeDeptId
|
||||||
this.activeDeptName = user.activeDeptName;
|
this.activeDeptName = user.activeDeptName
|
||||||
this.parentDeptId = user.parentDeptId;
|
this.parentDeptId = user.parentDeptId
|
||||||
this.parentDeptName = user.parentDeptName;
|
this.parentDeptName = user.parentDeptName
|
||||||
this.edusubject = user.edusubject;
|
this.edusubject = user.edusubject
|
||||||
this.edudegree = user.edudegree;
|
this.edudegree = user.edudegree
|
||||||
this.edustage = user.edustage;
|
this.edustage = user.edustage
|
||||||
this.userType = user.userType;
|
this.userType = user.userType
|
||||||
this.studentId = user.studentId;
|
this.studentId = user.studentId
|
||||||
this.timUserId = user.timuserid;
|
this.timUserId = user.timuserid
|
||||||
this.plainpwd = user.plainpwd;
|
this.plainpwd = user.plainpwd
|
||||||
|
|
||||||
this.roles = res.roles;
|
this.roles = res.roles
|
||||||
|
|
||||||
resolve(res)
|
resolve(res)
|
||||||
}).catch(error => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -81,14 +83,16 @@ const useUserStore = defineStore(
|
||||||
// 退出系统
|
// 退出系统
|
||||||
logOut() {
|
logOut() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
logout(this.token).then(() => {
|
logout(this.token)
|
||||||
|
.then(() => {
|
||||||
this.token = ''
|
this.token = ''
|
||||||
this.roles = []
|
this.roles = []
|
||||||
this.permissions = []
|
this.permissions = []
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
removeToken()
|
removeToken()
|
||||||
resolve()
|
resolve()
|
||||||
}).catch(error => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
removeToken() // zdg: 网络异常时,清除前端退出进入登录页
|
removeToken() // zdg: 网络异常时,清除前端退出进入登录页
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
|
@ -96,6 +100,6 @@ const useUserStore = defineStore(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
persist: true
|
persist: true
|
||||||
})
|
})
|
||||||
|
|
||||||
export default useUserStore
|
export default useUserStore
|
||||||
|
|
|
@ -1,56 +1,58 @@
|
||||||
|
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
const baseConfig = () => {
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const baseConfig = {
|
return {
|
||||||
// Electron 设置cookie
|
// Electron 设置cookie
|
||||||
// url: import.meta.env.VITE_APP_BASE_API,
|
url: import.meta.env.VITE_APP_BUILD_BASE_PATH,
|
||||||
url: 'https://file.ysaix.com:7868',
|
// url: 'https://file.ysaix.com:7868',
|
||||||
//cookie 名称 这里为 token
|
//cookie 名称 这里为 token
|
||||||
name: 'Admin-Token',
|
name: 'Admin-Token',
|
||||||
//cookie 值
|
//cookie 值
|
||||||
value: userStore.token,
|
value: userStore.token,
|
||||||
// 域名
|
// 域名
|
||||||
domain: 'file.ysaix.com',
|
domain: import.meta.env.VITE_APP_DOMAIN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 作业布置
|
export default () => {
|
||||||
const homeWork = {
|
return {
|
||||||
data: { ...baseConfig},
|
// 作业布置
|
||||||
|
homeWork: {
|
||||||
|
data: { ...baseConfig() },
|
||||||
// 完整路径
|
// 完整路径
|
||||||
fullPath: `${baseConfig.url}/teaching/classtaskassign?titleName=%E4%BD%9C%E4%B8%9A%E5%B8%83%E7%BD%AE`
|
fullPath: `${baseConfig().url}/teaching/classtaskassign?titleName=%E4%BD%9C%E4%B8%9A%E5%B8%83%E7%BD%AE`
|
||||||
}
|
},
|
||||||
|
|
||||||
// 作业反馈
|
// 作业反馈
|
||||||
const feedback = {
|
feedback: {
|
||||||
data: { ...baseConfig},
|
data: { ...baseConfig() },
|
||||||
// 完整路径
|
// 完整路径
|
||||||
fullPath: `${baseConfig.url}/teaching/classtaskassign?titleName=作业反馈`
|
fullPath: `${baseConfig().url}/teaching/classtaskassign?titleName=作业反馈`
|
||||||
}
|
},
|
||||||
|
|
||||||
// 高考研读
|
// 高考研读
|
||||||
const gk = {
|
gk: {
|
||||||
data: { ...baseConfig},
|
data: { ...baseConfig() },
|
||||||
fullPath: `${baseConfig.url}/education/colentrance`
|
fullPath: `${baseConfig().url}/education/colentrance`
|
||||||
}
|
},
|
||||||
|
|
||||||
// 课标研读
|
// 课标研读
|
||||||
const standard = {
|
standard: {
|
||||||
data: { ...baseConfig},
|
data: { ...baseConfig() },
|
||||||
fullPath: `${baseConfig.url}/teaching/chatwithstandard`
|
fullPath: `${baseConfig().url}/teaching/chatwithstandard`
|
||||||
}
|
},
|
||||||
|
|
||||||
// 教学大模型
|
// 教学大模型
|
||||||
const aiModel = {
|
aiModel: {
|
||||||
data: { ...baseConfig},
|
data: { ...baseConfig() },
|
||||||
fullPath: `${baseConfig.url}/platofai`
|
fullPath: `${baseConfig().url}/platofai`
|
||||||
}
|
},
|
||||||
|
|
||||||
export default {
|
getBaseData: () => {
|
||||||
homeWork,
|
return {
|
||||||
feedback,
|
data: { ...baseConfig() },
|
||||||
gk,
|
fullPath: `${baseConfig().url}`
|
||||||
standard,
|
}
|
||||||
aiModel
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
// 所有事件
|
||||||
|
export function handleevent(canvas, imgarr, type = 'defalut') {
|
||||||
|
// // 鼠标按下
|
||||||
|
// canvas.on('mouse:down', function (e) {})
|
||||||
|
// // // 监听鼠标移动事件
|
||||||
|
// // canvas.on('mouse:move', (options) => {
|
||||||
|
// // console.log('Mouse move event:', options);
|
||||||
|
// // });
|
||||||
|
|
||||||
|
// // 监听鼠标释放事件
|
||||||
|
// canvas.on('mouse:up', (options) => {
|
||||||
|
// //判断是点击的哪一个
|
||||||
|
// if (type == 'defalut') {
|
||||||
|
// if (imgarr.value[0].index == 0) {
|
||||||
|
// imgarr.value[0].JSONdata = canvas.toJSON()
|
||||||
|
// }
|
||||||
|
// if (imgarr.value[1]?.index == 0) {
|
||||||
|
// imgarr.value[1].JSONdata = canvas.toJSON()
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if (imgarr.value[0].index == 1) {
|
||||||
|
// imgarr.value[0].JSONdata = canvas.toJSON()
|
||||||
|
// }
|
||||||
|
// if (imgarr.value[1]?.index == 1) {
|
||||||
|
// imgarr.value[1].JSONdata = canvas.toJSON()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// console.log(imgarr.value)
|
||||||
|
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
// 保存数据
|
||||||
|
export function savecanvsStore(imgarr, canvsStore) {
|
||||||
|
// canvsStore.pageArr = mergeAndReplace(canvsStore.pageArr, imgarr.value)
|
||||||
|
}
|
||||||
|
// 重显数据
|
||||||
|
export function displayData(canvas, canvsStore, canvasobj, fabric, img) {
|
||||||
|
// // 初始化
|
||||||
|
// if (!canvsStore.pageArr.length) {
|
||||||
|
// fabric.Image.fromURL(img.src, (img) => {
|
||||||
|
// img.set({
|
||||||
|
// left: 0,
|
||||||
|
// top: 0,
|
||||||
|
// scaleX: canvas.value.width / img.width,
|
||||||
|
// scaleY: canvas.value.height / img.height
|
||||||
|
// })
|
||||||
|
// canvas.value.setBackgroundImage(img, canvas.value.renderAll.bind(canvas.value))
|
||||||
|
// })
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// canvsStore.pageArr.forEach((item) => {
|
||||||
|
// //初始化
|
||||||
|
// if (item.page == canvasobj.page) {
|
||||||
|
// // canvas.value.clear() // 清除 Canvas
|
||||||
|
// // console.log(item.JSONdata, '找到一样的数据')
|
||||||
|
// canvas.value.loadFromJSON(item.JSONdata, () => {
|
||||||
|
// // 在所有对象加载完成后重新渲染画布
|
||||||
|
// canvas.value.renderAll.bind(canvas.value)
|
||||||
|
// canvas.value.renderAll()
|
||||||
|
// // requestAnimationFrame(() => {
|
||||||
|
// // // 渲染所有对象
|
||||||
|
|
||||||
|
// // })
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// // 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
|
||||||
|
// // // 清除 Canvas
|
||||||
|
// canvas.value.clear()
|
||||||
|
// requestAnimationFrame(function () {
|
||||||
|
// fabric.Image.fromURL(img.src, (img) => {
|
||||||
|
// img.set({
|
||||||
|
// left: 0,
|
||||||
|
// top: 0,
|
||||||
|
// scaleX: canvas.value.width / img.width,
|
||||||
|
// scaleY: canvas.value.height / img.height
|
||||||
|
// })
|
||||||
|
// canvas.value.setBackgroundImage(img, canvas.value.renderAll.bind(canvas.value))
|
||||||
|
// })
|
||||||
|
// // 渲染所有对象
|
||||||
|
// canvas.value.renderAll.bind(canvas.value)
|
||||||
|
// canvas.value.renderAll()
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
//page 一样替换
|
||||||
|
const mergeAndReplace = (arr1, arr2) => {
|
||||||
|
// // 用于存储替换后的数组
|
||||||
|
// const resultArray = array1.map(item1 => {
|
||||||
|
// // 在 array2 中查找 page 相同的对象
|
||||||
|
// const replacement = array2.find(item2 => item2.page == item1.page);
|
||||||
|
// // 如果找到替换对象,则返回替换对象,否则返回原对象
|
||||||
|
// return replacement ? replacement : item1;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // 将 array2 中 page 不在 array1 中的对象追加到结果数组中
|
||||||
|
// array2.forEach(item2 => {
|
||||||
|
// const existsInArray1 = array1.some(item1 => item1.page == item2.page);
|
||||||
|
// if (!existsInArray1) {
|
||||||
|
// resultArray.push(item2);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return resultArray;
|
||||||
|
|
||||||
|
// 创建一个映射,将 arr2 中的对象按 page 属性存储
|
||||||
|
let map = new Map(arr2.map((item) => [item.page, item]))
|
||||||
|
|
||||||
|
// 使用 map 替换 arr1 中相应 page 的对象,并添加 arr2 中的对象
|
||||||
|
arr1 = arr1.map((item) => (map.has(item.page) ? map.get(item.page) : item))
|
||||||
|
|
||||||
|
// 将 map 中存在但 arr1 中不存在的对象添加到 arr1
|
||||||
|
for (let [page, obj] of map) {
|
||||||
|
if (!arr1.some((item) => item.page === page)) {
|
||||||
|
arr1.push(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
export function initcanvasdata(canvas) {
|
||||||
|
canvas.value.clear() // 清除 Canvas
|
||||||
|
// 设置画布的背景色或其他属性
|
||||||
|
canvas.value.backgroundColor = 'rgba(255, 255, 255, 1)' // 白色背景
|
||||||
|
|
||||||
|
// 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
|
||||||
|
requestAnimationFrame(function () {
|
||||||
|
// 渲染所有对象
|
||||||
|
canvas.value.renderAll.bind(canvas.value)
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,108 +1,187 @@
|
||||||
|
|
||||||
// 所有事件
|
// 所有事件
|
||||||
export function handleevent(canvas, imgarr, type = 'defalut') {
|
export function handleevent(canvas, imgarr, type = 'defalut') {
|
||||||
// 鼠标按下
|
// // 鼠标按下
|
||||||
canvas.on('mouse:down', function (e) {})
|
// canvas.on('mouse:down', function (e) {})
|
||||||
// // 监听鼠标移动事件
|
// // // 监听鼠标移动事件
|
||||||
// canvas.on('mouse:move', (options) => {
|
// // canvas.on('mouse:move', (options) => {
|
||||||
// console.log('Mouse move event:', options);
|
// // console.log('Mouse move event:', options);
|
||||||
// });
|
// // });
|
||||||
|
|
||||||
// 监听鼠标释放事件
|
// // 监听鼠标释放事件
|
||||||
canvas.on('mouse:up', (options) => {
|
// canvas.on('mouse:up', (options) => {
|
||||||
//判断是点击的哪一个
|
// //判断是点击的哪一个
|
||||||
if (type == 'defalut') {
|
// if (type == 'defalut') {
|
||||||
if (imgarr.value[0].index == 0) {
|
// if (imgarr.value[0].index == 0) {
|
||||||
imgarr.value[0].JSONdata = canvas.toJSON()
|
// imgarr.value[0].JSONdata = canvas.toJSON()
|
||||||
}
|
// }
|
||||||
if (imgarr.value[1]?.index == 0) {
|
// if (imgarr.value[1]?.index == 0) {
|
||||||
imgarr.value[1].JSONdata = canvas.toJSON()
|
// imgarr.value[1].JSONdata = canvas.toJSON()
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
if (imgarr.value[0].index == 1) {
|
// if (imgarr.value[0].index == 1) {
|
||||||
imgarr.value[0].JSONdata = canvas.toJSON()
|
// imgarr.value[0].JSONdata = canvas.toJSON()
|
||||||
}
|
// }
|
||||||
if (imgarr.value[1]?.index == 1) {
|
// if (imgarr.value[1]?.index == 1) {
|
||||||
imgarr.value[1].JSONdata = canvas.toJSON()
|
// imgarr.value[1].JSONdata = canvas.toJSON()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
console.log(imgarr.value)
|
// console.log(imgarr.value)
|
||||||
|
|
||||||
})
|
// })
|
||||||
}
|
}
|
||||||
// 保存数据
|
// 保存数据
|
||||||
export function savecanvsStore(imgarr, canvsStore) {
|
export function savecanvsStore(imgarr, canvsStore) {
|
||||||
canvsStore.pageArr = mergeAndReplace(canvsStore.pageArr, imgarr.value)
|
canvsStore.pageArr = mergeAndReplace(canvsStore.pageArr, imgarr.value)
|
||||||
}
|
}
|
||||||
// 重显数据
|
// 重显数据
|
||||||
export function displayData(canvas, canvsStore, canvasobj, fabric, img) {
|
export function displayData(FabricVue, canvsStore, canvasobj, fabric, img) {
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
if (!canvsStore.pageArr.length) {
|
const canvas = FabricVue.value.canvas
|
||||||
fabric.Image.fromURL(img.src, (img) => {
|
if (!canvas) {
|
||||||
img.set({
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
scaleX: canvas.value.width / img.width,
|
|
||||||
scaleY: canvas.value.height / img.height
|
|
||||||
})
|
|
||||||
canvas.value.setBackgroundImage(img, canvas.value.renderAll.bind(canvas.value))
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
canvsStore.pageArr.forEach((item) => {
|
if (!canvsStore.pageArr.length) {
|
||||||
//初始化
|
fabric.Image.fromURL(
|
||||||
if (item.page == canvasobj.page) {
|
img.src,
|
||||||
// canvas.value.clear() // 清除 Canvas
|
(image) => {
|
||||||
// console.log(item.JSONdata, '找到一样的数据')
|
image.set({
|
||||||
canvas.value.loadFromJSON(item.JSONdata, () => {
|
|
||||||
// 在所有对象加载完成后重新渲染画布
|
|
||||||
canvas.value.renderAll.bind(canvas.value)
|
|
||||||
canvas.value.renderAll()
|
|
||||||
// requestAnimationFrame(() => {
|
|
||||||
// // 渲染所有对象
|
|
||||||
|
|
||||||
// })
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
|
|
||||||
// // 清除 Canvas
|
|
||||||
canvas.value.clear()
|
|
||||||
requestAnimationFrame(function () {
|
|
||||||
fabric.Image.fromURL(img.src, (img) => {
|
|
||||||
img.set({
|
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
scaleX: canvas.value.width / img.width,
|
scaleX: canvas.width / img.width,
|
||||||
scaleY: canvas.value.height / img.height
|
scaleY: canvas.height / img.height,
|
||||||
|
erasable: false // 不允许擦拭
|
||||||
})
|
})
|
||||||
canvas.value.setBackgroundImage(img, canvas.value.renderAll.bind(canvas.value))
|
// FabricVue.setBackgroundImage(image, FabricVue.renderAll.bind(FabricVue))
|
||||||
|
// setBackgroundImage(image,FabricVue)
|
||||||
|
// canvas.setBackgroundImage(image, canvas.renderAll.bind(canvas));
|
||||||
|
|
||||||
|
canvas.setBackgroundImage(image, () => {
|
||||||
|
canvas.renderAll.bind(canvas)
|
||||||
|
FabricVue.value.render()
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{crossOrigin: 'anonymous'}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isfind=findObjectByPage(canvsStore.pageArr,canvasobj.page)
|
||||||
|
if(isfind){
|
||||||
|
const canvdata={
|
||||||
|
objects:JSON.parse(isfind.JSONdata),
|
||||||
|
version: "5.3.0"
|
||||||
|
}
|
||||||
|
requestAnimationFrame(function () {
|
||||||
|
canvas.loadFromJSON(canvdata, () => {
|
||||||
|
// 在所有对象加载完成后重新渲染画布
|
||||||
|
// Utils.handleCanvasJSONLoaded(canvas)
|
||||||
|
canvas.requestRenderAll() // 批量重绘
|
||||||
|
})
|
||||||
|
fabric.Image.fromURL(
|
||||||
|
img.src,
|
||||||
|
(image) => {
|
||||||
|
image.set({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
scaleX: canvas.width / img.width,
|
||||||
|
scaleY: canvas.height / img.height,
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
erasable: false // 不允许擦拭
|
||||||
|
})
|
||||||
|
// canvas.setBackgroundImage(image, canvas.renderAll.bind(canvas));
|
||||||
|
canvas.setBackgroundImage(image, () => {
|
||||||
|
canvas.renderAll.bind(canvas)
|
||||||
|
FabricVue.value.render()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{crossOrigin: 'anonymous'}
|
||||||
|
)
|
||||||
// 渲染所有对象
|
// 渲染所有对象
|
||||||
canvas.value.renderAll.bind(canvas.value)
|
canvas.requestRenderAll() // 批量重绘
|
||||||
canvas.value.renderAll()
|
})
|
||||||
|
}else{
|
||||||
|
// 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
|
||||||
|
// // 清除 Canvas
|
||||||
|
// canvas.clear()
|
||||||
|
requestAnimationFrame(function () {
|
||||||
|
fabric.Image.fromURL(
|
||||||
|
img.src,
|
||||||
|
(image) => {
|
||||||
|
image.set({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
scaleX: canvas.width / img.width,
|
||||||
|
scaleY: canvas.height / img.height,
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
erasable: false // 不允许擦拭
|
||||||
|
})
|
||||||
|
// canvas.setBackgroundImage(image, canvas.renderAll.bind(canvas));
|
||||||
|
canvas.setBackgroundImage(image, () => {
|
||||||
|
canvas.renderAll.bind(canvas)
|
||||||
|
FabricVue.value.render()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{crossOrigin: 'anonymous'}
|
||||||
|
)
|
||||||
|
// 渲染所有对象
|
||||||
|
canvas.requestRenderAll() // 批量重绘
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// canvsStore.pageArr.forEach((item) => {
|
||||||
|
// //初始化
|
||||||
|
// if (item.page == canvasobj.page) {
|
||||||
|
// // item.JSONdata
|
||||||
|
// canvas.loadFromJSON(JSON.parse(item.JSONdata), () => {
|
||||||
|
// // 在所有对象加载完成后重新渲染画布
|
||||||
|
// // Utils.handleCanvasJSONLoaded(canvas)
|
||||||
|
// canvas.requestRenderAll() // 批量重绘
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
// // 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
|
||||||
|
// // // 清除 Canvas
|
||||||
|
// // canvas.clear()
|
||||||
|
// requestAnimationFrame(function () {
|
||||||
|
// fabric.Image.fromURL(
|
||||||
|
// img.src,
|
||||||
|
// (image) => {
|
||||||
|
// image.set({
|
||||||
|
// left: 0,
|
||||||
|
// top: 0,
|
||||||
|
// scaleX: canvas.width / img.width,
|
||||||
|
// scaleY: canvas.height / img.height,
|
||||||
|
// selectable: false,
|
||||||
|
// evented: false,
|
||||||
|
// erasable: false // 不允许擦拭
|
||||||
|
// })
|
||||||
|
// // canvas.setBackgroundImage(image, canvas.renderAll.bind(canvas));
|
||||||
|
// canvas.setBackgroundImage(image, () => {
|
||||||
|
// canvas.renderAll.bind(canvas)
|
||||||
|
// FabricVue.value.render()
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// {crossOrigin: 'anonymous'}
|
||||||
|
// )
|
||||||
|
// // 渲染所有对象
|
||||||
|
// canvas.requestRenderAll() // 批量重绘
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
// 找到page一样的
|
||||||
|
const findObjectByPage=(array, page)=> {
|
||||||
|
const foundObject = array.find(obj => obj.page === page);
|
||||||
|
return foundObject || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//page 一样替换
|
//page 一样替换
|
||||||
const mergeAndReplace = (arr1, arr2) => {
|
const mergeAndReplace = (arr1, arr2) => {
|
||||||
// // 用于存储替换后的数组
|
|
||||||
// const resultArray = array1.map(item1 => {
|
|
||||||
// // 在 array2 中查找 page 相同的对象
|
|
||||||
// const replacement = array2.find(item2 => item2.page == item1.page);
|
|
||||||
// // 如果找到替换对象,则返回替换对象,否则返回原对象
|
|
||||||
// return replacement ? replacement : item1;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // 将 array2 中 page 不在 array1 中的对象追加到结果数组中
|
|
||||||
// array2.forEach(item2 => {
|
|
||||||
// const existsInArray1 = array1.some(item1 => item1.page == item2.page);
|
|
||||||
// if (!existsInArray1) {
|
|
||||||
// resultArray.push(item2);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return resultArray;
|
|
||||||
|
|
||||||
// 创建一个映射,将 arr2 中的对象按 page 属性存储
|
// 创建一个映射,将 arr2 中的对象按 page 属性存储
|
||||||
let map = new Map(arr2.map((item) => [item.page, item]))
|
let map = new Map(arr2.map((item) => [item.page, item]))
|
||||||
|
|
|
@ -9,6 +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)
|
||||||
ipcRenderer.send('download-file-default', {
|
ipcRenderer.send('download-file-default', {
|
||||||
url: item.fileFullPath,
|
url: item.fileFullPath,
|
||||||
fileName: item.fileNewName
|
fileName: item.fileNewName
|
||||||
|
|
|
@ -6,14 +6,17 @@
|
||||||
// Remote.app.getAppPath() E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar
|
// Remote.app.getAppPath() E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar
|
||||||
// path.join(__dirname) 根目录 E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar\out\renderer
|
// path.join(__dirname) 根目录 E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar\out\renderer
|
||||||
|
|
||||||
const path = require('path')
|
const isNode = typeof require !== 'undefined' // 是否支持node函数
|
||||||
const Remote = require('@electron/remote')
|
const path = isNode?require('path'):{}
|
||||||
const { ipcRenderer } = require('electron')
|
const Remote = isNode?require('@electron/remote'):{}
|
||||||
|
const { ipcRenderer } = isNode?require('electron'):window.electron || {}
|
||||||
|
|
||||||
// 常用变量
|
// 常用变量
|
||||||
const BaseUrl = process.env['ELECTRON_RENDERER_URL']+'/#'
|
const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':''
|
||||||
const isDev = process.env.NODE_ENV !== 'production'
|
const isDev = isNode?process.env.NODE_ENV !== 'production':''
|
||||||
|
|
||||||
|
// 暴露Remote中的属性
|
||||||
|
export const ipcMain = Remote?.ipcMain || {}
|
||||||
/**
|
/**
|
||||||
* 获取静态资源,开发和生产环境
|
* 获取静态资源,开发和生产环境
|
||||||
* @param {*} url
|
* @param {*} url
|
||||||
|
@ -39,15 +42,38 @@ export const getStaticUrl = (url = '', type = 'app', exitPath = '', isFile = fal
|
||||||
* url:路由地址,width:窗口宽度,height:窗口高度,option:自定义选项
|
* url:路由地址,width:窗口宽度,height:窗口高度,option:自定义选项
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function ipcMsgSend(key, data) {
|
export function ipcMsgSend(key, data, type) {
|
||||||
|
if (type == 'old') {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// 返回结果-监听
|
// 返回结果-监听
|
||||||
ipcRenderer.once(`${key}-reply`, (e, res) => {
|
ipcRenderer?.once(`${key}-reply`, (e, res) => {
|
||||||
resolve(res)
|
resolve(res)
|
||||||
})
|
})
|
||||||
// 发送消息
|
// 发送消息
|
||||||
ipcRenderer.send(key, data)
|
ipcRenderer?.send(key, data)
|
||||||
})
|
})
|
||||||
|
} else if (type == 'send') { // 单独发送
|
||||||
|
ipcRenderer?.send(key, data)
|
||||||
|
} else { // 新版本-默认
|
||||||
|
return ipcRenderer?.invoke(key, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function ipcMsgSend2(key, ...args) {
|
||||||
|
return ipcRenderer?.send(key, ...args)
|
||||||
|
}
|
||||||
|
export function ipcMsgInvoke(key, ...args) {
|
||||||
|
return ipcRenderer?.invoke(key, ...args)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description 封装ipcRenderer事件监听
|
||||||
|
* @param {*} fn on once handle invoke
|
||||||
|
* @param {*} key
|
||||||
|
* @param {Function} cb 回调函数
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function ipcHandle(fn,key, cb) {
|
||||||
|
return ipcRenderer[fn](key, cb)
|
||||||
|
// return ipcRenderer?.[fn]?.(key, cb)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 创建-窗口 调用该方法
|
* 创建-窗口 调用该方法
|
||||||
|
@ -69,9 +95,9 @@ export const createWindow = async (type, data) => {
|
||||||
// parent: mainWin, // 父窗口
|
// parent: mainWin, // 父窗口
|
||||||
// autoClose: true, // 关闭窗口后自动关闭
|
// autoClose: true, // 关闭窗口后自动关闭
|
||||||
}
|
}
|
||||||
|
data.isConsole = true // 是否开启控制台
|
||||||
data.option = {...defOption, ...option}
|
data.option = {...defOption, ...option}
|
||||||
const win = await toolWindow(data)
|
const win = await toolWindow(data)
|
||||||
win.setTitle('窗口标题: 我的自定义参数')
|
|
||||||
win.type = type // 唯一标识
|
win.type = type // 唯一标识
|
||||||
win.show()
|
win.show()
|
||||||
win.setFullScreen(true) // 设置窗口为全屏
|
win.setFullScreen(true) // 设置窗口为全屏
|
||||||
|
@ -89,11 +115,13 @@ export const createWindow = async (type, data) => {
|
||||||
resizable: true, // 禁止窗口大小缩放
|
resizable: true, // 禁止窗口大小缩放
|
||||||
alwaysOnTop: false, // 窗口是否总是显示在其他窗口之前
|
alwaysOnTop: false, // 窗口是否总是显示在其他窗口之前
|
||||||
}
|
}
|
||||||
|
data.isConsole = true // 是否开启控制台
|
||||||
data.option = {...defOption, ...option}
|
data.option = {...defOption, ...option}
|
||||||
const win = await toolWindow(data)
|
const win = await toolWindow(data)
|
||||||
win.type = type // 唯一标识
|
win.type = type // 唯一标识
|
||||||
win.show()
|
win.show()
|
||||||
win.setFullScreen(true) // 设置窗口为全屏
|
win.setFullScreen(true) // 设置窗口为全屏
|
||||||
|
// win.webContents.openDevTools() // 打开调试工具
|
||||||
eventHandles(type, win) // 事件监听处理
|
eventHandles(type, win) // 事件监听处理
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -110,7 +138,7 @@ export const createWindow = async (type, data) => {
|
||||||
* @author: zdg
|
* @author: zdg
|
||||||
* @date 2021-07-05 14:07:01
|
* @date 2021-07-05 14:07:01
|
||||||
*/
|
*/
|
||||||
export function toolWindow({url, isFile, isConsole, option={}}) {
|
export function toolWindow({url, isConsole, option={}}) {
|
||||||
// width = window.screen.width
|
// width = window.screen.width
|
||||||
let width = option?.width || 800
|
let width = option?.width || 800
|
||||||
let height = option?.height || 600
|
let height = option?.height || 600
|
||||||
|
@ -145,7 +173,7 @@ export function toolWindow({url, isFile, isConsole, option={}}) {
|
||||||
// 内部监听器-是否打印
|
// 内部监听器-是否打印
|
||||||
if (!!isConsole) {
|
if (!!isConsole) {
|
||||||
win.webContents.on('console-message', (e,leve,m,lin,s) => {
|
win.webContents.on('console-message', (e,leve,m,lin,s) => {
|
||||||
console.log('console-msg: ', m)
|
console.log(`[${win.type}]`,m)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -173,20 +201,23 @@ const eventHandles = (type, win) => {
|
||||||
case 'tool-sphere': { // 创建-悬浮球
|
case 'tool-sphere': { // 创建-悬浮球
|
||||||
// 监听设置穿透
|
// 监听设置穿透
|
||||||
const setIgnore = (_, ignore) => {win.setIgnoreMouseEvents(ignore, {forward: true})}
|
const setIgnore = (_, ignore) => {win.setIgnoreMouseEvents(ignore, {forward: true})}
|
||||||
Remote.ipcMain.on('tool-sphere:set:ignore', setIgnore)
|
Remote.ipcMain.handle('tool-sphere:set:ignore', setIgnore) // 异步
|
||||||
// 关闭窗口
|
// 关闭窗口
|
||||||
Remote.ipcMain.once('tool-sphere:close', () => { win.destroy() })
|
Remote.ipcMain.handleOnce('tool-sphere:close', () => {win&&win.destroy()})
|
||||||
// 放大监听-测试
|
// // 放大监听-测试
|
||||||
Remote.ipcMain.once('maximize-window', () => {win.destroy()})
|
// Remote.ipcMain.once('maximize-window', () => {win&&win.destroy()})
|
||||||
const on = {
|
const on = {
|
||||||
onClosed: () => {Remote.ipcMain.off('tool-sphere:set:ignore', setIgnore)}
|
onClosed: () => {
|
||||||
|
Remote.ipcMain.removeHandler('tool-sphere:set:ignore', setIgnore)
|
||||||
|
Remote.ipcMain.removeHandler('tool-sphere:reset')
|
||||||
|
// Remote.ipcMain.removeAllListeners() // 移除所有监听事件
|
||||||
|
}
|
||||||
}
|
}
|
||||||
publicMethods(on) // 加载公共方法
|
publicMethods(on) // 加载公共方法
|
||||||
break}
|
break}
|
||||||
case 'open-PDF': {
|
case 'open-PDF': {
|
||||||
// 最小化窗口 minimize()
|
// 最小化窗口 minimize()
|
||||||
Remote.ipcMain.once('open-PDF:minimize', () => {win.destroy()})
|
Remote.ipcMain.once('open-PDF:minimize', () => {win&&win.destroy()})
|
||||||
win.webContents.openDevTools()
|
|
||||||
publicMethods() // 加载公共方法
|
publicMethods() // 加载公共方法
|
||||||
break}
|
break}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="pdfbox" ref="pdfbox">
|
<div class="pdfbox" ref="pdfbox">
|
||||||
<pdfCanvas :pdfObj="pdfObj" ref="pdfCanvaslist" @update:numPagesTotal="handleUpdate" />
|
<pdfCanvas v-if="isOnLoadShow" :pdfObj="pdfObj" ref="pdfCanvaslist" @update:numPagesTotal="handleUpdate" />
|
||||||
<div class="pdf-btn">
|
<div class="pdf-btn">
|
||||||
<el-button
|
<el-button
|
||||||
style="border-top-left-radius: 8px"
|
style="border-top-left-radius: 8px"
|
||||||
|
@ -32,20 +32,31 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch, reactive } from 'vue'
|
import { ref, onMounted, watch, reactive,watchEffect ,onBeforeUnmount} from 'vue'
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf'
|
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf'
|
||||||
import pdfCanvas from '@/components/pdf/index.vue'
|
import pdfCanvas from '@/components/pdf/index.vue'
|
||||||
import { getStaticUrl } from '@/utils/tool'
|
import { getStaticUrl } from '@/utils/tool'
|
||||||
const { ipcRenderer } = require('electron')
|
const { ipcRenderer } = require('electron')
|
||||||
|
import { getBookMarkById } from '@/api/eTextbook/index'
|
||||||
|
import {useToolState} from '@/store/modules/tool'
|
||||||
|
// const getStaticUrl=(url)=>{
|
||||||
|
// return url
|
||||||
|
// }
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = getStaticUrl('/lib/build/pdf.worker.mjs')
|
pdfjsLib.GlobalWorkerOptions.workerSrc = getStaticUrl('/lib/build/pdf.worker.mjs')
|
||||||
|
const toolState = useToolState();
|
||||||
|
const route = useRoute();
|
||||||
|
const isOnLoadShow = ref(false) //加载完毕显示
|
||||||
// 传过去的参数
|
// 传过去的参数
|
||||||
const pdfObj = reactive({
|
const pdfObj = reactive({
|
||||||
numberOfPdf: 2, //显示几页
|
numberOfPdf: 2, //显示几页
|
||||||
pdfUrl: getStaticUrl('aaa.pdf', 'user', 'selfFile', true),
|
// pdfUrl: getStaticUrl('aaa.pdf', 'user', 'selfFile', true),//本地
|
||||||
|
pdfUrl: null,
|
||||||
|
allPageData:[],
|
||||||
|
bookId:null,
|
||||||
numPages: 1 //当前页数
|
numPages: 1 //当前页数
|
||||||
})
|
})
|
||||||
|
const textbookId=ref(null) //书籍id
|
||||||
// 总条数
|
// 总条数
|
||||||
const numPagesTotal = ref(0)
|
const numPagesTotal = ref(0)
|
||||||
const pdfCanvaslist = ref(null)
|
const pdfCanvaslist = ref(null)
|
||||||
|
@ -60,17 +71,22 @@ const navtopage = (type) => {
|
||||||
pdfObj.numPages += num
|
pdfObj.numPages += num
|
||||||
}
|
}
|
||||||
if (pdfObj.numPages > numPagesTotal.value) return
|
if (pdfObj.numPages > numPagesTotal.value) return
|
||||||
pdfCanvaslist.value.initPdf()
|
pdfCanvaslist.value.initPdf('rest')
|
||||||
}
|
}
|
||||||
// 最小化窗口
|
// 最小化窗口
|
||||||
const minimize = () => {
|
const minimize = async () => {
|
||||||
ipcRenderer.send('open-PDF:minimize')
|
await pdfCanvaslist.value.savaDataStore()
|
||||||
|
// toolState.isPdfWin=false
|
||||||
|
// toolState.showBoardAll=true //恢复默认值
|
||||||
|
// console.log(toolState.showBoardAll,"关闭")
|
||||||
|
// // ipcRenderer.send('tool-sphere:reset') //重置tool状态
|
||||||
|
// ipcRenderer.send('open-PDF:minimize')
|
||||||
}
|
}
|
||||||
const handleUpdate = (data) => {
|
const handleUpdate = (data) => {
|
||||||
numPagesTotal.value = data
|
numPagesTotal.value = data
|
||||||
if (numPagesTotal.value == 1) {
|
if (numPagesTotal.value == 1) {
|
||||||
pdfObj.numberOfPdf = 1
|
pdfObj.numberOfPdf = 1
|
||||||
pdfCanvaslist.value.initPdf('restone')
|
pdfCanvaslist.value.initPdf('rest')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 单双页切换
|
// 单双页切换
|
||||||
|
@ -87,10 +103,35 @@ const switchPageMode = () => {
|
||||||
} else {
|
} else {
|
||||||
// 多页切单页
|
// 多页切单页
|
||||||
pdfObj.numberOfPdf = 1
|
pdfObj.numberOfPdf = 1
|
||||||
pdfCanvaslist.value.initPdf('restone')
|
pdfCanvaslist.value.initPdf('rest')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMounted(async () => {})
|
onMounted(async () => {
|
||||||
|
toolState.isPdfWin=true
|
||||||
|
pdfObj.pdfUrl = getStaticUrl(route.query.path, 'user', 'selfFile', true)
|
||||||
|
textbookId.value = route.query.textbookId
|
||||||
|
pdfObj.bookId=textbookId.value
|
||||||
|
getBookMarkById(textbookId.value).then(res=>{
|
||||||
|
pdfObj.allPageData=getUniqueArrayByLastOccurrence(res.data)
|
||||||
|
isOnLoadShow.value=true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//根据page去重
|
||||||
|
const getUniqueArrayByLastOccurrence=(array)=> {
|
||||||
|
const uniqueItems = array.reduce((acc, current) => {
|
||||||
|
// 使用 Map 来跟踪最后一个出现的 pageNum 和它的对象
|
||||||
|
acc.set(current.pageNum, current);
|
||||||
|
return acc;
|
||||||
|
}, new Map());
|
||||||
|
|
||||||
|
// 将 Map 的值转换回数组
|
||||||
|
const resultArray = Array.from(uniqueItems.values());
|
||||||
|
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref,defineProps,defineEmits} from "vue";
|
import {ref} from "vue";
|
||||||
const activeIndex = ref('0')
|
const activeIndex = ref('0')
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
classList:{
|
classList:{
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<el-container class="class-reserv-wrap">
|
||||||
|
<div class="class-reserv-tabs">
|
||||||
|
<el-segmented v-model="tabActive" block :options="tabOptions" size="large" />
|
||||||
|
</div>
|
||||||
|
<div class="class-reserv-body">
|
||||||
|
<reserv-item
|
||||||
|
v-for="(item, index) in activeDataList"
|
||||||
|
v-show="tabActive === '进行中'"
|
||||||
|
:key="index"
|
||||||
|
:item="item"
|
||||||
|
@open-edit="reservDialog.openDialog(item)"
|
||||||
|
@delete-reserv="deleteReserv(item)"
|
||||||
|
></reserv-item>
|
||||||
|
<reserv-item
|
||||||
|
v-for="(item, index) in doneDataList"
|
||||||
|
v-show="tabActive === '已结束'"
|
||||||
|
:key="index"
|
||||||
|
:item="item"
|
||||||
|
></reserv-item>
|
||||||
|
</div>
|
||||||
|
<reserv ref="reservDialog"></reserv>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, computed, watch } from 'vue'
|
||||||
|
import { getSelfReserv } from '@/api/classManage'
|
||||||
|
import ReservItem from '@/views/classManage/reserv-item.vue'
|
||||||
|
import Reserv from '@/views/prepare/container/reserv.vue'
|
||||||
|
import { useToolState } from '@/store/modules/tool'
|
||||||
|
const reservDialog = ref(null)
|
||||||
|
const tabOptions = ref(['进行中', '已结束'])
|
||||||
|
const tabActive = ref('进行中')
|
||||||
|
const dataList = ref([])
|
||||||
|
|
||||||
|
const activeDataList = computed(() => {
|
||||||
|
return dataList.value.filter((item) => {
|
||||||
|
return item.status !== '已结束'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const deleteReserv = (item) => {
|
||||||
|
dataList.value = dataList.value.filter((is) => {
|
||||||
|
return is.id !== item.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const doneDataList = computed(() => {
|
||||||
|
return dataList.value.filter((item) => {
|
||||||
|
return item.status === '已结束'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const toolStore = useToolState()
|
||||||
|
watch(
|
||||||
|
() => toolStore.isToolWin,
|
||||||
|
(newD, oldD) => {
|
||||||
|
getSelfReserv().then((res) => {
|
||||||
|
dataList.value = [...res.data]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
onMounted(() => {
|
||||||
|
getSelfReserv().then((res) => {
|
||||||
|
dataList.value = res.data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.class-reserv-wrap {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 15px 30px;
|
||||||
|
.class-reserv-tabs {
|
||||||
|
width: 30%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.class-reserv-body {
|
||||||
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,133 @@
|
||||||
|
<template>
|
||||||
|
<div class="class-reserv-item">
|
||||||
|
<div class="class-reserv-item-img">
|
||||||
|
<img :src="basePath + item.bookImg" alt="封面" />
|
||||||
|
</div>
|
||||||
|
<div class="class-reserv-item-body">
|
||||||
|
<div class="class-reserv-item-title1">
|
||||||
|
<label>{{ item.className }}</label>
|
||||||
|
<el-tag style="margin-left: 5px" type="primary"> {{ item.classType }}</el-tag>
|
||||||
|
<el-tag style="margin-left: 5px" type="primary"> {{ item.classSubject }}</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="class-reserv-item-title2">
|
||||||
|
{{ item.classDay }} {{ item.startTime }} ~ {{ item.classDay }} {{ item.endTime }}
|
||||||
|
{{ item.createUserName }}老师
|
||||||
|
</div>
|
||||||
|
<div class="class-reserv-item-title3">
|
||||||
|
<span v-for="(tag, index) in item.classItemList" :key="index" style="margin-left: 5px">
|
||||||
|
{{ index === 0 ? tag.name : '、' + tag.name }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="class-reserv-item-tool">
|
||||||
|
<el-button v-if="item.status !== '已结束'" type="primary" @click="startClassR(item)"
|
||||||
|
>上课</el-button
|
||||||
|
>
|
||||||
|
<el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>
|
||||||
|
<!-- <el-button v-if="item.status === '上课中'" type="info" @click="endClassR(item)"
|
||||||
|
>下课</el-button
|
||||||
|
>-->
|
||||||
|
<el-button type="danger" @click="deleteReserv">删除</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { useToolState } from '@/store/modules/tool'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { createWindow } from '@/utils/tool'
|
||||||
|
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { listEntpcourse } from '@/api/teaching/classwork'
|
||||||
|
const emit = defineEmits(['openEdit', 'deleteReserv'])
|
||||||
|
const props = defineProps({
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
|
||||||
|
const openEdit = () => {
|
||||||
|
emit('openEdit', props.item)
|
||||||
|
}
|
||||||
|
const deleteReserv = () => {
|
||||||
|
deleteSmartReserv([props.item.id]).then((res) => {
|
||||||
|
if (res.data === true) {
|
||||||
|
ElMessage({
|
||||||
|
message: '删除成功',
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
emit('deleteReserv', props.item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const startClassR = (item) => {
|
||||||
|
startClass(item.id).then((res) => {
|
||||||
|
if (res.data === true) {
|
||||||
|
item.status = '上课中'
|
||||||
|
openLesson()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const toolStore = useToolState()
|
||||||
|
// 上课-工具类悬浮
|
||||||
|
const openLesson = () => {
|
||||||
|
if (toolStore.isToolWin) return ElMessage.error('您当前已开始上课,请勿重复操作')
|
||||||
|
startClass(props.item.id)
|
||||||
|
listEntpcourse({
|
||||||
|
evalid: props.item.ex2,
|
||||||
|
edituserid: useUserStore().user.userId,
|
||||||
|
pageSize: 500
|
||||||
|
}).then(res=>{
|
||||||
|
if (res.rows[0].id) {
|
||||||
|
createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res.rows[0].id + "&reservId=" + props.item.id })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const endClassR = (item) => {
|
||||||
|
endClass(item.id).then((res) => {
|
||||||
|
if (res.data === true) {
|
||||||
|
ElMessage({
|
||||||
|
message: '下课成功',
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
item.status = '已结束'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.class-reserv-item {
|
||||||
|
display: flex;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
.class-reserv-item-img {
|
||||||
|
width: 80px;
|
||||||
|
padding-left: 20px;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.class-reserv-item-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
.class-reserv-item-title1 {
|
||||||
|
flex: 1;
|
||||||
|
label {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.class-reserv-item-tool {
|
||||||
|
margin-left: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,39 +1,58 @@
|
||||||
<template>
|
<template>
|
||||||
<el-card style="margin-top: 20px">
|
<el-card style="overflow: auto; height: 100%">
|
||||||
<div class="common-layout" style="overflow-y: auto">
|
<div class="common-layout" style="overflow-y: auto">
|
||||||
<el-container>
|
<el-container>
|
||||||
<el-main>
|
<el-main style="--el-main-padding: 0">
|
||||||
<template v-for="(itemFirst,indexFirst) in title" :key="indexFirst">
|
<template v-for="(itemFirst, indexFirst) in title" :key="indexFirst">
|
||||||
<el-card style="margin-bottom: 10px">
|
<el-card style="margin-bottom: 10px">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div style="text-align: left">{{itemFirst.name}}</div>
|
<div style="text-align: left">{{ itemFirst.name }}</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- 教学工作台-->
|
<div :class="itemFirst.id === 1 || itemFirst.id === 2 ? 'six' : 'three'">
|
||||||
<div :class="itemFirst.id === 1 || itemFirst.id === 2 ? 'six':'three'">
|
<template v-for="(itemSec, indexSec) in itemFirst.child" :key="indexSec">
|
||||||
<template v-for="(itemSec,indexSec) in itemFirst.child" :key="indexSec">
|
<el-popover :disabled="itemSec.child1.length === 0" width="auto" trigger="hover">
|
||||||
<el-popover
|
<div style="display: flex; justify-content: space-between">
|
||||||
width="auto"
|
<!-- 鼠标移上去的一列为三级菜单-->
|
||||||
trigger="hover"
|
<template v-for="(itemThird, indexThird) in itemSec.child1" :key="indexThird">
|
||||||
v-if="itemSec.child1.length > 0"
|
<div
|
||||||
|
style="width: 120px"
|
||||||
|
:class="[
|
||||||
|
itemFirst.id == 1
|
||||||
|
? 'a1'
|
||||||
|
: itemFirst.id == 2
|
||||||
|
? 'a2'
|
||||||
|
: itemFirst.id == 3
|
||||||
|
? 'a3'
|
||||||
|
: 'a4',
|
||||||
|
'CustomBox'
|
||||||
|
]"
|
||||||
|
@click="handleOutLink(itemThird.url)"
|
||||||
>
|
>
|
||||||
<div style="display: flex;justify-content: space-between">
|
|
||||||
<!-- 鼠标移上去的一列为三级菜单-->
|
|
||||||
<template v-for="(itemThird,indexThird) in itemSec.child1" :key="indexThird">
|
|
||||||
<div :class="[itemFirst.id==1?'a1':itemFirst.id==2?'a2':itemFirst.id==3?'a3':'a4','CustomBox']">
|
|
||||||
<span :class="itemThird.img"></span>
|
<span :class="itemThird.img"></span>
|
||||||
<span>{{ itemThird.name }}</span>
|
<span>{{ itemThird.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<!-- 最外层的一列为二级菜单-->
|
<!-- 最外层的一列为二级菜单-->
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<div :class="[itemFirst.id==1?'a1':itemFirst.id==2?'a2':itemFirst.id==3?'a3':'a4','CustomBox']">
|
<div
|
||||||
|
:class="[
|
||||||
|
itemFirst.id == 1
|
||||||
|
? 'a1'
|
||||||
|
: itemFirst.id == 2
|
||||||
|
? 'a2'
|
||||||
|
: itemFirst.id == 3
|
||||||
|
? 'a3'
|
||||||
|
: 'a4',
|
||||||
|
'CustomBox'
|
||||||
|
]"
|
||||||
|
@click="handleOutLink(itemSec.url, itemSec.type)"
|
||||||
|
>
|
||||||
<span :class="itemSec.img"></span>
|
<span :class="itemSec.img"></span>
|
||||||
<span>{{ itemSec.name }}</span>
|
<span>{{ itemSec.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
@ -45,360 +64,112 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive } from "vue"
|
import { reactive } from 'vue'
|
||||||
const title = reactive([
|
import { useRouter } from 'vue-router'
|
||||||
|
import outLink from '@/utils/linkConfig'
|
||||||
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
const router = useRouter()
|
||||||
|
const title = reactive([
|
||||||
{
|
{
|
||||||
name: "教学工作台",
|
name: '教学工作台',
|
||||||
img: "iconfont icon-gongzuotai",
|
img: 'iconfont icon-gongzuotai',
|
||||||
id:1,
|
id: 1,
|
||||||
child: [
|
child: [
|
||||||
{
|
{
|
||||||
name: "教学分析",
|
name: '课程教学',
|
||||||
img: "iconfont icon-shuyi_jiaoxueguanli",
|
img: 'iconfont icon-PPT',
|
||||||
child1: [{
|
type: 'hash',
|
||||||
name: "课标分析",
|
url: '/prepare',
|
||||||
url: "/teaching/chatwithstandard",
|
child1: []
|
||||||
img: "iconfont icon-kecheng",
|
|
||||||
}, {
|
|
||||||
name: "教材分析",
|
|
||||||
url: "/teaching/chatwithtextbook",
|
|
||||||
img: "iconfont icon-yanjiushi",
|
|
||||||
}, {
|
|
||||||
name: "考试分析",
|
|
||||||
url: "/education/colentrance",
|
|
||||||
img: "iconfont icon-icon_kaoshifenxi",
|
|
||||||
}, {
|
|
||||||
name: "学情分析",
|
|
||||||
url: "",
|
|
||||||
img: "iconfont icon-fenxi",
|
|
||||||
}, {
|
|
||||||
name: "资源分析",
|
|
||||||
url: "",
|
|
||||||
img: "iconfont icon-business-report",
|
|
||||||
}],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "教学设计",
|
name: '作业管理',
|
||||||
img: "iconfont icon-PPT",
|
img: 'iconfont icon-36zuoyepingtai',
|
||||||
url:"/teaching/aptindex",
|
url: '/teaching/classtaskassign?titleName=作业布置',
|
||||||
|
child1: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '教学研究室',
|
||||||
|
img: 'iconfont icon-yanjiushi',
|
||||||
|
id: 2,
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
name: '课程研究',
|
||||||
|
img: 'iconfont icon-shuyi_jiaoxueguanli',
|
||||||
child1: [
|
child1: [
|
||||||
{
|
{
|
||||||
name: "创作类型选择",
|
name: '课标分析',
|
||||||
url: "",
|
url: '/teaching/chatwithstandard',
|
||||||
img: "",
|
img: 'iconfont icon-kecheng'
|
||||||
}, {
|
|
||||||
name: "创作模式选择",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
}, {
|
|
||||||
name: "创作途径选择",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
}, {
|
|
||||||
name: "创作要素调用",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
}, {
|
|
||||||
name: "创作作品运用",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
}]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "教学实施",
|
name: '教材分析',
|
||||||
img: "iconfont icon-ldc-position",
|
url: '/teaching/chatwithtextbook',
|
||||||
url:"/teaching/classteachingprep",
|
img: 'iconfont icon-yanjiushi'
|
||||||
child1: [{
|
|
||||||
name: "教学课件选择",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
}, {
|
|
||||||
name: "教学场景选择",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
}, {
|
|
||||||
name: "教学对象选择",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
}, {
|
|
||||||
name: "教学过程进行",
|
|
||||||
url: "",
|
|
||||||
img: "",
|
|
||||||
},]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "作业管理",
|
|
||||||
img: "iconfont icon-36zuoyepingtai",
|
|
||||||
url:"/teaching/classtaskassign?titleName=作业布置",
|
|
||||||
child1: [{
|
|
||||||
name: "作业布置",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "作业反馈",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "教学评价",
|
|
||||||
img: "iconfont icon-ziyuanfenxi",
|
|
||||||
url:"",
|
|
||||||
child1: [{
|
|
||||||
name: "教学评价",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "学情诊断",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "教学管理",
|
|
||||||
img: "iconfont icon-ziyuanfenxi",
|
|
||||||
url:"",
|
|
||||||
child1: [{
|
|
||||||
name: "教学评价",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "学情诊断",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '高考研究',
|
||||||
|
url: '/education/colentrance',
|
||||||
|
img: 'iconfont icon-icon_kaoshifenxi',
|
||||||
|
child1: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '资源研究',
|
||||||
|
url: '/resource',
|
||||||
|
type: 'hash',
|
||||||
|
img: 'iconfont icon-business-report',
|
||||||
|
child1: []
|
||||||
}
|
}
|
||||||
, {
|
]
|
||||||
name: "教学研究室",
|
},
|
||||||
img: "iconfont icon-yanjiushi",
|
{
|
||||||
id:2,
|
name: '教学资源库',
|
||||||
|
img: 'iconfont icon-zhuanyeziyuanku',
|
||||||
|
id: 3,
|
||||||
child: [
|
child: [
|
||||||
{
|
{
|
||||||
name: "自主研修",
|
name: '教学素材',
|
||||||
img: "iconfont icon-jiaoxueyanxiu",
|
img: 'iconfont icon-sucai',
|
||||||
child1: [{
|
url: '/teaching/materialbank',
|
||||||
name: "文献查阅",
|
child1: []
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "专家咨询",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "培训课程",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "集体备课",
|
name: '课程资源',
|
||||||
img: "iconfont icon-beike",
|
img: 'iconfont icon-kechengziyuan',
|
||||||
child1: [{
|
url: '/teaching/coursewareresource',
|
||||||
name: "教学分析讨论",
|
child1: []
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "教学设计审核",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "作业设计审核",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "教学反馈分析",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "观课议课",
|
name: '习题资源',
|
||||||
img: "iconfont icon-decheng_xianshangxuexi",
|
img: 'iconfont icon-iconku-zhuanqu-',
|
||||||
child1: [{
|
url: '/teaching/quesbank',
|
||||||
name: "观看课堂教学实录",
|
child1: []
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "查看课堂教学报告",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "进行课堂教学评议",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "生成观课课报告",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: "课题研究",
|
|
||||||
img: "iconfont icon-keti",
|
|
||||||
child1: [{
|
|
||||||
name: "选题建议",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "立项申报",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "研究开展",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "中期检查",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "结项评审",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: "论文撰写",
|
|
||||||
img: "iconfont icon-xiezuo1",
|
|
||||||
child1: [{
|
|
||||||
name: "论文选题",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "论文写作",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "论文发表",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
, {
|
])
|
||||||
name: "教学资源库",
|
const handleOutLink = (path, type) => {
|
||||||
img: "iconfont icon-zhuanyeziyuanku",
|
if (!path) return
|
||||||
id:3,
|
if (type === 'hash') {
|
||||||
child: [{
|
router.push(path)
|
||||||
name: "教学素材",
|
} else {
|
||||||
img: "iconfont icon-sucai",
|
// key 对应的 linkConfig.js 外部链接配置
|
||||||
url:"/teaching/materialbank",
|
let configObj = outLink.getBaseData()
|
||||||
child1: [{
|
let fullPath = configObj.fullPath + path
|
||||||
name: "素材上传",
|
fullPath = fullPath.replaceAll('//', '/')
|
||||||
img: "",
|
// 通知主进程
|
||||||
url: ""
|
ipcRenderer.send('openWindow', {
|
||||||
}, {
|
fullPath: fullPath,
|
||||||
name: "素材编辑",
|
cookieData: { ...configObj.data }
|
||||||
img: "",
|
})
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "收费设置",
|
|
||||||
img: "",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "素材浏览",
|
|
||||||
img: "iconfont icon-liulan",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "素材下架",
|
|
||||||
img: "iconfont icon-xiajia1",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: "课程资源",
|
|
||||||
img: "iconfont icon-kechengziyuan",
|
|
||||||
url: "/teaching/coursewareresource",
|
|
||||||
child1: [{
|
|
||||||
name: "资源集成",
|
|
||||||
img: "iconfont icon-yaosu",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "资源收藏",
|
|
||||||
img: "iconfont icon-shoucang1",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "资源分享",
|
|
||||||
img: "iconfont icon-fenxiang1",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: "习题资源",
|
|
||||||
img: "iconfont icon-iconku-zhuanqu-",
|
|
||||||
url:"/teaching/quesbank",
|
|
||||||
child1: [{
|
|
||||||
name: "习题上传",
|
|
||||||
img: "iconfont icon-qwe",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "习题收藏",
|
|
||||||
img: "iconfont icon-shoucang",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "习题浏览",
|
|
||||||
img: "iconfont icon-liulan",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "习题下架",
|
|
||||||
img: "iconfont icon-xiajia",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
, {
|
}
|
||||||
name: "教学共同体",
|
|
||||||
img: "iconfont icon-pengyou",
|
|
||||||
id:4,
|
|
||||||
child: [{
|
|
||||||
name: "云集体",
|
|
||||||
img: "iconfont icon-jitibeike-",
|
|
||||||
child1: [{
|
|
||||||
name: "学科组",
|
|
||||||
img: "iconfont icon-xuekezuhe",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "班级",
|
|
||||||
img: "iconfont icon-banji",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "加好友",
|
|
||||||
img: "iconfont icon-zhucetianjiahaoyou",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "创建群",
|
|
||||||
img: "iconfont icon-qunzu",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: "云会话",
|
|
||||||
img: "iconfont icon-dangqianhuihua",
|
|
||||||
child1: [{
|
|
||||||
name: "资源分享",
|
|
||||||
img: "icontfont icon-a-fenxiang2",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "即时交流",
|
|
||||||
img: "iconfont icon-jiaoliu",
|
|
||||||
url: ""
|
|
||||||
}, {
|
|
||||||
name: "系统通知",
|
|
||||||
img: "iconfont icon-tongzhizhongxin",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: "云动态",
|
|
||||||
img: "iconfont icon-pengyouquan",
|
|
||||||
child1: [{
|
|
||||||
name: "分享动态",
|
|
||||||
img: "iconfont icon-fenxiang",
|
|
||||||
url: ""
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style scoped>
|
||||||
@import '@/assets/teachingIconFont/iconfont.css';
|
|
||||||
.CustomTitle {
|
.CustomTitle {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -415,14 +186,17 @@ import { reactive } from "vue"
|
||||||
color: #7b7a7a;
|
color: #7b7a7a;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.six{
|
.six {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
& > div {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.three{
|
.three {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
& > div {
|
||||||
width: 50%;
|
width: 20%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.CustomBox span:nth-child(1) {
|
.CustomBox span:nth-child(1) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -446,26 +220,26 @@ import { reactive } from "vue"
|
||||||
.CustomBox span:nth-child(2) {
|
.CustomBox span:nth-child(2) {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
.CustomBox div{
|
.CustomBox div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.a1 span:nth-child(1){
|
.a1 span:nth-child(1) {
|
||||||
color:#1296DB;
|
color: #1296db;
|
||||||
}
|
}
|
||||||
|
|
||||||
.a2 span:nth-child(1){
|
.a2 span:nth-child(1) {
|
||||||
color:#F56C6C;
|
color: #f56c6c;
|
||||||
}
|
}
|
||||||
.a3 span:nth-child(1){
|
.a3 span:nth-child(1) {
|
||||||
color:#E6A23A;
|
color: #e6a23a;
|
||||||
}
|
}
|
||||||
.a4 span:nth-child(1){
|
.a4 span:nth-child(1) {
|
||||||
color:#67C23A;
|
color: #67c23a;
|
||||||
}
|
}
|
||||||
.ac span:nth-child(1){
|
.ac span:nth-child(1) {
|
||||||
background-color:#1296DB;
|
background-color: #1296db;
|
||||||
color:#ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--选择学科-->
|
<!--选择学科-->
|
||||||
<SelectSubject v-model="isSubject" @onSuccess="successEditSubject" />
|
<SelectSubject v-model="isSubject" :loginData="loginForm" @onSuccess="successEditSubject"/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
|
@ -35,7 +35,7 @@ import leftBg2 from '@/assets/images/login/left-bg2.png'
|
||||||
import WindowTools from '@/components/window-tools/index.vue'
|
import WindowTools from '@/components/window-tools/index.vue'
|
||||||
import SelectSubject from '@/components/select-subject/index.vue'
|
import SelectSubject from '@/components/select-subject/index.vue'
|
||||||
|
|
||||||
const { BrowserWindow, session } = require('@electron/remote')
|
const { session } = require('@electron/remote')
|
||||||
|
|
||||||
const { ipcRenderer } = window.electron || {}
|
const { ipcRenderer } = window.electron || {}
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
|
@ -48,13 +48,14 @@ const loginForm = reactive({
|
||||||
password: '',
|
password: '',
|
||||||
rememberMe: false
|
rememberMe: false
|
||||||
})
|
})
|
||||||
|
|
||||||
//表单规则
|
//表单规则
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
|
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
|
||||||
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
|
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
|
||||||
})
|
})
|
||||||
|
|
||||||
let curWinUrl;
|
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH;
|
||||||
|
|
||||||
//登录
|
//登录
|
||||||
const submitForm = async (formEl) => {
|
const submitForm = async (formEl) => {
|
||||||
|
@ -131,9 +132,7 @@ const setCookie = (name, value) => {
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 当前窗口URL
|
localStorage.clear()
|
||||||
curWinUrl = BrowserWindow.getFocusedWindow().webContents.getURL()
|
|
||||||
|
|
||||||
getCookie()
|
getCookie()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,8 +6,19 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="prepare-body-main-item-info" @click="openFileWin(item)">
|
<div class="prepare-body-main-item-info" @click="openFileWin(item)">
|
||||||
<div class="prepare-item-info-title" :title="item.fileShowName">
|
<div class="prepare-item-info-title" :title="item.fileShowName">
|
||||||
|
<div></div>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>
|
||||||
{{ item.fileShowName }}
|
{{ item.fileShowName }}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="prepare-item-info-message">
|
<div class="prepare-item-info-message">
|
||||||
<div style="width: 60px">
|
<div style="width: 60px">
|
||||||
<template v-if="item.uniquekey">
|
<template v-if="item.uniquekey">
|
||||||
|
@ -27,18 +38,21 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="item.fileSize">|</template>
|
<template v-if="item.fileSize">|</template>
|
||||||
<div style="width: 70px" v-if="item.fileSize">{{ formatFileSize(item.fileSize) }}</div>
|
<div v-if="item.fileSize" style="width: 70px">{{ formatFileSize(item.fileSize) }}</div>
|
||||||
<template v-if="item.uploadTime">|</template>
|
<template v-if="item.uploadTime">|</template>
|
||||||
<div style="width: 70px" v-if="item.uploadTime">{{ toTimeText(item.uploadTime, true) }}</div>
|
<div v-if="item.uploadTime" style="width: 70px">
|
||||||
|
{{ toTimeText(item.uploadTime, true) }}
|
||||||
|
</div>
|
||||||
<template v-if="item.levelFirstName">| </template>
|
<template v-if="item.levelFirstName">| </template>
|
||||||
<div
|
<div
|
||||||
v-if="item.levelFirstName"
|
v-if="item.levelFirstName"
|
||||||
style="
|
style="
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 200px;
|
||||||
"
|
"
|
||||||
:title="
|
:title="
|
||||||
item.levelFirstName +
|
item.levelFirstName +
|
||||||
|
@ -67,13 +81,13 @@
|
||||||
<div class="item-popover" @click="closePopver(index)">
|
<div class="item-popover" @click="closePopver(index)">
|
||||||
<template v-if="item.uniquekey">
|
<template v-if="item.uniquekey">
|
||||||
<div class="item-popover-item">
|
<div class="item-popover-item">
|
||||||
<el-button text @click="editTalk(item, index)">
|
<el-button text @click="setHomeWork(item, index)">
|
||||||
<i class="iconfont icon-bianji"></i>
|
<i class="iconfont icon-bianji"></i>
|
||||||
<span>布置</span>
|
<span>布置</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-popover-item">
|
<div class="item-popover-item">
|
||||||
<el-button text @click="deleteTalk(item)">
|
<el-button text @click="deleteHomework(item)">
|
||||||
<i class="iconfont icon-shanchu"></i>
|
<i class="iconfont icon-shanchu"></i>
|
||||||
<span>删除</span>
|
<span>删除</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
@ -145,7 +159,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: { 'on-move': null, 'on-delete': null },
|
emits: { 'on-move': null, 'on-delete': null, 'on-set': null, 'on-delhomework': null },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
listenList: []
|
listenList: []
|
||||||
|
@ -230,6 +244,14 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
//布置
|
||||||
|
setHomeWork(item) {
|
||||||
|
this.$emit('on-set', item)
|
||||||
|
},
|
||||||
|
// 删除作业
|
||||||
|
deleteHomework(item){
|
||||||
|
this.$emit('on-delhomework', item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,9 +280,9 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
.icon-zuoye{
|
.icon-zuoye {
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
color: #707070
|
color: #707070;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,9 +302,7 @@ export default {
|
||||||
.prepare-item-info-title {
|
.prepare-item-info-title {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
white-space: nowrap;
|
display: flex;
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepare-item-info-message {
|
.prepare-item-info-message {
|
||||||
|
|
|
@ -0,0 +1,257 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="centerDialogVisible"
|
||||||
|
class="reserv-dialog"
|
||||||
|
title="预约课程"
|
||||||
|
destroy-on-close
|
||||||
|
:before-close="closeDialog"
|
||||||
|
width="600"
|
||||||
|
style="text-align: left"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormDialog"
|
||||||
|
:model="form"
|
||||||
|
:rules="ruleForm"
|
||||||
|
label-width="auto"
|
||||||
|
style="max-width: 600px"
|
||||||
|
>
|
||||||
|
<el-form-item label="课程名称" prop="name">
|
||||||
|
<el-input v-model="form.name" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="课程类型" prop="type">
|
||||||
|
<el-segmented v-model="form.type" :block="false" :options="locationOptions" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label=" ">
|
||||||
|
<div>{{ locationMessage }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上课时间">
|
||||||
|
<el-form-item prop="day">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.day"
|
||||||
|
type="date"
|
||||||
|
:editable="false"
|
||||||
|
class="reserv-date-pick"
|
||||||
|
placeholder="请选择日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
<el-col :span="2" class="text-center">
|
||||||
|
<span class="text-gray-500">-</span>
|
||||||
|
</el-col>
|
||||||
|
<el-form-item prop="time">
|
||||||
|
<el-col :span="11">
|
||||||
|
<el-time-picker
|
||||||
|
v-model="form.time"
|
||||||
|
is-range
|
||||||
|
:editable="false"
|
||||||
|
format="HH:mm"
|
||||||
|
value-format="HH:mm"
|
||||||
|
class="reserv-time-pick"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="授课对象" prop="resource">
|
||||||
|
<el-checkbox-group v-model="form.resource">
|
||||||
|
<el-checkbox v-for="(item, index) in classList" :key="index" name="type" :value="item.id"
|
||||||
|
>{{ item.caption }}({{ item.classstudentcount }})人
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="教室" prop="classRoom">
|
||||||
|
<el-input v-model="form.classRoom" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="centerDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitForm"> 提交 </el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, defineExpose, onMounted, reactive, computed, watch } from 'vue'
|
||||||
|
import { addSmartClassReserv, updateSmartClassReserv, listClassmain } from '@/api/classManage'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const emit = defineEmits(['addSuccess'])
|
||||||
|
const props = defineProps({
|
||||||
|
bookId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
currentNode: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const ruleFormDialog = ref(null)
|
||||||
|
const nowType = ref('add')
|
||||||
|
const userStore = useUserStore().user
|
||||||
|
const centerDialogVisible = ref(false)
|
||||||
|
const form = reactive({
|
||||||
|
name: '',
|
||||||
|
type: '常规课',
|
||||||
|
day: '',
|
||||||
|
time: [],
|
||||||
|
resource: [],
|
||||||
|
classRoom: ''
|
||||||
|
})
|
||||||
|
const updateForm = ref({})
|
||||||
|
watch(
|
||||||
|
() => props.currentNode,
|
||||||
|
(newValue, oldValue) => {
|
||||||
|
form.name = newValue.label
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const ruleForm = reactive({
|
||||||
|
name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }],
|
||||||
|
day: [{ required: true, message: '请选择上课日期', trigger: 'change' }],
|
||||||
|
time: [{ required: true, message: '请选择上课时间', trigger: 'change' }],
|
||||||
|
classRoom: [{ required: false, message: '请选择上课时间', trigger: 'change' }],
|
||||||
|
resource: [{ required: true, message: '请选择授课对象', trigger: 'change' }]
|
||||||
|
})
|
||||||
|
const locationOptions = [
|
||||||
|
{
|
||||||
|
label: '常规课',
|
||||||
|
value: '常规课',
|
||||||
|
disabled: false,
|
||||||
|
message: '现场公屏授课,学生无需长时间打开平板上。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '互动课',
|
||||||
|
value: '互动课',
|
||||||
|
disabled: false,
|
||||||
|
message: '现场公屏授课,学生需打开平开与老师进行互动,如点赞、互动作业。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '直播课',
|
||||||
|
value: '直播课',
|
||||||
|
disabled: true,
|
||||||
|
message: '远程直播授课,学生需打开平开观看实时直播教学,并与老师进行互动。'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const locationMessage = computed(() => {
|
||||||
|
return locationOptions.find((item) => item.value === form.type).message
|
||||||
|
})
|
||||||
|
const openDialog = (data) => {
|
||||||
|
if (data) {
|
||||||
|
updateForm.value = data
|
||||||
|
nowType.value = 'update'
|
||||||
|
form.name = data.className
|
||||||
|
form.type = data.classType
|
||||||
|
form.day = data.classDay
|
||||||
|
form.time = [data.startTime, data.endTime]
|
||||||
|
form.resource = data.classList.split(',').map((item) => parseInt(item))
|
||||||
|
form.classRoom = data.classRoom
|
||||||
|
}
|
||||||
|
centerDialogVisible.value = true
|
||||||
|
}
|
||||||
|
const closeDialog = () => {
|
||||||
|
ruleFormDialog.value.resetFields()
|
||||||
|
centerDialogVisible.value = false
|
||||||
|
form.name = props.currentNode.label
|
||||||
|
}
|
||||||
|
const classList = ref([])
|
||||||
|
onMounted(() => {
|
||||||
|
listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' }).then(
|
||||||
|
(response) => {
|
||||||
|
console.log(response)
|
||||||
|
classList.value = [...response.rows]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
const submitForm = async () => {
|
||||||
|
const formEl = ruleFormDialog.value
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
if (nowType.value === 'update') {
|
||||||
|
updateClassReserv(form)
|
||||||
|
} else {
|
||||||
|
addClassReserv(form)
|
||||||
|
}
|
||||||
|
centerDialogVisible.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const updateClassReserv = (formData) => {
|
||||||
|
let ids = formData.resource.join(',')
|
||||||
|
let param = {
|
||||||
|
className: formData.name,
|
||||||
|
classType: formData.type,
|
||||||
|
classDay: formData.day,
|
||||||
|
startTime: formData.time[0],
|
||||||
|
endTime: formData.time[1],
|
||||||
|
classList: ids,
|
||||||
|
classRoom: formData.classRoom,
|
||||||
|
id: updateForm.value.id
|
||||||
|
}
|
||||||
|
updateSmartClassReserv(param).then((res) => {
|
||||||
|
if (res.data === true) {
|
||||||
|
closeDialog()
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '修改预约成功!'
|
||||||
|
})
|
||||||
|
Object.assign(updateForm.value, param)
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: res.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const addClassReserv = (formData) => {
|
||||||
|
let ids = formData.resource.join(',')
|
||||||
|
let param = {
|
||||||
|
className: formData.name,
|
||||||
|
classType: formData.type,
|
||||||
|
classDay: formData.day,
|
||||||
|
startTime: formData.time[0],
|
||||||
|
endTime: formData.time[1],
|
||||||
|
classList: ids,
|
||||||
|
classRoom: formData.classRoom,
|
||||||
|
classSubject: props.currentNode.edusubject,
|
||||||
|
ex1: props.bookId,
|
||||||
|
ex2: props.currentNode.id
|
||||||
|
}
|
||||||
|
addSmartClassReserv(param).then((res) => {
|
||||||
|
if (res.data === true) {
|
||||||
|
closeDialog()
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '预约成功!'
|
||||||
|
})
|
||||||
|
emit('addSuccess')
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: res.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
openDialog,
|
||||||
|
closeDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
:deep(.reserv-date-pick) {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
:deep(.reserv-time-pick) {
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,326 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisible" center top="10vh" width="600px" :show-close="false" append-to-body
|
||||||
|
style="border-radius: 10px; padding: 10px 15px;">
|
||||||
|
<template #header>
|
||||||
|
<div class="homerwork-header flex">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<i class="iconfont icon-guanbi" @click="cloneDialog"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-loading="setLoading">
|
||||||
|
<el-form :model="form" label-width="80px" ref="ruleForm" :rules="rules">
|
||||||
|
<el-form-item label="班级" prop="grade">
|
||||||
|
<el-scrollbar max-height="200px" style="width: 100%;">
|
||||||
|
<el-tree ref="treeRef" :data="gradeList" :props="defaultProps" :load="getLoad" node-key="id"
|
||||||
|
@check="handleCheckChange" lazy show-checkbox />
|
||||||
|
</el-scrollbar>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选中学生" prop="student">
|
||||||
|
<el-scrollbar max-height="200px">
|
||||||
|
<el-tag v-for="(tag, index) in studentList" :key="tag.studentid" closable type="primary"
|
||||||
|
@close="delStudent(index)">
|
||||||
|
{{ tag.name }}
|
||||||
|
</el-tag>
|
||||||
|
</el-scrollbar>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="完成要求" prop="feedback">
|
||||||
|
<el-radio-group v-model="form.feedback">
|
||||||
|
<el-radio value="必做" size="large">必做</el-radio>
|
||||||
|
<el-radio value="选做" size="large">选做</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="截止时间" prop="deaddate">
|
||||||
|
<el-date-picker v-model="form.deaddate" value-format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
|
||||||
|
time-format="HH:mm" type="datetime" :clearable="false" placeholder="请选择截止时间" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="推荐用时" prop="timelength">
|
||||||
|
<el-input-number v-model="form.timelength" :min="1" :max="500" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click.stop="cloneDialog">取消</el-button>
|
||||||
|
<el-button type="primary" @click.stop="onSubmit('ruleForm')">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { listClassmain, listClassgroup } from '@/api/classManage/index'
|
||||||
|
import { saveByClassWorkArray } from '@/api/teaching/classwork'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { uniqBy, groupBy } from 'lodash'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
entpcourseid: {
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '布置作业'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
defaultProps: {
|
||||||
|
children: 'children',
|
||||||
|
label: 'label',
|
||||||
|
isLeaf: 'leaf',
|
||||||
|
},
|
||||||
|
setLoading: false,
|
||||||
|
// 用户信息
|
||||||
|
userInfo: null,
|
||||||
|
// 班级列表
|
||||||
|
gradeList: [],
|
||||||
|
curGradeId: '',
|
||||||
|
// 小组列表
|
||||||
|
groupList: [],
|
||||||
|
// 选中的学生
|
||||||
|
studentList: [],
|
||||||
|
// 表单
|
||||||
|
form: {
|
||||||
|
feedback: '必做',
|
||||||
|
deaddate: '',
|
||||||
|
timelength: 1
|
||||||
|
},
|
||||||
|
// 表单规则
|
||||||
|
rules: {
|
||||||
|
grade: [
|
||||||
|
{ validator: this.validateGrade, trigger: 'blur' }
|
||||||
|
],
|
||||||
|
student: [
|
||||||
|
{ validator: this.validateStudent, trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
//截至时间默认值
|
||||||
|
this.form.deaddate = this.getCurrentDate() + ' ' + '10:00:00'
|
||||||
|
this.userInfo = useUserStore().user
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 获取班级列表
|
||||||
|
getGradeList() {
|
||||||
|
listClassmain({ classuserid: this.userInfo.userId, pageSize: 100, status: 'open' }).then(res => {
|
||||||
|
let list = res.rows
|
||||||
|
list.forEach(item => {
|
||||||
|
item.label = item.caption
|
||||||
|
item.level = 0
|
||||||
|
item.children = []
|
||||||
|
item.classstudentlist = JSON.parse("[" + item.classstudentlist + "]")
|
||||||
|
item.classstudentlist.forEach(el => {
|
||||||
|
el.classId = item.id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.gradeList = list
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获取节点下一级
|
||||||
|
getLoad(node, resolve) {
|
||||||
|
if (node.level == 0) return resolve([])
|
||||||
|
// 获取二级节点 小组
|
||||||
|
if (node.level == 1) {
|
||||||
|
listClassgroup({ classid: node.key, orderby: 'orderidx', pageSize: 100 }).then(res => {
|
||||||
|
if (res.rows.length > 0) {
|
||||||
|
let ary = []
|
||||||
|
res.rows.forEach(item => {
|
||||||
|
if (item.parentid === 0) {
|
||||||
|
//studentGroup 小组学生
|
||||||
|
let studentGroup = JSON.parse("[" + item.studentlist + "]")
|
||||||
|
studentGroup.forEach(el => {
|
||||||
|
el.label = el.name
|
||||||
|
el.leaf = true
|
||||||
|
el.level = 2,
|
||||||
|
el.id = el.studentid
|
||||||
|
el.classId = item.classid
|
||||||
|
})
|
||||||
|
ary.push({
|
||||||
|
label: item.groupname,
|
||||||
|
...item,
|
||||||
|
level: 1,
|
||||||
|
children: studentGroup
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(ary)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 三级节点 小组学生
|
||||||
|
if (node.level == 2) {
|
||||||
|
resolve(node.data.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
//节点勾选后触发 拿学生
|
||||||
|
handleCheckChange(data, checked) {
|
||||||
|
this.studentList = []
|
||||||
|
// 选中节点集合
|
||||||
|
let checkNodes = checked.checkedNodes
|
||||||
|
let ary = []
|
||||||
|
checkNodes.forEach(item => {
|
||||||
|
// 一级节点 班级
|
||||||
|
if (item.level == 0) {
|
||||||
|
ary = [...ary, ...(item.classstudentlist)]
|
||||||
|
}
|
||||||
|
// 二级节点 班级下面的小组
|
||||||
|
if (item.level == 1) {
|
||||||
|
ary = [...ary, ...(item.children)]
|
||||||
|
}
|
||||||
|
// 三级节点 小组下面的学生
|
||||||
|
if (item.level == 2) {
|
||||||
|
ary = [...ary, item]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.studentList = uniqBy(ary, 'studentid')
|
||||||
|
},
|
||||||
|
// 删除学生
|
||||||
|
delStudent(index) {
|
||||||
|
this.studentList.splice(index, 1)
|
||||||
|
},
|
||||||
|
onSubmit(formName) {
|
||||||
|
|
||||||
|
this.$refs[formName].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
/**
|
||||||
|
* 根据学生列表中的classId分班
|
||||||
|
* studentList 为选中的所有学生 这些学生可能来自不同班级
|
||||||
|
*/
|
||||||
|
let gradeObj = groupBy(this.studentList, 'classId')
|
||||||
|
// 处理要提交的参数
|
||||||
|
let ary = []
|
||||||
|
for (const value in gradeObj) {
|
||||||
|
//这些参数 参照AIx web端 作业推送
|
||||||
|
let obj = {
|
||||||
|
id: 0,
|
||||||
|
parentid: this.row.id,
|
||||||
|
classid: value,
|
||||||
|
classcourseid: 0,
|
||||||
|
entpcourseid: this.entpcourseid,
|
||||||
|
studentlist: JSON.stringify(gradeObj[value]),
|
||||||
|
feedback: this.form.feedback,
|
||||||
|
workkey: "",
|
||||||
|
timelength: this.form.timelength,
|
||||||
|
weights: 1,
|
||||||
|
deaddate: this.form.deaddate,
|
||||||
|
workdate: this.getCurrentDate(),
|
||||||
|
uniquekey: this.row.uniquekey,
|
||||||
|
entpcourseworklist: "[" + this.row.entpcourseworklist + "]",
|
||||||
|
needMsgNotifine: 'false',
|
||||||
|
msgkey: 'newclasswork',
|
||||||
|
title: "作业任务",
|
||||||
|
msgcontent: '',
|
||||||
|
teachername: this.userInfo.nickName,
|
||||||
|
unixstamp: new Date().getTime(),
|
||||||
|
worktype: this.row.worktype
|
||||||
|
}
|
||||||
|
|
||||||
|
ary.push(obj)
|
||||||
|
}
|
||||||
|
this.setLoading = true
|
||||||
|
saveByClassWorkArray({
|
||||||
|
classworkarray: JSON.stringify(ary)
|
||||||
|
}).then(() => {
|
||||||
|
this.setLoading = false
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
this.cloneDialog()
|
||||||
|
}).catch(()=>{
|
||||||
|
this.setLoading = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 关闭弹窗
|
||||||
|
cloneDialog() {
|
||||||
|
this.$emit('on-close')
|
||||||
|
this.studentList = []
|
||||||
|
this.$refs['ruleForm'].resetFields();
|
||||||
|
},
|
||||||
|
// 获取当前年月日
|
||||||
|
getCurrentDate() {
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = now.getMonth() + 1; // 月份是从0开始的,所以需要+1
|
||||||
|
const day = now.getDate()
|
||||||
|
return `${year}-${month.length == 2 ? month : '0' + month}-${day.length == 2 ? day : '0' + day}`;
|
||||||
|
},
|
||||||
|
validateGrade(rule, value, callback) {
|
||||||
|
if (this.studentList.length == 0) {
|
||||||
|
callback(new Error('请勾选班级或者学生'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validateStudent(rule, value, callback) {
|
||||||
|
if (this.studentList.length == 0) {
|
||||||
|
callback(new Error('学生不能为空'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
this.dialogVisible = val
|
||||||
|
if (val) {
|
||||||
|
this.getGradeList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.homerwork-header {
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
.icon-guanbi {
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.dialog-footer{
|
||||||
|
padding-bottom: 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-checkbox){
|
||||||
|
transform : scale(1.3)
|
||||||
|
}
|
||||||
|
:deep(.el-icon){
|
||||||
|
transform : scale(1.3)
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -4,20 +4,23 @@
|
||||||
<div class="page-right">
|
<div class="page-right">
|
||||||
<div class="header-top flex">
|
<div class="header-top flex">
|
||||||
<div class="textbook-img">
|
<div class="textbook-img">
|
||||||
<el-image style="width: 80px; height: 110px" :src="curBookImg" />
|
<el-image style="width: 80px; height: 110px" :src="curBookImg" @click="navtoPdf"/>
|
||||||
|
<el-progress v-if="downloadNum>0 && downloadNum < 100" style="position:absolute;left: 0;z-index: 999;" type="circle" :percentage="downloadNum" />
|
||||||
</div>
|
</div>
|
||||||
<div class="top-item">
|
<div class="top-item">
|
||||||
<el-button class="btn" @click="handleOutLink('standard')">课标研读</el-button>
|
<el-button class="btn" @click="handleOutLink('standard')">课标研读</el-button>
|
||||||
<el-button class="btn" @click="navtoPdf">电子课本</el-button>
|
<el-button class="btn" @click="openReserv">预约课程</el-button>
|
||||||
<el-button class="btn" @click="handleOutLink('gk')">高考研读</el-button>
|
<el-button class="btn" @click="handleOutLink('gk')">高考研读</el-button>
|
||||||
<el-button class="btn" @click="handleOutLink('aiModel')">教学大模型</el-button>
|
<el-button class="btn" @click="handleOutLink('aiModel')">教学大模型</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" class="to-class-btn" @click="openLesson">
|
<el-button :type="!curClassReserv.id?'info' : 'primary'" :disabled="!curClassReserv.id" class="to-class-btn" @click="openLesson">
|
||||||
<i class="iconfont icon-lingdang"></i>上课</el-button>
|
<label><i class="iconfont icon-lingdang"></i>上课</label>
|
||||||
|
<label>{{ curClassReserv.classDay }} {{ getWeekday1(curClassReserv.classDay) }}</label>
|
||||||
|
<label>{{ curClassReserv.startTime }}-{{ curClassReserv.endTime }}</label>
|
||||||
|
</el-button>
|
||||||
<div class="top-zoom-style"></div>
|
<div class="top-zoom-style"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="prepare-body-header">
|
<div class="prepare-body-header">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label style="font-size: 15px">共{{ currentFileList.length }}个文件</label>
|
<label style="font-size: 15px">共{{ currentFileList.length }}个文件</label>
|
||||||
<el-popover placement="top-start" :width="250" trigger="hover">
|
<el-popover placement="top-start" :width="250" trigger="hover">
|
||||||
|
@ -47,8 +50,8 @@
|
||||||
<el-checkbox-group v-model="checkFileList" class="prepare-body-main"
|
<el-checkbox-group v-model="checkFileList" class="prepare-body-main"
|
||||||
:style="{ 'margin-bottom': checkFileList.length > 0 ? '40px' : '0' }">
|
:style="{ 'margin-bottom': checkFileList.length > 0 ? '40px' : '0' }">
|
||||||
<file-list-item v-for="(item, index) in currentFileList" :key="index" :item="item" :index="index"
|
<file-list-item v-for="(item, index) in currentFileList" :key="index" :item="item" :index="index"
|
||||||
@on-move="onMoveSingleFile" @on-delete="deleteTalk">
|
@on-move="onMoveSingleFile" @on-delete="deleteTalk" @on-set="openSet" @on-delhomework="delhomework">
|
||||||
<el-checkbox label="" :value="item" />
|
<el-checkbox label="" :value="item" v-if="!item.uniquekey" />
|
||||||
</file-list-item>
|
</file-list-item>
|
||||||
</el-checkbox-group>
|
</el-checkbox-group>
|
||||||
<file-oper-batch v-show="checkFileList.length > 0"
|
<file-oper-batch v-show="checkFileList.length > 0"
|
||||||
|
@ -58,17 +61,22 @@
|
||||||
</div>
|
</div>
|
||||||
<MoveFile v-model="isMoveDialogOpen" @on-submit="chooseMoveCata" />
|
<MoveFile v-model="isMoveDialogOpen" @on-submit="chooseMoveCata" />
|
||||||
<uploadDialog v-model="isDialogOpen" @submit-file="submitFile" />
|
<uploadDialog v-model="isDialogOpen" @submit-file="submitFile" />
|
||||||
|
<SetHomework v-model="setDialog" :entpcourseid="entpcourseid" :row="row" @on-close="closeHomework" />
|
||||||
</div>
|
</div>
|
||||||
|
<reserv ref="reservDialog" @add-success="initReserv" :current-node="currentNode" :book-id="uploadData.textbookId"></reserv>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Check } from '@element-plus/icons-vue'
|
import { Check } from '@element-plus/icons-vue'
|
||||||
|
import Reserv from '@/views/prepare/container/reserv.vue'
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
const Remote = require('@electron/remote')
|
||||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||||
import uploadDialog from '@/components/upload-dialog/index.vue'
|
import uploadDialog from '@/components/upload-dialog/index.vue'
|
||||||
import { Refresh } from '@element-plus/icons-vue'
|
import { Refresh } from '@element-plus/icons-vue'
|
||||||
import uploaderState from '@/store/modules/uploader'
|
import uploaderState from '@/store/modules/uploader'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { useToolState } from '@/store/modules/tool'
|
||||||
import MoveFile from '@/components/move-file/index.vue'
|
import MoveFile from '@/components/move-file/index.vue'
|
||||||
import FileListItem from '@/views/prepare/container/file-list-item.vue'
|
import FileListItem from '@/views/prepare/container/file-list-item.vue'
|
||||||
import { getSmarttalkPage, moveSmarttalk } from '@/api/file'
|
import { getSmarttalkPage, moveSmarttalk } from '@/api/file'
|
||||||
|
@ -77,15 +85,27 @@ import { toTimeText } from '@/utils/date'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { parseCataByNode, creatPPT, asyncLocalFile } from '@/utils/talkFile'
|
import { parseCataByNode, creatPPT, asyncLocalFile } from '@/utils/talkFile'
|
||||||
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
|
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
|
||||||
|
import SetHomework from './container/set-homework.vue'
|
||||||
import outLink from '@/utils/linkConfig'
|
import outLink from '@/utils/linkConfig'
|
||||||
import { createWindow } from '@/utils/tool'
|
import { createWindow } from '@/utils/tool'
|
||||||
import { uniqBy, cloneDeep } from 'lodash'
|
import { uniqBy, cloneDeep } from 'lodash'
|
||||||
|
import { delClasswork, addEntpcourse } from '@/api/teaching/classwork'
|
||||||
|
import { getSelfReserv, startClass } from '@/api/classManage'
|
||||||
|
const toolStore = useToolState()
|
||||||
|
const fs = require('fs')
|
||||||
const { ipcRenderer } = window.electron || {}
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Prepare',
|
name: 'Prepare',
|
||||||
components: { ChooseTextbook, Refresh, uploadDialog, FileListItem, FileOperBatch, MoveFile },
|
components: {
|
||||||
|
ChooseTextbook,
|
||||||
|
Refresh,
|
||||||
|
uploadDialog,
|
||||||
|
FileListItem,
|
||||||
|
FileOperBatch,
|
||||||
|
MoveFile,
|
||||||
|
SetHomework
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
moveFile: [],
|
moveFile: [],
|
||||||
|
@ -98,6 +118,9 @@ export default {
|
||||||
fileList: [],
|
fileList: [],
|
||||||
currentNode: {},
|
currentNode: {},
|
||||||
currentFileList: [],
|
currentFileList: [],
|
||||||
|
curBookPath: '',
|
||||||
|
curClassReserv: {},
|
||||||
|
downloadNum: 0,
|
||||||
lastAsyncAllTime: '',
|
lastAsyncAllTime: '',
|
||||||
uploadData: {
|
uploadData: {
|
||||||
textbookId: null,
|
textbookId: null,
|
||||||
|
@ -111,7 +134,10 @@ export default {
|
||||||
// 用户信息
|
// 用户信息
|
||||||
userStore: '',
|
userStore: '',
|
||||||
entpcourseid: '',
|
entpcourseid: '',
|
||||||
timerId: null
|
timerId: null,
|
||||||
|
// 布置作业弹窗
|
||||||
|
setDialog: false,
|
||||||
|
row: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -121,23 +147,85 @@ export default {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route(to) {
|
||||||
|
if (to.path != '/prepare' && this.timerId) {
|
||||||
|
clearInterval(this.timerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.userStore = useUserStore().user
|
this.userStore = useUserStore().user
|
||||||
ipcRenderer.removeAllListeners('copy-file-default-reply')
|
ipcRenderer.removeAllListeners('copy-file-default-reply')
|
||||||
|
|
||||||
ipcRenderer.on('copy-file-default-reply', (e, param) => {
|
ipcRenderer.on('copy-file-default-reply', (e, param) => {
|
||||||
this.callback(param)
|
this.callback(param)
|
||||||
})
|
})
|
||||||
this.lastAsyncAllTime = localStorage.getItem('lastAsyncAllTime')
|
this.lastAsyncAllTime = localStorage.getItem('lastAsyncAllTime')
|
||||||
|
this.initReserv()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.$watch(()=>toolStore.isToolWin,(newD, oldD)=>{
|
||||||
|
setTimeout(this.initReserv, 500)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
activated() {
|
// activated() {
|
||||||
if (this.uploadData.textbookId !== null) {
|
// if (this.uploadData.textbookId !== null) {
|
||||||
this.asyncAllFile()
|
// this.asyncAllFile()
|
||||||
}
|
// this.initHomeWork()
|
||||||
},
|
// this.initReserv()
|
||||||
|
// }
|
||||||
|
// },
|
||||||
methods: {
|
methods: {
|
||||||
|
initReserv() {
|
||||||
|
getSelfReserv().then((res) => {
|
||||||
|
let list = res.data.filter((item) => {
|
||||||
|
return item.status !== '已结束'
|
||||||
|
})
|
||||||
|
if (list.length > 0) {
|
||||||
|
this.curClassReserv = list[list.length - 1]
|
||||||
|
}else {
|
||||||
|
this.curClassReserv = {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getBookPathFromServer() {
|
||||||
|
let fileName = this.curBookPath
|
||||||
|
if (!fileName) return
|
||||||
|
fileName = fileName.replace('.txt', '.pdf')
|
||||||
|
return new Promise((resolve, reject)=>{
|
||||||
|
const userDataPath = Remote.app.getPath('userData')
|
||||||
|
const appRootFilePath = userDataPath + '\\selfFile\\'
|
||||||
|
let filePath = appRootFilePath + fileName
|
||||||
|
fs.access(filePath, fs.constants.F_OK, (err) => {
|
||||||
|
let filePath = import.meta.env.VITE_APP_RES_FILE_PATH + fileName
|
||||||
|
if (err) {
|
||||||
|
//从线上下载
|
||||||
|
ipcRenderer.send('download-file-default', {
|
||||||
|
url: filePath,
|
||||||
|
fileName: fileName
|
||||||
|
})
|
||||||
|
ipcRenderer.removeListener('download-file-default-prog' + fileName, this.progDownFile)
|
||||||
|
ipcRenderer.on('download-file-default-prog' + fileName, this.progDownFile)
|
||||||
|
ipcRenderer.once('download-file-default' + fileName, (e, isSuccess) => {
|
||||||
|
if (isSuccess === true) {
|
||||||
|
resolve(fileName)
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'info',
|
||||||
|
message: `下载教材失败!`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve(fileName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
progDownFile(e, num) {
|
||||||
|
this.downloadNum = num
|
||||||
|
},
|
||||||
createFile() {
|
createFile() {
|
||||||
creatPPT(this.currentNode.label + '.pptx', this.uploadData).then((res) => {
|
creatPPT(this.currentNode.label + '.pptx', this.uploadData).then((res) => {
|
||||||
this.currentFileList.unshift(res.resData)
|
this.currentFileList.unshift(res.resData)
|
||||||
|
@ -232,7 +320,7 @@ export default {
|
||||||
},
|
},
|
||||||
asyncAllFile() {
|
asyncAllFile() {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
getSmarttalkPage({
|
return getSmarttalkPage({
|
||||||
...this.uploadData,
|
...this.uploadData,
|
||||||
orderByColumn: 'uploadTime',
|
orderByColumn: 'uploadTime',
|
||||||
isAsc: 'desc',
|
isAsc: 'desc',
|
||||||
|
@ -249,14 +337,17 @@ export default {
|
||||||
await asyncLocalFile(item)
|
await asyncLocalFile(item)
|
||||||
}
|
}
|
||||||
this.asyncAllFileVisiable = false
|
this.asyncAllFileVisiable = false
|
||||||
|
return Promise.resolve()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
|
return Promise.resolve()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async nodeClick(data) {
|
async nodeClick(data) {
|
||||||
if (this.currentNode.id === data.node.id) return
|
if (this.currentNode.id === data.node.id) return
|
||||||
this.curBookImg = data.textBook.curBookImg
|
this.curBookImg = data.textBook.curBookImg
|
||||||
|
this.curBookPath = data.textBook.curBookPath
|
||||||
this.checkFileList = []
|
this.checkFileList = []
|
||||||
let cata = parseCataByNode(data.node)
|
let cata = parseCataByNode(data.node)
|
||||||
this.currentNode = data.node
|
this.currentNode = data.node
|
||||||
|
@ -264,16 +355,58 @@ export default {
|
||||||
this.uploadData.levelSecondId = cata[1]
|
this.uploadData.levelSecondId = cata[1]
|
||||||
this.uploadData.levelThirdId = cata[2]
|
this.uploadData.levelThirdId = cata[2]
|
||||||
this.uploadData.textbookId = data.textBook.curBookId
|
this.uploadData.textbookId = data.textBook.curBookId
|
||||||
this.asyncAllFile()
|
const toolStore = useToolState()
|
||||||
|
toolStore.curSubjectNode.data = data
|
||||||
|
toolStore.curSubjectNode.querySearch = this.uploadData
|
||||||
|
this.initHomeWork()
|
||||||
|
await this.asyncAllFile()
|
||||||
|
|
||||||
|
},
|
||||||
|
async initHomeWork() {
|
||||||
|
if (this.timerId) {
|
||||||
|
clearInterval(this.timerId)
|
||||||
|
}
|
||||||
if (this.uploadData.levelSecondId) {
|
if (this.uploadData.levelSecondId) {
|
||||||
// 获取作业列表所需ID
|
// 获取作业列表所需ID 可能存在没有
|
||||||
const res = await this.getChapterId()
|
let { rows } = await this.getChapterId()
|
||||||
this.entpcourseid = res.rows[0].id
|
if (rows.length > 0) {
|
||||||
|
this.entpcourseid = rows[0].id
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
await this.createEntpcourse()
|
||||||
|
let { rows } = await this.getChapterId()
|
||||||
|
this.entpcourseid = rows[0].id
|
||||||
|
}
|
||||||
|
|
||||||
// 查询作业
|
// 查询作业
|
||||||
this.getHomeWorkList()
|
this.getHomeWorkList()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 创建新的entpcourse
|
||||||
|
createEntpcourse() {
|
||||||
|
// 参照 web AIx 传入参数
|
||||||
|
var cform = {};
|
||||||
|
cform.entpid = this.userStore.deptId;
|
||||||
|
cform.level = 1;
|
||||||
|
cform.parentid = 0;
|
||||||
|
cform.dictid = 0;
|
||||||
|
cform.evalid = this.currentNode.id;
|
||||||
|
cform.evalparentid = 0;
|
||||||
|
cform.edusubject = this.currentNode.edusubject;
|
||||||
|
cform.edudegree = this.currentNode.edudegree;
|
||||||
|
cform.edustage = this.currentNode.edustage;
|
||||||
|
cform.coursetype = '课标学科';
|
||||||
|
cform.coursetitle = this.currentNode.itemtitle;
|
||||||
|
cform.coursedesc = '';
|
||||||
|
cform.status = '';
|
||||||
|
cform.dflag = 0;
|
||||||
|
cform.edituserid = this.userStore.userId;
|
||||||
|
cform.createblankfile = 'yes';
|
||||||
|
return addEntpcourse(cform)
|
||||||
|
},
|
||||||
|
openReserv(){
|
||||||
|
this.$refs['reservDialog'].openDialog()
|
||||||
|
},
|
||||||
// 打开外部链接
|
// 打开外部链接
|
||||||
handleOutLink(key) {
|
handleOutLink(key) {
|
||||||
if (key == 'homeWork') {
|
if (key == 'homeWork') {
|
||||||
|
@ -281,39 +414,47 @@ export default {
|
||||||
this.createTimer()
|
this.createTimer()
|
||||||
}
|
}
|
||||||
// key 对应的 linkConfig.js 外部链接配置
|
// key 对应的 linkConfig.js 外部链接配置
|
||||||
let configObj = outLink[key]
|
let configObj = outLink()[key]
|
||||||
// 通知主进程
|
// 通知主进程
|
||||||
ipcRenderer.send('openWindow', {
|
ipcRenderer.send('openWindow', {
|
||||||
|
key,
|
||||||
fullPath: configObj.fullPath,
|
fullPath: configObj.fullPath,
|
||||||
cookieData: { ...(configObj.data) }
|
cookieData: { ...configObj.data }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 根据教材章节单元ID 查询作业列表所需ID
|
// 根据教材章节单元ID 查询作业列表所需ID
|
||||||
getChapterId() {
|
getChapterId() {
|
||||||
return listEntpcourse({ evalid: this.uploadData.levelSecondId, edituserid: this.userStore.userId, pageSize: 500 })
|
console.log(this.userStore,'this.userStore')
|
||||||
|
return listEntpcourse({
|
||||||
|
evalid: this.uploadData.levelSecondId,
|
||||||
|
edituserid: this.userStore.userId,
|
||||||
|
pageSize: 500
|
||||||
|
})
|
||||||
},
|
},
|
||||||
//
|
// 查询作业列表定时器
|
||||||
createTimer() {
|
createTimer() {
|
||||||
this.timerId = setInterval(() => {
|
this.timerId = setInterval(() => {
|
||||||
this.getHomeWorkList()
|
this.getHomeWorkList()
|
||||||
}, 1500)
|
}, 2500)
|
||||||
},
|
},
|
||||||
// 查询作业列表
|
// 查询作业列表
|
||||||
getHomeWorkList() {
|
getHomeWorkList() {
|
||||||
homeworklist({ entpcourseid: this.entpcourseid, edituserid: this.userStore.userId, pageSize: 100 }).then(res => {
|
homeworklist({
|
||||||
|
entpcourseid: this.entpcourseid,
|
||||||
|
edituserid: this.userStore.userId,
|
||||||
|
pageSize: 100
|
||||||
|
}).then((res) => {
|
||||||
//以下代码 参照AIx web端 作业布置
|
//以下代码 参照AIx web端 作业布置
|
||||||
let list = []
|
let list = []
|
||||||
for (var i = 0; i < res.rows.length; i++) {
|
for (var i = 0; i < res.rows.length; i++) {
|
||||||
|
res.rows[i].taskconfig = []
|
||||||
res.rows[i].taskconfig = [];
|
|
||||||
|
|
||||||
// 找child
|
// 找child
|
||||||
for (var j = 0; j < res.rows.length; j++) {
|
for (var j = 0; j < res.rows.length; j++) {
|
||||||
if (res.rows[j].parentid == res.rows[i].id) {
|
if (res.rows[j].parentid == res.rows[i].id) {
|
||||||
var ss = [];
|
var ss = []
|
||||||
if (res.rows[j].classworkdatastudentids != null) {
|
if (res.rows[j].classworkdatastudentids != null) {
|
||||||
ss = JSON.parse('[' + res.rows[j].classworkdatastudentids + ']');
|
ss = JSON.parse('[' + res.rows[j].classworkdatastudentids + ']')
|
||||||
}
|
}
|
||||||
var js = {
|
var js = {
|
||||||
id: res.rows[j].id,
|
id: res.rows[j].id,
|
||||||
|
@ -336,7 +477,7 @@ export default {
|
||||||
weights: res.rows[j].weights,
|
weights: res.rows[j].weights,
|
||||||
feedtype: res.rows[j].feedtype
|
feedtype: res.rows[j].feedtype
|
||||||
}
|
}
|
||||||
res.rows[i].taskconfig.push(js);
|
res.rows[i].taskconfig.push(js)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.rows[i].fileShowName = res.rows[i].uniquekey
|
res.rows[i].fileShowName = res.rows[i].uniquekey
|
||||||
|
@ -344,15 +485,16 @@ export default {
|
||||||
// 注意slideid>0的,这一些作业是添加到PPT页面的,所以在作业管理中不能出现
|
// 注意slideid>0的,这一些作业是添加到PPT页面的,所以在作业管理中不能出现
|
||||||
// 2024-05-15,酉阳,jackyshen
|
// 2024-05-15,酉阳,jackyshen
|
||||||
if (res.rows[i].classid == 0 && res.rows[i].slideid == 0) {
|
if (res.rows[i].classid == 0 && res.rows[i].slideid == 0) {
|
||||||
list.push(res.rows[i]);
|
list.push(res.rows[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 如果是习题训练任务,则检查一共有多少道
|
// 如果是习题训练任务,则检查一共有多少道
|
||||||
if (res.rows[i].entpcourseworklist != '') {
|
if (res.rows[i].entpcourseworklist != '') {
|
||||||
res.rows[i].entpcourseworklistarray = JSON.parse('[' + res.rows[i].entpcourseworklist + ']');
|
res.rows[i].entpcourseworklistarray = JSON.parse(
|
||||||
|
'[' + res.rows[i].entpcourseworklist + ']'
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
res.rows[i].entpcourseworklistarray = [];
|
res.rows[i].entpcourseworklistarray = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 去重
|
// 去重
|
||||||
|
@ -361,25 +503,51 @@ export default {
|
||||||
this.currentFileList = cloneDeep(ary)
|
this.currentFileList = cloneDeep(ary)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
getWeekday1(date) {
|
||||||
|
const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||||
|
const weekday = new Date(date).getDay()
|
||||||
|
return weekdays[weekday]
|
||||||
|
},
|
||||||
|
// 打开布置作业窗口
|
||||||
|
openSet(row) {
|
||||||
|
this.row = row
|
||||||
|
this.setDialog = true
|
||||||
|
},
|
||||||
|
// 删除作业
|
||||||
|
delhomework(item) {
|
||||||
|
this.isLoading = true
|
||||||
|
delClasswork(item.id).then(async res => {
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
this.isLoading = false
|
||||||
|
await this.asyncAllFile()
|
||||||
|
this.getHomeWorkList()
|
||||||
|
}).catch(() => {
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeHomework() {
|
||||||
|
this.setDialog = false
|
||||||
|
},
|
||||||
|
|
||||||
// 打开PDF-课件
|
// 打开PDF-课件
|
||||||
navtoPdf() {
|
async navtoPdf() {
|
||||||
createWindow('open-PDF', { url: '/classBegins/index' })
|
if (toolStore.isPdfWin) return this.$message.error('您当前已打开课本,请勿重复操作')
|
||||||
|
let path = await this.getBookPathFromServer()
|
||||||
|
createWindow('open-PDF', {
|
||||||
|
url: '/classBegins/index?textbookId=' + this.uploadData.textbookId + '&path=' + path
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// 上课-工具类悬浮
|
// 上课-工具类悬浮
|
||||||
openLesson() {
|
openLesson() {
|
||||||
createWindow('tool-sphere', { url: '/tool/sphere' })
|
const toolStore = useToolState()
|
||||||
}
|
if (toolStore.isToolWin) return this.$message.error('您当前已开始上课,请勿重复操作')
|
||||||
},
|
startClass(this.curClassReserv.id)
|
||||||
watch: {
|
createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + this.entpcourseid +'&label=' + this.currentNode.label+ "&reservId=" + this.curClassReserv.id })
|
||||||
$route(to) {
|
|
||||||
if (to.path != '/prepare' && this.timerId) {
|
|
||||||
clearInterval(this.timerId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style lang="scss">
|
||||||
.prepare-popper {
|
.prepare-popper {
|
||||||
width: 80px !important;
|
width: 80px !important;
|
||||||
min-width: 80px !important;
|
min-width: 80px !important;
|
||||||
|
@ -449,6 +617,14 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
:deep(.el-progress-circle) {
|
||||||
|
width: 90px!important;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-item {
|
.top-item {
|
||||||
|
@ -465,7 +641,7 @@ export default {
|
||||||
border-color: #ffffff;
|
border-color: #ffffff;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(255, 255, 255, 0.3)
|
background: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
|
@ -485,7 +661,13 @@ export default {
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
& label:hover {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
& > :deep(span) {
|
||||||
|
flex-direction: column !important;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
.icon-lingdang {
|
.icon-lingdang {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
@ -502,7 +684,6 @@ export default {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepare-body-main {
|
.prepare-body-main {
|
||||||
|
|
|
@ -213,6 +213,7 @@ export default {
|
||||||
> .btn {
|
> .btn {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin-top: 30px;
|
||||||
> .upload {
|
> .upload {
|
||||||
display: block;
|
display: block;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
|
@ -222,7 +223,6 @@ export default {
|
||||||
left: 0;
|
left: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
margin-top: 30px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.personal-authentica {
|
.personal-authentica {
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
import { ref, getCurrentInstance } from 'vue'
|
import { ref, getCurrentInstance } from 'vue'
|
||||||
import { updateUserProfile } from '@/api/system/user'
|
import { updateUserProfile } from '@/api/system/user'
|
||||||
import { listEvaluation } from '@/api/subject/index'
|
import { listEvaluation } from '@/api/subject/index'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import {ElMessage} from 'element-plus'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
user: {
|
user: {
|
||||||
|
@ -42,6 +44,7 @@ const props = defineProps({
|
||||||
})
|
})
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const rules = ref({
|
const rules = ref({
|
||||||
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
|
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
|
||||||
|
@ -96,7 +99,17 @@ function submit() {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
console.log(props.user)
|
console.log(props.user)
|
||||||
updateUserProfile(props.user).then((response) => {
|
updateUserProfile(props.user).then((response) => {
|
||||||
proxy.$modal.msgSuccess('修改成功')
|
if(response.code == 200){
|
||||||
|
userStore.getInfo().then(res => {
|
||||||
|
if(res.code === 200){
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
}else{
|
||||||
|
ElMessage.error(response.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
ElMessage.error(response.msg)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,13 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="resource-list" v-loading="sourceStore.loading">
|
<div v-loading="sourceStore.loading" class="resource-list">
|
||||||
<el-scrollbar height="400px">
|
<el-scrollbar>
|
||||||
<el-empty description="暂无数据" v-if="!sourceStore.result.list.length" />
|
<el-empty v-if="!sourceStore.result.list.length" description="暂无数据" />
|
||||||
<ul>
|
<ul>
|
||||||
<li class="list-item" v-for="item in sourceStore.result.list" :key="item.id" @click="handleRow">
|
<li
|
||||||
|
v-for="item in sourceStore.result.list"
|
||||||
|
:key="item.id"
|
||||||
|
class="list-item"
|
||||||
|
@click="handleRow"
|
||||||
|
>
|
||||||
<div class="item-left flex">
|
<div class="item-left flex">
|
||||||
<FileImage :fileName="item.fileShowName" :size="50" />
|
<FileImage :file-name="item.fileShowName" :size="50" />
|
||||||
<div class="flex item-left-content">
|
<div class="flex item-left-content">
|
||||||
<div class="name flex">{{ item.fileShowName }}</div>
|
<div class="name flex" :title="item.fileShowName">
|
||||||
|
<div></div>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ item.fileShowName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="item-tags flex">
|
<div class="item-tags flex">
|
||||||
<el-tag type="info" class="mr-10">{{ item.fileFlag }}</el-tag>
|
<el-tag type="info" class="mr-10">{{ item.fileFlag }}</el-tag>
|
||||||
<el-tag type="info" class="mr-10">{{ getFileSuffix(item.fileShowName) }}</el-tag>
|
<el-tag type="info" class="mr-10">{{ getFileSuffix(item.fileShowName) }}</el-tag>
|
||||||
|
@ -18,10 +36,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-btns" @click.stop>
|
<div class="item-btns" @click.stop>
|
||||||
<el-popover placement="bottom-end" trigger="hover" popper-class="custom-popover"
|
<el-popover
|
||||||
:visible="item.showPopover">
|
placement="bottom-end"
|
||||||
|
trigger="hover"
|
||||||
|
popper-class="custom-popover"
|
||||||
|
:visible="item.showPopover"
|
||||||
|
>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button link type="primary"> <i class="iconfont icon-shenglvehao"></i></el-button>
|
<el-button link type="primary">
|
||||||
|
<i class="iconfont icon-shenglvehao"></i
|
||||||
|
></el-button>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="item-popover">
|
<div class="item-popover">
|
||||||
|
@ -29,7 +53,11 @@
|
||||||
<i class="iconfont icon-bianji"></i>
|
<i class="iconfont icon-bianji"></i>
|
||||||
<span>编辑</span>
|
<span>编辑</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-popover-item" v-if="userInfo.userId == item.uploadUserId" @click="delRow(item)">
|
<div
|
||||||
|
v-if="userInfo.userId == item.uploadUserId"
|
||||||
|
class="item-popover-item"
|
||||||
|
@click="delRow(item)"
|
||||||
|
>
|
||||||
<i class="iconfont icon-shanchu"></i>
|
<i class="iconfont icon-shanchu"></i>
|
||||||
<span>删除</span>
|
<span>删除</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,24 +65,29 @@
|
||||||
<i class="iconfont icon-xiazai"></i>
|
<i class="iconfont icon-xiazai"></i>
|
||||||
<span>下载</span>
|
<span>下载</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<el-button size="small" plain round type="primary" @click="addLesson(item)">
|
<el-button size="small" plain round type="primary" @click="addLesson(item)">
|
||||||
<i class="iconfont icon-jiahao"></i>
|
<i class="iconfont icon-jiahao"></i>
|
||||||
备课</el-button>
|
备课</el-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<div class="pagination-box">
|
<div class="pagination-box">
|
||||||
<el-pagination v-model:current-page="sourceStore.query.pageNum" v-model:page-size="sourceStore.query.pageSize"
|
<el-pagination
|
||||||
:page-sizes="[10, 20, 30, 50]" background layout="total, sizes, prev, pager, next, jumper"
|
v-model:current-page="sourceStore.query.pageNum"
|
||||||
:total="sourceStore.result.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
v-model:page-size="sourceStore.query.pageSize"
|
||||||
|
:page-sizes="[10, 20, 30, 50]"
|
||||||
|
background
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="sourceStore.result.total"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -108,7 +141,7 @@ const editRow = (item) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(() => { })
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
// 删除
|
// 删除
|
||||||
const delRow = (item) => {
|
const delRow = (item) => {
|
||||||
|
@ -128,7 +161,7 @@ const addLesson = ({ id }) => {
|
||||||
let data = {
|
let data = {
|
||||||
id,
|
id,
|
||||||
fileRoot: '备课',
|
fileRoot: '备课',
|
||||||
...(toRaw(sourceStore.nodeData)),
|
...toRaw(sourceStore.nodeData)
|
||||||
}
|
}
|
||||||
// 过滤空值
|
// 过滤空值
|
||||||
for (let key in data) {
|
for (let key in data) {
|
||||||
|
@ -142,13 +175,12 @@ const addLesson = ({ id }) => {
|
||||||
addFileToPrepare(data).then(() => {
|
addFileToPrepare(data).then(() => {
|
||||||
ElMessage.success('操作成功')
|
ElMessage.success('操作成功')
|
||||||
})
|
})
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
sourceStore.loading = false
|
sourceStore.loading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
const handleRow = () =>{
|
const handleRow = () => {
|
||||||
ElMessage.warning('请先加入备课,在备课里面进行预览!')
|
ElMessage.warning('请先加入备课,在备课里面进行预览!')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -181,6 +213,10 @@ const handleRow = () =>{
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-list {
|
.resource-list {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: auto;
|
||||||
.list-item {
|
.list-item {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
@ -189,10 +225,13 @@ const handleRow = () =>{
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(144, 147, 153, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.item-left {
|
.item-left {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
}
|
}
|
||||||
|
@ -200,12 +239,14 @@ const handleRow = () =>{
|
||||||
.item-left-content {
|
.item-left-content {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #3b3b3b;
|
color: #3b3b3b;
|
||||||
margin-bottom: 10px
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line {
|
.line {
|
||||||
|
@ -226,7 +267,6 @@ const handleRow = () =>{
|
||||||
color: #909399;
|
color: #909399;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
|
@ -258,15 +298,13 @@ const handleRow = () =>{
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-box {
|
.pagination-box {
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
height: 65px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,22 +1,28 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page-resource flex">
|
<div class="page-resource flex">
|
||||||
<!--左侧 教材 目录-->
|
<!--左侧 教材 目录-->
|
||||||
<ChooseTextbook @changeBook="getData" @nodeClick="getData" />
|
<ChooseTextbook @change-book="getData" @node-click="getData" />
|
||||||
|
|
||||||
<div class="page-right">
|
<div class="page-right">
|
||||||
<!-- 搜索 -->
|
<!-- 搜索 -->
|
||||||
<ResoureSearch #add>
|
<ResoureSearch #add>
|
||||||
<el-button v-if="sourceStore.isCreate" type="primary" round @click="openDialog" class="create-btn">
|
<el-button
|
||||||
|
v-if="sourceStore.isCreate"
|
||||||
|
type="primary"
|
||||||
|
round
|
||||||
|
class="create-btn"
|
||||||
|
@click="openDialog"
|
||||||
|
>
|
||||||
<i class="iconfont icon-jiahao"></i>
|
<i class="iconfont icon-jiahao"></i>
|
||||||
新建资源</el-button>
|
新建资源</el-button
|
||||||
|
>
|
||||||
</ResoureSearch>
|
</ResoureSearch>
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<ResoureList />
|
<ResoureList />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 上传弹窗 -->
|
<!-- 上传弹窗 -->
|
||||||
<uploadDialog v-model="isDialogOpen" @submitFile="submitFile" />
|
<uploadDialog v-model="isDialogOpen" @submit-file="submitFile" />
|
||||||
<!-- <el-button @click="testClick">测试</el-button> -->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -28,32 +34,26 @@ import ResoureList from './container/resoure-list.vue'
|
||||||
import uploadDialog from '@/components/upload-dialog/index.vue'
|
import uploadDialog from '@/components/upload-dialog/index.vue'
|
||||||
import uploaderState from '@/store/modules/uploader'
|
import uploaderState from '@/store/modules/uploader'
|
||||||
import { createWindow } from '@/utils/tool'
|
import { createWindow } from '@/utils/tool'
|
||||||
//
|
import { useToolState } from '@/store/modules/tool'
|
||||||
const sourceStore = useResoureStore()
|
const sourceStore = useResoureStore()
|
||||||
const isDialogOpen = ref(false)
|
const isDialogOpen = ref(false)
|
||||||
|
const toolStore = useToolState()
|
||||||
const openDialog = () => {
|
const openDialog = () => {
|
||||||
isDialogOpen.value = true
|
isDialogOpen.value = true
|
||||||
}
|
}
|
||||||
onMounted(async () => {
|
// onMounted(async () => {
|
||||||
// const params = { url: '/tool/sphere' }
|
// console.log('toolStore: ', toolStore.windowState)
|
||||||
// const res = await ipcMsgSend('tool-sphere:create', params)
|
// })
|
||||||
// console.log('消息返回:', res)
|
|
||||||
})
|
|
||||||
const testClick = async() => {
|
|
||||||
const win = await createWindow('tool-sphere', { url: '/tool/sphere' })
|
|
||||||
console.log('消息返回:', win)
|
|
||||||
}
|
|
||||||
// 查询
|
// 查询
|
||||||
const getData = (data) => {
|
const getData = (data) => {
|
||||||
const { textBook, node } = data
|
const { textBook, node } = data
|
||||||
let textbookId = textBook.curBookId
|
let textbookId = textBook.curBookId
|
||||||
let levelSecondId = node.id
|
let levelSecondId = node.id
|
||||||
let levelFirstId
|
let levelFirstId
|
||||||
if(node.parentNode){
|
if (node.parentNode) {
|
||||||
levelFirstId = node.parentNode.id
|
levelFirstId = node.parentNode.id
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
levelFirstId = node.id
|
levelFirstId = node.id
|
||||||
levelSecondId = ''
|
levelSecondId = ''
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ const getData = (data) => {
|
||||||
sourceStore.nodeData = {
|
sourceStore.nodeData = {
|
||||||
textbookId,
|
textbookId,
|
||||||
levelFirstId,
|
levelFirstId,
|
||||||
levelSecondId,
|
levelSecondId
|
||||||
}
|
}
|
||||||
sourceStore.handleQuery()
|
sourceStore.handleQuery()
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ const submitFile = (data) => {
|
||||||
let fileList = toRaw(data)
|
let fileList = toRaw(data)
|
||||||
const { textbookId, levelFirstId, levelSecondId, fileSource, fileRoot } = sourceStore.query
|
const { textbookId, levelFirstId, levelSecondId, fileSource, fileRoot } = sourceStore.query
|
||||||
// 给每个文件添加属性
|
// 给每个文件添加属性
|
||||||
fileList.forEach(item => {
|
fileList.forEach((item) => {
|
||||||
let fileData = { textbookId, levelFirstId, levelSecondId, fileSource, fileRoot }
|
let fileData = { textbookId, levelFirstId, levelSecondId, fileSource, fileRoot }
|
||||||
fileData.fileShowName = item.fileData.fileShowName
|
fileData.fileShowName = item.fileData.fileShowName
|
||||||
fileData.fileFlag = item.fileData.fileFlag
|
fileData.fileFlag = item.fileData.fileFlag
|
||||||
|
@ -83,17 +83,15 @@ const submitFile = (data) => {
|
||||||
uploaderState().pushFile(fileList)
|
uploaderState().pushFile(fileList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const fileCallBack = (res) => {
|
const fileCallBack = (res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
sourceStore.handleQuery()
|
sourceStore.handleQuery()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(() => {
|
||||||
sourceStore.getCreate()
|
sourceStore.getCreate()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -103,6 +101,8 @@ onMounted(()=>{
|
||||||
|
|
||||||
.page-right {
|
.page-right {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -116,7 +116,7 @@ onMounted(()=>{
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.create-btn{
|
.create-btn {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 5px 13px;
|
padding: 5px 13px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,66 @@
|
||||||
<template>
|
<template>
|
||||||
|
<div>
|
||||||
<canvas ref="canvasRef" />
|
<canvas ref="canvasRef" />
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
// 功能说明:画板
|
// 功能说明:画板
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, watch,defineExpose } from 'vue'
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
import {FabricVue, TYPES} from '@/plugins/fabric'
|
||||||
import { useBoardStore, useDrawStore } from '@/store/modules/draw'
|
import { useToolState } from '@/store/modules/tool'
|
||||||
const canvasRef = ref(null)
|
const toolStore = useToolState()
|
||||||
|
const canvasRef = ref(null) // 画布
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: String
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
// 画板初始化配置
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
if (canvasRef.value) {
|
if (canvasRef.value) {
|
||||||
useBoardStore().backgroundColor = 'transparent'
|
FabricVue.drawConfig.drawColors = ['red']
|
||||||
useDrawStore().drawColors = ['red']
|
FabricVue.boardConfig.mode = TYPES.ActionMode.OTHER
|
||||||
|
FabricVue.boardConfig.backgroundColor = 'transparent'
|
||||||
const option = { freeDrawingCursor: 'default' }
|
const option = { freeDrawingCursor: 'default' }
|
||||||
await FabricVue.initCanvas(canvasRef.value, option)
|
await FabricVue.initCanvas(canvasRef.value, option)
|
||||||
// FabricVue.canvas.backgroundColor = 'transparent'
|
|
||||||
// FabricVue.canvas.setWidth(500)
|
|
||||||
// FabricVue.canvas.setHeight(500)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const handleMode = (newVal, oldVal) => {
|
||||||
|
if(toolStore.isPdfWin){
|
||||||
|
if(newVal=='clear'){
|
||||||
|
emit('update:modelValue', oldVal)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch(newVal) {
|
||||||
|
case 'select': // 选择模式
|
||||||
|
FabricVue.handleMode(TYPES.ActionMode.OTHER)
|
||||||
|
break
|
||||||
|
case 'brush': // 画笔模式
|
||||||
|
FabricVue.handleMode(TYPES.ActionMode.DRAW)
|
||||||
|
FabricVue.canvas.freeDrawingCursor = 'default'
|
||||||
|
break
|
||||||
|
case 'erase': // 板擦模式
|
||||||
|
FabricVue.handleMode(TYPES.ActionMode.ERASE)
|
||||||
|
break
|
||||||
|
case 'clear': // 清空画布
|
||||||
|
if(oldVal){
|
||||||
|
FabricVue.history?.clean()
|
||||||
|
emit('update:modelValue', oldVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 监听
|
||||||
|
watch(() => props.modelValue, (newVal, oldVal) => {
|
||||||
|
// console.log(newVal, oldVal)
|
||||||
|
handleMode(newVal, oldVal)
|
||||||
|
})
|
||||||
|
// 暴露属性方法
|
||||||
|
defineExpose({
|
||||||
|
handleMode
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,342 @@
|
||||||
|
<template>
|
||||||
|
<div class="page-homework">
|
||||||
|
|
||||||
|
<transition name="slide" @before-enter="beforeEnter" @enter="enter" @leave="leave">
|
||||||
|
<div v-if="showLeft" class="homework homework-content">
|
||||||
|
<div class="homework-top flex">
|
||||||
|
<div>
|
||||||
|
<h3 class="title">{{ lesson }}</h3>
|
||||||
|
<div class="grade">{{ usertore.edusubject }}</div>
|
||||||
|
</div>
|
||||||
|
<i class="iconfont icon-xiangyou" @click="showLeft = !showLeft"></i>
|
||||||
|
</div>
|
||||||
|
<div class="homework-list" v-loading="loading">
|
||||||
|
<el-scrollbar height="360px">
|
||||||
|
<ul>
|
||||||
|
<!--资源-->
|
||||||
|
<li class="item flex" v-for="item in resourceList" :key="item.id">
|
||||||
|
<div class="item-left flex">
|
||||||
|
<FileImage :size="50" :file-name="item.fileShowName" />
|
||||||
|
<div class="item-info flex">
|
||||||
|
<span class="item-name">{{ item.fileShowName }}</span>
|
||||||
|
<div class="item-student">
|
||||||
|
<span class="item-option">{{ item.fileFlag }}</span>
|
||||||
|
<span>{{ item.fileSuffix }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<!--作业-->
|
||||||
|
<li class="item flex" v-for="item in dataList" :key="item.id">
|
||||||
|
<div class="item-left flex">
|
||||||
|
<svg class="icon homework-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-zuoye2"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="item-info flex">
|
||||||
|
<span class="item-name">{{ item.uniquekey }}</span>
|
||||||
|
<div class="item-student">
|
||||||
|
<span class="item-option">作业</span>
|
||||||
|
<span>{{ item.worktype }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-button color="#349d44" @click="sendHomework(item)">推送</el-button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<SetHomework v-model="setDialog" :title="'推送作业'" :entpcourseid="entpcourseid" :row="curRow"
|
||||||
|
@on-close="closeHomework" />
|
||||||
|
</div>
|
||||||
|
<div class="homework flex" v-else>
|
||||||
|
<div class="unit-top flex">
|
||||||
|
<div class="unit-top-left" @click="showLeft = !showLeft">
|
||||||
|
<i class="iconfont icon-xiangzuo"></i>
|
||||||
|
<span>返回</span>
|
||||||
|
</div>
|
||||||
|
<span class="unit-top-center">选择目录</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-content">
|
||||||
|
<Lesson @changeChapter="changeChapter" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, toRaw } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { homeworklist } from '@/api/teaching/classwork'
|
||||||
|
import { getSmarttalkPage, getPrepareById } from '@/api/file'
|
||||||
|
import SetHomework from '@/views/prepare/container/set-homework.vue'
|
||||||
|
import FileImage from '@/components/file-image/index.vue'
|
||||||
|
import { useGetHomework } from '@/hooks/useGetHomework'
|
||||||
|
import { ipcMsgSend } from '@/utils/tool'
|
||||||
|
import { useToolState } from '@/store/modules/tool'
|
||||||
|
import { asyncLocalFile } from '@/utils/talkFile'
|
||||||
|
import Lesson from './lesson.vue';
|
||||||
|
import { parseCataByNode } from '@/utils/talkFile'
|
||||||
|
|
||||||
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
const route = useRoute();
|
||||||
|
const usertore = useUserStore().user
|
||||||
|
const toolStore = useToolState()
|
||||||
|
const entpcourseid = ref('')
|
||||||
|
const lesson = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const showLeft = ref(true)
|
||||||
|
//作业数据
|
||||||
|
const dataList = ref([])
|
||||||
|
const setDialog = ref(false)
|
||||||
|
// 资源数据
|
||||||
|
const resourceList = ref([])
|
||||||
|
|
||||||
|
//
|
||||||
|
const curRow = ref('')
|
||||||
|
|
||||||
|
|
||||||
|
const sendHomework = (row) => {
|
||||||
|
curRow.value = row
|
||||||
|
setTimeout(() => {
|
||||||
|
ipcMsgSend('tool-sphere:set:ignore', false)
|
||||||
|
}, 200)
|
||||||
|
setDialog.value = true
|
||||||
|
}
|
||||||
|
//
|
||||||
|
const closeHomework = () => {
|
||||||
|
ipcMsgSend('tool-sphere:set:ignore', true)
|
||||||
|
setDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const changeChapter = async (data)=>{
|
||||||
|
const res = await useGetHomework(data)
|
||||||
|
dataList.value = res
|
||||||
|
let cata = parseCataByNode(data)
|
||||||
|
|
||||||
|
toolStore.curSubjectNode.querySearch.levelFirstId = cata[0]
|
||||||
|
toolStore.curSubjectNode.querySearch.levelSecondId = cata[1]
|
||||||
|
lesson.value = data.label
|
||||||
|
showLeft.value = !showLeft.value
|
||||||
|
getResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取资源数据
|
||||||
|
const getResource = () => {
|
||||||
|
let querySearch = toRaw(toolStore.curSubjectNode).querySearch
|
||||||
|
|
||||||
|
querySearch.orderByColumn = 'uploadTime'
|
||||||
|
querySearch.isAsc = 'desc'
|
||||||
|
querySearch.pageSize = 500
|
||||||
|
|
||||||
|
getSmarttalkPage(querySearch).then(res => {
|
||||||
|
resourceList.value = res.rows
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取作业数据
|
||||||
|
const getHomework = () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
homeworklist({
|
||||||
|
entpcourseid: entpcourseid.value,
|
||||||
|
edituserid: usertore.userId,
|
||||||
|
pageSize: 100
|
||||||
|
}).then(res => {
|
||||||
|
//以下代码 参照AIx web端 作业布置
|
||||||
|
let list = []
|
||||||
|
for (var i = 0; i < res.rows.length; i++) {
|
||||||
|
res.rows[i].taskconfig = []
|
||||||
|
|
||||||
|
// 找child
|
||||||
|
for (var j = 0; j < res.rows.length; j++) {
|
||||||
|
if (res.rows[j].parentid == res.rows[i].id) {
|
||||||
|
var ss = []
|
||||||
|
if (res.rows[j].classworkdatastudentids != null) {
|
||||||
|
ss = JSON.parse('[' + res.rows[j].classworkdatastudentids + ']')
|
||||||
|
}
|
||||||
|
var js = {
|
||||||
|
id: res.rows[j].id,
|
||||||
|
classid: res.rows[j].classid,
|
||||||
|
classcaption: res.rows[j].classcaption,
|
||||||
|
parentid: 0,
|
||||||
|
worktype: '',
|
||||||
|
workkey: res.rows[j].workkey,
|
||||||
|
worktag: '',
|
||||||
|
entpcourseid: 0,
|
||||||
|
evalid: 0,
|
||||||
|
edusubject: '',
|
||||||
|
edudegree: '',
|
||||||
|
workdate: '',
|
||||||
|
title: '',
|
||||||
|
workcodes: '',
|
||||||
|
studentlist: ss,
|
||||||
|
deaddate: res.rows[j].deaddate,
|
||||||
|
timelength: res.rows[j].timelength,
|
||||||
|
weights: res.rows[j].weights,
|
||||||
|
feedtype: res.rows[j].feedtype
|
||||||
|
}
|
||||||
|
res.rows[i].taskconfig.push(js)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.rows[i].fileShowName = res.rows[i].uniquekey
|
||||||
|
|
||||||
|
// 注意slideid>0的,这一些作业是添加到PPT页面的,所以在作业管理中不能出现
|
||||||
|
// 2024-05-15,酉阳,jackyshen
|
||||||
|
if (res.rows[i].classid == 0 && res.rows[i].slideid == 0) {
|
||||||
|
list.push(res.rows[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是习题训练任务,则检查一共有多少道
|
||||||
|
if (res.rows[i].entpcourseworklist != '') {
|
||||||
|
res.rows[i].entpcourseworklistarray = JSON.parse(
|
||||||
|
'[' + res.rows[i].entpcourseworklist + ']'
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
res.rows[i].entpcourseworklistarray = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataList.value = list
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function beforeEnter(el) {
|
||||||
|
el.style.transform = 'translateX(100%)';
|
||||||
|
}
|
||||||
|
|
||||||
|
function enter(el, done) {
|
||||||
|
el.offsetHeight; // trigger reflow
|
||||||
|
el.style.transition = 'transform 0.5s ease';
|
||||||
|
el.style.transform = 'translateX(0)';
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
function leave(el, done) {
|
||||||
|
el.style.transition = 'transform 0.5s ease';
|
||||||
|
el.style.transform = 'translateX(-100%)';
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
entpcourseid.value = route.query.entpcourseid
|
||||||
|
lesson.value = route.query.label
|
||||||
|
getHomework()
|
||||||
|
setTimeout(()=>{
|
||||||
|
getResource()
|
||||||
|
},5000)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-homework {
|
||||||
|
position: relative;
|
||||||
|
height: 435px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homework {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homework-content {
|
||||||
|
padding: 0 15px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.homework-top {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.icon-xiangyou {
|
||||||
|
font-size: 26px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade {
|
||||||
|
margin-top: 5px;
|
||||||
|
color: #cecece;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
background-color: #384342;
|
||||||
|
padding: 15px 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.item-left {
|
||||||
|
.homework-icon {
|
||||||
|
font-size: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-info {
|
||||||
|
margin-left: 8px;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-student {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #d7d7d7;
|
||||||
|
|
||||||
|
.item-option {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-enter-active,
|
||||||
|
.slide-leave-active {
|
||||||
|
transition: transform 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-enter,
|
||||||
|
.slide-leave-to
|
||||||
|
|
||||||
|
/* .slide-leave-active in <2.1.8 */
|
||||||
|
{
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-top {
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.unit-top-left {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.icon-xiangzuo {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unit-top-center{
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unit-content{
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,171 @@
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<el-scrollbar>
|
||||||
|
<el-tree ref="refTree" :data="treeData" :props="defaultProps" node-key="id"
|
||||||
|
:default-expanded-keys="defaultExpandedKeys" :current-node-key="currentNodeId" highlight-current
|
||||||
|
@node-click="handleNodeClick">
|
||||||
|
<template #default="{ node }">
|
||||||
|
<span :title="node.label" class="tree-label">{{ node.label }}</span>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
</el-scrollbar>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, nextTick, markRaw, toRaw } from 'vue'
|
||||||
|
import { useToolState } from '@/store/modules/tool'
|
||||||
|
import { parseCataByNode } from '@/utils/talkFile'
|
||||||
|
|
||||||
|
const emit = defineEmits(['changeChapter'])
|
||||||
|
const toolStore = useToolState()
|
||||||
|
const treeData = ref([])
|
||||||
|
const defaultProps = {
|
||||||
|
children: 'children',
|
||||||
|
label: 'label',
|
||||||
|
class: 'textbook-tree'
|
||||||
|
}
|
||||||
|
// 当前选中的节点ID
|
||||||
|
const currentNodeId = ref(0)
|
||||||
|
// 当前选中的节点名称
|
||||||
|
const currentNodeName = ref('')
|
||||||
|
// 默认展开的节点
|
||||||
|
const defaultExpandedKeys = ref([])
|
||||||
|
// tree
|
||||||
|
const refTree = ref(null)
|
||||||
|
// 教材ID
|
||||||
|
const curBookId = ref('')
|
||||||
|
//
|
||||||
|
const evaluationList = ref([])
|
||||||
|
|
||||||
|
const getTreeData = () => {
|
||||||
|
evaluationList.value = JSON.parse(localStorage.getItem('evaluationList'))
|
||||||
|
|
||||||
|
//上册
|
||||||
|
let volumeOne = evaluationList.value.filter(item => item.level == 1 && item.semester == '上册')
|
||||||
|
//下册
|
||||||
|
let volumeTwo = evaluationList.value.filter(item => item.level == 1 && item.semester == '下册')
|
||||||
|
//数据过滤
|
||||||
|
|
||||||
|
let upData = transData(volumeOne)
|
||||||
|
let downData = transData(volumeTwo)
|
||||||
|
if(upData.length && downData.length){
|
||||||
|
treeData.value = [...upData,...downData]
|
||||||
|
}
|
||||||
|
else if(upData.length || downData.length){
|
||||||
|
treeData.value = upData.length ? upData : downData
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
treeData.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
let defaultKey
|
||||||
|
if(toolStore.curSubjectNode.querySearch.levelSecondId){
|
||||||
|
defaultKey = toolStore.curSubjectNode.querySearch.levelSecondId
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
defaultKey = toolStore.curSubjectNode.querySearch.levelFirstId
|
||||||
|
}
|
||||||
|
currentNodeId.value = defaultKey
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNodeClick = (data, node) => {
|
||||||
|
/**
|
||||||
|
* data : 当前节点数据
|
||||||
|
* node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象
|
||||||
|
*/
|
||||||
|
|
||||||
|
const nodeData = data;
|
||||||
|
const parentNode = node.parent.data;
|
||||||
|
|
||||||
|
if (Array.isArray(parentNode)) {
|
||||||
|
nodeData.parentNode = null
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nodeData.parentNode = parentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('changeChapter', toRaw(nodeData))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 根据教材章节单元ID 查询作业列表所需ID
|
||||||
|
const getChapterId = () => {
|
||||||
|
return listEntpcourse({
|
||||||
|
evalid: this.uploadData.levelSecondId,
|
||||||
|
edituserid: this.userStore.userId,
|
||||||
|
pageSize: 500
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const transData = (data) => {
|
||||||
|
let ary = []
|
||||||
|
data.forEach(item => {
|
||||||
|
let obj = {}
|
||||||
|
if (item.rootid == curBookId.value) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
obj.children = ary2
|
||||||
|
})
|
||||||
|
ary.push(obj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ary
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
setTimeout(()=>{
|
||||||
|
let data = toRaw(toolStore.curSubjectNode)
|
||||||
|
curBookId.value = data.data.textBook.curBookId
|
||||||
|
getTreeData()
|
||||||
|
},500)
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-tree{
|
||||||
|
background-color: #121212
|
||||||
|
}
|
||||||
|
:deep(.el-tree-node) {
|
||||||
|
.el-tree-node__content {
|
||||||
|
height: 45px;
|
||||||
|
color: #EFEFEF;
|
||||||
|
//#368FFF
|
||||||
|
&:hover {
|
||||||
|
background-color: #272728;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
|
||||||
|
background-color: #121212 !important;
|
||||||
|
color: #409EFF
|
||||||
|
}
|
||||||
|
:deep(.el-scrollbar__view){
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,142 @@
|
||||||
|
<template>
|
||||||
|
<div class="warp" ref="btnRef">
|
||||||
|
<slot name="start"></slot>
|
||||||
|
<!-- 工具按钮 -->
|
||||||
|
<el-space direction="vertical">
|
||||||
|
<template v-for="(item,index) in list">
|
||||||
|
<slot :name="item.prop" :item="item" :index="index">
|
||||||
|
<div class="c-btn flex flex-col items-center gap-2 p-2" @click.stop="clickHandel(item,$event)">
|
||||||
|
<i class="iconfont" :class="item.icon" :style="item.style" />
|
||||||
|
<span>{{item.label||item.text||item.name}}</span>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<slot name="append"></slot>
|
||||||
|
</el-space>
|
||||||
|
<slot name="end"></slot>
|
||||||
|
<!-- 内容部分 -->
|
||||||
|
<transition name="el-fade-in">
|
||||||
|
<div class="c-popover" :style="`--top: ${topPos}px;--height:${hPost}px;`" v-show="isVisible">
|
||||||
|
<div class="content">
|
||||||
|
<homework/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { computed, defineProps, ref, reactive, watchEffect, onMounted } from 'vue'
|
||||||
|
import homework from './homework.vue';
|
||||||
|
|
||||||
|
// 功能说明:侧边-工具栏
|
||||||
|
const colors = ['#00f389', '#ff7f00', '#ffff00', '#409EFF', '#00baff', '#13b189', '#F56C6C']
|
||||||
|
const emit = defineEmits(['update:modelValue','change'])
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: { // 是否显示
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
data: { // 数据
|
||||||
|
type: Array,
|
||||||
|
default: () => [
|
||||||
|
{ label: '资源', prop: 'resource', icon: 'icon-kechengziyuan1' },
|
||||||
|
{ label: '互动', prop: 'interact', icon: 'icon-hudong' },
|
||||||
|
{ label: '窗口', prop: 'win', icon: 'icon-tubiaozhizuomobanyihuifu-' },
|
||||||
|
{ label: '下课', prop: 'over', isExtra: true, icon: 'icon-a-lujing13357' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const isVisible = ref(false) // 是否显示内容
|
||||||
|
const activeObj = ref(null) // 当前激活的按钮
|
||||||
|
const btnRef = ref(null) // 按钮元素-ref
|
||||||
|
const topPos = ref(30) // 顶部距离-内容的距离
|
||||||
|
const hPost = ref(0) // 顶部距离-内容的距离
|
||||||
|
let posBtnAll = {} // 存储位置
|
||||||
|
// === 计算属性 ===
|
||||||
|
const list = computed(() => props.data.map((o,i) => {
|
||||||
|
o.style = getStyle(o.style, i)
|
||||||
|
return o
|
||||||
|
}))
|
||||||
|
onMounted(() => {
|
||||||
|
posBtnAll = btnRef.value.getBoundingClientRect()
|
||||||
|
hPost.value = posBtnAll.height
|
||||||
|
})
|
||||||
|
// === 方法 ===
|
||||||
|
// 获取颜色索引
|
||||||
|
const getIndex = i => i % colors.length
|
||||||
|
// 获取样式
|
||||||
|
const getStyle = (style,index) => {
|
||||||
|
const color = colors[getIndex(index)]
|
||||||
|
if (!style) return {color}
|
||||||
|
else {
|
||||||
|
if (typeof style === 'object') return {...style, color}
|
||||||
|
return `${style}${style.endsWith(';')?'':';'}color:${color};`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 事件
|
||||||
|
const clickHandel = (o, e) => {
|
||||||
|
if (!o.isExtra) { // 忽略其他操作: 打开内容窗口,指示器指向当前
|
||||||
|
const node = e.target.parentNode.getBoundingClientRect()
|
||||||
|
const isColse = activeObj.value && activeObj.value.prop === o.prop && isVisible.value
|
||||||
|
isVisible.value = !isColse // 相同的按钮且打开状态,点击关闭
|
||||||
|
activeObj.value = o
|
||||||
|
const nodeH = parseInt(node.height / 2) // 高度的一半
|
||||||
|
topPos.value = parseInt(node.top) - posBtnAll.top + nodeH
|
||||||
|
}
|
||||||
|
emit('change', o)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.warp{
|
||||||
|
border: 1px solid;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
right: 10px;
|
||||||
|
min-height: 40vh;
|
||||||
|
min-width: 4em;
|
||||||
|
border-radius: 4em;
|
||||||
|
background-color: #121212;
|
||||||
|
.el-space{margin: 20px 0;}
|
||||||
|
.c-btn{
|
||||||
|
color: #d9dce3;
|
||||||
|
cursor: pointer;
|
||||||
|
.iconfont{
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
span{font-size: 12px;}
|
||||||
|
&:hover{
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #454545fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.c-popover{
|
||||||
|
--top: 30px;
|
||||||
|
--height: 40vh;
|
||||||
|
position: fixed;
|
||||||
|
inset: 50% 75px auto auto;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: #121212;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
min-height: var(--height);
|
||||||
|
width: 30em;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:before{
|
||||||
|
content: "";
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: #121212;
|
||||||
|
position: absolute;
|
||||||
|
right: -5px;
|
||||||
|
top: var(--top);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
color: red;
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<template>
|
||||||
|
<button v-if="props.test" @click="trigger">测试</button>
|
||||||
|
<div ref="warpRef" class="c-warp">
|
||||||
|
<template v-for="i in sum">
|
||||||
|
<slot><el-icon><Star /></el-icon></slot>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
// 功能说明:动画-点赞
|
||||||
|
import { nextTick, ref } from 'vue'
|
||||||
|
import {Star} from '@element-plus/icons-vue'
|
||||||
|
const warpRef = ref(null)
|
||||||
|
const props = defineProps({ test: Boolean })
|
||||||
|
const sum = ref(0)
|
||||||
|
// === 方法 ===
|
||||||
|
const trigger = () => {
|
||||||
|
sum.value++
|
||||||
|
nextTick(() => {
|
||||||
|
const el = warpRef.value.lastElementChild // 获取最后一个新加的元素
|
||||||
|
animInit(el)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 初始化动画
|
||||||
|
const animInit = (el) => {
|
||||||
|
const { height, width } = el.getBoundingClientRect()
|
||||||
|
const sumH = warpRef.value.clientHeight
|
||||||
|
const sumW = warpRef.value.clientWidth
|
||||||
|
const maxH = sumH - height // 最大高度
|
||||||
|
const maxw = sumW - width // 最大宽度
|
||||||
|
let bottom = 0 // 底部距离
|
||||||
|
let opacity = 0 // 透明度
|
||||||
|
let scale = 0 // 缩放
|
||||||
|
let transform = '' // 旋转 缩放 偏移等操作
|
||||||
|
let right = getRandom(5, maxw, false, true) // 随机-右距离
|
||||||
|
let isPlus = getRandomBool()?'':'-' // 随机-方向
|
||||||
|
let rotate = getRandom(0, 60, false, true) // 随机-旋转
|
||||||
|
let scaleMax = getRandom(0, 1, true, true)+1 // 随机-缩放倍数
|
||||||
|
setStatic(el,'position','absolute') // 设置-定位
|
||||||
|
setStatic(el, 'right', right, 'px') // 设置-右距离
|
||||||
|
const amFn = () => { // opacity: 0.5;
|
||||||
|
// 到顶了- 移除元素
|
||||||
|
if (bottom > maxH) {el.remove();return}
|
||||||
|
// 透明度: 进入-慢慢出现
|
||||||
|
if (bottom < 101 && opacity < 1) opacity = toNumber(opacity + 0.01)
|
||||||
|
else if (bottom > maxH - 100 && opacity > 0) opacity = toNumber(opacity - 0.01)
|
||||||
|
// 缩放:进入-慢慢放大
|
||||||
|
if (bottom < (scaleMax/0.1) && scale < scaleMax) scale = toNumber(scale + 0.1)
|
||||||
|
// 缩放:退出-慢慢缩小
|
||||||
|
else if (bottom > maxH - (scaleMax/0.1) && scale > 0) scale = toNumber(scale - 0.1)
|
||||||
|
bottom++
|
||||||
|
// console.log('zdg: ', scale)
|
||||||
|
// 设置样式
|
||||||
|
transform = `rotate(${isPlus}${rotate}deg) scale(${scale})`
|
||||||
|
setStatic(el, 'bottom', bottom, 'px')
|
||||||
|
setStatic(el, 'opacity', opacity)
|
||||||
|
setStatic(el, 'transform', transform)
|
||||||
|
requestAnimationFrame(amFn)
|
||||||
|
}
|
||||||
|
amFn() // 初次执行
|
||||||
|
}
|
||||||
|
// 设置-静态样式
|
||||||
|
const setStatic = (el, type, val, end) => el.style[type] = val + (end || '')
|
||||||
|
// 随机数 isFloat:是否小数 isMax:是否包含最大值
|
||||||
|
const getRandom = (min, max, isFloat, isMax, pos=2) => {
|
||||||
|
const maxVal = isMax ? max - min + 1 : max - min
|
||||||
|
const getVal = () => Math.random() * maxVal + min
|
||||||
|
return isFloat ? toNumber(getVal(), pos) : toNumber(getVal(), 0)
|
||||||
|
}
|
||||||
|
// 随机布尔值
|
||||||
|
const getRandomBool = () => Math.random() > 0.5
|
||||||
|
// 转换数字-(小数精度丢失)
|
||||||
|
const toNumber = (v, pos = 2) => Number(v.toFixed(pos))
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({ trigger })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.c-warp {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 30%;
|
||||||
|
height: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,111 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 工具栏-拖拽 自定义指令
|
||||||
|
*/
|
||||||
|
class Drag {
|
||||||
|
isDrag = false // 是否开始拖拽
|
||||||
|
rafId // requestAnimationFrame id
|
||||||
|
x = 0 // 鼠标按下时的x坐标
|
||||||
|
y = 0 // 鼠标按下时的y坐标
|
||||||
|
data // 其他参数 元素实际坐标值
|
||||||
|
el; handle; // el, handle 挂载元素和拖拽元素
|
||||||
|
pos // 拖拽元素实际坐标值
|
||||||
|
max // 拖拽元素最大边界
|
||||||
|
// 构造器
|
||||||
|
constructor(el, binding) {
|
||||||
|
this.el = el
|
||||||
|
const { value } = binding
|
||||||
|
const handleSelector = value instanceof Object ? value.handle : value
|
||||||
|
if (!!handleSelector) {
|
||||||
|
if (handleSelector instanceof HTMLElement) this.handle = handleSelector
|
||||||
|
else {
|
||||||
|
this.handle = document.querySelector(handleSelector)
|
||||||
|
// .forEach((child) => { this.handleArray.push(child) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.handle) this.handle = el // 默认为当前元素
|
||||||
|
// 被拖拽元素初始坐标
|
||||||
|
const pos = this.handle?.getBoundingClientRect()
|
||||||
|
this.data = {left:this.toRound(pos.left), top:this.toRound(pos.top)}
|
||||||
|
this.pos = pos
|
||||||
|
this.max = {w: window.innerWidth, h: window.innerHeight}
|
||||||
|
}
|
||||||
|
// 移入
|
||||||
|
down(e) {
|
||||||
|
this.isDrag = true
|
||||||
|
const {cx, cy} = this.getMousePos(e)
|
||||||
|
this.x = cx
|
||||||
|
this.y = cy
|
||||||
|
// 手动-触发事件 v-drag-start
|
||||||
|
this.el.dispatchEvent(new CustomEvent('v-drag-start', {detail:{drag: this}}))
|
||||||
|
}
|
||||||
|
// 移动过程
|
||||||
|
move = (e) => {
|
||||||
|
if (!this.isDrag) return
|
||||||
|
if (this.rafId) cancelAnimationFrame(this.rafId) // 清除上一次动画
|
||||||
|
this.rafId = requestAnimationFrame(() => this.updatePosition(e))
|
||||||
|
}
|
||||||
|
// 移出
|
||||||
|
up = (e) => {
|
||||||
|
// e.preventDefault(); // 阻止默认行为
|
||||||
|
// e.stopPropagation(); // 阻止事件传播
|
||||||
|
this.isDrag = false
|
||||||
|
cancelAnimationFrame(this.rafId)
|
||||||
|
this.rafId = null
|
||||||
|
document.removeEventListener('mousemove', this.move);
|
||||||
|
document.removeEventListener('mouseup', this.up);
|
||||||
|
document.addEventListener('touchmove', this.move);
|
||||||
|
document.addEventListener('touchend', this.up);
|
||||||
|
}
|
||||||
|
// 业务逻辑
|
||||||
|
updatePosition(e) {
|
||||||
|
const {cx, cy} = this.getMousePos(e)
|
||||||
|
const {left, top} = this.data
|
||||||
|
const dx = cx - this.x
|
||||||
|
const dy = cy - this.y
|
||||||
|
this.x = cx
|
||||||
|
this.y = cy
|
||||||
|
const {x, y} = this.getPos(left + dx, top + dy) // 调用边界函数
|
||||||
|
this.data.left = x // 设置边界-x
|
||||||
|
this.data.top = y // 设置边界-y
|
||||||
|
// console.log(JSON.stringify(this))
|
||||||
|
this.handle.style.left = `${this.data?.left}px`
|
||||||
|
this.handle.style.top = `${this.data?.top}px`
|
||||||
|
this.handle.style.bottom = 'unset'
|
||||||
|
this.handle.style.transform = 'unset'
|
||||||
|
this.rafId = requestAnimationFrame(() => this.updatePosition(e))
|
||||||
|
}
|
||||||
|
// 获取鼠标位置 | Get mouse position
|
||||||
|
getMousePos(e){
|
||||||
|
let cx = e.clientX || e.touches[0].clientX
|
||||||
|
let cy = e.clientY || e.touches[0].clientY
|
||||||
|
cx = this.toRound(cx)
|
||||||
|
cy = this.toRound(cy)
|
||||||
|
return {cx, cy}
|
||||||
|
}
|
||||||
|
// 获取移动后坐标
|
||||||
|
getPos(x, y) {
|
||||||
|
const w = this.max.w - this.toRound(this.el.clientWidth)
|
||||||
|
const h = this.max.h - this.toRound(this.el.clientHeight)
|
||||||
|
x = x < 0 ? 0 : x > w ? w : x
|
||||||
|
y = y < 0 ? 0 : y > h ? h : y
|
||||||
|
return { x, y }
|
||||||
|
}
|
||||||
|
// 小数转整数
|
||||||
|
toRound = v => Math.round(v)
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
mounted(el, binding) {
|
||||||
|
// const { style } = binding.value
|
||||||
|
const drag = new Drag(el, binding)
|
||||||
|
const dragStart = (e) => {
|
||||||
|
drag.down(e)
|
||||||
|
document.addEventListener('mousemove', drag.move);
|
||||||
|
document.addEventListener('mouseup', drag.up);
|
||||||
|
document.addEventListener('touchmove', drag.move);
|
||||||
|
document.addEventListener('touchend', drag.up);
|
||||||
|
}
|
||||||
|
el.addEventListener('mousedown', dragStart)
|
||||||
|
el.addEventListener('touchstart', dragStart)
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* 处理元素是否穿透-窗口
|
||||||
|
*/
|
||||||
|
import { ipcMsgSend } from '@/utils/tool'
|
||||||
|
class Ignore {
|
||||||
|
el // 绑定元素
|
||||||
|
binding // 绑定对象(参数)
|
||||||
|
value // 绑定值
|
||||||
|
isAuto // 是否自动控制
|
||||||
|
constructor(el, binding) {
|
||||||
|
this.el = el
|
||||||
|
this.binding = binding
|
||||||
|
this.value = binding.value
|
||||||
|
this.isAuto = !(this.value && this.value instanceof Boolean)
|
||||||
|
}
|
||||||
|
// 设置是否穿透
|
||||||
|
send(bool) {
|
||||||
|
ipcMsgSend('tool-sphere:set:ignore', bool)
|
||||||
|
}
|
||||||
|
// 监听元素移入移出,自动设置
|
||||||
|
mounted() {
|
||||||
|
this.el.addEventListener('mouseenter', e => { // 进入
|
||||||
|
this.send(false) // 元素不穿透,鼠标有效
|
||||||
|
this.changeHandle(e)
|
||||||
|
})
|
||||||
|
this.el.addEventListener('mouseleave', e => { // 离开
|
||||||
|
this.send(true) // 元素穿透,鼠标无效
|
||||||
|
this.changeHandle(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
changeHandle(e) {
|
||||||
|
const data = {detail:{ignore: this, e, type: e.type}}
|
||||||
|
const customEvent = new CustomEvent('ignore-mounted', data)
|
||||||
|
this.el.dispatchEvent(customEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
mounted(el, binding) {
|
||||||
|
const ignore = new Ignore(el, binding)
|
||||||
|
if (ignore.isAuto) { // 自动控制
|
||||||
|
ignore.mounted()
|
||||||
|
} else { // 手动设置
|
||||||
|
ignore.send(ignore.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,71 +1,137 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="warp-all">
|
<div class="warp-all">
|
||||||
<board-vue></board-vue>
|
<!-- 画板 -->
|
||||||
<!-- 底部工具栏 -->
|
<board-vue v-model="tabActive" v-show="isShow" ref="boardVueRef"></board-vue>
|
||||||
<el-row id="test" class="tool-bottom-all" @mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
|
||||||
<el-col :span="3" class="flex justify-center items-center">
|
<!-- 侧边工具栏 -->
|
||||||
<div class="c-logo"><el-image :src="logo" @click="tabChange('close')" /></div>
|
<side-vue v-ignore @ignore-mounted="sideMouse" @change="sideChange"></side-vue>
|
||||||
</el-col>
|
|
||||||
<el-col :span="20">
|
<!-- 点赞组件 -->
|
||||||
|
<upvote-vue></upvote-vue>
|
||||||
|
|
||||||
|
<!-- 底部工具栏 :style="dataPos.style"-->
|
||||||
|
<div class="tool-bottom-all" @mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
||||||
|
<div v-drag="{handle:'.tool-bottom-all', dragtime}"
|
||||||
|
@v-drag-start="dragtime = Date.now()">
|
||||||
|
<div class="c-logo" @click="logoHandle" title="拖动 | 折叠 | 展开">
|
||||||
|
<el-image :src="logo" draggable="false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tool-btns" v-show="!isFold">
|
||||||
<el-segmented class="c-btns" v-model="tabActive" :options="btnList" size="large" block
|
<el-segmented class="c-btns" v-model="tabActive" :options="btnList" size="large" block
|
||||||
@change="tabChange">
|
@change="tabChange">
|
||||||
<template #default="{item}">
|
<template #default="{item}">
|
||||||
<div class="c-btn flex flex-col items-center gap-2 p-2">
|
<div class="c-btn flex flex-col items-center gap-2 p-2">
|
||||||
<i class="iconfont" :class="item.icon"></i>
|
<i class="iconfont" :class="item.icon" :style="item.style" />
|
||||||
<span>{{item.label}}</span>
|
<span>{{item.label}}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-segmented>
|
</el-segmented>
|
||||||
</el-col>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
// 功能说明:electron 悬浮球
|
// 功能说明:electron 悬浮球
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref, reactive, watchEffect } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { endClass } from '@/api/classManage'
|
||||||
import logo from '@root/resources/icon.png' // logo
|
import logo from '@root/resources/icon.png' // logo
|
||||||
import boardVue from './components/board.vue'
|
import boardVue from './components/board.vue' // 画板-子组件
|
||||||
// const Remote = require('@electron/remote') // remote对象
|
import sideVue from './components/side.vue' // 画板-子组件
|
||||||
const { ipcRenderer } = require('electron')
|
import upvoteVue from './components/upvote.vue' // 点赞-子组件
|
||||||
|
import vDrag from './directive/drag' // 自定义指令-拖拽
|
||||||
|
import vIgnore from './directive/ignore' // 自定义指令-穿透
|
||||||
const tabActive = ref('select')
|
import { useToolState } from '@/store/modules/tool' // 数据状态-缓存
|
||||||
const btnList = [
|
import { ipcMsgSend, ipcHandle, ipcMain, ipcMsgInvoke } from '@/utils/tool' // 相关工具
|
||||||
|
const route = useRoute();
|
||||||
|
const tabActive = ref('select') // 工具栏当前选中项
|
||||||
|
const isFold = ref(false) // 折叠工具栏
|
||||||
|
const isDrag = ref(false) // 开始拖拽
|
||||||
|
const dragtime = ref(0) // 拖拽时间-计算点击还是拖动
|
||||||
|
const isShow = ref(false) // 是否显示-画板
|
||||||
|
const toolStore = useToolState() // 状态管理
|
||||||
|
const boardVueRef=ref(null) // 画板ref
|
||||||
|
const btnList = [ // 工具栏按钮列表
|
||||||
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
||||||
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
||||||
{ label: '板擦', value: 'eraser', icon: 'icon-xiangpica' },
|
{ label: '板擦', value: 'erase', icon: 'icon-xiangpica' },
|
||||||
{ label: '互动', value: 'interact', icon: 'icon-hudong' },
|
{ label: '清除', value: 'clear', icon: 'icon-xiangpica', style: 'color: #ccc' },
|
||||||
{ label: '聚焦', value: 'focus', icon: 'icon-jujiao' },
|
// { label: '互动', value: 'interact', icon: 'icon-hudong' },
|
||||||
{ label: '更多', value: 'more', icon: 'icon-xiazai9' },
|
// { label: '聚焦', value: 'focus', icon: 'icon-jujiao' },
|
||||||
|
// { label: '更多', value: 'more', icon: 'icon-xiazai9' },
|
||||||
]
|
]
|
||||||
|
// === 页面加载完毕 ===
|
||||||
|
onMounted(async() => {
|
||||||
|
resetStatus() // 开启重置状态-监听
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
// ==== 方法 ===
|
// ==== 方法 ===
|
||||||
const tabChange = (val) => { // 切换tab-change
|
// const test = (e) => { console.log('test', e) }
|
||||||
console.log('xxxx', val)
|
// 切换tab-change
|
||||||
switch (val) {
|
const tabChange = (val) => {
|
||||||
case 'brush':
|
const bool = !toolStore.isPdfWin && !toolStore.showBoardAll
|
||||||
|
if(bool) toolStore.showBoardAll = true
|
||||||
|
// ipcMsgSend('tool-sphere:close')
|
||||||
|
toolStore.model = val // 存储当前tab
|
||||||
|
}
|
||||||
|
// logo 点击-事件 折叠|展开
|
||||||
|
const logoHandle = (e,t) => {
|
||||||
|
if (Date.now() - dragtime.value < 200) {
|
||||||
|
isFold.value = !isFold.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 底部工具栏:移入移出-是否穿透
|
||||||
|
const mouseChange = (bool) => {
|
||||||
|
let resBool = false
|
||||||
|
if (tabActive.value == 'select') resBool = !!bool
|
||||||
|
if (!isShow.value) resBool = !!bool
|
||||||
|
setIgnore(resBool)
|
||||||
|
}
|
||||||
|
// 忽略鼠标穿透
|
||||||
|
const setIgnore = (bool) => {ipcMsgSend('tool-sphere:set:ignore', bool)}
|
||||||
|
// 重置状态: 鼠标|画板
|
||||||
|
const resetStatus = () => {
|
||||||
|
if (toolStore.isToolWin) return // 已经打开过-忽略
|
||||||
|
ipcMain?.handle?.('tool-sphere:reset', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
boardVueRef.value.handleMode(tabActive.value)
|
||||||
|
mouseChange(1)
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
toolStore.isToolWin = true // 标记状态
|
||||||
|
}
|
||||||
|
// 侧边工具栏: 移入移出
|
||||||
|
const sideMouse = e => {
|
||||||
|
const {type} = e.detail
|
||||||
|
mouseChange(type == 'mouseleave')
|
||||||
|
}
|
||||||
|
// 侧边工具栏: 操作变化
|
||||||
|
const sideChange = o => {
|
||||||
|
// console.log(o)
|
||||||
|
switch(o.prop) {
|
||||||
|
case 'resource': // 资源
|
||||||
break
|
break
|
||||||
case 'eraser':
|
case 'interact': // 互动
|
||||||
break
|
break
|
||||||
case 'interact':
|
case 'win': // 窗口
|
||||||
break
|
break
|
||||||
case 'focus':
|
case 'over': // 下课
|
||||||
break
|
toolStore.isToolWin = false
|
||||||
case 'more':
|
endClass(route.query.reservId)
|
||||||
break
|
ipcMsgSend('tool-sphere:close')
|
||||||
case 'close':
|
|
||||||
ipcRenderer.send('tool-sphere:close')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
|
||||||
let resBool = false
|
// === 监听器 ===
|
||||||
if (tabActive.value == 'select') resBool = !!bool
|
watchEffect(() => {
|
||||||
ipcRenderer.send('tool-sphere:set:ignore', resBool)
|
// 不同的时候, 更新值: 是否显示-画板
|
||||||
}
|
const show = !toolStore.isPdfWin && toolStore.showBoardAll
|
||||||
|
if (show != isShow.value) isShow.value = show
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -74,7 +140,8 @@ const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
||||||
}
|
}
|
||||||
// 底部工具栏
|
// 底部工具栏
|
||||||
.tool-bottom-all{
|
.tool-bottom-all{
|
||||||
width: 45vw;
|
// width: 45vw;
|
||||||
|
display: flex;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 3em;
|
bottom: 3em;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -89,9 +156,12 @@ const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
||||||
height: 5rem;
|
height: 5rem;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: 0px 0px 8px rgb(0 0 0 / 40%);
|
box-shadow: 0px 0px 8px rgb(0 0 0 / 40%);
|
||||||
position: absolute;
|
user-select: none;
|
||||||
left: 0;
|
// &:hover{
|
||||||
|
// .el-image{transform: scale(1.1);}
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
.tool-btns{margin: 0 35px 0 7px;}
|
||||||
.c-btn{
|
.c-btn{
|
||||||
i{font-size: 2rem;}
|
i{font-size: 2rem;}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +169,7 @@ const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
||||||
:deep(.el-segmented__item){
|
:deep(.el-segmented__item){
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 0.5rem;
|
margin: 0 0.5rem;
|
||||||
&:nth-last-child(1):before,
|
&:not(:nth-of-type(1)):before{
|
||||||
&:nth-last-child(2):before{
|
|
||||||
content: "";
|
content: "";
|
||||||
width: 2px;
|
width: 2px;
|
||||||
height: calc(100% - 20px);
|
height: calc(100% - 20px);
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<canvas ref="canvasRef" />
|
<canvas ref="canvasRef" />
|
||||||
|
<button @click="eraseTo">橡皮擦
|
||||||
|
<i class="iconfont icon-xiangpica"></i>
|
||||||
|
</button>
|
||||||
|
<button @click="close">销毁</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -7,23 +11,38 @@
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
// import { FabricVue } from '@/plugins/fabric'
|
// import { FabricVue } from '@/plugins/fabric'
|
||||||
// import { useBoardStore } from '@/store/modules/draw'
|
// import { useBoardStore } from '@/store/modules/draw'
|
||||||
import FabricVue from '@/plugins/fabric/test'
|
import {FabricVue, TYPES} from '@/plugins/fabric'
|
||||||
let canvasRef = ref(null)
|
let canvasRef = ref(null)
|
||||||
|
let canvas = null
|
||||||
|
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
console.log(canvasRef, FabricVue)
|
// console.log(canvasRef, FabricVue)
|
||||||
|
// canvasRef.value = 123
|
||||||
if (canvasRef.value) {
|
if (canvasRef.value) {
|
||||||
// useBoardStore().backgroundColor = 'transparent'
|
// useBoardStore().backgroundColor = 'transparent'
|
||||||
const option = { freeDrawingCursor: 'default' }
|
const option = { freeDrawingCursor: 'default' }
|
||||||
// await FabricVue.initCanvas(canvasRef.value, option)
|
await FabricVue.initCanvas(canvasRef.value, option)
|
||||||
// FabricVue.canvas.setWidth(500)
|
// FabricVue.canvas.setWidth(500)
|
||||||
// FabricVue.canvas.setHeight(500)
|
// FabricVue.canvas.setHeight(500)
|
||||||
await FabricVue.initCanvas(canvasRef.value, option)
|
|
||||||
// const pencilBrush = new fabric.PencilBrush(canvas)
|
|
||||||
FabricVue.canvas.isDrawingMode = true
|
|
||||||
}
|
}
|
||||||
|
// if (canvasRef.value) {
|
||||||
|
// canvas = new fabric.Canvas(canvasRef.value,{
|
||||||
|
// isDrawingMode: true,
|
||||||
|
// freeDrawingCursor: 'default',
|
||||||
|
// backgroundColor: 'transparent',
|
||||||
|
// width: window.innerWidth,
|
||||||
|
// height: window.innerHeight
|
||||||
|
// })
|
||||||
|
// canvas.isDrawingMode = true
|
||||||
|
// }
|
||||||
})
|
})
|
||||||
|
const eraseTo = () => { // 橡皮擦
|
||||||
|
FabricVue.handleMode(TYPES.ActionMode.ERASE)
|
||||||
|
// FabricVue.removeCanvas()
|
||||||
|
// FabricVue.canvas.dispose()
|
||||||
|
// canvas.dispose()
|
||||||
|
}
|
||||||
|
const close = () => { FabricVue.removeCanvas() }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
Loading…
Reference in New Issue