zdg #105
|
@ -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/'
|
||||||
|
|
|
@ -7,6 +7,8 @@ VITE_APP_ENV = 'production'
|
||||||
# AIx融合数字管理系统/生产环境
|
# AIx融合数字管理系统/生产环境
|
||||||
VITE_APP_BASE_API = 'https://file.ysaix.com:7868/prod-api'
|
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'
|
VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api'
|
||||||
|
|
||||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
|
|
|
@ -24,8 +24,8 @@ export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/dev-api': {
|
'/dev-api': {
|
||||||
// target: 'http://27.128.240.72:7865',
|
target: 'http://27.128.240.72:7865',
|
||||||
target: 'http://192.168.2.52:7863',
|
// target: 'http://192.168.2.52:7863',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (p) => p.replace(/^\/dev-api/, '')
|
rewrite: (p) => p.replace(/^\/dev-api/, '')
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,13 @@
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||||
"@vueuse/core": "^10.11.0",
|
"@vueuse/core": "^10.11.0",
|
||||||
|
"cropperjs": "^1.6.2",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"electron-dl-manager": "^3.0.0",
|
"electron-dl-manager": "^3.0.0",
|
||||||
"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-with-erasing": "^1.0.1",
|
"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",
|
||||||
|
@ -39,7 +40,6 @@
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"vue-cropper": "^1.1.4",
|
|
||||||
"vue-router": "^4.4.0",
|
"vue-router": "^4.4.0",
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,17 +31,17 @@ 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' // 唯一标识
|
||||||
// handleUpdate(loginWindow,ipcMain)
|
// handleUpdate(loginWindow,ipcMain)
|
||||||
// const loginURL = is.dev ? `http://localhost:5173/#/login` : `file://${__dirname}/index.html/#/login`
|
// const loginURL = is.dev ? `http://localhost:5173/#/login` : `file://${__dirname}/index.html/#/login`
|
||||||
// loginWindow.loadURL(loginURL)
|
// loginWindow.loadURL(loginURL)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,17 +70,18 @@ function createMainWindow() {
|
||||||
preload: join(__dirname, '../preload/index.js'),
|
preload: join(__dirname, '../preload/index.js'),
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
// nodeIntegration: true,
|
// nodeIntegration: true,
|
||||||
nodeIntegration: true, // nodeApi调用
|
nodeIntegration: true, // nodeApi调用
|
||||||
contextIsolation: false, // 沙箱取消
|
contextIsolation: false // 沙箱取消
|
||||||
// webSecurity: false // 跨域关闭
|
// webSecurity: false // 跨域关闭
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mainWindow.type = 'main' // 唯一标识
|
mainWindow.type = 'main' // 唯一标识
|
||||||
mainWindow.on('ready-to-show', () => {
|
mainWindow.on('ready-to-show', () => {
|
||||||
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,11 @@ function createMainWindow() {
|
||||||
remote.enable(mainWindow.webContents)
|
remote.enable(mainWindow.webContents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 作业窗口相关-开发中
|
// 作业窗口相关-开发中
|
||||||
let linkWindow
|
let linkWindow
|
||||||
async function createLinkWin(data) {
|
async function createLinkWin(data) {
|
||||||
if (linkWindow) return
|
if (linkWindow) return
|
||||||
linkWindow = new BrowserWindow({
|
linkWindow = new BrowserWindow({
|
||||||
width: 650,
|
|
||||||
height: 500,
|
|
||||||
show: false,
|
show: false,
|
||||||
frame: true,
|
frame: true,
|
||||||
maximizable: true,
|
maximizable: true,
|
||||||
|
@ -122,14 +120,17 @@ async function createLinkWin(data) {
|
||||||
contextIsolation: true
|
contextIsolation: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
linkWindow.type = 'link' // 唯一标识
|
linkWindow.type = 'link' // 唯一标识
|
||||||
let cookieDetails = { ...data.cookieData }
|
let cookieDetails = { ...data.cookieData }
|
||||||
await linkWindow.webContents.session.cookies.set(cookieDetails).then(()=>{
|
await linkWindow.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) => {
|
||||||
|
console.error('Cookie is error', error)
|
||||||
|
})
|
||||||
|
data.fullPath = data.fullPath.replaceAll('//', '/')
|
||||||
linkWindow.loadURL(data.fullPath)
|
linkWindow.loadURL(data.fullPath)
|
||||||
|
|
||||||
linkWindow.once('ready-to-show', () => {
|
linkWindow.once('ready-to-show', () => {
|
||||||
|
@ -171,9 +172,10 @@ app.on('ready', () => {
|
||||||
}
|
}
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
mainWindow.close() // 先发出这个关闭指令
|
mainWindow.close() // 先发出这个关闭指令
|
||||||
setTimeout(() => { //
|
setTimeout(() => {
|
||||||
|
//
|
||||||
mainWindow.destroy()
|
mainWindow.destroy()
|
||||||
}, 200);
|
}, 200)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -201,21 +203,19 @@ app.on('ready', () => {
|
||||||
createLinkWin(data)
|
createLinkWin(data)
|
||||||
})
|
})
|
||||||
// 新窗口创建-监听
|
// 新窗口创建-监听
|
||||||
ipcMain.on('new-window', (e,data) => {
|
ipcMain.on('new-window', (e, data) => {
|
||||||
const { id, type } = data
|
const { id, type } = data
|
||||||
const win = BrowserWindow.fromId(id)
|
const win = BrowserWindow.fromId(id)
|
||||||
win.type = type // 绑定独立标识
|
win.type = type // 绑定独立标识
|
||||||
remote.enable(win.webContents) // 开启远程服务
|
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
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
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>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ export const listEvaluation = (params)=> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const addFileToPrepare = (params) => {
|
export const addFileToPrepare = (params) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/smarttalk/file/addFileToPrepare',
|
url: '/smarttalk/file/addFileToPrepare',
|
||||||
|
@ -17,3 +16,4 @@ export const addFileToPrepare = (params) => {
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询classwork列表
|
||||||
|
export function homeworklist(params) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classwork/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询entpcourse列表
|
||||||
|
export function listEntpcourse(query) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcourse/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 布置作业
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,539 @@
|
||||||
|
/* Logo 字体 */
|
||||||
|
@font-face {
|
||||||
|
font-family: "iconfont logo";
|
||||||
|
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||||
|
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||||
|
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||||
|
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||||
|
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-family: "iconfont logo";
|
||||||
|
font-size: 160px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tabs */
|
||||||
|
.nav-tabs {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-more {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 42px;
|
||||||
|
line-height: 42px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabs {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabs li {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#tabs .active {
|
||||||
|
border-bottom-color: #f00;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-container .content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面布局 */
|
||||||
|
.main {
|
||||||
|
padding: 30px 100px;
|
||||||
|
width: 960px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .logo {
|
||||||
|
color: #333;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
line-height: 1;
|
||||||
|
height: 110px;
|
||||||
|
margin-top: -50px;
|
||||||
|
overflow: hidden;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .logo a {
|
||||||
|
font-size: 160px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helps {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helps pre {
|
||||||
|
padding: 20px;
|
||||||
|
margin: 10px 0;
|
||||||
|
border: solid 1px #e7e1cd;
|
||||||
|
background-color: #fffdef;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists {
|
||||||
|
width: 100% !important;
|
||||||
|
overflow: hidden;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists li {
|
||||||
|
width: 100px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-right: 20px;
|
||||||
|
text-align: center;
|
||||||
|
list-style: none !important;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists li .code-name {
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists .icon {
|
||||||
|
display: block;
|
||||||
|
height: 100px;
|
||||||
|
line-height: 100px;
|
||||||
|
font-size: 42px;
|
||||||
|
margin: 10px auto;
|
||||||
|
color: #333;
|
||||||
|
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||||
|
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||||
|
transition: font-size 0.25s linear, width 0.25s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists .icon:hover {
|
||||||
|
font-size: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists .svg-icon {
|
||||||
|
/* 通过设置 font-size 来改变图标大小 */
|
||||||
|
width: 1em;
|
||||||
|
/* 图标和文字相邻时,垂直对齐 */
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||||
|
fill: currentColor;
|
||||||
|
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||||
|
normalize.css 中也包含这行 */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_lists li .name,
|
||||||
|
.icon_lists li .code-name {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* markdown 样式 */
|
||||||
|
.markdown {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown img {
|
||||||
|
vertical-align: middle;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h1 {
|
||||||
|
color: #404040;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 40px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h2,
|
||||||
|
.markdown h3,
|
||||||
|
.markdown h4,
|
||||||
|
.markdown h5,
|
||||||
|
.markdown h6 {
|
||||||
|
color: #404040;
|
||||||
|
margin: 1.6em 0 0.6em 0;
|
||||||
|
font-weight: 500;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h1 {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h2 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h4 {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h5 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h6 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown hr {
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
background: #e9e9e9;
|
||||||
|
margin: 16px 0;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown p {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>p,
|
||||||
|
.markdown>blockquote,
|
||||||
|
.markdown>.highlight,
|
||||||
|
.markdown>ol,
|
||||||
|
.markdown>ul {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown ul>li {
|
||||||
|
list-style: circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>ul li,
|
||||||
|
.markdown blockquote ul>li {
|
||||||
|
margin-left: 20px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>ul li p,
|
||||||
|
.markdown>ol li p {
|
||||||
|
margin: 0.6em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown ol>li {
|
||||||
|
list-style: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>ol li,
|
||||||
|
.markdown blockquote ol>li {
|
||||||
|
margin-left: 20px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown code {
|
||||||
|
margin: 0 3px;
|
||||||
|
padding: 0 5px;
|
||||||
|
background: #eee;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown strong,
|
||||||
|
.markdown b {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0px;
|
||||||
|
empty-cells: show;
|
||||||
|
border: 1px solid #e9e9e9;
|
||||||
|
width: 95%;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table th {
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table th,
|
||||||
|
.markdown>table td {
|
||||||
|
border: 1px solid #e9e9e9;
|
||||||
|
padding: 8px 16px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>table th {
|
||||||
|
background: #F7F7F7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown blockquote {
|
||||||
|
font-size: 90%;
|
||||||
|
color: #999;
|
||||||
|
border-left: 4px solid #e9e9e9;
|
||||||
|
padding-left: 0.8em;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown blockquote p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown .anchor {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown .waiting {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h1:hover .anchor,
|
||||||
|
.markdown h2:hover .anchor,
|
||||||
|
.markdown h3:hover .anchor,
|
||||||
|
.markdown h4:hover .anchor,
|
||||||
|
.markdown h5:hover .anchor,
|
||||||
|
.markdown h6:hover .anchor {
|
||||||
|
opacity: 1;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown>br,
|
||||||
|
.markdown>p>br {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
background: white;
|
||||||
|
padding: 0.5em;
|
||||||
|
color: #333333;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-meta {
|
||||||
|
color: #969896;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-strong,
|
||||||
|
.hljs-emphasis,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #df5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-type {
|
||||||
|
color: #a71d5d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-attribute {
|
||||||
|
color: #0086b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-name {
|
||||||
|
color: #63a35c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-tag {
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-selector-attr,
|
||||||
|
.hljs-selector-pseudo {
|
||||||
|
color: #795da3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-addition {
|
||||||
|
color: #55a532;
|
||||||
|
background-color: #eaffea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion {
|
||||||
|
color: #bd2c00;
|
||||||
|
background-color: #ffecec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 代码高亮 */
|
||||||
|
/* PrismJS 1.15.0
|
||||||
|
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||||
|
/**
|
||||||
|
* prism.js default theme for JavaScript, CSS and HTML
|
||||||
|
* Based on dabblet (http://dabblet.com)
|
||||||
|
* @author Lea Verou
|
||||||
|
*/
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: black;
|
||||||
|
background: none;
|
||||||
|
text-shadow: 0 1px white;
|
||||||
|
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::-moz-selection,
|
||||||
|
pre[class*="language-"] ::-moz-selection,
|
||||||
|
code[class*="language-"]::-moz-selection,
|
||||||
|
code[class*="language-"] ::-moz-selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::selection,
|
||||||
|
pre[class*="language-"] ::selection,
|
||||||
|
code[class*="language-"]::selection,
|
||||||
|
code[class*="language-"] ::selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: .5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre)>code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background: #f5f2f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre)>code[class*="language-"] {
|
||||||
|
padding: .1em;
|
||||||
|
border-radius: .3em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.doctype,
|
||||||
|
.token.cdata {
|
||||||
|
color: slategray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.namespace {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.property,
|
||||||
|
.token.tag,
|
||||||
|
.token.boolean,
|
||||||
|
.token.number,
|
||||||
|
.token.constant,
|
||||||
|
.token.symbol,
|
||||||
|
.token.deleted {
|
||||||
|
color: #905;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector,
|
||||||
|
.token.attr-name,
|
||||||
|
.token.string,
|
||||||
|
.token.char,
|
||||||
|
.token.builtin,
|
||||||
|
.token.inserted {
|
||||||
|
color: #690;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.operator,
|
||||||
|
.token.entity,
|
||||||
|
.token.url,
|
||||||
|
.language-css .token.string,
|
||||||
|
.style .token.string {
|
||||||
|
color: #9a6e3a;
|
||||||
|
background: hsla(0, 0%, 100%, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.keyword {
|
||||||
|
color: #07a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.function,
|
||||||
|
.token.class-name {
|
||||||
|
color: #DD4A68;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.regex,
|
||||||
|
.token.important,
|
||||||
|
.token.variable {
|
||||||
|
color: #e90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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=1721815727687') format('woff2'),
|
src: url('iconfont.woff2?t=1722393125520') format('woff2'),
|
||||||
url('iconfont.woff?t=1721815727687') format('woff'),
|
url('iconfont.woff?t=1722393125520') format('woff'),
|
||||||
url('iconfont.ttf?t=1721815727687') format('truetype'),
|
url('iconfont.ttf?t=1722393125520') format('truetype'),
|
||||||
url('iconfont.svg?t=1721815727687#iconfont') format('svg');
|
url('iconfont.svg?t=1722393125520#iconfont') format('svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
|
@ -14,6 +14,462 @@
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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-zuoye1:before {
|
||||||
|
content: "\e610";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-xiazai9:before {
|
.icon-xiazai9:before {
|
||||||
content: "\e60b";
|
content: "\e60b";
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,804 @@
|
||||||
"css_prefix_text": "icon-",
|
"css_prefix_text": "icon-",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"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": "8023423",
|
||||||
|
"name": "作业",
|
||||||
|
"font_class": "zuoye1",
|
||||||
|
"unicode": "e610",
|
||||||
|
"unicode_decimal": 58896
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "720967",
|
"icon_id": "720967",
|
||||||
"name": "更多",
|
"name": "更多",
|
||||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 256 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<svg class="icon file-icon" aria-hidden="true" :style="{'font-size': size + 'px'}">
|
<svg class="icon file-icon" aria-hidden="true" :style="{ 'font-size': size + 'px' }">
|
||||||
<use :xlink:href="getFileTypeIcon()"></use>
|
<use :xlink:href="getFileTypeIcon()"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
@ -36,10 +36,15 @@ const getFileTypeIcon = () => {
|
||||||
rar: 'icon-rar',
|
rar: 'icon-rar',
|
||||||
|
|
||||||
}
|
}
|
||||||
return '#' + iconObj[name]
|
if (iconObj[name]) {
|
||||||
|
return '#' + iconObj[name]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return '#icon-zuoye1'
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
</style>
|
|
|
@ -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,26 @@ 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;
|
||||||
|
if (localStorage.getItem('evaluationList')) {
|
||||||
|
evaluationList.value = JSON.parse(localStorage.getItem('evaluationList'))
|
||||||
|
data = evaluationList.value
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const { rows } = await listEvaluation(params)
|
||||||
|
localStorage.setItem('evaluationList', JSON.stringify(rows))
|
||||||
|
evaluationList.value = rows
|
||||||
|
data = rows
|
||||||
|
}
|
||||||
|
|
||||||
//获取教材版本
|
//获取教材版本
|
||||||
getSubject()
|
getSubject()
|
||||||
|
@ -121,20 +141,36 @@ const getSubjectContent = () => {
|
||||||
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))
|
||||||
|
}
|
||||||
|
|
||||||
// 默认第一个
|
// 默认第一个
|
||||||
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)
|
||||||
treeData.value = upData.length ? upData : downData
|
treeData.value = upData.length ? upData : downData
|
||||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
|
||||||
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 +333,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,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){
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
|
@ -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,13 +20,17 @@
|
||||||
</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>
|
||||||
|
@ -44,13 +54,36 @@ 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', {
|
||||||
|
fullPath: fullPath,
|
||||||
|
cookieData: { ...configObj.data }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*const menus = ref([
|
||||||
|
{
|
||||||
|
icon: 'icon-zhuye2 icon-homepage',
|
||||||
|
name: '主页',
|
||||||
|
path: '/homepage'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: 'icon-jiaoxueziyuan icon-resource',
|
icon: 'icon-jiaoxueziyuan icon-resource',
|
||||||
name: '资源',
|
name: '资源',
|
||||||
|
@ -61,12 +94,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(
|
||||||
|
@ -77,20 +110,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,20 +131,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(() => {
|
||||||
|
// router.replace('/login')
|
||||||
|
ipcRenderer && ipcRenderer.send('openLoginWindow')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}).catch(() => { });
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits(['setLayout'])
|
const emits = defineEmits(['setLayout'])
|
||||||
function setLayout() {
|
function setLayout() {
|
||||||
emits('setLayout');
|
emits('setLayout')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -155,6 +192,10 @@ function setLayout() {
|
||||||
color: #f99b53;
|
color: #f99b53;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-homepage {
|
||||||
|
color: #0a84ff;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-prepare {
|
.icon-prepare {
|
||||||
color: #b088e8;
|
color: #b088e8;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +211,7 @@ function setLayout() {
|
||||||
|
|
||||||
.active-li {
|
.active-li {
|
||||||
background: #d3e3fb;
|
background: #d3e3fb;
|
||||||
color: #409EFF;
|
color: #409eff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +235,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;
|
||||||
|
|
|
@ -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,17 +1,784 @@
|
||||||
/**
|
/**
|
||||||
* @description 封装fabric js
|
* @description 封装fabric js
|
||||||
*/
|
*/
|
||||||
// import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { fabric } from 'fabric-with-erasing'
|
// import { fabric } from 'fabric-with-erasing'
|
||||||
|
// import fabric from './fabric'
|
||||||
|
// import * as fabric from 'fabric'
|
||||||
import { diff, unpatch, patch } from 'jsondiffpatch'
|
import { diff, unpatch, patch } from 'jsondiffpatch'
|
||||||
|
|
||||||
|
function baseBrush() {
|
||||||
|
/** ERASER_START */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add `eraser` to enlivened props
|
||||||
|
*/
|
||||||
|
fabric.Object.ENLIVEN_PROPS.push('eraser');
|
||||||
|
|
||||||
|
var __drawClipPath = fabric.Object.prototype._drawClipPath;
|
||||||
|
var _needsItsOwnCache = fabric.Object.prototype.needsItsOwnCache;
|
||||||
|
var _toObject = fabric.Object.prototype.toObject;
|
||||||
|
var _getSvgCommons = fabric.Object.prototype.getSvgCommons;
|
||||||
|
var __createBaseClipPathSVGMarkup = fabric.Object.prototype._createBaseClipPathSVGMarkup;
|
||||||
|
var __createBaseSVGMarkup = fabric.Object.prototype._createBaseSVGMarkup;
|
||||||
|
|
||||||
|
fabric.Object.prototype.cacheProperties.push('eraser');
|
||||||
|
fabric.Object.prototype.stateProperties.push('eraser');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fires erasing:end
|
||||||
|
*/
|
||||||
|
fabric.util.object.extend(fabric.Object.prototype, {
|
||||||
|
/**
|
||||||
|
* Indicates whether this object can be erased by {@link fabric.EraserBrush}
|
||||||
|
* The `deep` option introduces fine grained control over a group's `erasable` property.
|
||||||
|
* When set to `deep` the eraser will erase nested objects if they are erasable, leaving the group and the other objects untouched.
|
||||||
|
* When set to `true` the eraser will erase the entire group. Once the group changes the eraser is propagated to its children for proper functionality.
|
||||||
|
* When set to `false` the eraser will leave all objects including the group untouched.
|
||||||
|
* @tutorial {@link http://fabricjs.com/erasing#erasable_property}
|
||||||
|
* @type boolean | 'deep'
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
erasable: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tutorial {@link http://fabricjs.com/erasing#eraser}
|
||||||
|
* @type fabric.Eraser
|
||||||
|
*/
|
||||||
|
eraser: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
* @returns Boolean
|
||||||
|
*/
|
||||||
|
needsItsOwnCache: function () {
|
||||||
|
return _needsItsOwnCache.call(this) || !!this.eraser;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw eraser above clip path
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
* @param {fabric.Object} clipPath
|
||||||
|
*/
|
||||||
|
_drawClipPath: function (ctx, clipPath) {
|
||||||
|
__drawClipPath.call(this, ctx, clipPath);
|
||||||
|
if (this.eraser) {
|
||||||
|
// update eraser size to match instance
|
||||||
|
var size = this._getNonTransformedDimensions();
|
||||||
|
this.eraser.isType('eraser') && this.eraser.set({
|
||||||
|
width: size.x,
|
||||||
|
height: size.y
|
||||||
|
});
|
||||||
|
__drawClipPath.call(this, ctx, this.eraser);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object representation of an instance
|
||||||
|
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
|
||||||
|
* @return {Object} Object representation of an instance
|
||||||
|
*/
|
||||||
|
toObject: function (propertiesToInclude) {
|
||||||
|
var object = _toObject.call(this, ['erasable'].concat(propertiesToInclude));
|
||||||
|
if (this.eraser && !this.eraser.excludeFromExport) {
|
||||||
|
object.eraser = this.eraser.toObject(propertiesToInclude);
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
},
|
||||||
|
|
||||||
|
/* _TO_SVG_START_ */
|
||||||
|
/**
|
||||||
|
* Returns id attribute for svg output
|
||||||
|
* @override
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
getSvgCommons: function () {
|
||||||
|
return _getSvgCommons.call(this) + (this.eraser ? 'mask="url(#' + this.eraser.clipPathId + ')" ' : '');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create svg markup for eraser
|
||||||
|
* use <mask> to achieve erasing for svg, credit: https://travishorn.com/removing-parts-of-shapes-in-svg-b539a89e5649
|
||||||
|
* must be called before object markup creation as it relies on the `clipPathId` property of the mask
|
||||||
|
* @param {Function} [reviver]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
_createEraserSVGMarkup: function (reviver) {
|
||||||
|
if (this.eraser) {
|
||||||
|
this.eraser.clipPathId = 'MASK_' + fabric.Object.__uid++;
|
||||||
|
return [
|
||||||
|
'<mask id="', this.eraser.clipPathId, '" >',
|
||||||
|
this.eraser.toSVG(reviver),
|
||||||
|
'</mask>', '\n'
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_createBaseClipPathSVGMarkup: function (objectMarkup, options) {
|
||||||
|
return [
|
||||||
|
this._createEraserSVGMarkup(options && options.reviver),
|
||||||
|
__createBaseClipPathSVGMarkup.call(this, objectMarkup, options)
|
||||||
|
].join('');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_createBaseSVGMarkup: function (objectMarkup, options) {
|
||||||
|
return [
|
||||||
|
this._createEraserSVGMarkup(options && options.reviver),
|
||||||
|
__createBaseSVGMarkup.call(this, objectMarkup, options)
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
/* _TO_SVG_END_ */
|
||||||
|
});
|
||||||
|
|
||||||
|
var __restoreObjectsState = fabric.Group.prototype._restoreObjectsState;
|
||||||
|
fabric.util.object.extend(fabric.Group.prototype, {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {fabric.Path} path
|
||||||
|
*/
|
||||||
|
_addEraserPathToObjects: function (path) {
|
||||||
|
this._objects.forEach(function (object) {
|
||||||
|
fabric.EraserBrush.prototype._addPathToObjectEraser.call(
|
||||||
|
fabric.EraserBrush.prototype,
|
||||||
|
object,
|
||||||
|
path
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the group's eraser to its objects
|
||||||
|
* @tutorial {@link http://fabricjs.com/erasing#erasable_property}
|
||||||
|
*/
|
||||||
|
applyEraserToObjects: function () {
|
||||||
|
var _this = this, eraser = this.eraser;
|
||||||
|
if (eraser) {
|
||||||
|
delete this.eraser;
|
||||||
|
var transform = _this.calcTransformMatrix();
|
||||||
|
eraser.clone(function (eraser) {
|
||||||
|
var clipPath = _this.clipPath;
|
||||||
|
eraser.getObjects('path')
|
||||||
|
.forEach(function (path) {
|
||||||
|
// first we transform the path from the group's coordinate system to the canvas'
|
||||||
|
var originalTransform = fabric.util.multiplyTransformMatrices(
|
||||||
|
transform,
|
||||||
|
path.calcTransformMatrix()
|
||||||
|
);
|
||||||
|
fabric.util.applyTransformToObject(path, originalTransform);
|
||||||
|
if (clipPath) {
|
||||||
|
clipPath.clone(function (_clipPath) {
|
||||||
|
var eraserPath = fabric.EraserBrush.prototype.applyClipPathToPath.call(
|
||||||
|
fabric.EraserBrush.prototype,
|
||||||
|
path,
|
||||||
|
_clipPath,
|
||||||
|
transform
|
||||||
|
);
|
||||||
|
_this._addEraserPathToObjects(eraserPath);
|
||||||
|
}, ['absolutePositioned', 'inverted']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_this._addEraserPathToObjects(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagate the group's eraser to its objects, crucial for proper functionality of the eraser within the group and nested objects.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_restoreObjectsState: function () {
|
||||||
|
this.erasable === true && this.applyEraserToObjects();
|
||||||
|
return __restoreObjectsState.call(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object's Eraser
|
||||||
|
* @private
|
||||||
|
* @class fabric.Eraser
|
||||||
|
* @extends fabric.Group
|
||||||
|
* @memberof fabric
|
||||||
|
*/
|
||||||
|
fabric.Eraser = fabric.util.createClass(fabric.Group, {
|
||||||
|
/**
|
||||||
|
* @readonly
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
type: 'eraser',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
originX: 'center',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
originY: 'center',
|
||||||
|
|
||||||
|
drawObject: function (ctx) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = 'black';
|
||||||
|
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
|
||||||
|
ctx.restore();
|
||||||
|
this.callSuper('drawObject', ctx);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* eraser should retain size
|
||||||
|
* dimensions should not change when paths are added or removed
|
||||||
|
* handled by {@link fabric.Object#_drawClipPath}
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getBounds: function () {
|
||||||
|
// noop
|
||||||
|
},
|
||||||
|
|
||||||
|
/* _TO_SVG_START_ */
|
||||||
|
/**
|
||||||
|
* Returns svg representation of an instance
|
||||||
|
* use <mask> to achieve erasing for svg, credit: https://travishorn.com/removing-parts-of-shapes-in-svg-b539a89e5649
|
||||||
|
* for masking we need to add a white rect before all paths
|
||||||
|
*
|
||||||
|
* @param {Function} [reviver] Method for further parsing of svg representation.
|
||||||
|
* @return {String} svg representation of an instance
|
||||||
|
*/
|
||||||
|
_toSVG: function (reviver) {
|
||||||
|
var svgString = ['<g ', 'COMMON_PARTS', ' >\n'];
|
||||||
|
var x = -this.width / 2, y = -this.height / 2;
|
||||||
|
var rectSvg = [
|
||||||
|
'<rect ', 'fill="white" ',
|
||||||
|
'x="', x, '" y="', y,
|
||||||
|
'" width="', this.width, '" height="', this.height,
|
||||||
|
'" />\n'
|
||||||
|
].join('');
|
||||||
|
svgString.push('\t\t', rectSvg);
|
||||||
|
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||||
|
svgString.push('\t\t', this._objects[i].toSVG(reviver));
|
||||||
|
}
|
||||||
|
svgString.push('</g>\n');
|
||||||
|
return svgString;
|
||||||
|
},
|
||||||
|
/* _TO_SVG_END_ */
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@link fabric.Eraser} instance from an object representation
|
||||||
|
* @static
|
||||||
|
* @memberOf fabric.Eraser
|
||||||
|
* @param {Object} object Object to create an Eraser from
|
||||||
|
* @param {Function} [callback] Callback to invoke when an eraser instance is created
|
||||||
|
*/
|
||||||
|
fabric.Eraser.fromObject = function (object, callback) {
|
||||||
|
var objects = object.objects;
|
||||||
|
fabric.util.enlivenObjects(objects, function (enlivenedObjects) {
|
||||||
|
var options = fabric.util.object.clone(object, true);
|
||||||
|
delete options.objects;
|
||||||
|
fabric.util.enlivenObjectEnlivables(object, options, function () {
|
||||||
|
callback && callback(new fabric.Eraser(enlivenedObjects, options, true));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var __renderOverlay = fabric.Canvas.prototype._renderOverlay;
|
||||||
|
/**
|
||||||
|
* @fires erasing:start
|
||||||
|
* @fires erasing:end
|
||||||
|
*/
|
||||||
|
fabric.util.object.extend(fabric.Canvas.prototype, {
|
||||||
|
/**
|
||||||
|
* Used by {@link #renderAll}
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
isErasing: function () {
|
||||||
|
return (
|
||||||
|
this.isDrawingMode &&
|
||||||
|
this.freeDrawingBrush &&
|
||||||
|
this.freeDrawingBrush.type === 'eraser' &&
|
||||||
|
this.freeDrawingBrush._isErasing
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* While erasing the brush clips out the erasing path from canvas
|
||||||
|
* so we need to render it on top of canvas every render
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
*/
|
||||||
|
_renderOverlay: function (ctx) {
|
||||||
|
__renderOverlay.call(this, ctx);
|
||||||
|
if (this.isErasing() && !this.freeDrawingBrush.inverted) {
|
||||||
|
this.freeDrawingBrush._render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EraserBrush class
|
||||||
|
* Supports selective erasing meaning that only erasable objects are affected by the eraser brush.
|
||||||
|
* Supports **inverted** erasing meaning that the brush can "undo" erasing.
|
||||||
|
*
|
||||||
|
* In order to support selective erasing, the brush clips the entire canvas
|
||||||
|
* and then draws all non-erasable objects over the erased path using a pattern brush so to speak (masking).
|
||||||
|
* If brush is **inverted** there is no need to clip canvas. The brush draws all erasable objects without their eraser.
|
||||||
|
* This achieves the desired effect of seeming to erase or unerase only erasable objects.
|
||||||
|
* After erasing is done the created path is added to all intersected objects' `eraser` property.
|
||||||
|
*
|
||||||
|
* In order to update the EraserBrush call `preparePattern`.
|
||||||
|
* It may come in handy when canvas changes during erasing (i.e animations) and you want the eraser to reflect the changes.
|
||||||
|
*
|
||||||
|
* @tutorial {@link http://fabricjs.com/erasing}
|
||||||
|
* @class fabric.EraserBrush
|
||||||
|
* @extends fabric.PencilBrush
|
||||||
|
* @memberof fabric
|
||||||
|
*/
|
||||||
|
fabric.EraserBrush = fabric.util.createClass(
|
||||||
|
fabric.PencilBrush,
|
||||||
|
/** @lends fabric.EraserBrush.prototype */ {
|
||||||
|
type: 'eraser',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to `true` the brush will create a visual effect of undoing erasing
|
||||||
|
*/
|
||||||
|
inverted: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_isErasing: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {fabric.Object} object
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
_isErasable: function (object) {
|
||||||
|
return object.erasable !== false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* This is designed to support erasing a collection with both erasable and non-erasable objects.
|
||||||
|
* Iterates over collections to allow nested selective erasing.
|
||||||
|
* Prepares the pattern brush that will draw on the top context to achieve the desired visual effect.
|
||||||
|
* If brush is **NOT** inverted render all non-erasable objects.
|
||||||
|
* If brush is inverted render all erasable objects that have been erased with their clip path inverted.
|
||||||
|
* This will render the erased parts as if they were not erased.
|
||||||
|
*
|
||||||
|
* @param {fabric.Collection} collection
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
* @param {{ visibility: fabric.Object[], eraser: fabric.Object[], collection: fabric.Object[] }} restorationContext
|
||||||
|
*/
|
||||||
|
_prepareCollectionTraversal: function (collection, ctx, restorationContext) {
|
||||||
|
collection.forEachObject(function (obj) {
|
||||||
|
if (obj.forEachObject && obj.erasable === 'deep') {
|
||||||
|
// traverse
|
||||||
|
this._prepareCollectionTraversal(obj, ctx, restorationContext);
|
||||||
|
}
|
||||||
|
else if (!this.inverted && obj.erasable && obj.visible) {
|
||||||
|
// render only non-erasable objects
|
||||||
|
obj.visible = false;
|
||||||
|
collection.dirty = true;
|
||||||
|
restorationContext.visibility.push(obj);
|
||||||
|
restorationContext.collection.push(collection);
|
||||||
|
}
|
||||||
|
else if (this.inverted && obj.visible) {
|
||||||
|
// render only erasable objects that were erased
|
||||||
|
if (obj.erasable && obj.eraser) {
|
||||||
|
obj.eraser.inverted = true;
|
||||||
|
obj.dirty = true;
|
||||||
|
collection.dirty = true;
|
||||||
|
restorationContext.eraser.push(obj);
|
||||||
|
restorationContext.collection.push(collection);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj.visible = false;
|
||||||
|
collection.dirty = true;
|
||||||
|
restorationContext.visibility.push(obj);
|
||||||
|
restorationContext.collection.push(collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the pattern for the erasing brush
|
||||||
|
* This pattern will be drawn on the top context, achieving a visual effect of erasing only erasable objects
|
||||||
|
* @todo decide how overlay color should behave when `inverted === true`, currently draws over it which is undesirable
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
preparePattern: function () {
|
||||||
|
if (!this._patternCanvas) {
|
||||||
|
this._patternCanvas = fabric.util.createCanvasElement();
|
||||||
|
}
|
||||||
|
var canvas = this._patternCanvas;
|
||||||
|
canvas.width = this.canvas.width;
|
||||||
|
canvas.height = this.canvas.height;
|
||||||
|
var patternCtx = canvas.getContext('2d');
|
||||||
|
if (this.canvas._isRetinaScaling()) {
|
||||||
|
var retinaScaling = this.canvas.getRetinaScaling();
|
||||||
|
this.canvas.__initRetinaScaling(retinaScaling, canvas, patternCtx);
|
||||||
|
}
|
||||||
|
var backgroundImage = this.canvas.backgroundImage,
|
||||||
|
bgErasable = backgroundImage && this._isErasable(backgroundImage),
|
||||||
|
overlayImage = this.canvas.overlayImage,
|
||||||
|
overlayErasable = overlayImage && this._isErasable(overlayImage);
|
||||||
|
if (!this.inverted && ((backgroundImage && !bgErasable) || !!this.canvas.backgroundColor)) {
|
||||||
|
if (bgErasable) { this.canvas.backgroundImage = undefined; }
|
||||||
|
this.canvas._renderBackground(patternCtx);
|
||||||
|
if (bgErasable) { this.canvas.backgroundImage = backgroundImage; }
|
||||||
|
}
|
||||||
|
else if (this.inverted && (backgroundImage && bgErasable)) {
|
||||||
|
var color = this.canvas.backgroundColor;
|
||||||
|
this.canvas.backgroundColor = undefined;
|
||||||
|
this.canvas._renderBackground(patternCtx);
|
||||||
|
this.canvas.backgroundColor = color;
|
||||||
|
}
|
||||||
|
patternCtx.save();
|
||||||
|
patternCtx.transform.apply(patternCtx, this.canvas.viewportTransform);
|
||||||
|
var restorationContext = { visibility: [], eraser: [], collection: [] };
|
||||||
|
this._prepareCollectionTraversal(this.canvas, patternCtx, restorationContext);
|
||||||
|
this.canvas._renderObjects(patternCtx, this.canvas._objects);
|
||||||
|
restorationContext.visibility.forEach(function (obj) { obj.visible = true; });
|
||||||
|
restorationContext.eraser.forEach(function (obj) {
|
||||||
|
obj.eraser.inverted = false;
|
||||||
|
obj.dirty = true;
|
||||||
|
});
|
||||||
|
restorationContext.collection.forEach(function (obj) { obj.dirty = true; });
|
||||||
|
patternCtx.restore();
|
||||||
|
if (!this.inverted && ((overlayImage && !overlayErasable) || !!this.canvas.overlayColor)) {
|
||||||
|
if (overlayErasable) { this.canvas.overlayImage = undefined; }
|
||||||
|
__renderOverlay.call(this.canvas, patternCtx);
|
||||||
|
if (overlayErasable) { this.canvas.overlayImage = overlayImage; }
|
||||||
|
}
|
||||||
|
else if (this.inverted && (overlayImage && overlayErasable)) {
|
||||||
|
var color = this.canvas.overlayColor;
|
||||||
|
this.canvas.overlayColor = undefined;
|
||||||
|
__renderOverlay.call(this.canvas, patternCtx);
|
||||||
|
this.canvas.overlayColor = color;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets brush styles
|
||||||
|
* @private
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
*/
|
||||||
|
_setBrushStyles: function (ctx) {
|
||||||
|
this.callSuper('_setBrushStyles', ctx);
|
||||||
|
ctx.strokeStyle = 'black';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Customiztion**
|
||||||
|
*
|
||||||
|
* if you need the eraser to update on each render (i.e animating during erasing) override this method by **adding** the following (performance may suffer):
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* if(ctx === this.canvas.contextTop) {
|
||||||
|
* this.preparePattern();
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @override fabric.BaseBrush#_saveAndTransform
|
||||||
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
*/
|
||||||
|
_saveAndTransform: function (ctx) {
|
||||||
|
this.callSuper('_saveAndTransform', ctx);
|
||||||
|
this._setBrushStyles(ctx);
|
||||||
|
ctx.globalCompositeOperation = ctx === this.canvas.getContext() ? 'destination-out' : 'source-over';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We indicate {@link fabric.PencilBrush} to repaint itself if necessary
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
needsFullRender: function () {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {fabric.Point} pointer
|
||||||
|
* @param {fabric.IEvent} options
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
onMouseDown: function (pointer, options) {
|
||||||
|
if (!this.canvas._isMainEvent(options.e)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._prepareForDrawing(pointer);
|
||||||
|
// capture coordinates immediately
|
||||||
|
// this allows to draw dots (when movement never occurs)
|
||||||
|
this._captureDrawingPath(pointer);
|
||||||
|
|
||||||
|
// prepare for erasing
|
||||||
|
this.preparePattern();
|
||||||
|
this._isErasing = true;
|
||||||
|
this.canvas.fire('erasing:start');
|
||||||
|
this._render();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendering Logic:
|
||||||
|
* 1. Use brush to clip canvas by rendering it on top of canvas (unnecessary if `inverted === true`)
|
||||||
|
* 2. Render brush with canvas pattern on top context
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_render: function () {
|
||||||
|
var ctx;
|
||||||
|
if (!this.inverted) {
|
||||||
|
// clip canvas
|
||||||
|
ctx = this.canvas.getContext();
|
||||||
|
this.callSuper('_render', ctx);
|
||||||
|
}
|
||||||
|
// render brush and mask it with image of non erasables
|
||||||
|
ctx = this.canvas.contextTop;
|
||||||
|
this.canvas.clearContext(ctx);
|
||||||
|
this.callSuper('_render', ctx);
|
||||||
|
ctx.save();
|
||||||
|
var t = this.canvas.getRetinaScaling(), s = 1 / t;
|
||||||
|
ctx.scale(s, s);
|
||||||
|
ctx.globalCompositeOperation = 'source-in';
|
||||||
|
ctx.drawImage(this._patternCanvas, 0, 0);
|
||||||
|
ctx.restore();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates fabric.Path object
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @param {(string|number)[][]} pathData Path data
|
||||||
|
* @return {fabric.Path} Path to add on canvas
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
createPath: function (pathData) {
|
||||||
|
var path = this.callSuper('createPath', pathData);
|
||||||
|
path.globalCompositeOperation = this.inverted ? 'source-over' : 'destination-out';
|
||||||
|
path.stroke = this.inverted ? 'white' : 'black';
|
||||||
|
return path;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to apply a clip path to a path.
|
||||||
|
* Used to preserve clipping on eraser paths in nested objects.
|
||||||
|
* Called when a group has a clip path that should be applied to the path before applying erasing on the group's objects.
|
||||||
|
* @param {fabric.Path} path The eraser path in canvas coordinate plane
|
||||||
|
* @param {fabric.Object} clipPath The clipPath to apply to the path
|
||||||
|
* @param {number[]} clipPathContainerTransformMatrix The transform matrix of the object that the clip path belongs to
|
||||||
|
* @returns {fabric.Path} path with clip path
|
||||||
|
*/
|
||||||
|
applyClipPathToPath: function (path, clipPath, clipPathContainerTransformMatrix) {
|
||||||
|
var pathInvTransform = fabric.util.invertTransform(path.calcTransformMatrix()),
|
||||||
|
clipPathTransform = clipPath.calcTransformMatrix(),
|
||||||
|
transform = clipPath.absolutePositioned ?
|
||||||
|
pathInvTransform :
|
||||||
|
fabric.util.multiplyTransformMatrices(
|
||||||
|
pathInvTransform,
|
||||||
|
clipPathContainerTransformMatrix
|
||||||
|
);
|
||||||
|
// when passing down a clip path it becomes relative to the parent
|
||||||
|
// so we transform it acoordingly and set `absolutePositioned` to false
|
||||||
|
clipPath.absolutePositioned = false;
|
||||||
|
fabric.util.applyTransformToObject(
|
||||||
|
clipPath,
|
||||||
|
fabric.util.multiplyTransformMatrices(
|
||||||
|
transform,
|
||||||
|
clipPathTransform
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// We need to clip `path` with both `clipPath` and it's own clip path if existing (`path.clipPath`)
|
||||||
|
// so in turn `path` erases an object only where it overlaps with all it's clip paths, regardless of how many there are.
|
||||||
|
// this is done because both clip paths may have nested clip paths of their own (this method walks down a collection => this may reccur),
|
||||||
|
// so we can't assign one to the other's clip path property.
|
||||||
|
path.clipPath = path.clipPath ? fabric.util.mergeClipPaths(clipPath, path.clipPath) : clipPath;
|
||||||
|
return path;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to apply a clip path to a path.
|
||||||
|
* Used to preserve clipping on eraser paths in nested objects.
|
||||||
|
* Called when a group has a clip path that should be applied to the path before applying erasing on the group's objects.
|
||||||
|
* @param {fabric.Path} path The eraser path
|
||||||
|
* @param {fabric.Object} object The clipPath to apply to path belongs to object
|
||||||
|
* @param {Function} callback Callback to be invoked with the cloned path after applying the clip path
|
||||||
|
*/
|
||||||
|
clonePathWithClipPath: function (path, object, callback) {
|
||||||
|
var objTransform = object.calcTransformMatrix();
|
||||||
|
var clipPath = object.clipPath;
|
||||||
|
var _this = this;
|
||||||
|
path.clone(function (_path) {
|
||||||
|
clipPath.clone(function (_clipPath) {
|
||||||
|
callback(_this.applyClipPathToPath(_path, _clipPath, objTransform));
|
||||||
|
}, ['absolutePositioned', 'inverted']);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds path to object's eraser, walks down object's descendants if necessary
|
||||||
|
*
|
||||||
|
* @fires erasing:end on object
|
||||||
|
* @param {fabric.Object} obj
|
||||||
|
* @param {fabric.Path} path
|
||||||
|
*/
|
||||||
|
_addPathToObjectEraser: function (obj, path) {
|
||||||
|
var _this = this;
|
||||||
|
// object is collection, i.e group
|
||||||
|
if (obj.forEachObject && obj.erasable === 'deep') {
|
||||||
|
var targets = obj._objects.filter(function (_obj) {
|
||||||
|
return _obj.erasable;
|
||||||
|
});
|
||||||
|
if (targets.length > 0 && obj.clipPath) {
|
||||||
|
this.clonePathWithClipPath(path, obj, function (_path) {
|
||||||
|
targets.forEach(function (_obj) {
|
||||||
|
_this._addPathToObjectEraser(_obj, _path);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (targets.length > 0) {
|
||||||
|
targets.forEach(function (_obj) {
|
||||||
|
_this._addPathToObjectEraser(_obj, path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// prepare eraser
|
||||||
|
var eraser = obj.eraser;
|
||||||
|
if (!eraser) {
|
||||||
|
eraser = new fabric.Eraser();
|
||||||
|
obj.eraser = eraser;
|
||||||
|
}
|
||||||
|
// clone and add path
|
||||||
|
path.clone(function (path) {
|
||||||
|
// http://fabricjs.com/using-transformations
|
||||||
|
var desiredTransform = fabric.util.multiplyTransformMatrices(
|
||||||
|
fabric.util.invertTransform(
|
||||||
|
obj.calcTransformMatrix()
|
||||||
|
),
|
||||||
|
path.calcTransformMatrix()
|
||||||
|
);
|
||||||
|
fabric.util.applyTransformToObject(path, desiredTransform);
|
||||||
|
eraser.addWithUpdate(path);
|
||||||
|
obj.set('dirty', true);
|
||||||
|
obj.fire('erasing:end', {
|
||||||
|
path: path
|
||||||
|
});
|
||||||
|
if (obj.group && Array.isArray(_this.__subTargets)) {
|
||||||
|
_this.__subTargets.push(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the eraser path to canvas drawables' clip paths
|
||||||
|
*
|
||||||
|
* @param {fabric.Canvas} source
|
||||||
|
* @param {fabric.Canvas} path
|
||||||
|
* @returns {Object} canvas drawables that were erased by the path
|
||||||
|
*/
|
||||||
|
applyEraserToCanvas: function (path) {
|
||||||
|
var canvas = this.canvas;
|
||||||
|
var drawables = {};
|
||||||
|
[
|
||||||
|
'backgroundImage',
|
||||||
|
'overlayImage',
|
||||||
|
].forEach(function (prop) {
|
||||||
|
var drawable = canvas[prop];
|
||||||
|
if (drawable && drawable.erasable) {
|
||||||
|
this._addPathToObjectEraser(drawable, path);
|
||||||
|
drawables[prop] = drawable;
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
return drawables;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On mouseup after drawing the path on contextTop canvas
|
||||||
|
* we use the points captured to create an new fabric path object
|
||||||
|
* and add it to every intersected erasable object.
|
||||||
|
*/
|
||||||
|
_finalizeAndAddPath: function () {
|
||||||
|
var ctx = this.canvas.contextTop, canvas = this.canvas;
|
||||||
|
ctx.closePath();
|
||||||
|
if (this.decimate) {
|
||||||
|
this._points = this.decimatePoints(this._points, this.decimate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear
|
||||||
|
canvas.clearContext(canvas.contextTop);
|
||||||
|
this._isErasing = false;
|
||||||
|
|
||||||
|
var pathData = this._points && this._points.length > 1 ?
|
||||||
|
this.convertPointsToSVGPath(this._points) :
|
||||||
|
null;
|
||||||
|
if (!pathData || this._isEmptySVGPath(pathData)) {
|
||||||
|
canvas.fire('erasing:end');
|
||||||
|
// do not create 0 width/height paths, as they are
|
||||||
|
// rendered inconsistently across browsers
|
||||||
|
// Firefox 4, for example, renders a dot,
|
||||||
|
// whereas Chrome 10 renders nothing
|
||||||
|
canvas.requestRenderAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = this.createPath(pathData);
|
||||||
|
// needed for `intersectsWithObject`
|
||||||
|
path.setCoords();
|
||||||
|
// commense event sequence
|
||||||
|
canvas.fire('before:path:created', { path: path });
|
||||||
|
|
||||||
|
// finalize erasing
|
||||||
|
var drawables = this.applyEraserToCanvas(path);
|
||||||
|
var _this = this;
|
||||||
|
this.__subTargets = [];
|
||||||
|
var targets = [];
|
||||||
|
canvas.forEachObject(function (obj) {
|
||||||
|
if (obj.erasable && obj.intersectsWithObject(path, true, true)) {
|
||||||
|
_this._addPathToObjectEraser(obj, path);
|
||||||
|
targets.push(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// fire erasing:end
|
||||||
|
canvas.fire('erasing:end', {
|
||||||
|
path: path,
|
||||||
|
targets: targets,
|
||||||
|
subTargets: this.__subTargets,
|
||||||
|
drawables: drawables
|
||||||
|
});
|
||||||
|
delete this.__subTargets;
|
||||||
|
|
||||||
|
canvas.requestRenderAll();
|
||||||
|
this._resetShadow();
|
||||||
|
|
||||||
|
// fire event 'path' created
|
||||||
|
canvas.fire('path:created', { path: path });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/** ERASER_END */
|
||||||
|
}
|
||||||
|
baseBrush() // 加载橡皮擦到组件上
|
||||||
// 当前使用到的常量|类型(枚举) ============================
|
// 当前使用到的常量|类型(枚举) ============================
|
||||||
export class TYPES {
|
export class TYPES {
|
||||||
static ActionMode = {
|
static ActionMode = {
|
||||||
DRAW: 'draw', // 画笔模式
|
DRAW: 'draw', // 画笔模式
|
||||||
ERASE: 'erase', // 橡皮擦模式
|
ERASE: 'erase', // 橡皮擦模式
|
||||||
SELECT: 'select', // 选择模式
|
SELECT: 'select', // 选择模式
|
||||||
Board: 'board' // 画板模式
|
Board: 'board', // 画板模式
|
||||||
|
OTHER: 'other', // 其他模式
|
||||||
}
|
}
|
||||||
// 画笔类型
|
// 画笔类型
|
||||||
static DrawType = {
|
static DrawType = {
|
||||||
|
@ -378,10 +1145,21 @@ export const FreeStyle = {
|
||||||
const canvas = fabricVue?.canvas
|
const canvas = fabricVue?.canvas
|
||||||
const drawConfig = fabricVue?.drawConfig
|
const drawConfig = fabricVue?.drawConfig
|
||||||
const eraserBrush = new fabric.EraserBrush(canvas)
|
const eraserBrush = new fabric.EraserBrush(canvas)
|
||||||
|
const width = Utils.getWidth(drawConfig.eraserWidth, fabricVue)
|
||||||
canvas.isDrawingMode = true
|
canvas.isDrawingMode = true
|
||||||
canvas.freeDrawingBrush = eraserBrush
|
canvas.freeDrawingBrush = eraserBrush
|
||||||
canvas.freeDrawingBrush.width = Utils.getWidth(drawConfig.eraserWidth, fabricVue)
|
canvas.freeDrawingBrush.width = width
|
||||||
canvas.freeDrawingBrush.color = '#FFF'
|
canvas.freeDrawingBrush.color = '#FFF'
|
||||||
|
// FabricVue.canvas.freeDrawingCursor = `url(/imgs/erase.svg) 10 10,crosshair`
|
||||||
|
canvas.freeDrawingCursor = FreeStyle.reaserSvg(width)
|
||||||
|
},
|
||||||
|
reaserSvg: (width, color = '#ccc') => { // 橡皮擦-鼠标样式(自定义)
|
||||||
|
const svg = `
|
||||||
|
<svg style="fill: currentColor;color: ${color};" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1023 366.1L662.3 5.2 585 82.6l-34.4-34.4L69.7 538l34.4 34.4-51.5 34.4c-68.7 68.7-68.7 171.9 0 240.6l120.2 120.3c68.7 68.7 171.8 68.7 240.5 0l42.9-43 34.4 34.4 489.5-489.8-34.4-25.8 77.3-77.4zM662.3 65.4l300.6 300.8-51.5 51.6-300.6-309.5 51.5-42.9zM404.7 924.7c-60.1 60.2-154.6 60.2-214.7 0l-94.5-94.5c-60.1-60.2-60.1-154.7 0-214.8l34.4-17.2L430.5 899l-25.8 25.7z" />
|
||||||
|
</svg>`
|
||||||
|
const svgUrl = `data:image/svg+xml;base64,${btoa(svg)}`
|
||||||
|
return `url(${svgUrl}) ${width/2} ${width}, crosshair`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 事件类
|
// 事件类
|
||||||
|
@ -600,8 +1378,8 @@ export class CanvasEvent {
|
||||||
}
|
}
|
||||||
// 移除事件
|
// 移除事件
|
||||||
removeEvent() {
|
removeEvent() {
|
||||||
this.windowEvent.removeWindowEvent()
|
this.windowEvent?.removeWindowEvent()
|
||||||
this.touchEvent.removeTouchEvent()
|
this.touchEvent?.removeTouchEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 历史类
|
// 历史类
|
||||||
|
@ -624,7 +1402,7 @@ export class History {
|
||||||
const canvas = this.FabricVue?.canvas
|
const canvas = this.FabricVue?.canvas
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
this.diffs = this.diffs.slice(0, this.index)
|
this.diffs = this.diffs.slice(0, this.index)
|
||||||
const canvasJson = Utils.getCanvasJSON()
|
const canvasJson = Utils.getCanvasJSON(canvas)
|
||||||
const delta = diff(canvasJson, this.canvasData)
|
const delta = diff(canvasJson, this.canvasData)
|
||||||
this.diffs.push(delta)
|
this.diffs.push(delta)
|
||||||
|
|
||||||
|
@ -675,18 +1453,18 @@ export class History {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 清除记录
|
||||||
clean() {
|
clean() {
|
||||||
this.FabricVue?.canvas?.clear()
|
this.FabricVue?.canvas?.clear()
|
||||||
this.index = 0
|
this.index = 0
|
||||||
this.diffs = []
|
this.diffs = []
|
||||||
this.canvasData = {}
|
this.canvasData = {}
|
||||||
}
|
}
|
||||||
|
// 重新加载历史记录
|
||||||
initHistory() {
|
initHistory() {
|
||||||
const canvas = this.FabricVue.canvas
|
const canvas = this.FabricVue.canvas
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
const canvasJson = Utils.getCanvasJSON()
|
const canvasJson = Utils.getCanvasJSON(canvas)
|
||||||
this.canvasData = canvasJson
|
this.canvasData = canvasJson
|
||||||
this.index = 0
|
this.index = 0
|
||||||
this.diffs = []
|
this.diffs = []
|
||||||
|
@ -835,6 +1613,10 @@ export class fabricVue {
|
||||||
objectSet.hoverCursor = undefined
|
objectSet.hoverCursor = undefined
|
||||||
this.canvas.selection = true
|
this.canvas.selection = true
|
||||||
break
|
break
|
||||||
|
case TYPES.ActionMode.OTHER: // 其他(工具选择)
|
||||||
|
this.canvas.isDrawingMode = false
|
||||||
|
this.canvas.freeDrawingCursor = 'default'
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -860,7 +1642,7 @@ export class fabricVue {
|
||||||
*/
|
*/
|
||||||
deleteObject() {
|
deleteObject() {
|
||||||
// Disable deletion in text input state
|
// Disable deletion in text input state
|
||||||
if (this.textElement.isTextEditing) {
|
if (this.textElement?.isTextEditing) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.canvas) {
|
if (this.canvas) {
|
||||||
|
|
|
@ -13,8 +13,14 @@ export const constantRoutes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/resource',
|
redirect: '/homepage',
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: '/homepage',
|
||||||
|
component: () => import('@/views/homePage/index.vue'),
|
||||||
|
name: 'homepage',
|
||||||
|
meta: {title: '主页'}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/resource',
|
path: '/resource',
|
||||||
component: () => import('@/views/resource/index.vue'),
|
component: () => import('@/views/resource/index.vue'),
|
||||||
|
|
|
@ -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
|
|
@ -1,101 +1,105 @@
|
||||||
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: () => ({
|
||||||
{
|
token: getToken(),
|
||||||
state: () => ({
|
id: '',
|
||||||
token: getToken(),
|
name: '',
|
||||||
id: '',
|
avatar: '',
|
||||||
name: '',
|
roles: [],
|
||||||
avatar: '',
|
permissions: [],
|
||||||
roles: [],
|
user: {}
|
||||||
permissions: [],
|
}),
|
||||||
user: {}
|
actions: {
|
||||||
}),
|
// 登录
|
||||||
actions: {
|
login(userInfo) {
|
||||||
// 登录
|
const username = userInfo.username.trim()
|
||||||
login(userInfo) {
|
const password = userInfo.password
|
||||||
const username = userInfo.username.trim()
|
const code = userInfo.code
|
||||||
const password = userInfo.password
|
const uuid = userInfo.uuid
|
||||||
const code = userInfo.code
|
return new Promise((resolve, reject) => {
|
||||||
const uuid = userInfo.uuid
|
login(username, password, code, uuid)
|
||||||
return new Promise((resolve, reject) => {
|
.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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 退出系统
|
// 退出系统
|
||||||
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
persist: true
|
persist: true
|
||||||
})
|
})
|
||||||
|
|
||||||
export default useUserStore
|
export default useUserStore
|
||||||
|
|
|
@ -1,47 +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: 'https://file.ysaix.com:7868',
|
url: import.meta.env.VITE_APP_BUILD_BASE_PATH,
|
||||||
|
// 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: {
|
||||||
fullPath: `${baseConfig.url}/teaching/classtaskassign?titleName=%E4%BD%9C%E4%B8%9A%E5%B8%83%E7%BD%AE`
|
data: { ...baseConfig() },
|
||||||
}
|
// 完整路径
|
||||||
|
fullPath: `${baseConfig().url}/teaching/classtaskassign?titleName=%E4%BD%9C%E4%B8%9A%E5%B8%83%E7%BD%AE`
|
||||||
|
},
|
||||||
|
|
||||||
// 高考研读
|
// 作业反馈
|
||||||
const gk = {
|
feedback: {
|
||||||
data: { ...baseConfig},
|
data: { ...baseConfig() },
|
||||||
fullPath: `${baseConfig.url}/education/colentrance`
|
// 完整路径
|
||||||
}
|
fullPath: `${baseConfig().url}/teaching/classtaskassign?titleName=作业反馈`
|
||||||
|
},
|
||||||
|
|
||||||
// 课标研读
|
// 高考研读
|
||||||
const standard = {
|
gk: {
|
||||||
data: { ...baseConfig},
|
data: { ...baseConfig() },
|
||||||
fullPath: `${baseConfig.url}/teaching/chatwithstandard`
|
fullPath: `${baseConfig().url}/education/colentrance`
|
||||||
}
|
},
|
||||||
|
|
||||||
// 教学大模型
|
// 课标研读
|
||||||
const aiModel = {
|
standard: {
|
||||||
data: { ...baseConfig},
|
data: { ...baseConfig() },
|
||||||
fullPath: `${baseConfig.url}/platofai`
|
fullPath: `${baseConfig().url}/teaching/chatwithstandard`
|
||||||
}
|
},
|
||||||
|
|
||||||
export default {
|
// 教学大模型
|
||||||
homeWork,
|
aiModel: {
|
||||||
gk,
|
data: { ...baseConfig() },
|
||||||
standard,
|
fullPath: `${baseConfig().url}/platofai`
|
||||||
aiModel
|
},
|
||||||
|
|
||||||
|
getBaseData: () => {
|
||||||
|
return {
|
||||||
|
data: { ...baseConfig() },
|
||||||
|
fullPath: `${baseConfig().url}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
|
@ -69,9 +69,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) // 设置窗口为全屏
|
||||||
|
@ -110,7 +110,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 +145,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(m)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -175,9 +175,9 @@ const eventHandles = (type, win) => {
|
||||||
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.on('tool-sphere:set:ignore', setIgnore)
|
||||||
// 关闭窗口
|
// 关闭窗口
|
||||||
Remote.ipcMain.once('tool-sphere:close', () => { win.destroy() })
|
Remote.ipcMain.once('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.off('tool-sphere:set:ignore', setIgnore)}
|
||||||
}
|
}
|
||||||
|
@ -185,8 +185,7 @@ const eventHandles = (type, win) => {
|
||||||
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:
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref,defineProps,defineEmits,onMounted} from "vue";
|
import {ref,defineProps,defineEmits} from "vue";
|
||||||
const activeIndex = ref('0')
|
const activeIndex = ref('0')
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
classList:{
|
classList:{
|
||||||
|
@ -43,9 +43,7 @@ const handleSelect = (itemDom,pathKey) => {
|
||||||
// })
|
// })
|
||||||
emits('handleSelect',{index,id})
|
emits('handleSelect',{index,id})
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
|
||||||
console.log(props.classList,'props.classList')
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
<el-col :span="4">未选学生:</el-col>
|
<el-col :span="4">未选学生:</el-col>
|
||||||
<el-col :span="20" class="studentContent">
|
<el-col :span="20" class="studentContent">
|
||||||
<div class="aItem" v-for="(stuItem,stuIndex) in avaliableStudents" :key="stuIndex">
|
<div class="aItem" v-for="(stuItem,stuIndex) in avaliableStudents" :key="stuIndex">
|
||||||
<el-button style="width: 100%" plain @click="chooseItem(stuItem,stuIndex)">{{stuItem.name || stuItem.studentname}}</el-button>
|
<el-button style="width: 100%" plain @click="chooseItem(stuItem,stuIndex)">{{stuItem.studentname || stuItem.name}}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
@ -121,7 +121,7 @@ const groupForm = reactive({
|
||||||
groupname: '',
|
groupname: '',
|
||||||
orderidx: 0,
|
orderidx: 0,
|
||||||
classid: Number(props.classId),
|
classid: Number(props.classId),
|
||||||
edituserid: userStore.id,
|
edituserid: userStore.userId,
|
||||||
edusubject: userStore.edusubject,
|
edusubject: userStore.edusubject,
|
||||||
userid : 0,
|
userid : 0,
|
||||||
studentid : 0,
|
studentid : 0,
|
||||||
|
@ -188,7 +188,7 @@ const getClassInfo = () => {
|
||||||
const chooseItem = (item,stuIndex) => {
|
const chooseItem = (item,stuIndex) => {
|
||||||
let formdata = {}
|
let formdata = {}
|
||||||
formdata.classid = props.classId;
|
formdata.classid = props.classId;
|
||||||
formdata.edituserid = userStore.id;
|
formdata.edituserid = userStore.userId;
|
||||||
formdata.userid = item.userid;
|
formdata.userid = item.userid;
|
||||||
formdata.parentid = parentId.value;
|
formdata.parentid = parentId.value;
|
||||||
formdata.studentid = item.studentid;
|
formdata.studentid = item.studentid;
|
||||||
|
|
|
@ -2,25 +2,19 @@
|
||||||
<el-card style="width: 100%;height: 100%">
|
<el-card style="width: 100%;height: 100%">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div style="text-align: left">
|
<div style="text-align: left">
|
||||||
<el-button v-if="classInfo.teacher.length > 0" type="danger" @click="deleteClassRoom">删除班级</el-button>
|
<el-button type="danger" @click="deleteClassRoom">删除班级</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="classInfo.teacher.length > 0">
|
<el-descriptions :column="1">
|
||||||
<el-descriptions :column="1">
|
<el-descriptions-item label="班级名称">{{ classInfo.caption }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="班级名称">{{ classInfo.className }}</el-descriptions-item>
|
<el-descriptions-item label="教师">
|
||||||
<el-descriptions-item label="教师">
|
<template v-if="classInfo.teacher.length > 0">
|
||||||
<template v-if="classInfo.teacher.length > 0">
|
<el-tag type="primary" v-for="(item, index) in classInfo.teacher" :key="index">{{item.name}}</el-tag>
|
||||||
<el-tag type="primary" v-for="(item, index) in classInfo.teacher" :key="index">{{item.name}}</el-tag>
|
</template>
|
||||||
</template>
|
<template v-else>{{ classInfo.teachername }}</template>
|
||||||
<template v-else>暂无</template>
|
</el-descriptions-item>
|
||||||
</el-descriptions-item>
|
<el-descriptions-item label="学生人数">{{ classInfo.classstudentcount || 0 }}人</el-descriptions-item>
|
||||||
<el-descriptions-item label="学生人数">{{ classInfo.student.length }}人</el-descriptions-item>
|
</el-descriptions>
|
||||||
</el-descriptions>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<el-empty description="暂无班级信息" style="margin: 0 auto"/>
|
|
||||||
</template>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -48,7 +42,7 @@
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
leaveClass({ classid: props.classId, userid: userStore.id, inrole: 'teacher' }).then(() => {
|
leaveClass({ classid: props.classId, userid: userStore.userId, inrole: 'teacher' }).then(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: '删除成功',
|
message: '删除成功',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -63,7 +57,7 @@
|
||||||
const getClassInfo = () => {
|
const getClassInfo = () => {
|
||||||
if(props.classId){
|
if(props.classId){
|
||||||
getClassmain(props.classId).then(response => {
|
getClassmain(props.classId).then(response => {
|
||||||
classInfo.className = response.data.caption
|
Object.assign(classInfo,response.data)
|
||||||
listClassuser({classid:props.classId,pageSize:100}).then(res => {
|
listClassuser({classid:props.classId,pageSize:100}).then(res => {
|
||||||
classInfo.teacher = res.rows.filter(item => item.inrole === 'teacher')
|
classInfo.teacher = res.rows.filter(item => item.inrole === 'teacher')
|
||||||
classInfo.student = res.rows.filter(item => item.inrole === 'student')
|
classInfo.student = res.rows.filter(item => item.inrole === 'student')
|
||||||
|
|
|
@ -145,12 +145,13 @@
|
||||||
// 获取班级信息
|
// 获取班级信息
|
||||||
const getClassInfo = () => {
|
const getClassInfo = () => {
|
||||||
classList.value = []
|
classList.value = []
|
||||||
listClassmain({ entpid: userStore.deptId, pageSize: 500, status: 'open' }).then(response => {
|
listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' }).then(response => {
|
||||||
response.rows.forEach(item => {
|
// response.rows.forEach(item => {
|
||||||
if(item.classteacherids && Number(item.classteacherids) === userStore.userId){
|
// if(item.teacherid && Number(item.teacherid) === userStore.userId){
|
||||||
classList.value.push(item)
|
// classList.value.push(item)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
|
classList.value = [...response.rows]
|
||||||
if(classList.value.length > 0){
|
if(classList.value.length > 0){
|
||||||
classId.value = classList.value[0].id
|
classId.value = classList.value[0].id
|
||||||
currentIndex.value = 0
|
currentIndex.value = 0
|
||||||
|
@ -190,7 +191,7 @@
|
||||||
classForm.entpid = userStore.deptId;
|
classForm.entpid = userStore.deptId;
|
||||||
classForm.status = 'open';
|
classForm.status = 'open';
|
||||||
classForm.teachername = userStore.nickName;
|
classForm.teachername = userStore.nickName;
|
||||||
classForm.teacherid = userStore.id;
|
classForm.teacherid = userStore.userId;
|
||||||
classForm.teacherSubject = classForm.edusubject;
|
classForm.teacherSubject = classForm.edusubject;
|
||||||
addClassmain(classForm).then(response => {
|
addClassmain(classForm).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="height: 100%">
|
<div style="height: 100%">
|
||||||
<el-card style="width: 100%;height: 100%">
|
<el-card style="width: 100%;height: 100%;overflow-y: auto">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div style="text-align: left;display: flex;justify-content: space-between">
|
<div style="text-align: left;display: flex;justify-content: space-between">
|
||||||
<div>
|
<div>
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button type="warning" v-show="studentForm.id > 0" @click="delStudent(1)">移出班级</el-button>
|
<el-button type="warning" v-show="studentForm.id > 0" @click="delStudent(1)">移出班级</el-button>
|
||||||
<el-button type="danger" v-show="studentForm.id > 0 && studentForm.editoruserid == userStore.id" @click="delStudent(2)">删除学生</el-button>
|
<el-button type="danger" v-show="studentForm.id > 0 && studentForm.editoruserid == userStore.userId" @click="delStudent(2)">删除学生</el-button>
|
||||||
<el-button @click="studentVisible = false">取 消</el-button>
|
<el-button @click="studentVisible = false">取 消</el-button>
|
||||||
<el-button type="primary" @click="btnStudentSave">确 定</el-button>
|
<el-button type="primary" @click="btnStudentSave">确 定</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
@ -162,7 +162,7 @@
|
||||||
],
|
],
|
||||||
birthday: [
|
birthday: [
|
||||||
{ required: true, message: '请选择出生日期', trigger: 'blur' }
|
{ required: true, message: '请选择出生日期', trigger: 'blur' }
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取班级学生信息
|
// 获取班级学生信息
|
||||||
|
@ -201,7 +201,7 @@
|
||||||
studentForm.classname = classInfo.value.caption;
|
studentForm.classname = classInfo.value.caption;
|
||||||
studentForm.schoolname = classInfo.value.entpname;
|
studentForm.schoolname = classInfo.value.entpname;
|
||||||
studentForm.status = '';
|
studentForm.status = '';
|
||||||
studentForm.editoruserid = userStore.id;
|
studentForm.editoruserid = userStore.userId;
|
||||||
addStudentmain(studentForm).then((response) => {
|
addStudentmain(studentForm).then((response) => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
|
@ -225,6 +225,10 @@
|
||||||
studentForm.id = 0
|
studentForm.id = 0
|
||||||
studentForm.entpid = 0
|
studentForm.entpid = 0
|
||||||
studentForm.name = ''
|
studentForm.name = ''
|
||||||
|
studentForm.address = ''
|
||||||
|
studentForm.birthday = ''
|
||||||
|
studentForm.parentname = ''
|
||||||
|
studentForm.parentmobile = ''
|
||||||
studentVisible.value = true
|
studentVisible.value = true
|
||||||
}else{
|
}else{
|
||||||
getStudentmain(id).then((response) => {
|
getStudentmain(id).then((response) => {
|
||||||
|
@ -249,7 +253,7 @@
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
leaveClass({ classid: props.classId, studentid: studentForm.id, inrole: 'student' }).then((response) => {
|
leaveClass({ classid: props.classId, studentid: studentForm.id, inrole: 'student' }).then(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message: '移出成功',
|
message: '移出成功',
|
||||||
|
@ -422,7 +426,7 @@
|
||||||
formdata.classname = classInfo.value.caption;
|
formdata.classname = classInfo.value.caption;
|
||||||
formdata.schoolname = classInfo.value.entpname;
|
formdata.schoolname = classInfo.value.entpname;
|
||||||
formdata.status = '';
|
formdata.status = '';
|
||||||
formdata.editoruserid = userStore.id;
|
formdata.editoruserid = userStore.userId;
|
||||||
|
|
||||||
addStudentmainByNameArray(formdata).then(res => {
|
addStudentmainByNameArray(formdata).then(res => {
|
||||||
if(res.code == 200){
|
if(res.code == 200){
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
<template>
|
||||||
|
<el-card style="overflow: auto; height: 100%">
|
||||||
|
<div class="common-layout" style="overflow-y: auto">
|
||||||
|
<el-container>
|
||||||
|
<el-main style="--el-main-padding: 0">
|
||||||
|
<template v-for="(itemFirst, indexFirst) in title" :key="indexFirst">
|
||||||
|
<el-card style="margin-bottom: 10px">
|
||||||
|
<template #header>
|
||||||
|
<div style="text-align: left">{{ itemFirst.name }}</div>
|
||||||
|
</template>
|
||||||
|
<div :class="itemFirst.id === 1 || itemFirst.id === 2 ? 'six' : 'three'">
|
||||||
|
<template v-for="(itemSec, indexSec) in itemFirst.child" :key="indexSec">
|
||||||
|
<el-popover :disabled="itemSec.child1.length === 0" width="auto" trigger="hover">
|
||||||
|
<div style="display: flex; justify-content: space-between">
|
||||||
|
<!-- 鼠标移上去的一列为三级菜单-->
|
||||||
|
<template v-for="(itemThird, indexThird) in itemSec.child1" :key="indexThird">
|
||||||
|
<div
|
||||||
|
style="width: 120px"
|
||||||
|
:class="[
|
||||||
|
itemFirst.id == 1
|
||||||
|
? 'a1'
|
||||||
|
: itemFirst.id == 2
|
||||||
|
? 'a2'
|
||||||
|
: itemFirst.id == 3
|
||||||
|
? 'a3'
|
||||||
|
: 'a4',
|
||||||
|
'CustomBox'
|
||||||
|
]"
|
||||||
|
@click="handleOutLink(itemThird.url)"
|
||||||
|
>
|
||||||
|
<span :class="itemThird.img"></span>
|
||||||
|
<span>{{ itemThird.name }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<!-- 最外层的一列为二级菜单-->
|
||||||
|
<template #reference>
|
||||||
|
<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>{{ itemSec.name }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import outLink from '@/utils/linkConfig'
|
||||||
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
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: '课程研究',
|
||||||
|
img: 'iconfont icon-shuyi_jiaoxueguanli',
|
||||||
|
child1: [
|
||||||
|
{
|
||||||
|
name: '课标分析',
|
||||||
|
url: '/teaching/chatwithstandard',
|
||||||
|
img: 'iconfont icon-kecheng'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '教材分析',
|
||||||
|
url: '/teaching/chatwithtextbook',
|
||||||
|
img: 'iconfont icon-yanjiushi'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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-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 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', {
|
||||||
|
fullPath: fullPath,
|
||||||
|
cookieData: { ...configObj.data }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.CustomTitle {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CustomTitle span:nth-child(1) {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #7b7a7a;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.six {
|
||||||
|
display: flex;
|
||||||
|
& > div {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.three {
|
||||||
|
display: flex;
|
||||||
|
& > div {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.CustomBox span:nth-child(1) {
|
||||||
|
display: flex;
|
||||||
|
width: 60px;
|
||||||
|
justify-content: center;
|
||||||
|
height: 60px;
|
||||||
|
background-color: #ebe9e9;
|
||||||
|
border-radius: 50%;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 32px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CustomBox {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.CustomBox span:nth-child(2) {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.CustomBox div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.a1 span:nth-child(1) {
|
||||||
|
color: #1296db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.a2 span:nth-child(1) {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
.a3 span:nth-child(1) {
|
||||||
|
color: #e6a23a;
|
||||||
|
}
|
||||||
|
.a4 span:nth-child(1) {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
.ac span:nth-child(1) {
|
||||||
|
background-color: #1296db;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -6,33 +6,53 @@
|
||||||
</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">
|
||||||
{{ item.fileShowName }}
|
<div></div>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ 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">
|
||||||
<el-icon
|
<template v-if="item.uniquekey">
|
||||||
v-loading="item.async === 'on'"
|
{{ item.worktype }}
|
||||||
style="background-color: green; border-radius: 20px; color: white; top: 2px"
|
</template>
|
||||||
>
|
<template v-else>
|
||||||
<Check v-if="item.async === true" />
|
<el-icon
|
||||||
<UploadFilled v-if="!item.async" />
|
v-loading="item.async === 'on'"
|
||||||
</el-icon>
|
style="background-color: green; border-radius: 20px; color: white; top: 2px"
|
||||||
{{ item.async === true ? '已同步' : '' }}
|
>
|
||||||
{{ !item.async ? '待同步' : '' }}
|
<Check v-if="item.async === true" />
|
||||||
{{ item.async === 'on' ? '同步中' : '' }}
|
<UploadFilled v-if="!item.async" />
|
||||||
|
</el-icon>
|
||||||
|
{{ item.async === true ? '已同步' : '' }}
|
||||||
|
{{ !item.async ? '待同步' : '' }}
|
||||||
|
{{ item.async === 'on' ? '同步中' : '' }}
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
<template v-if="item.fileSize">|</template>
|
||||||
<div style="width: 70px">{{ formatFileSize(item.fileSize) }}</div>
|
<div v-if="item.fileSize" style="width: 70px">{{ formatFileSize(item.fileSize) }}</div>
|
||||||
|
|
<template v-if="item.uploadTime">|</template>
|
||||||
<div style="width: 70px">{{ toTimeText(item.uploadTime, true) }}</div>
|
<div v-if="item.uploadTime" style="width: 70px">
|
||||||
|
|
{{ toTimeText(item.uploadTime, true) }}
|
||||||
|
</div>
|
||||||
|
<template v-if="item.levelFirstName">| </template>
|
||||||
<div
|
<div
|
||||||
|
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 +
|
||||||
|
@ -59,32 +79,48 @@
|
||||||
<template #default>
|
<template #default>
|
||||||
<div style="width: 100%">
|
<div style="width: 100%">
|
||||||
<div class="item-popover" @click="closePopver(index)">
|
<div class="item-popover" @click="closePopver(index)">
|
||||||
<div class="item-popover-item">
|
<template v-if="item.uniquekey">
|
||||||
<el-button text @click="editTalk(item, index)">
|
<div class="item-popover-item">
|
||||||
<i class="iconfont icon-bianji"></i>
|
<el-button text @click="setHomeWork(item, index)">
|
||||||
<span>重命名</span>
|
<i class="iconfont icon-bianji"></i>
|
||||||
</el-button>
|
<span>布置</span>
|
||||||
</div>
|
</el-button>
|
||||||
<div class="item-popover-item">
|
</div>
|
||||||
<el-button text @click="deleteTalk(item)">
|
<div class="item-popover-item">
|
||||||
<i class="iconfont icon-shanchu"></i>
|
<el-button text @click="deleteHomework(item)">
|
||||||
<span>删除</span>
|
<i class="iconfont icon-shanchu"></i>
|
||||||
</el-button>
|
<span>删除</span>
|
||||||
</div>
|
</el-button>
|
||||||
<div class="item-popover-item">
|
</div>
|
||||||
<el-button text @click="downloadFile(item)">
|
</template>
|
||||||
<i class="iconfont icon-xiazai"></i>
|
<template v-else>
|
||||||
<span>下载</span>
|
<div class="item-popover-item">
|
||||||
</el-button>
|
<el-button text @click="editTalk(item, index)">
|
||||||
</div>
|
<i class="iconfont icon-bianji"></i>
|
||||||
<div class="item-popover-item">
|
<span>重命名</span>
|
||||||
<el-button text @click="moveSmarttalkFun(item)">
|
</el-button>
|
||||||
<el-icon>
|
</div>
|
||||||
<Switch />
|
<div class="item-popover-item">
|
||||||
</el-icon>
|
<el-button text @click="deleteTalk(item)">
|
||||||
<span>移动</span>
|
<i class="iconfont icon-shanchu"></i>
|
||||||
</el-button>
|
<span>删除</span>
|
||||||
</div>
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="item-popover-item">
|
||||||
|
<el-button text @click="downloadFile(item)">
|
||||||
|
<i class="iconfont icon-xiazai"></i>
|
||||||
|
<span>下载</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="item-popover-item">
|
||||||
|
<el-button text @click="moveSmarttalkFun(item)">
|
||||||
|
<el-icon>
|
||||||
|
<Switch />
|
||||||
|
</el-icon>
|
||||||
|
<span>移动</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -123,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: []
|
||||||
|
@ -208,6 +244,14 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
//布置
|
||||||
|
setHomeWork(item) {
|
||||||
|
this.$emit('on-set', item)
|
||||||
|
},
|
||||||
|
// 删除作业
|
||||||
|
deleteHomework(item){
|
||||||
|
this.$emit('on-delhomework', item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,6 +280,10 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
.icon-zuoye {
|
||||||
|
font-size: 40px;
|
||||||
|
color: #707070;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.prepare-body-main-item-tool {
|
.prepare-body-main-item-tool {
|
||||||
|
@ -254,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,320 @@
|
||||||
|
<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>布置作业</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="cloneDialog">取消</el-button>
|
||||||
|
<el-button type="primary" @click="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: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
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>
|
|
@ -13,17 +13,23 @@
|
||||||
<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="primary" class="to-class-btn" @click="openLesson">
|
||||||
<i class="iconfont icon-lingdang"></i>上课</el-button>
|
<i class="iconfont icon-lingdang"></i>上课</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">
|
||||||
<template #default>
|
<template #default>
|
||||||
<div>
|
<div>
|
||||||
<el-button v-if="lastAsyncAllTime" type="success" size="small" :icon="Check" circle />
|
<el-button
|
||||||
|
v-if="lastAsyncAllTime"
|
||||||
|
type="success"
|
||||||
|
size="small"
|
||||||
|
:icon="Check"
|
||||||
|
circle
|
||||||
|
/>
|
||||||
{{ lastAsyncAllTime ? toTimeText(lastAsyncAllTime) + '同步成功' : '' }}
|
{{ lastAsyncAllTime ? toTimeText(lastAsyncAllTime) + '同步成功' : '' }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -38,25 +44,46 @@
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
|
<el-button @click="handleOutLink('feedback')">作业反馈</el-button>
|
||||||
<el-button @click="handleOutLink('homeWork')">布置作业</el-button>
|
<el-button @click="handleOutLink('homeWork')">布置作业</el-button>
|
||||||
<el-button @click="isDialogOpen = true">上传资料</el-button>
|
<el-button @click="isDialogOpen = true">上传资料</el-button>
|
||||||
<el-button type="primary" style="margin-left: 10px" @click="createFile">新建课件</el-button>
|
<el-button type="primary" style="margin-left: 10px" @click="createFile"
|
||||||
|
>新建课件</el-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-checkbox-group v-model="checkFileList" class="prepare-body-main"
|
<el-checkbox-group
|
||||||
:style="{ 'margin-bottom': checkFileList.length > 0 ? '40px' : '0' }">
|
v-model="checkFileList"
|
||||||
<file-list-item v-for="(item, index) in currentFileList" :key="index" :item="item" :index="index"
|
class="prepare-body-main"
|
||||||
@on-move="onMoveSingleFile" @on-delete="deleteTalk">
|
:style="{ 'margin-bottom': checkFileList.length > 0 ? '40px' : '0' }"
|
||||||
<el-checkbox label="" :value="item" />
|
>
|
||||||
|
<file-list-item
|
||||||
|
v-for="(item, index) in currentFileList"
|
||||||
|
:key="index"
|
||||||
|
:item="item"
|
||||||
|
:index="index"
|
||||||
|
@on-move="onMoveSingleFile"
|
||||||
|
@on-delete="deleteTalk"
|
||||||
|
@on-set="openSet"
|
||||||
|
@on-delhomework="delhomework"
|
||||||
|
>
|
||||||
|
<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"
|
||||||
:indeterminate="checkFileList.length > 0 && checkFileList.length < currentFileList.length"
|
:indeterminate="checkFileList.length > 0 && checkFileList.length < currentFileList.length"
|
||||||
:choose="checkFileList" :check-all="isCheckAll" @click-delete="clickDelete" @click-move="clickMove"
|
:choose="checkFileList"
|
||||||
@cancel="checkFileList = []" @click-choose="clickChoose"></file-oper-batch>
|
:check-all="isCheckAll"
|
||||||
|
@click-delete="clickDelete"
|
||||||
|
@click-move="clickMove"
|
||||||
|
@cancel="checkFileList = []"
|
||||||
|
@click-choose="clickChoose"
|
||||||
|
></file-oper-batch>
|
||||||
</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>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -67,20 +94,34 @@ 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 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'
|
||||||
|
import { homeworklist, listEntpcourse } from '@/api/teaching/classwork'
|
||||||
import { toTimeText } from '@/utils/date'
|
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 { delClasswork } from '@/api/teaching/classwork'
|
||||||
|
|
||||||
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: [],
|
||||||
|
@ -103,6 +144,13 @@ export default {
|
||||||
},
|
},
|
||||||
// 当前教材封面图
|
// 当前教材封面图
|
||||||
curBookImg: '',
|
curBookImg: '',
|
||||||
|
// 用户信息
|
||||||
|
userStore: '',
|
||||||
|
entpcourseid: '',
|
||||||
|
timerId: null,
|
||||||
|
// 布置作业弹窗
|
||||||
|
setDialog: false,
|
||||||
|
row: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -112,16 +160,22 @@ export default {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route(to) {
|
||||||
|
if (to.path != '/prepare' && this.timerId) {
|
||||||
|
clearInterval(this.timerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
|
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')
|
||||||
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
},
|
},
|
||||||
|
mounted() {},
|
||||||
activated() {
|
activated() {
|
||||||
if (this.uploadData.textbookId !== null) {
|
if (this.uploadData.textbookId !== null) {
|
||||||
this.asyncAllFile()
|
this.asyncAllFile()
|
||||||
|
@ -222,7 +276,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',
|
||||||
|
@ -239,12 +293,14 @@ 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()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
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.checkFileList = []
|
this.checkFileList = []
|
||||||
|
@ -254,18 +310,131 @@ 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()
|
await this.asyncAllFile()
|
||||||
|
if (this.uploadData.levelSecondId) {
|
||||||
|
// 获取作业列表所需ID 可能存在没有
|
||||||
|
const { rows } = await this.getChapterId()
|
||||||
|
if(!rows.length) return
|
||||||
|
this.entpcourseid = rows[0].id
|
||||||
|
// 查询作业
|
||||||
|
this.getHomeWorkList()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 打开外部链接
|
// 打开外部链接
|
||||||
handleOutLink(key){
|
handleOutLink(key) {
|
||||||
|
if (key == 'homeWork') {
|
||||||
|
// 查询作业
|
||||||
|
this.createTimer()
|
||||||
|
}
|
||||||
// key 对应的 linkConfig.js 外部链接配置
|
// key 对应的 linkConfig.js 外部链接配置
|
||||||
let configObj = outLink[key]
|
let configObj = outLink()[key]
|
||||||
// 通知主进程
|
// 通知主进程
|
||||||
ipcRenderer.send('openWindow', {
|
ipcRenderer.send('openWindow', {
|
||||||
fullPath: configObj.fullPath,
|
fullPath: configObj.fullPath,
|
||||||
cookieData: {...(configObj.data)}
|
cookieData: { ...configObj.data }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 根据教材章节单元ID 查询作业列表所需ID
|
||||||
|
getChapterId() {
|
||||||
|
return listEntpcourse({
|
||||||
|
evalid: this.uploadData.levelSecondId,
|
||||||
|
edituserid: this.userStore.userId,
|
||||||
|
pageSize: 500
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 清除查询作业列表定时器
|
||||||
|
createTimer() {
|
||||||
|
this.timerId = setInterval(() => {
|
||||||
|
this.getHomeWorkList()
|
||||||
|
}, 1500)
|
||||||
|
},
|
||||||
|
// 查询作业列表
|
||||||
|
getHomeWorkList() {
|
||||||
|
homeworklist({
|
||||||
|
entpcourseid: this.entpcourseid,
|
||||||
|
edituserid: this.userStore.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 = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 去重
|
||||||
|
let ary = uniqBy([...this.currentFileList, ...list], 'id')
|
||||||
|
// 深度克隆
|
||||||
|
this.currentFileList = cloneDeep(ary)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 打开布置作业窗口
|
||||||
|
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() {
|
navtoPdf() {
|
||||||
createWindow('open-PDF', { url: '/classBegins/index' })
|
createWindow('open-PDF', { url: '/classBegins/index' })
|
||||||
|
@ -329,7 +498,8 @@ export default {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(#b0d1ef, #3e7bcb);
|
background: linear-gradient(#b0d1ef, #3e7bcb);
|
||||||
.top-zoom-style{
|
|
||||||
|
.top-zoom-style {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
@ -337,7 +507,8 @@ export default {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
clip-path: polygon(3% 0%, 97% 0%, 100% 100%, 0% 100%);
|
clip-path: polygon(3% 0%, 97% 0%, 100% 100%, 0% 100%);
|
||||||
}
|
}
|
||||||
.textbook-img{
|
|
||||||
|
.textbook-img {
|
||||||
height: 120px;
|
height: 120px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
@ -346,36 +517,43 @@ export default {
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.top-item{
|
|
||||||
|
.top-item {
|
||||||
width: 230px;
|
width: 230px;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
.btn{
|
|
||||||
|
.btn {
|
||||||
width: 102px;
|
width: 102px;
|
||||||
background: none;
|
background: none;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
border-color: #ffffff;
|
border-color: #ffffff;
|
||||||
&:hover{
|
|
||||||
background: rgba(255, 255, 255, 0.3)
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
&:first-child{
|
|
||||||
|
&:first-child {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
&:nth-child(2){
|
|
||||||
|
&:nth-child(2) {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.to-class-btn{
|
|
||||||
|
.to-class-btn {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
.icon-lingdang{
|
|
||||||
|
.icon-lingdang {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
@ -391,7 +569,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 {
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
<!-- 裁剪头像弹窗 -->
|
||||||
|
<template>
|
||||||
|
<div class="personal-authentica">
|
||||||
|
<div class="context">
|
||||||
|
<div class="text-box" v-loading="upLoading">
|
||||||
|
<!-- 关闭按钮 -->
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div id="Box">
|
||||||
|
<h3>头像设置</h3>
|
||||||
|
|
||||||
|
<!-- 裁剪+效果 -->
|
||||||
|
<div class="box">
|
||||||
|
<div class="box_1">
|
||||||
|
<img :src="filePath" ref="image" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--裁剪完的图片-->
|
||||||
|
<div class="box_2">
|
||||||
|
<div class="before"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 裁剪按钮-->
|
||||||
|
<div class="btn">
|
||||||
|
<el-button style="margin-right: 20px">选择</el-button>
|
||||||
|
<input
|
||||||
|
class="upload"
|
||||||
|
type="file"
|
||||||
|
accept=".png, .jpg, .jpeg"
|
||||||
|
@change="uploadImg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-button @click="cancle">取消</el-button>
|
||||||
|
<el-button @click="sureSava">提交</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMounted, reactive, toRefs, ref } from "vue";
|
||||||
|
//引入依赖
|
||||||
|
import Cropper from "cropperjs";
|
||||||
|
import "cropperjs/dist/cropper.css";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
filePath: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
aspectRatio: { // 裁剪比例,需要可传
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props, context) {
|
||||||
|
|
||||||
|
// 原生上传的文件
|
||||||
|
const uploadImg = (e) => {
|
||||||
|
if (e.target.files && e.target.files[0]) {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
dataFile.image.src = URL.createObjectURL(file);
|
||||||
|
if (dataFile.myCropper) {
|
||||||
|
dataFile.myCropper.destroy();
|
||||||
|
}
|
||||||
|
copper();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataFile = reactive({
|
||||||
|
// 裁剪后的图片
|
||||||
|
afterImg: "",
|
||||||
|
// 裁剪的图片
|
||||||
|
image: null,
|
||||||
|
// 进行裁剪
|
||||||
|
myCropper: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 实例化裁剪框
|
||||||
|
const copper = () => {
|
||||||
|
dataFile.myCropper = new Cropper(dataFile.image, {
|
||||||
|
/*
|
||||||
|
* viewMode 视图控制
|
||||||
|
- 0 无限制
|
||||||
|
- 1 限制裁剪框不能超出图片的范围
|
||||||
|
- 2 限制裁剪框不能超出图片的范围 且图片填充模式为 cover 最长边填充
|
||||||
|
- 3 限制裁剪框不能超出图片的范围 且图片填充模式为 contain 最短边填充
|
||||||
|
* */
|
||||||
|
viewMode: 1,
|
||||||
|
// 设置图片是否可以拖拽功能
|
||||||
|
/*
|
||||||
|
* dragMode 拖拽图片模式
|
||||||
|
- crop 形成新的裁剪框
|
||||||
|
- move 图片可移动
|
||||||
|
- none 什么也没有
|
||||||
|
* */
|
||||||
|
dragMode: "move",
|
||||||
|
// 是否显示图片后面的网格背景,一般默认为true
|
||||||
|
background: true,
|
||||||
|
// 进行图片预览的效果
|
||||||
|
preview: ".before",
|
||||||
|
// 设置裁剪区域占图片的大小 值为 0-1 默认 0.8 表示 80%的区域
|
||||||
|
autoCropArea: 0.8,
|
||||||
|
// 设置图片是否可以进行收缩功能
|
||||||
|
zoomOnWheel: true,
|
||||||
|
// 是否显示 + 箭头
|
||||||
|
center: true,
|
||||||
|
// 设置裁剪框为固定的宽高比
|
||||||
|
aspectRatio: props.aspectRatio,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 裁剪后
|
||||||
|
const sureSava = () => {
|
||||||
|
if (dataFile.myCropper) {
|
||||||
|
// 拿到裁剪后的图片
|
||||||
|
dataFile.afterImg = dataFile.myCropper
|
||||||
|
.getCroppedCanvas({
|
||||||
|
imageSmoothingQuality: "high",
|
||||||
|
})
|
||||||
|
.toDataURL("image/jpeg"); // 设置图片格式
|
||||||
|
context.emit("sureSava",convertDataUrlToBlob(dataFile.afterImg))
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: "请先选择图片",
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取上传文件签名
|
||||||
|
const upLoading = ref(false);
|
||||||
|
const convertDataUrlToBlob = (dataUrl) => {
|
||||||
|
const parts = dataUrl.split(';base64,');
|
||||||
|
const contentType = parts[0].split(':')[1];
|
||||||
|
const byteCharacters = atob(parts[1]);
|
||||||
|
const byteArrays = [];
|
||||||
|
|
||||||
|
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
|
||||||
|
const slice = byteCharacters.slice(offset, offset + 512);
|
||||||
|
|
||||||
|
const byteNumbers = new Array(slice.length);
|
||||||
|
for (let i = 0; i < slice.length; i++) {
|
||||||
|
byteNumbers[i] = slice.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byteArray = new Uint8Array(byteNumbers);
|
||||||
|
byteArrays.push(byteArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Blob(byteArrays, { type: contentType });
|
||||||
|
};
|
||||||
|
const cancle = () => {
|
||||||
|
context.emit("cancle");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面刷新自动执行
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.filePath) {
|
||||||
|
dataFile.image.src = props.filePath;
|
||||||
|
copper();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(dataFile),
|
||||||
|
sureSava,
|
||||||
|
// boxClose,
|
||||||
|
uploadImg,
|
||||||
|
upLoading,
|
||||||
|
cancle,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#Box {
|
||||||
|
padding: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 100%;
|
||||||
|
> h3 {
|
||||||
|
}
|
||||||
|
> .box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
> div {
|
||||||
|
width: 400px;
|
||||||
|
height: 200px;
|
||||||
|
> img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .box_2 {
|
||||||
|
> .before {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
position: relative;
|
||||||
|
left: 150px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .btn {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
margin-top: 30px;
|
||||||
|
> .upload {
|
||||||
|
display: block;
|
||||||
|
width: 60px;
|
||||||
|
height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.personal-authentica {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ccc;
|
||||||
|
> .context {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 400;
|
||||||
|
left: 50%;
|
||||||
|
top: 30%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
> .text-box {
|
||||||
|
position: relative;
|
||||||
|
// margin: 120px auto 0;
|
||||||
|
width: 720px;
|
||||||
|
height: 350px;
|
||||||
|
background-color: #fff;
|
||||||
|
> i {
|
||||||
|
position: absolute;
|
||||||
|
color: #aaa;
|
||||||
|
right: 20px;
|
||||||
|
top: 8px;
|
||||||
|
font-size: 35px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .mark {
|
||||||
|
position: fixed;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: rgba(43, 43, 43, 0.7215686275);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,112 +2,45 @@
|
||||||
<div class="user-info-head" @click="editCropper()">
|
<div class="user-info-head" @click="editCropper()">
|
||||||
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
|
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:title="title"
|
|
||||||
v-model="open"
|
v-model="open"
|
||||||
width="800px"
|
|
||||||
append-to-body
|
append-to-body
|
||||||
@opened="modalOpened"
|
@opened="modalOpened"
|
||||||
@close="closeDialog"
|
@close="closeDialog"
|
||||||
>
|
>
|
||||||
<el-row>
|
<CropperImage
|
||||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
ref="cropper"
|
||||||
<vue-cropper
|
:filePath="options.img"
|
||||||
ref="cropper"
|
@sureSava="uploadImg"
|
||||||
:img="options.img"
|
@cancle="cancle"
|
||||||
:info="true"
|
></CropperImage>
|
||||||
:autoCrop="options.autoCrop"
|
|
||||||
:autoCropWidth="options.autoCropWidth"
|
|
||||||
:autoCropHeight="options.autoCropHeight"
|
|
||||||
:fixedBox="options.fixedBox"
|
|
||||||
:outputType="options.outputType"
|
|
||||||
@realTime="realTime"
|
|
||||||
v-if="visible"
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
|
||||||
<div class="avatar-upload-preview">
|
|
||||||
<div v-show="show" class="large" :style="[{backgroundImage:`url(${options.img})`},largePosition]"></div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<br />
|
|
||||||
<el-row>
|
|
||||||
<el-col :lg="2" :md="2">
|
|
||||||
<el-upload
|
|
||||||
action="#"
|
|
||||||
:http-request="requestUpload"
|
|
||||||
:show-file-list="false"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
>
|
|
||||||
<el-button>
|
|
||||||
选择
|
|
||||||
<el-icon class="el-icon--right"><Upload /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</el-upload>
|
|
||||||
</el-col>
|
|
||||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
|
||||||
<el-button :icon="Plus" @click="changeScale(1)"></el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
|
||||||
<el-button :icon="Minus" @click="changeScale(-1)"></el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
|
||||||
<el-button :icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
|
||||||
<el-button :icon="RefreshRight" @click="rotateRight()"></el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
|
|
||||||
<el-button type="primary" @click="uploadImg()">提 交</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Upload, Plus, Minus, RefreshLeft, RefreshRight } from '@element-plus/icons-vue'
|
import {ref, reactive} from 'vue'
|
||||||
import {ref, reactive, getCurrentInstance, watch} from 'vue'
|
|
||||||
import 'vue-cropper/dist/index.css'
|
|
||||||
import { VueCropper } from 'vue-cropper'
|
|
||||||
import { uploadAvatar } from '@/api/system/user'
|
import { uploadAvatar } from '@/api/system/user'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { useMouseInElement } from '@vueuse/core'
|
import CropperImage from './cropperImage.vue'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { proxy } = getCurrentInstance()
|
|
||||||
|
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const title = ref('修改头像')
|
|
||||||
|
|
||||||
//图片裁剪数据
|
//图片裁剪数据
|
||||||
const options = reactive({
|
const options = reactive({
|
||||||
img: userStore.user.avatar, // 裁剪图片的地址
|
img: userStore.user.avatar, // 裁剪图片的地址
|
||||||
autoCrop: true, // 是否默认生成截图框
|
autoCrop: true, // 是否默认生成截图框
|
||||||
autoCropWidth: 200, // 默认生成截图框宽度
|
autoCropWidth: 400, // 默认生成截图框宽度
|
||||||
autoCropHeight: 200, // 默认生成截图框高度
|
autoCropHeight: 400, // 默认生成截图框高度
|
||||||
fixedBox: true, // 固定截图框大小 不允许改变
|
fixedBox: true, // 固定截图框大小 不允许改变
|
||||||
outputType: 'png', // 默认生成截图为PNG格式
|
outputType: 'png', // 默认生成截图为PNG格式
|
||||||
previews: {} //预览数据
|
previews: {} //预览数据
|
||||||
})
|
})
|
||||||
// 是否显示遮罩和大图
|
|
||||||
const show = ref(false)
|
|
||||||
// 遮罩的坐标(样式)
|
|
||||||
const layerPosition = reactive({
|
|
||||||
left: 0,
|
|
||||||
top: 0
|
|
||||||
})
|
|
||||||
// 大图背景定位(样式)
|
|
||||||
const largePosition = reactive({
|
|
||||||
backgroundPositionX: 0,
|
|
||||||
backgroundPositionY: 0
|
|
||||||
})
|
|
||||||
const cropper = ref(null)
|
const cropper = ref(null)
|
||||||
const { elementX, elementY } = useMouseInElement(cropper)
|
|
||||||
//监听是否拖动
|
|
||||||
const transform = ref({})
|
|
||||||
|
|
||||||
/** 编辑头像 */
|
/** 编辑头像 */
|
||||||
function editCropper() {
|
function editCropper() {
|
||||||
|
@ -117,84 +50,30 @@ function editCropper() {
|
||||||
function modalOpened() {
|
function modalOpened() {
|
||||||
visible.value = true
|
visible.value = true
|
||||||
}
|
}
|
||||||
/** 覆盖默认上传行为 */
|
|
||||||
function requestUpload() {}
|
|
||||||
/** 向左旋转 */
|
|
||||||
function rotateLeft() {
|
|
||||||
proxy.$refs.cropper.rotateLeft()
|
|
||||||
}
|
|
||||||
/** 向右旋转 */
|
|
||||||
function rotateRight() {
|
|
||||||
proxy.$refs.cropper.rotateRight()
|
|
||||||
}
|
|
||||||
/** 图片缩放 */
|
|
||||||
function changeScale(num) {
|
|
||||||
num = num || 1
|
|
||||||
proxy.$refs.cropper.changeScale(num)
|
|
||||||
}
|
|
||||||
/** 上传预处理 */
|
|
||||||
function beforeUpload(file) {
|
|
||||||
if (file.type.indexOf('image/') == -1) {
|
|
||||||
ElMessage({
|
|
||||||
message: '文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.readAsDataURL(file)
|
|
||||||
reader.onload = () => {
|
|
||||||
options.img = reader.result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** 上传图片 */
|
/** 上传图片 */
|
||||||
function uploadImg() {
|
function uploadImg(data) {
|
||||||
proxy.$refs.cropper.getCropBlob((data) => {
|
let formData = new FormData()
|
||||||
let formData = new FormData()
|
formData.append('avatarfile', data)
|
||||||
formData.append('avatarfile', data)
|
uploadAvatar(formData).then((response) => {
|
||||||
uploadAvatar(formData).then((response) => {
|
open.value = false
|
||||||
open.value = false
|
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl
|
||||||
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl
|
userStore.user.avatar = options.img
|
||||||
userStore.user.avatar = options.img
|
ElMessage({
|
||||||
ElMessage({
|
message: '上传成功',
|
||||||
message: '上传成功',
|
type: 'success',
|
||||||
type: 'success',
|
|
||||||
})
|
|
||||||
visible.value = false
|
|
||||||
})
|
})
|
||||||
|
visible.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 实时预览 */
|
|
||||||
function realTime(data) {
|
|
||||||
options.previews = data
|
|
||||||
transform.value = Object.assign({}, data.img)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关闭窗口 */
|
/** 关闭窗口 */
|
||||||
function closeDialog() {
|
function closeDialog() {
|
||||||
options.img = userStore.avatar
|
// options.img = userStore.avatar
|
||||||
options.visible = false
|
options.visible = false
|
||||||
}
|
}
|
||||||
watch(transform, () => {
|
const cancle = () => {
|
||||||
// 根据得到数据设置样式数据和是否显示数据
|
open.value = false
|
||||||
show.value = true
|
}
|
||||||
// 计算坐标
|
|
||||||
const position = { x: 0, y: 0 }
|
|
||||||
|
|
||||||
if (elementX.value < 100) position.x = 0
|
|
||||||
else if (elementX.value > 300) position.x = 200
|
|
||||||
else position.x = elementX.value - 100
|
|
||||||
|
|
||||||
if (elementY.value < 100) position.y = 0
|
|
||||||
else if (elementY.value > 300) position.y = 200
|
|
||||||
else position.y = elementY.value - 100
|
|
||||||
// 给样式赋值
|
|
||||||
layerPosition.left = position.x + 'px'
|
|
||||||
layerPosition.top = position.y + 'px'
|
|
||||||
largePosition.backgroundPositionX = -2 * position.x + 'px'
|
|
||||||
largePosition.backgroundPositionY = -2 * position.y + 'px'
|
|
||||||
},{deep:true})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
@ -69,7 +102,7 @@ import useUserStore from '@/store/modules/user'
|
||||||
import useResoureStore from '../store'
|
import useResoureStore from '../store'
|
||||||
|
|
||||||
const { ipcRenderer } = window.electron || {}
|
const { ipcRenderer } = window.electron || {}
|
||||||
const userstore = useUserStore()
|
const userstore = useUserStore()
|
||||||
const sourceStore = useResoureStore()
|
const sourceStore = useResoureStore()
|
||||||
|
|
||||||
const userInfo = userstore.user
|
const userInfo = userstore.user
|
||||||
|
@ -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,21 +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> -->
|
<!-- <el-button @click="testClick">测试</el-button> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -40,7 +47,7 @@ onMounted(async () => {
|
||||||
// const res = await ipcMsgSend('tool-sphere:create', params)
|
// const res = await ipcMsgSend('tool-sphere:create', params)
|
||||||
// console.log('消息返回:', res)
|
// console.log('消息返回:', res)
|
||||||
})
|
})
|
||||||
const testClick = async() => {
|
const testClick = async () => {
|
||||||
const win = await createWindow('tool-sphere', { url: '/tool/sphere' })
|
const win = await createWindow('tool-sphere', { url: '/tool/sphere' })
|
||||||
console.log('消息返回:', win)
|
console.log('消息返回:', win)
|
||||||
}
|
}
|
||||||
|
@ -48,12 +55,11 @@ const testClick = async() => {
|
||||||
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 +69,7 @@ const getData = (data) => {
|
||||||
sourceStore.nodeData = {
|
sourceStore.nodeData = {
|
||||||
textbookId,
|
textbookId,
|
||||||
levelFirstId,
|
levelFirstId,
|
||||||
levelSecondId,
|
levelSecondId
|
||||||
}
|
}
|
||||||
sourceStore.handleQuery()
|
sourceStore.handleQuery()
|
||||||
}
|
}
|
||||||
|
@ -73,7 +79,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 +89,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 +107,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%;
|
||||||
|
@ -111,12 +117,12 @@ onMounted(()=>{
|
||||||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
||||||
}
|
}
|
||||||
.icon-jiahao {
|
.icon-jiahao {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.create-btn{
|
.create-btn {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 5px 13px;
|
padding: 5px 13px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,42 @@
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
// 功能说明:画板
|
// 功能说明:画板
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import { FabricVue } from '@/plugins/fabric'
|
import {FabricVue, TYPES} from '@/plugins/fabric'
|
||||||
import { useBoardStore, useDrawStore } from '@/store/modules/draw'
|
const canvasRef = ref(null) // 画布
|
||||||
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)
|
|
||||||
|
// 监听
|
||||||
|
watch(() => props.modelValue, (newVal, oldVal) => {
|
||||||
|
// console.log(newVal, oldVal)
|
||||||
|
switch(newVal) {
|
||||||
|
case 'select': // 选择模式
|
||||||
|
FabricVue.handleMode(TYPES.ActionMode.OTHER)
|
||||||
|
break
|
||||||
|
case 'brush': // 画笔模式
|
||||||
|
FabricVue.handleMode(TYPES.ActionMode.DRAW)
|
||||||
|
FabricVue.canvas.freeDrawingCursor = 'default'
|
||||||
|
break
|
||||||
|
case 'eraser': // 板擦模式
|
||||||
|
FabricVue.handleMode(TYPES.ActionMode.ERASE)
|
||||||
|
break
|
||||||
|
case 'clear': // 清空画布
|
||||||
|
FabricVue.history?.clean()
|
||||||
|
emit('update:modelValue', oldVal)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -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)
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,52 +1,60 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="warp-all">
|
<div class="warp-all">
|
||||||
<board-vue></board-vue>
|
<board-vue v-model="tabActive"></board-vue>
|
||||||
<!-- 底部工具栏 -->
|
<!-- 底部工具栏 :style="dataPos.style"-->
|
||||||
<el-row id="test" class="tool-bottom-all" @mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
<div class="tool-bottom-all"
|
||||||
<el-col :span="3" class="flex justify-center items-center">
|
@mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
||||||
<div class="c-logo"><el-image :src="logo" @click="tabChange('close')" /></div>
|
<div v-drag="{handle:'.tool-bottom-all', dragtime}"
|
||||||
</el-col>
|
@v-drag-start="dragtime = Date.now()">
|
||||||
<el-col :span="20">
|
<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"></i>
|
||||||
<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 } from 'vue'
|
||||||
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' // 画板
|
||||||
|
import vDrag from './directive/drag'
|
||||||
// const Remote = require('@electron/remote') // remote对象
|
// const Remote = require('@electron/remote') // remote对象
|
||||||
const { ipcRenderer } = require('electron')
|
const { ipcRenderer } = require('electron') // app使用
|
||||||
|
// const ipcRenderer = { send: () => {} } // 网页测试使用
|
||||||
|
|
||||||
|
const tabActive = ref('select') // 工具栏当前选中项
|
||||||
const tabActive = ref('select')
|
const isFold = ref(false) // 折叠工具栏
|
||||||
const btnList = [
|
const isDrag = ref(false) // 开始拖拽
|
||||||
|
const dragtime = ref(0)
|
||||||
|
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: 'eraser', 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' },
|
||||||
]
|
]
|
||||||
|
|
||||||
// ==== 方法 ===
|
// ==== 方法 ===
|
||||||
const tabChange = (val) => { // 切换tab-change
|
const tabChange = (val) => { // 切换tab-change
|
||||||
console.log('xxxx', val)
|
console.log(val)
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case 'brush':
|
case 'brush': // 画笔
|
||||||
break
|
break
|
||||||
case 'eraser':
|
case 'eraser': // 板擦
|
||||||
break
|
break
|
||||||
case 'interact':
|
case 'interact':
|
||||||
break
|
break
|
||||||
|
@ -61,6 +69,12 @@ const tabChange = (val) => { // 切换tab-change
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const logoHandle = (e,t) => { // logo 点击-事件 折叠|展开
|
||||||
|
if (Date.now() - dragtime.value < 200) {
|
||||||
|
isFold.value = !isFold.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
||||||
let resBool = false
|
let resBool = false
|
||||||
if (tabActive.value == 'select') resBool = !!bool
|
if (tabActive.value == 'select') resBool = !!bool
|
||||||
|
@ -74,7 +88,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 +104,11 @@ 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;
|
// &:hover{
|
||||||
left: 0;
|
// .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 +116,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,6 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<canvas ref="canvasRef" />
|
<canvas ref="canvasRef" />
|
||||||
<button @click="eraseTo">橡皮擦</button>
|
<button @click="eraseTo">橡皮擦
|
||||||
|
<i class="iconfont icon-xiangpica"></i>
|
||||||
|
</button>
|
||||||
|
<button @click="close">销毁</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -10,25 +13,36 @@ import { ref, onMounted } from 'vue'
|
||||||
// import { useBoardStore } from '@/store/modules/draw'
|
// import { useBoardStore } from '@/store/modules/draw'
|
||||||
import {FabricVue, TYPES} from '@/plugins/fabric'
|
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)
|
||||||
FabricVue.drawConfig.drawColors = ['red']
|
|
||||||
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 = () => { // 橡皮擦
|
const eraseTo = () => { // 橡皮擦
|
||||||
FabricVue.handleMode(TYPES.ActionMode.ERASE)
|
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