Compare commits

..

No commits in common. "main" and "2.1.3" have entirely different histories.
main ... 2.1.3

119 changed files with 3821 additions and 8422 deletions

View File

@ -15,5 +15,3 @@ VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api'
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'true'

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = 文枢课堂
VITE_APP_TITLE = AIX智慧课堂
# 生产环境配置
VITE_APP_ENV = 'production'
@ -17,5 +17,3 @@ VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'false'

View File

@ -17,5 +17,3 @@ VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'true'

View File

@ -17,5 +17,3 @@ VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'true'

View File

@ -1,21 +0,0 @@
# 页面标题
VITE_APP_TITLE = 实训教学
# 生产环境配置
VITE_APP_ENV = 'production'
# AIx融合数字管理系统/生产环境
VITE_APP_BASE_API = 'https://prev.ysaix.com:7868/prod-api'
VITE_APP_DOMAIN = 'prev.ysaix.com'
VITE_APP_UPLOAD_API = 'https://prev.ysaix.com:7868/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'false'

View File

@ -1,10 +1,10 @@
appId: com.electron.app
productName: 文枢课堂
productName: AIx
directories:
output: dist
buildResources: build
win:
executableName: 文枢课堂
executableName: AIx
icon: resources/logo2.ico
files:
- '!**/.vscode/*'
@ -17,7 +17,7 @@ asarUnpack:
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
artifactName: ${name}-yc-${version}-setup.${ext}
artifactName: ${name}-${version}-setup.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
@ -30,7 +30,7 @@ mac:
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false
dmg:
artifactName: ${name}-yc-${version}.${ext}
artifactName: ${name}-${version}.${ext}
linux:
target:
- AppImage
@ -39,11 +39,11 @@ linux:
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-yc-${version}.${ext}
artifactName: ${name}-${version}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://prev.ysaix.com:7868/src/assets/smarttalkyc/
url: https://prev.ysaix.com:7868/src/assets/smarttalk/
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
# 额外依赖打包到输出目录

View File

@ -1,54 +0,0 @@
appId: com.electron.app.yc2
productName: 实训教学
directories:
output: dist
buildResources: build
win:
executableName: 实训教学
icon: resources/logo2.ico
files:
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
asarUnpack:
- resources/**
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
artifactName: ${name}-yc-${version}-setup.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false
dmg:
artifactName: ${name}-yc-${version}.${ext}
linux:
target:
- AppImage
- snap
- deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-yc-${version}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://prev.ysaix.com:7868/src/assets/smarttalkyc/
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
# 额外依赖打包到输出目录
extraFiles:
- from: ./node_modules/im_electron_sdk/lib/
to: ./resources
filter:
- '**/*'

View File

@ -16,7 +16,7 @@ win:
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
artifactName: ${name}-${version}-test.${ext}
artifactName: ${name}-${version}-setup.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always

View File

@ -43,11 +43,6 @@ export default defineConfig({
changeOrigin: true,
rewrite: (p) => p.replace(/^\/baidubce/, '')
},
'/parth': {
target: 'https://zwapi.xfyun.cn', // 第三方API的地址
changeOrigin: true, // 改变请求的起源
rewrite: (path) => path.replace(/^\/parth/, '') // 重写路径
},
},
},
plugins: [vue(), WindiCSS()],

View File

@ -1,9 +1,9 @@
{
"name": "aix-win",
"version": "2.1.37",
"version": "2.1.3",
"description": "",
"main": "./out/main/index.js",
"author": "上海交大重庆人工智能研究院",
"author": "example.com",
"homepage": "https://electron-vite.org",
"scripts": {
"format": "prettier --write .",
@ -16,12 +16,16 @@
"build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml",
"build:test": "electron-vite build --mode test && electron-builder --win --config ./electron-builder.yml",
"build:prod": "electron-vite build --mode production && electron-builder --win --config ./electron-builder-prod.yml",
"build:yc": "electron-vite build --mode yc && electron-builder --win --config ./electron-builder-yc.yml",
"build:yc2": "electron-vite build --mode yc2 && electron-builder --win --config ./electron-builder-yc2.yml",
"build:lt": "electron-vite build --mode lt && electron-builder --win --config ./electron-builder-lt.yml",
"build:mac": "electron-vite build --mode production && electron-builder --mac --config ./electron-builder-prod.yml",
"build:linux": "npm run build && electron-builder --linux"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0",
"@electron/remote": "^2.1.2",
"@element-plus/icons-vue": "^2.3.1",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"@antv/x6": "^2.18.1",
"@antv/x6-plugin-clipboard": "^2.1.6",
"@antv/x6-plugin-dnd": "^2.1.1",
@ -30,11 +34,6 @@
"@antv/x6-plugin-selection": "^2.2.2",
"@antv/x6-plugin-snapline": "^2.1.7",
"@antv/x6-plugin-transform": "^2.1.8",
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0",
"@electron/remote": "^2.1.2",
"@element-plus/icons-vue": "^2.3.1",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"@vue-office/docx": "^1.6.2",
"@vue-office/excel": "^1.7.11",
"@vue-office/pdf": "^2.0.2",
@ -54,21 +53,20 @@
"js-cookie": "^3.0.5",
"jsencrypt": "^3.3.2",
"jsondiffpatch": "0.6.0",
"less": "^4.2.0",
"less-loader": "^7.3.0",
"lodash": "^4.17.21",
"node-addon-api": "^8.1.0",
"pdfjs-dist": "4.4.168",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"qs": "^6.13.1",
"spark-md5": "^3.0.2",
"vite-plugin-electron": "^0.28.8",
"vue-qr": "^4.0.9",
"vue-router": "^4.4.0",
"whiteboard_lyc": "^0.1.3",
"xgplayer": "^3.0.19",
"xlsx": "^0.18.5"
"xlsx": "^0.18.5",
"less": "^4.2.0",
"less-loader": "^7.3.0",
"whiteboard_lyc": "^0.0.8"
},
"devDependencies": {
"@electron-toolkit/eslint-config": "^1.0.2",

View File

@ -243,82 +243,6 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
})
})
/*创建新的ppt文件*/
ipcMain.on('creat-ai-file-default', (e, { name, url, uploadData, cookie }) => {
createFolder('tempFile').then(async () => {
let lastname = decodeURIComponent(url);
name = lastname.substring(lastname.lastIndexOf("/")+1)
let path = appTempFilePath + name.replace(/[\\/:*?"<>|]/, '')
let {type,item} = await downloadFiles(url,name)
if (type==="成功") {
let fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
let formData = new FormData()
for (let key in uploadData) {
if (Object.prototype.hasOwnProperty.call(uploadData, key)) {
// 检查是否是对象自身的属性
formData.append(key, uploadData[key])
}
}
formData.append('fileFlag', '课件')
uploadFileByFS({
url: uploadUrl,
path,
name,
cookie,
fileType,
formData,
success: (response) => {
e.reply('creat-ai-file-default-reply', response.data)
console.log('File uploaded successfully:', response.data)
},
error: (err) => {
console.error('Error uploading file:', err)
}
})
}else {
e.reply('creat-ai-file-default-reply', type)
}
})
})
function downloadFiles(url,fileName) {
console.log(url,fileName)
return new Promise((resolve, reject)=>{
const browserWindow = BrowserWindow.getAllWindows()
console.log(browserWindow)
const id = manager.download({
window: browserWindow[0],
url: url,
saveAsFilename: fileName,
directory: appTempFilePath,
callbacks: {
onDownloadStarted: async ({ id, item, webContents }) => {
// Do something with the download id
},
onDownloadProgress: async ({ id, item, percentCompleted }) => {
// console.log(percentCompleted)
},
onDownloadCompleted: async ({ id, item }) => {
console.log('完成')
resolve({type:"成功",item})
},
onDownloadCancelled: async () => {
console.log("取消")
reject({type:"取消了下载"})
},
onDownloadInterrupted: async () => {
console.log('中断')
reject({type:"下载被中断"})
},
onError: (err, data) => {
console.log(err.toString())
reject({type:"下载出错",err})
}
}
})
})
}
//获取应用文件目录
ipcMain.on('get-root-file-path', (e) => {
e.reply('get-root-file-path-reply', appRootFilePath)

View File

@ -46,7 +46,7 @@ if(!gotTheLock){
function createLoginWindow() {
if (loginWindow) return
loginWindow = new BrowserWindow({
width: import.meta.env.MODE==='yc'||import.meta.env.MODE==='yc2'?1160:888,
width: 888,
height: 520,
show: false,
frame: false,
@ -73,7 +73,8 @@ function createLoginWindow() {
loginWindow.loadFile(join(__dirname, '../renderer/index.html'), { hash: 'login' })
updateInit(loginWindow)
}
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') loginWindow.webContents.openDevTools()
// loginWindow.webContents.openDevTools()
loginWindow.once('ready-to-show', () => {
loginWindow.show()
})
@ -127,7 +128,7 @@ function createMainWindow() {
shell.openExternal(details.url)
return { action: 'deny' }
})
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') mainWindow.webContents.openDevTools()
// mainWindow.webContents.openDevTools()
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
@ -137,7 +138,6 @@ function createMainWindow() {
// mainWindow.setAlwaysOnTop(true, "screen-saver") // 将窗口设置为顶层窗口
// mainWindow.setVisibleOnAllWorkspaces(true) // 如果窗口在所有工作区都可见
mainWindow.maximize();
// 第三步: 开启remote服务
remote.enable(mainWindow.webContents)
}
@ -175,7 +175,6 @@ async function createLinkWin(data) {
data.fullPath += '?urlSource=smarttalk&t' + Date.now()
}
linkWin[data.key].loadURL(data.fullPath)
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') linkWin[data.key].webContents.openDevTools()
linkWin[data.key].once('ready-to-show', () => {
linkWin[data.key].show()
@ -272,13 +271,12 @@ app.on('window-all-closed', () => {
function handleAll() {
const chatInstance = chat.initialize() // im-chat 实例
// 新窗口创建-监听
ipcMain.handle('new-window', (e, data) => {
ipcMain.on('new-window', (e, data) => {
const { id, type } = data
const win = BrowserWindow.fromId(id)
win.type = type // 绑定独立标识
remote.enable(win.webContents) // 开启远程服务
chatInstance.enable(win.webContents) // 开启im-chat
console.log(`主进程 [${type}]: 窗口注册-远程代理-完毕(${Date.now()})`)
})
// 用于监听-状态管理变化-同步所有窗口
ipcMain.handle('pinia-state-change', (e, storeName, jsonStr) => {

View File

@ -13,7 +13,6 @@ const defaultData = {
showBoardAll: false, // 全屏画板-是否显示
isPdfWin: false, // pdf窗口是否打开
isToolWin: false, // 工具窗口是否打开
isTaskWin: false, // 批改窗口是否打开
curSubjectNode: {
querySearch: {} // 查询资源所需参数
},
@ -23,9 +22,7 @@ const defaultData = {
curNode: null, // 当前选中的节点
defaultExpandedKeys: [], //展开的节点
subjectTree: [] // "树结构" 章节
},
env: {}, // 不走同步 Pinia - 变量
curr: {} // 不走同步 Pinia - 当前信息
}
},
local: { // 本地(永久localStorage)
},

File diff suppressed because one or more lines are too long

View File

@ -2782,8 +2782,7 @@
--editor-toolbar-base-offset:105px;
--main-color:rgb(12 12 13);
/* --body-bg-color:rgb(255, 255, 255); */
--body-bg-color:rgb(241, 241, 252);
--body-bg-color:rgb(255, 255, 255);
--progressBar-color:rgb(10 132 255);
--progressBar-bg-color:rgb(221 221 222);
--progressBar-blend-color:rgb(116 177 239);
@ -3149,12 +3148,12 @@ body{
}
#toolbarViewer{
width: auto;
height: 60px;
width: 300px;
height: 32px;
position: fixed;
z-index: 9999;
bottom: 15px;
right: 2%;
left: 50%;
background-color: #fff;
border-radius: 4px;
transform: translateX(-50%);
@ -3478,9 +3477,8 @@ body{
}
#toolbarViewerLeft{
/* padding-inline-start:1px; 修改样式*/
padding-inline-start:1px;
}
#toolbarViewerRight{
padding-inline-end:1px;
}
@ -3695,7 +3693,7 @@ body{
mask-image:var(--findbarButton-next-icon);
}
/* #previous::before{
#previous::before{
-webkit-mask-image:var(--toolbarButton-pageUp-icon);
mask-image:var(--toolbarButton-pageUp-icon);
}
@ -3703,27 +3701,8 @@ body{
#next::before{
-webkit-mask-image:var(--toolbarButton-pageDown-icon);
mask-image:var(--toolbarButton-pageDown-icon);
} */
/* 修改样式 */
#toolbarViewerLeft .toolbarButton{
width: 80px;
height: 60px;
margin: 0;
}
#toolbarViewerLeft .toolbarButton::before{
opacity: 0;
}
#toolbarViewerLeft .toolbarButton span{
width: auto;
height: auto;
}
.toolpageBtn{
height: 60px;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
}
#zoomOut::before{
-webkit-mask-image:var(--toolbarButton-zoomOut-icon);
mask-image:var(--toolbarButton-zoomOut-icon);

View File

@ -44,12 +44,12 @@ See https://github.com/adobe-type-tools/cmap-resources
<div id="toolbarSidebar">
<div id="toolbarSidebarLeft">
<div id="sidebarViewButtons" class="splitToolbarButton toggled" role="radiogroup">
<button id="viewThumbnail" class="toolbarButton toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="pdfjs-thumbs-button" role="radio" aria-checked="true" aria-controls="thumbnailView">
<span data-l10n-id="pdfjs-thumbs-button-label">Thumbnails</span>
</button>
<button id="viewOutline" class="toolbarButton" title="Show Document Outline (double-click to expand/collapse all items)" tabindex="3" data-l10n-id="pdfjs-document-outline-button" role="radio" aria-checked="false" aria-controls="outlineView">
<span data-l10n-id="pdfjs-document-outline-button-label">Document Outline</span>
</button>
<button id="viewThumbnail" class="toolbarButton toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="pdfjs-thumbs-button" role="radio" aria-checked="true" aria-controls="thumbnailView">
<span data-l10n-id="pdfjs-thumbs-button-label">Thumbnails</span>
</button>
<button id="viewAttachments" class="toolbarButton" title="Show Attachments" tabindex="4" data-l10n-id="pdfjs-attachments-button" role="radio" aria-checked="false" aria-controls="attachmentsView">
<span data-l10n-id="pdfjs-attachments-button-label">Attachments</span>
</button>
@ -273,17 +273,14 @@ See https://github.com/adobe-type-tools/cmap-resources
<div id="toolbarContainer">
<div id="toolbarViewer">
<div id="toolbarViewerLeft">
<button id="sidebarToggle" class="toolbarButton" tabindex="11" aria-expanded="false" aria-controls="sidebarContainer">
<span>目录</span>
</button>
<!-- <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="11" data-l10n-id="pdfjs-toggle-sidebar-button" aria-expanded="false" aria-controls="sidebarContainer">
<button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="11" data-l10n-id="pdfjs-toggle-sidebar-button" aria-expanded="false" aria-controls="sidebarContainer">
<span data-l10n-id="pdfjs-toggle-sidebar-button-label">Toggle Sidebar</span>
</button> -->
<!-- <div class="toolbarButtonSpacer"></div> -->
<button style="display: none;" id="viewFind" class="toolbarButton" title="Find in Document" tabindex="12" data-l10n-id="pdfjs-findbar-button" aria-expanded="false" aria-controls="findbar">
</button>
<div class="toolbarButtonSpacer"></div>
<button id="viewFind" class="toolbarButton" title="Find in Document" tabindex="12" data-l10n-id="pdfjs-findbar-button" aria-expanded="false" aria-controls="findbar">
<span data-l10n-id="pdfjs-findbar-button-label">Find</span>
</button>
<div class="splitToolbarButton toolpageBtn">
<div class="splitToolbarButton hiddenSmallView">
<button class="toolbarButton" title="Previous Page" id="previous" tabindex="13" data-l10n-id="pdfjs-previous-button">
<span data-l10n-id="pdfjs-previous-button-label">Previous</span>
</button>
@ -292,12 +289,12 @@ See https://github.com/adobe-type-tools/cmap-resources
<span data-l10n-id="pdfjs-next-button-label">Next</span>
</button>
</div>
<span class="loadingInput start" style="display: none;">
<span class="loadingInput start">
<input type="number" id="pageNumber" class="toolbarField" title="Page" value="1" min="1" tabindex="15" data-l10n-id="pdfjs-page-input" autocomplete="off">
</span>
<span id="numPages" class="toolbarLabel" style="display: none;"></span>
<span id="numPages" class="toolbarLabel"></span>
</div>
<div id="toolbarViewerRight" style="display: none;">
<div id="toolbarViewerRight">
<div id="editorModeButtons" class="splitToolbarButton toggled" role="radiogroup">
<button id="editorHighlight" class="toolbarButton" hidden="true" disabled="disabled" title="Highlight" role="radio" aria-checked="false" aria-controls="editorHighlightParamsToolbar" tabindex="31" data-l10n-id="pdfjs-editor-highlight-button">
<span data-l10n-id="pdfjs-editor-highlight-button-label">Highlight</span>

View File

@ -7407,8 +7407,7 @@ class PDFSidebar {
this.isInitialViewSet = false;
this.isInitialEventDispatched = false;
this.#hideUINotification(true);
// this.switchView(SidebarView.THUMBS);默认开启书签模式
this.switchView(SidebarView.OUTLINE);
this.switchView(SidebarView.THUMBS);
this.outlineButton.disabled = false;
this.attachmentsButton.disabled = false;
this.layersButton.disabled = false;
@ -13096,17 +13095,12 @@ const PDFViewerApplication = {
this.pdfViewer.scrollMode = scroll;
}
if (isValidSpreadMode(spread)) {
//默认双页
this.pdfViewer.spreadMode = spread;
//默认双页
// this.pdfViewer.spreadMode = 1;
}
};
this.isInitialViewSet = true;
this.pdfSidebar?.setInitialView(sidebarView);
//默认双页
setViewerModes(scrollMode, spreadMode);
// setViewerModes(scrollMode, 1);
if (this.initialBookmark) {
setRotation(this.initialRotation);
delete this.initialRotation;

View File

@ -26,13 +26,3 @@ export class imChat {
// 获取腾讯im-chat appid 签名
static getTxCloudSign = data => ApiService.publicHttp('/system/user/txCloudSign', data)
}
// zdg: url跳转-后端存储
export class toLink {
// 设置链接-返回key(默认15分钟有效时间)
static setLink = url => ApiService.publicHttp(`/smarttalk/toLink`, {url}, 'post', null, 'form')
// 获取链接
static getLink = key => ApiService.publicHttp(`/smarttalk/toLink/${key}`)
// 删除链接-缓存
static delLink = key => ApiService.publicHttp(`/smarttalk/toLink/${key}`, null, 'delete')
}

View File

@ -147,11 +147,10 @@ export function updateSmartClassReserv(data) {
})
}
//查询课程预约
export function getSelfReserv(params) {
export function getSelfReserv() {
return request({
url: '/smarttalk/classReserv/getSelfReserv',
method: 'get',
params
method: 'get'
})
}
export function deleteSmartReserv(id) {

View File

@ -18,14 +18,6 @@ export function listClassworkdataByDeadDate(query) {
})
}
// 查询classworkdata详细
export function getClassworkdata(id) {
return request({
url: '/education/classworkdata/' + id,
method: 'get'
})
}
// 查询classworkdata列表 班级作业列表
export function listClassworkdata(query) {
return request({
@ -35,15 +27,6 @@ export function listClassworkdata(query) {
})
}
// 新接口---查询classworkdata列表 班级作业列表
export function listClassworkdataNew(query) {
return request({
url: '/education/classworkdata/new/list',
method: 'get',
params: query
})
}
// 查询entpcoursework列表 课程作业列表
export function listEntpcoursework(query) {
return request({
@ -71,16 +54,6 @@ export function updateClassworkeval(data) {
})
}
// 修改classworkeval
export function updateClassworkevalList(data) {
return request({
url: '/education/classworkeval/updateList',
method: 'put',
data: data
})
}
// 修改classworkdata
export function updateClassworkdata(data) {
return request({
@ -89,13 +62,6 @@ export function updateClassworkdata(data) {
data: data
})
}
export function updateClassWorkDataAutoFinish(data) {
return request({
url: '/education/classworkdata/updAutoFinish',
method: 'put',
data: data
})
}
// 修改classwork
export function updateClasswork(data) {
@ -125,90 +91,90 @@ export function addClassworkeval(data) {
// 查询evaluationclue列表
export function listEvaluationclue(query) {
return request({
url: '/education/evaluationclue/list',
method: 'get',
params: query
url: '/education/evaluationclue/list',
method: 'get',
params: query
})
}
// 查询evaluationclue详细
export function getEvaluationclue(id) {
return request({
url: '/education/evaluationclue/' + id,
method: 'get'
url: '/education/evaluationclue/' + id,
method: 'get'
})
}
// 新增evaluationclue
export function addEvaluationclueReturnId(data) {
return request({
url: '/education/evaluationclue/addReturnId',
method: 'post',
data: data
url: '/education/evaluationclue/addReturnId',
method: 'post',
data: data
})
}
// 新增evaluationclue
export function addEvaluationclue(data) {
return request({
url: '/education/evaluationclue',
method: 'post',
data: data
url: '/education/evaluationclue',
method: 'post',
data: data
})
}
// 修改evaluationclue
export function updateEvaluationclue(data) {
return request({
url: '/education/evaluationclue',
method: 'put',
data: data
url: '/education/evaluationclue',
method: 'put',
data: data
})
}
// 删除evaluationclue
export function delEvaluationclue(id) {
return request({
url: '/education/evaluationclue/' + id,
method: 'delete'
url: '/education/evaluationclue/' + id,
method: 'delete'
})
}
// 新增evaluationclue保存base64图片
export function saveBase64File(data) {
return request({
url: '/education/evaluationclue/saveBase64File',
method: 'post',
data: data
url: '/education/evaluationclue/saveBase64File',
method: 'post',
data: data
})
}
// 新增evaluationclue上传
export function saveEvaluationClueUploadFile(data) {
return request({
url: '/education/evaluationclue/saveUploadFile',
method: 'post',
data: data
url: '/education/evaluationclue/saveUploadFile',
method: 'post',
data: data
})
}
// 读取文件内容
export function readFile(data) {
return fetch(import.meta.env.VITE_APP_RES_FILE_PATH + data.cluelink, {
method: "get",
headers: {
'Content-Type': 'text/plain', // 请求头设置为纯文本
'Accept': 'text/plain' // 接受头设置为纯文本
},
method: "get",
headers: {
'Content-Type': 'text/plain', // 请求头设置为纯文本
'Accept': 'text/plain' // 接受头设置为纯文本
},
})
.then(response => response.text())
.then(text => {
return Promise.resolve(text);
})
.catch(error => {
console.error('读取文件出错:', error);
return Promise.reject();
});
.then(response => response.text())
.then(text => {
return Promise.resolve(text);
})
.catch(error => {
console.error('读取文件出错:', error);
return Promise.reject();
});
/*return request({
url: '/education/evaluationclue/readFile',
method: 'post',

View File

@ -24,13 +24,6 @@ export const getPrepareById = (id) => {
})
}
export const addFileToKj = (id) => {
return request({
url: '/smarttalk/file/addFileToKj/' + id,
method: 'get'
})
}
export function deleteSmarttalk(id) {
return request({
url: '/smarttalk/file/' + id,
@ -60,14 +53,3 @@ export const moveSmarttalk = (params) => {
params
})
}
export const addFileToPrepareThird = (data) => {
return request({
url: '/smarttalk/file/addFileToPrepareThird',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data
})
}

View File

@ -1,27 +0,0 @@
import request from '@/utils/request'
// 查询分析列表
export function getEvaluationclueList(params) {
return request({
url: '/education/evaluationclue/list',
method: 'get',
params
})
}
//修改分析内容
export function updateEvaluationclue(data) {
return request({
url: '/education/evaluationclue',
method: 'put',
data
})
}
// 新增分析
export function addEvaluationclue(data) {
return request({
url: '/education/evaluationclue',
method: 'post',
data
})
}

View File

@ -9,14 +9,6 @@ export function listClasscourse(query) {
})
}
export function listClasscourseNew(query) {
return request({
url: '/education/classcourse/new/list',
method: 'get',
params: query
})
}
// 查询classcourse详细
export function getClasscourse(id) {
return request({

View File

@ -1,9 +1,9 @@
@font-face {
font-family: "iconfont"; /* Project id 2794390 */
src: url('iconfont.woff2?t=1732002934577') format('woff2'),
url('iconfont.woff?t=1732002934577') format('woff'),
url('iconfont.ttf?t=1732002934577') format('truetype'),
url('iconfont.svg?t=1732002934577#iconfont') format('svg');
src: url('iconfont.woff2?t=1727403518918') format('woff2'),
url('iconfont.woff?t=1727403518918') format('woff'),
url('iconfont.ttf?t=1727403518918') format('truetype'),
url('iconfont.svg?t=1727403518918#iconfont') format('svg');
}
.iconfont {
@ -14,38 +14,6 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-a-shiyanpingshiyanyanjiu:before {
content: "\e9a0";
}
.icon-banji2:before {
content: "\e6c0";
}
.icon-set:before {
content: "\e691";
}
.icon-shouye:before {
content: "\e637";
}
.icon-gongzuotai:before {
content: "\e690";
}
.icon-A1:before {
content: "\e635";
}
.icon-A:before {
content: "\e6ef";
}
.icon--kejian:before {
content: "\e6a3";
}
.icon-a-1_jiaoxuefansi:before {
content: "\e6cd";
}

File diff suppressed because one or more lines are too long

View File

@ -1,66 +1,10 @@
{
"id": "2794390",
"name": "文枢2.1",
"name": "electron",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "41507853",
"name": "实验瓶",
"font_class": "a-shiyanpingshiyanyanjiu",
"unicode": "e9a0",
"unicode_decimal": 59808
},
{
"icon_id": "1017928",
"name": "班级",
"font_class": "banji2",
"unicode": "e6c0",
"unicode_decimal": 59072
},
{
"icon_id": "376364",
"name": "设置",
"font_class": "set",
"unicode": "e691",
"unicode_decimal": 59025
},
{
"icon_id": "5835474",
"name": "首页",
"font_class": "shouye",
"unicode": "e637",
"unicode_decimal": 58935
},
{
"icon_id": "19108133",
"name": "工作台",
"font_class": "gongzuotai",
"unicode": "e690",
"unicode_decimal": 59024
},
{
"icon_id": "11657531",
"name": "A",
"font_class": "A1",
"unicode": "e635",
"unicode_decimal": 58933
},
{
"icon_id": "12688893",
"name": "A",
"font_class": "A",
"unicode": "e6ef",
"unicode_decimal": 59119
},
{
"icon_id": "6571029",
"name": "课件",
"font_class": "-kejian",
"unicode": "e6a3",
"unicode_decimal": 59043
},
{
"icon_id": "37027693",
"name": "教学反思",

View File

@ -14,22 +14,6 @@
/>
<missing-glyph />
<glyph glyph-name="a-shiyanpingshiyanyanjiu" unicode="&#59808;" d="M404.602646 837.887844l-16.783367-7.405737-128.245981-56.577561-16.787416-7.405736-16.787416-7.405737 100.716399-228.298334a18.342257 18.342257 0 0 1 24.189104-9.37763l161.816764 71.384985a18.338207 18.338207 0 0 1 9.377631 24.189104L421.390062 845.293581l-16.787416-7.405737zM422.325396 869.563666L202.995737 772.80725A18.354404 18.354404 0 0 1 193.614058 748.614097l25.735846-58.342952a18.350355 18.350355 0 0 1 24.193153-9.38168L462.872715 777.645881a18.350355 18.350355 0 0 1 9.38168 24.193153l-25.739895 58.347001a18.342257 18.342257 0 0 1-24.189104 9.377631zM921.226676 261.831663L517.555315 623.259148l-12.240318-13.669638-161.812715-71.384985-18.346306 0.076932 1.886863-540.157186c-0.149815-39.065362 14.386322-70.158119 40.940079-87.540747 26.549708-17.37858 60.853372-18.265324 96.590404-2.498272L895.547517 99.994654c35.765376 15.77515 58.193137 41.677008 63.157289 72.935776 4.956053 31.254719-8.353218 62.833364-37.47813 88.901233zM921.226676 261.831663l-136.445333 122.168337H325.694501l1.348338-385.879778c-0.149815-39.065362 14.386322-70.158119 40.940079-87.540747 26.549708-17.37858 60.853372-18.265324 96.590404-2.498272l430.974195 191.909402c35.765376 15.77515 58.193137 41.677008 63.157289 72.935776 4.956053 31.258768-8.353218 62.837413-37.47813 88.905282z" horiz-adv-x="1039" />
<glyph glyph-name="banji2" unicode="&#59072;" d="M865.793707 531.61984c0 12.357973-10.03008 22.386347-22.386347 22.386347-12.357973 0-22.386347-10.03008-22.386347-22.386347 0-61.743787-50.19136-111.93344-111.935147-111.93344s-111.935147 50.19136-111.935147 111.93344c0 12.357973-10.03008 22.386347-22.386347 22.386347-12.357973 0-22.386347-10.03008-22.386347-22.386347 0-86.413653 70.294187-156.70784 156.70784-156.70784S865.793707 445.206187 865.793707 531.61984zM1059.054933 208.631467c58.876587 23.237973 99.48672 81.08544 99.48672 145.916587 0 71.637333-47.684267 132.082347-115.24608 150.528 0.67072 8.820053 1.611093 17.55136 1.611093 26.550613 0 185.183573-150.662827 335.802027-335.802027 335.802027-185.183573 0-335.802027-150.618453-335.802027-335.802027 0-9.043627 1.477973-17.73056 2.194773-26.594987-67.428693-18.536107-115.024213-78.93504-115.024213-150.483627 0-64.832853 40.564053-122.68032 99.442347-145.916587L287.470933 1.59744c-4.118187-11.685547 2.059947-24.446293 13.70112-28.520107 2.46272-0.84992 4.969813-1.252693 7.386453-1.252693 9.222827 0 17.90976 5.77536 21.133653 14.999893l80.413013 229.643947c0.402773 1.02912 0.67072 2.148693 0.940373 3.268267 2.46272 11.55072-4.432213 23.058773-15.849813 26.238293-2.776747 0.807253-5.59616 0.896-8.193707 0.761173-47.72864 13.298347-81.75616 57.623893-81.75616 107.815253 0 49.206613 31.43168 91.24864 76.741973 106.069333 25.879893-117.306027 113.322667-215.048533 230.137173-250.374827l-72.174933-206.226773c-4.118187-11.685547 2.059947-24.446293 13.745493-28.520107 2.46272-0.896 4.92544-1.252693 7.386453-1.252693 9.268907 0 17.865387 5.77536 21.133653 14.999893l80.18944 229.15072c2.10432 6.045013 1.56672 12.670293-1.611093 18.26816-3.089067 5.59616-8.46336 9.581227-14.73024 10.970453-132.125013 29.146453-227.985067 148.60288-227.985067 283.997867 0 160.469333 130.56 291.027627 291.027627 291.027627s291.027627-130.56 291.027627-291.027627c0-12.043947-0.761173-23.86432-2.148693-35.551573-14.327467-116.993707-97.158827-213.03296-211.10784-244.642133-5.95456-1.657173-10.970453-5.686613-13.88032-11.19232-2.82112-5.46304-3.31264-11.910827-1.252693-17.73056l81.62304-233.270613c3.268267-9.222827 11.864747-14.999893 21.133653-14.999893 2.46272 0 4.92544 0.3584 7.386453 1.252693 11.685547 4.07552 17.819307 16.83456 13.745493 28.520107l-74.011307 211.46624c110.052693 38.99904 190.55616 131.76832 214.95808 245.225813 45.535573-14.685867 77.190827-56.818347 77.190827-106.15808 0-50.773333-34.833067-95.501653-83.457707-108.30848-2.194773 0-4.522667-0.31232-6.760107-1.02912-11.461973-3.403093-18.044587-15.849813-15.179093-27.446613 0.402773-1.431893 0.896-2.776747 1.477973-4.118187l79.428267-226.822827c3.223893-9.222827 11.820373-14.999893 21.087573-14.999893 2.46272 0 4.969813 0.402773 7.432533 1.252693 11.641173 4.07552 17.819307 16.83456 13.70112 28.520107L1059.054933 208.631467z" horiz-adv-x="1366" />
<glyph glyph-name="set" unicode="&#59025;" d="M512 558.182c-115.665 0-209.455-93.789-209.455-209.455 0-115.665 93.789-209.455 209.455-209.455s209.455 93.789 209.455 209.455c0 115.665-93.789 209.455-209.455 209.455zM512 185.818c-89.972 0-162.909 72.937-162.909 162.909 0 89.972 72.937 162.909 162.909 162.909s162.909-72.937 162.909-162.909c0-89.972-72.937-162.909-162.909-162.909zM907.636 465.091l-66.746 0c-4.143 11.59-8.797 22.9-14.057 33.932l47.197 47.197c18.199 18.199 18.199 47.663 0 65.815l-98.723 98.723c-18.199 18.199-47.663 18.199-65.815 0l-47.43-47.43c-10.938 5.213-22.109 10.1-33.652 14.196l0 66.839c0 25.693-20.852 46.545-46.545 46.545l-139.636 0c-25.693 0-46.545-20.852-46.545-46.545l0-66.839c-11.59-4.096-22.807-8.89-33.792-14.15l-47.383 47.383c-18.153 18.199-47.663 18.199-65.815 0l-98.723-98.723c-18.199-18.199-18.199-47.663 0-65.815l47.29-47.29c-5.26-10.985-10.1-22.249-14.196-33.839l-66.7 0c-25.693 0-46.545-20.852-46.545-46.545l0-139.636c0-25.693 20.852-46.545 46.545-46.545l66.746 0c4.143-11.59 8.797-22.9 14.057-33.885l-47.197-47.197c-18.199-18.153-18.199-47.663 0-65.815l98.723-98.723c18.199-18.199 47.663-18.199 65.815 0l47.43 47.43c10.938-5.213 22.109-10.1 33.652-14.196l0-66.886c0-25.693 20.852-46.545 46.545-46.545l139.636 0c25.693 0 46.545 20.852 46.545 46.545l0 66.746c11.59 4.143 22.9 8.797 33.885 14.057l47.197-47.197c18.199-18.199 47.663-18.199 65.815 0l98.723 98.723c18.199 18.199 18.199 47.663 0 65.815l-47.43 47.43c5.213 10.938 10.1 22.109 14.196 33.652l66.932 0c25.693 0 46.545 20.852 46.545 46.545l0 139.636c0 25.74-20.852 46.592-46.545 46.592zM907.636 302.182c0-12.847-10.426-23.273-23.273-23.273l-78.243 0c-7.54-31.837-20.108-61.673-36.817-88.762l55.343-55.389c9.076-9.076 9.076-23.831 0-32.908l-65.815-65.815c-9.076-9.076-23.831-9.076-32.908 0l-55.343 55.343c-27.089-16.71-56.925-29.277-88.762-36.817l0-78.196c0-12.847-10.426-23.273-23.273-23.273l-93.091 0c-12.847 0-23.273 10.426-23.273 23.273l0 78.243c-31.837 7.54-61.673 20.108-88.762 36.817l-55.343-55.343c-9.076-9.076-23.831-9.076-32.908 0l-65.815 65.815c-9.076 9.076-9.076 23.831 0 32.908l55.343 55.389c-16.71 27.043-29.277 56.879-36.817 88.716l-78.243 0c-12.847 0-23.273 10.426-23.273 23.273l0 93.091c0 12.847 10.426 23.273 23.273 23.273l78.243 0c7.54 31.837 20.108 61.673 36.817 88.762l-55.389 55.343c-9.076 9.076-9.076 23.831 0 32.908l65.815 65.815c9.076 9.076 23.831 9.076 32.908 0l55.343-55.343c27.136 16.71 56.972 29.277 88.809 36.817l0 78.243c0 12.847 10.426 23.273 23.273 23.273l93.091 0c12.847 0 23.273-10.426 23.273-23.273l0-78.243c31.837-7.54 61.673-20.108 88.762-36.817l55.343 55.343c9.076 9.076 23.831 9.076 32.908 0l65.815-65.815c9.076-9.076 9.076-23.831 0-32.908l-55.343-55.343c16.71-27.089 29.277-56.879 36.817-88.762l78.243 0c12.847 0 23.273-10.426 23.273-23.273l0-93.091z" horiz-adv-x="1024" />
<glyph glyph-name="shouye" unicode="&#58935;" d="M960 448c-6.4 0-12.9 1.9-18.6 6L512 760.7 82.6 454c-14.4-10.2-34.4-6.9-44.6 7.5-10.3 14.4-6.9 34.4 7.5 44.6l448 320c11.1 7.9 26.1 7.9 37.2 0l448-320c14.4-10.3 17.7-30.3 7.5-44.6-6.4-8.8-16.2-13.5-26.2-13.5zM608-64H416c-17.7 0-32 14.3-32 32V256c0 17.7 14.3 32 32 32h192c17.7 0 32-14.3 32-32v-288c0-17.7-14.3-32-32-32z m-160 64h128V224H448v-224zM832-64H192c-52.9 0-96 43.1-96 96V512c0 17.7 14.3 32 32 32s32-14.3 32-32v-480c0-17.6 14.4-32 32-32h640c17.6 0 32 14.4 32 32V512c0 17.7 14.3 32 32 32s32-14.3 32-32v-480c0-52.9-43.1-96-96-96z" horiz-adv-x="1024" />
<glyph glyph-name="gongzuotai" unicode="&#59024;" d="M320.284444 810.666667a85.333333 85.333333 0 0 0 85.333334-85.333334v-234.951111H170.666667a85.333333 85.333333 0 0 0-85.333334 85.333334V725.333333a85.333333 85.333333 0 0 0 85.333334 85.333334h149.617777m0 85.333333H170.666667a170.666667 170.666667 0 0 1-170.666667-170.666667v-149.617777a170.666667 170.666667 0 0 1 170.666667-170.666667h320.284444V725.333333a170.666667 170.666667 0 0 1-170.666667 170.666667zM405.617778 277.617778V42.666667a85.333333 85.333333 0 0 0-85.333334-85.333334H170.666667a85.333333 85.333333 0 0 0-85.333334 85.333334v149.617777a85.333333 85.333333 0 0 0 85.333334 85.333334h234.951111m85.333333 85.333333H170.666667a170.666667 170.666667 0 0 1-170.666667-170.666667V42.666667a170.666667 170.666667 0 0 1 170.666667-170.666667h149.617777a170.666667 170.666667 0 0 1 170.666667 170.666667V362.951111zM853.333333 277.617778a85.333333 85.333333 0 0 0 85.333334-85.333334V42.666667a85.333333 85.333333 0 0 0-85.333334-85.333334h-149.617777a85.333333 85.333333 0 0 0-85.333334 85.333334v234.951111H853.333333m0 85.333333H533.048889V42.666667a170.666667 170.666667 0 0 1 170.666667-170.666667H853.333333a170.666667 170.666667 0 0 1 170.666667 170.666667v149.617777a170.666667 170.666667 0 0 1-170.666667 170.666667zM853.333333 810.666667a85.333333 85.333333 0 0 0 85.333334-85.333334v-149.617777a85.333333 85.333333 0 0 0-85.333334-85.333334h-234.951111V725.333333a85.333333 85.333333 0 0 0 85.333334 85.333334H853.333333m0 85.333333h-149.617777a170.666667 170.666667 0 0 1-170.666667-170.666667v-320.284444H853.333333a170.666667 170.666667 0 0 1 170.666667 170.666667V725.333333a170.666667 170.666667 0 0 1-170.666667 170.666667z" horiz-adv-x="1024" />
<glyph glyph-name="A1" unicode="&#58933;" d="M449 339h121.5L507.5 505.5zM782 834H242C143 834 62 753 62 654v-540c0-99 81-180 180-180h540c99 0 180 81 180 180V654c0 99-81 180-180 180z m-139.5-675l-40.5 103.5H422L381.5 159H287L462.5 609H557l180-450h-94.5z" horiz-adv-x="1024" />
<glyph glyph-name="A" unicode="&#59119;" d="M841.589844 779.507813a65.917969 65.917969 0 0 0 65.917969-65.917969v-659.179688a65.917969 65.917969 0 0 0-65.917969-65.917969H182.410156a65.917969 65.917969 0 0 0-65.917969 65.917969V713.589844a65.917969 65.917969 0 0 0 65.917969 65.917969h659.179688z m0-49.438477H182.410156a16.479492 16.479492 0 0 1-16.21582-13.513183L165.930664 713.589844v-659.179688a16.479492 16.479492 0 0 1 13.513183-16.21582L182.410156 37.930664h659.179688a16.479492 16.479492 0 0 1 16.21582 13.513183L858.069336 54.410156V713.589844a16.479492 16.479492 0 0 1-13.513183 16.21582L841.589844 730.069336zM539.059326 614.712891l188.85498-461.425782h-69.543456l-53.822022 139.746094H411.606933L360.916016 153.287109H296.118652l177.1875 461.425782h65.786133z m-34.277344-48.449707c-5.899658-29.597168-14.172364-58.996582-24.884033-88.165284L429.207031 342.80127h156.456299l-48.153076 127.781982-4.779053 12.689209a1170.900879 1170.900879 0 0 0-27.982178 82.990723z" horiz-adv-x="1024" />
<glyph glyph-name="-kejian" unicode="&#59043;" d="M873.813333 638.293333c27.306667 0 47.786667-30.72 47.786667-64.853333v-436.906667c0-42.666667-27.306667-75.093333-64.853333-75.093333H573.44c-23.893333 0-44.373333-22.186667-44.373333-46.08h-29.013334c0 23.893333-18.773333 46.08-44.373333 46.08H175.786667C136.533333 63.146667 102.4 93.866667 102.4 136.533333V573.44c0 34.133333 27.306667 64.853333 54.613333 64.853333h716.8zM457.386667 97.28c23.893333 0 44.373333-10.24 58.026666-27.306667 13.653333 17.066667 34.133333 27.306667 58.026667 27.306667h281.6c17.066667 0 30.72 15.36 30.72 40.96V573.44c0 10.24-3.413333 18.773333-6.826667 25.6-3.413333 3.413333-5.12 5.12-6.826666 5.12h-716.8c-6.826667 0-18.773333-15.36-18.773334-30.72v-436.906667c0-22.186667 18.773333-40.96 39.253334-40.96h281.6zM837.973333 660.48c0 34.133333-22.186667 64.853333-54.613333 64.853333H609.28c-40.96 0-75.093333-23.893333-93.866667-58.026666-18.773333 34.133333-54.613333 58.026667-93.866666 58.026666H247.466667c-32.426667 0-61.44-30.72-61.44-64.853333l6.826666-494.933333c0-29.013333 23.893333-51.2 52.906667-51.2h213.333333c23.893333 0 44.373333-22.186667 44.373334-46.08h29.013333c0 23.893333 18.773333 46.08 44.373333 46.08h211.626667c29.013333 0 52.906667 23.893333 52.906667 52.906666V660.48zM293.546667 513.706667c-8.533333 0-17.066667 8.533333-17.066667 20.48s6.826667 20.48 17.066667 20.48h158.72c8.533333 0 17.066667-8.533333 17.066666-20.48s-6.826667-20.48-17.066666-20.48h-158.72zM293.546667 426.666667c-8.533333 0-17.066667 8.533333-17.066667 20.48s6.826667 20.48 17.066667 20.48h158.72c8.533333 0 17.066667-8.533333 17.066666-20.48s-6.826667-20.48-17.066666-20.48h-158.72zM293.546667 339.626667c-8.533333 0-17.066667 8.533333-17.066667 20.48s6.826667 20.48 17.066667 20.48h158.72c8.533333 0 17.066667-8.533333 17.066666-20.48s-6.826667-20.48-17.066666-20.48h-158.72zM583.68 513.706667c-8.533333 0-17.066667 8.533333-17.066667 20.48s6.826667 20.48 17.066667 20.48h158.72c8.533333 0 17.066667-8.533333 17.066667-20.48s-6.826667-20.48-17.066667-20.48h-158.72zM583.68 426.666667c-8.533333 0-17.066667 8.533333-17.066667 20.48s6.826667 20.48 17.066667 20.48h158.72c8.533333 0 17.066667-8.533333 17.066667-20.48s-6.826667-20.48-17.066667-20.48h-158.72zM583.68 339.626667c-8.533333 0-17.066667 8.533333-17.066667 20.48s6.826667 20.48 17.066667 20.48h158.72c8.533333 0 17.066667-8.533333 17.066667-20.48s-6.826667-20.48-17.066667-20.48h-158.72z" horiz-adv-x="1024" />
<glyph glyph-name="a-1_jiaoxuefansi" unicode="&#59085;" d="M410.8288 6.4512h195.9936v-63.2832H410.8288zM833.2288 645.4272A348.16 348.16 0 0 1 593.92 832.8192 347.4432 347.4432 0 0 1 509.952 844.8h0.512a311.0912 311.0912 0 0 1-78.4384-10.24 365.8752 365.8752 0 0 1-182.1696-96.6656 345.088 345.088 0 0 1-80.6912-370.4832 366.4896 366.4896 0 0 1 115.3024-155.7504 163.84 163.84 0 0 0 43.8272-54.272A180.224 180.224 0 0 0 342.1184 87.04v-13.5168h327.68v18.432a142.5408 142.5408 0 0 0 18.432 70.0416 163.84 163.84 0 0 0 48.3328 56.6272 357.2736 357.2736 0 0 1 96.8704 121.856 338.6368 338.6368 0 0 1 34.6112 151.3472 348.16 348.16 0 0 1-34.816 153.6z m-25.7024-153.6A278.6304 278.6304 0 0 0 699.392 268.1856a239.8208 239.8208 0 0 1-62.2592-74.4448 233.0624 233.0624 0 0 1-20.48-56.6272h-209.92a224.1536 224.1536 0 0 1-78.4384 126.464 274.7392 274.7392 0 0 0-94.6176 128.7168 303.3088 303.3088 0 0 0-13.824 158.1056 270.5408 270.5408 0 0 0 78.4384 146.7392 305.152 305.152 0 0 0 147.5584 81.3056c20.48 0 43.8272 2.2528 64.6144 2.2528h-0.512a333.9264 333.9264 0 0 0 66.9696-6.7584 283.3408 283.3408 0 0 0 117.6576-56.6272 294.8096 294.8096 0 0 0 82.944-101.6832A305.5616 305.5616 0 0 0 807.5264 496.64zM385.024 578.56a27.8528 27.8528 0 0 1 0-55.7056 27.8528 27.8528 0 1 1 0 55.7056zM385.024 469.6064a27.8528 27.8528 0 0 1 0-55.7056 27.8528 27.8528 0 1 1 0 55.7056zM456.704 467.456h206.6432v-55.6032H456.704zM456.704 578.6624h206.6432v-55.5008H456.704z" horiz-adv-x="1024" />
<glyph glyph-name="iconfontzhizuobiaozhunbduan3-1" unicode="&#65711;" d="M381.8288 877.098511c42.635029 11.310603 86.403882 17.002232 130.205481 17.002232 68.479651 0 135.072325-13.469779 197.999516-40.101323l-8.362458-369.71439L381.8288 877.098511 381.8288 877.098511zM71.201134 640.609379c22.278399 38.284955 49.218982 73.313749 80.134085 104.090707 48.393173 48.396243 104.980983 85.958744 168.285774 111.666242l255.614725-267.305999L71.201134 640.609379 71.201134 640.609379zM18.900977 253.724934c-11.276834 42.463114-17.000186 86.234013-17.000186 130.276089 0 68.548212 13.502525 135.140886 40.099277 197.933001l369.749182-8.293897L18.900977 253.724934 18.900977 253.724934zM255.39011-56.798355c-38.010709 22.1423-72.968895 49.079812-104.021122 80.134085-48.428989 48.325635-85.993536 104.911398-111.664196 168.283728l267.302929 255.513418L255.39011-56.798355 255.39011-56.798355zM512.034281-126.100744c-68.481697 0-135.072325 13.470803-197.999516 40.031738l8.362458 369.71439 319.843562-392.81348C599.535147-120.411161 555.80211-126.100744 512.034281-126.100744L512.034281-126.100744zM448.830797 178.903855l504.036631-51.54905c-22.107507-37.940101-48.976458-72.900334-80.167854-104.019075-48.327682-48.464805-104.980983-86.029352-168.283728-111.734804L448.830797 178.903855 448.830797 178.903855zM612.320335 194.292334l392.81348 319.915193c11.276834-42.499953 16.966417-86.302575 16.966417-130.206504 0-68.753897-13.502525-135.282103-40.066531-198.002586L612.320335 194.292334 612.320335 194.292334zM768.60989 824.764586c37.975916-22.038946 73.004711-48.942689 104.089683-80.0645 48.464805-48.326659 86.027305-104.980983 111.664196-168.284751l-267.302929-255.580956L768.60989 824.764586 768.60989 824.764586z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 371 KiB

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -12,20 +12,15 @@ const props = defineProps({
type: String,
required: true,
default: ''
},
isWin: {
type: Boolean,
default: false
}
})
/**pdf文件地址 */
const pdfUrl = ref('');
/**pdfjs文件地址 */
const fileUrl = props.isWin ? props.url : getAppInstallUrl('pdfjs-dist/web/viewer.html', 'user', '\\out\\renderer', true) + "?file=" //
const fileUrl = getAppInstallUrl('pdfjs-dist/web/viewer.html', 'user', '\\out\\renderer', true) + "?file=" //
onMounted(() => {
/** 将传入的pdf地址进行编码防止中文识别错误 */
if(props.isWin) pdfUrl.value = fileUrl
else pdfUrl.value = fileUrl + encodeURIComponent(props.url) + '#pageMode=outline'
pdfUrl.value = fileUrl + encodeURIComponent(props.url)
})
</script>

View File

@ -122,8 +122,7 @@ const outerAi = [
title: '教学大模型',
secondTit: '中小学基础教学大模型',
img: new URL('../../../src/assets/images/ai-02.png', import.meta.url).href,
path: '/ais/aimoss'
disabled: true,
},
{
id: 3,

View File

@ -49,7 +49,7 @@ import { useGetSubject } from '@/hooks/useGetSubject'
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
// emit
const emit = defineEmits(['nodeClick', 'changeBook'])
const emit = defineEmits(['nodeClick'])
let useSubject = null
const subjectList = ref([])
const dialogVisible = ref(false)
@ -139,23 +139,16 @@ const handleNodeClick = (data) => {
* data : 当前节点数据
*/
let nodeData = cloneDeep(toRaw(data));
//label label
nodeData.label = nodeData.itemtitle
let parentNode
// children
if(nodeData.children){
//
parentNode = null
// null
let parent = {
id: nodeData.parentid,
label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle
}
else{
parentNode = {
id: nodeData.parentid,
label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle
}
}
const parentNode = nodeData.parentid ? parent : null
nodeData.parentNode = parentNode
let curData = {
textBook: {
@ -171,6 +164,7 @@ const handleNodeClick = (data) => {
sessionStore.set('subject.defaultExpandedKeys', defaultExpandedKeys)
sessionStore.set('subject.curNode', nodeData)
emit('nodeClick', curData)
}
onMounted( async () => {
treeLoading.value = true

View File

@ -1,74 +0,0 @@
<template>
<!-- 试题详情 -->
<el-drawer
v-model="activeExamInfoDrawer"
title="题目详情"
:with-header="false"
direction="rtl"
size="60%"
>
<div style="height: calc(100% - 50px);">
<el-scrollbar style="background: #F3F5F8;border-radius: 8px;">
<el-row class="drawer-main">
<el-col :span="24">
<span>{{activeExam.worktag}}</span>
<span style="margin-left: 4px" v-html="activeExam.titleFormat" ></span>
</el-col>
<el-col :span="24" style="padding: 4px" v-html="activeExam.workdescFormat">
</el-col>
<el-col :span="3" class="drawer-main-col"><em>答案</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.workanswerFormat"></el-col>
<el-col :span="3" class="drawer-main-col"><em>分析</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.method"></el-col>
<el-col :span="3" class="drawer-main-col"><em>解答</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.analyse"></el-col>
<el-col :span="3" class="drawer-main-col" ><em>点评</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.discuss"></el-col>
</el-row>
</el-scrollbar>
</div>
<div class="drawer-footer">
<el-button type="primary" @click="activeExamInfoDrawer = false">关闭</el-button>
</div>
</el-drawer>
</template>
<script setup name="examDetailsDrawerRef">
import {ref, reactive, watch} from 'vue'
const props = defineProps({
});
const activeExamInfoDrawer = ref(false);
const activeExam = ref({});
//
const acceptParams = (params) => {
activeExamInfoDrawer.value = true;
activeExam.value = params.activeExam;
}
defineExpose({
acceptParams
})
</script>
<style lang="scss" scoped>
.drawer-main{
margin: 1%;
padding: 2%;
display: flex;
text-align: left;
.drawer-main-col{
padding: 10px 0px;
}
}
.drawer-footer{
padding-top: 15px;
display: flex;
justify-content: flex-end;
box-sizing: border-box;
}
</style>

View File

@ -34,7 +34,7 @@ const getFileTypeIcon = () => {
gif: 'icon-gif',
txt: 'icon-txt',
rar: 'icon-rar',
apt: 'icon-A'
apt: 'icon-lunwen'
}
if (iconObj[name]) {
return '#' + iconObj[name]

View File

@ -55,15 +55,14 @@ import { saveByClassWorkArray } from '@/api/teaching/classwork'
import useUserStore from '@/store/modules/user'
import { getCurrentTime } from '@/utils/date'
import { uniqBy, groupBy } from 'lodash'
import { sessionStore } from '@/utils/store'
const model = defineModel({ type: Boolean, default: false })
const props = defineProps({
entpcourseid: {
default: ''
},
rows: {
default: []
row: {
default: ''
},
title: {
type: String,
@ -212,8 +211,6 @@ const delStudent = (index) => {
const onSubmit = (formEl) => {
if (!formEl) return
// id
const classRoomId = sessionStore.get('curr.curClassRoom.id')
formEl.validate((valid) => {
if (valid) {
/**
@ -225,46 +222,40 @@ const onSubmit = (formEl) => {
let ary = []
for (const value in gradeObj) {
// AIx web
for (var i = 0; i < props.rows.length; i++) {
let obj = {
id: 0,
parentid: props.rows[i].id,
classid: value,
classcourseid: 0, // id-ppt使
classReservId: classRoomId, // id
entpcourseid: props.entpcourseid,
studentlist: JSON.stringify(gradeObj[value]),
feedback: form.feedback,
workkey: '',
timelength: form.timelength,
weights: 1,
deaddate: form.deaddate,
workdate: getCurrentTime('YYYY-MM-DD'),
uniquekey: props.rows[i].uniquekey,
entpcourseworklist: '[' + props.rows[i].entpcourseworklist + ']',
needMsgNotifine: 'false',
msgkey: 'newclasswork',
//title: '',
title: props.rows[i].title,
msgcontent: '',
teachername: userInfo.nickName,
unixstamp: new Date().getTime(),
worktype: props.rows[i].worktype,
status: '1'
}
ary.push(obj)
let obj = {
id: 0,
parentid: props.row.id,
classid: value,
classcourseid: 0,
entpcourseid: props.entpcourseid,
studentlist: JSON.stringify(gradeObj[value]),
feedback: form.feedback,
workkey: '',
timelength: form.timelength,
weights: 1,
deaddate: form.deaddate,
workdate: getCurrentTime('YYYY-MM-DD'),
uniquekey: props.row.uniquekey,
entpcourseworklist: '[' + props.row.entpcourseworklist + ']',
needMsgNotifine: 'false',
msgkey: 'newclasswork',
title: '作业任务',
msgcontent: '',
teachername: userInfo.nickName,
unixstamp: new Date().getTime(),
worktype: props.row.worktype,
status: '1'
}
ary.push(obj)
}
console.log('ary->', ary)
setLoading.value = true
saveByClassWorkArray({
classworkarray: JSON.stringify(ary)
})
.then((res) => {
setLoading.value = false
ElMessage.success('操作成功')
emit('on-success', res)
emit('on-success', res.data)
cloneDialog(formEl)
})
.catch(() => {
@ -285,13 +276,11 @@ const cloneDialog = (formEl) => {
expandedKeys.value = []
formEl.resetFields()
model.value = false
emit('on-close')
}
onMounted(() => {
// 3
// form.deaddate = getCurrentTime('YYYY-MM-DD HH:mm')
form.deaddate = `${getCurrentTime('YYYY-MM-DD')} ${getCurrentTime('HH+3')}:${getCurrentTime('mm')}`;
//
form.deaddate = getCurrentTime('YYYY-MM-DD HH:mm')
})
</script>

View File

@ -1,35 +0,0 @@
<template>
<el-drawer v-model="model" class="preview-drawer" :modal="true" :destroy-on-close="true" :with-header="false" :append-to-body="true"
size="50%">
<div class="drawer-content">
<el-image v-for="(url,index) in srcList" :key="index" :src="url" lazy />
</div>
</el-drawer>
</template>
<script setup>
import {ref} from 'vue'
import {getImgPath} from "@/api/file/third";
const model = defineModel()
//
const srcList = ref([])
const init = (id) => {
srcList.value = []
getImgPath({itemId:id}).then(res => {
if(res.code === 200){
res.data.forEach(item => {
item.subsets.forEach(previewItem => {
previewItem.previewFiles.forEach(fileItem => {
srcList.value.push(fileItem.fileUrl)
})
})
})
}
})
}
defineExpose({
init
})
</script>

View File

@ -60,12 +60,10 @@
<el-button>{{ type == 'design' ? '形状' : '工具' }}</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="square">正方形</el-dropdown-item>
<el-dropdown-item command="rectangle">矩形</el-dropdown-item>
<el-dropdown-item command="diamond">菱形</el-dropdown-item>
<el-dropdown-item command="triangle">三角形</el-dropdown-item>
<el-dropdown-item command="circle">圆形</el-dropdown-item>
<el-dropdown-item command="elliptic">椭圆形</el-dropdown-item>
<el-dropdown-item command="line">线段</el-dropdown-item>
<el-dropdown-item command="arrow">箭头</el-dropdown-item>
<template v-if="type != 'design'">
@ -399,7 +397,7 @@ const scroll = reactive({
//
const showGrid = ref(false)
//
const readonly = ref(false)
// const readonly = ref(false)
//
const elReadonly = ref(false)
@ -677,8 +675,8 @@ const getCanvasBase64 = async () =>{
let base64 = await app.exportImage({
type: 'image/jpeg',
renderBg: exportRenderBackground.value,
paddingX: 10,
paddingY: 10,
paddingX: 0,
paddingY: 0,
onlySelected: exportOnlySelected.value,
backgroundColor: '#ffffff'
})

View File

@ -8,10 +8,9 @@
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'
import useUserStore from '@/store/modules/user'
const Remote = require('@electron/remote')
const userStore = useUserStore()
const { ipcRenderer } = window.electron || {}
@ -38,7 +37,6 @@ const closeWindow = () => {
ElMessageBox.confirm('确认退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
customClass: 'login-close-tool',
type: 'warning'
}).then(() => {
userStore.logOut().then(() => {
@ -49,16 +47,8 @@ const closeWindow = () => {
})
}).catch(() => { });
}
onMounted(() =>{
isMaxSize.value = Remote.getCurrentWindow().isMaximized()
})
</script>
<style>
.login-close-tool {
-webkit-app-region: no-drag;
}
</style>
<style lang="scss" scoped>
.header-tool {
width: 100%;

View File

@ -1,118 +0,0 @@
import { listEntpcoursework,getEvaluationclue } from '@/api/classTask';
import { processList } from '@/hooks/useProcessList';
const isJson = (str) => {
if (typeof str == 'string') {
try {
let obj = JSON.parse(str)
if (typeof obj == 'object' && obj) {
return true
} else {
return false
}
} catch (e) {
return false
}
}
}
/**
* @description 编辑作业配置
* @param {*} row
*/
export const editListItem = (row, courseObj) => {
return new Promise((resolve, reject) => {
// 作业内容编辑-跳转到设计界面-传参
let classtaskObj = {
id: '', //
bookName: '', // 课程名称
uniquekey: '', // 设计中的标题
title: '', // 设计中的说明
worktype: '', // 设计中的作业类型
quizlist: [], // 设计中的试题列表
chooseWorkLists: [],// 设计中的框架梳理list
fileHomeworkList: [],// 设计中的常规作业list
whiteboardObj: '',// 设计中的课堂展示对象
question: '', // 设计中的[课堂展示]的问题
};
console.log('编辑的行', row);
//重新更新[新任务]中右侧列表数据
var listCourseWork = [];
for (var i=0; i < row.entpcourseworklistarray.length; i++) {
listCourseWork.push(row.entpcourseworklistarray[i]);
}
if (listCourseWork.length > 0) {
classtaskObj.id= row.id; //
classtaskObj.bookObj = courseObj; // 教材对象
classtaskObj.bookName = row.evaltitle? row.evalparenttitle? row.evalparenttitle+'/'+row.evaltitle : row.evaltitle: row.evalparenttitle// 课程名称:单元/章节: 单元
classtaskObj.uniquekey= row.uniquekey; // 设计中的标题
classtaskObj.title= row.title; // 设计中的说明
classtaskObj.worktype= row.worktype; // 设计中的作业类型
classtaskObj.quizlist= []; // 设计中的试题列表
classtaskObj.chooseWorkLists = []; //设计中的 框架梳理list
classtaskObj.fileHomeworkList = []; //设计中的 常规作业list
classtaskObj.whiteboardObj = ''; //设计中的 课堂展示对象
if (row.worktype == '框架梳理') {
// 框架梳理对应只有一个内容
getEvaluationclue(listCourseWork[0].id).then(res => {
if ( res.data==null || res.data==undefined ) {
return ;
}
res.data.worktype = '框架梳理';
res.data.score = listCourseWork[0].score;
classtaskObj.chooseWorkLists.push(res.data);
//
// console.log('框架梳理', classtaskObj);
return resolve(classtaskObj);
});
}
else if (row.worktype == '习题训练') {
const ids = listCourseWork.map(item => item.id).join(",");
listEntpcoursework({ids: ids, pageSize: 50}).then(idres => {
// for (var i=0; i<idres.rows.length; i++) {
// idres.rows[i].titletext = idres.rows[i].title.replace(/<[^>]+>/g, '');
// }
// 格式化试题
if(idres.rows&&idres.rows.length > 0){
processList(idres.rows);
//重新将task中的分值更新
row.entpcourseworklistarray.forEach(item => {
const quizItem = idres.rows.find(quiz => quiz.id === item.id);
if (quizItem) {
quizItem.score = item.score;
quizItem.scoreOrigin = item.score;
}
});
classtaskObj.quizlist = idres.rows;
//
// console.log('习题训练', classtaskObj);
return resolve(classtaskObj);
}
})
}
else if (row.worktype == '课堂展示') {
console.log('row.课堂展示', row.workcodes);
const workcodes = JSON.parse(row.workcodes);
classtaskObj.whiteboardObj = JSON.stringify(workcodes.json);
classtaskObj.question = row.worktag;
// console.log('课堂展示', classtaskObj);
return resolve(classtaskObj);
}
else if (row.worktype == '常规作业') {
if(isJson(row.workcodes)){
classtaskObj.fileHomeworkList = JSON.parse(row.workcodes);
//
// console.log('常规作业', classtaskObj);
return resolve(classtaskObj);
}
}
}
});
}

View File

@ -0,0 +1,202 @@
import { nextTick, toRaw } from 'vue'
import useUserStore from '@/store/modules/user'
import { listEvaluation } from '@/api/subject'
const userStore = useUserStore()
const { edustage, edusubject } = userStore.user
let evaluationList = []; // 教材版本list
let subjectList = []; // 教材list
//当前教材ID
let curBookId = -1;
/**
* 外部链接初始化获取 跳转web端的 unitId 专用
* 暂时 初始化作业设计专用后期可能会取消
*/
export const useGetClassWork = async () => {
const params = {
edusubject,
edustage,
// entpcourseedituserid: userId,
itemgroup: 'textbook',
orderby: 'orderidx asc',
pageSize: 10000
}
if(localStorage.getItem('evaluationList')){
evaluationList = JSON.parse(localStorage.getItem('evaluationList'))
}else{
const { rows } = await listEvaluation(params)
localStorage.setItem('evaluationList', JSON.stringify(rows))
evaluationList = rows
}
//获取教材版本
await getSubject()
//上册
/**
* 不区分上下册
* 2024/08/20调整
*/
// volumeOne = data.filter(item => item.level == 1 && item.semester == '上册')
// volumeTwo = data.filter(item => item.level == 1 && item.semester == '下册')
getTreeData()
}
//获取教材
const getSubject = async () => {
if(localStorage.getItem('subjectList')){
subjectList = JSON.parse(localStorage.getItem('subjectList'))
}else{
const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 10000,orderby: 'orderidx asc', })
// subjectList = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject)
subjectList = rows
localStorage.setItem('subjectList', JSON.stringify(subjectList))
}
// 默认第一个
if(!subjectList.length) return
// curBookName = subjectList[0].itemtitle
curBookId = subjectList[0].id
// curBookImg = BaseUrl + subjectList[0].avartar
// curBookPath = subjectList[0].fileurl
}
const getTreeData = () => {
//数据过滤
let upData = transData(evaluationList)
if(upData.length){
// treeData = [...upData]
}else{
// treeData = []
return
}
nextTick(() => {
// defaultExpandedKeys = [treeData[0].id]
// let currentNodeObj = {...getLastLevelData(upData)[0]}
let currentNodeId = getLastLevelData(upData)[0].id
let currentNodeName = getLastLevelData(upData)[0].label
let curNode = {
id: currentNodeId,
label: currentNodeName,
// itemtitle: currentNodeObj.itemtitle,
// edudegree: currentNodeObj.edudegree,
// edustage: currentNodeObj.edustage,
// edusubject: currentNodeObj.edusubject,
}
let parentNode = findParentByChildId(upData, currentNodeId)
curNode.parentNode = toRaw(parentNode)
let levelFirstId = '';
let levelSecondId = '';
if (curNode.parentNode) {
levelFirstId = curNode.parentNode.id
} else {
levelFirstId = curNode.id
levelSecondId = ''
}
let bookeId = curBookId
// 头部 教材分析、作业设计打开外部链接需要当前章节ID
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId, bookeId}))
// const data = {
// textBook: {
// curBookId: curBookId,
// curBookName: curBookName,
// curBookImg: curBookImg,
// curBookPath: curBookPath
// },
// node: curNode
// }
// emit('changeBook', data)
})
}
const getLastLevelData = (tree) => {
let lastLevelData = [];
// 递归函数遍历树形结构
function traverseTree(nodes) {
nodes.forEach((node) => {
// 如果当前节点有子节点,继续遍历
if (node.children && node.children.length > 0) {
traverseTree(node.children);
} else {
// 如果没有子节点,说明是最后一层的节点
lastLevelData.push(node);
}
});
}
// 调用递归函数开始遍历
traverseTree(tree);
// 返回最后一层的数据
return lastLevelData;
}
// 根据id 拿到父节点数据
const findParentByChildId = (treeData, targetNodeId) => {
// 递归查找函数
// 遍历树中的每个节点
for (let node of treeData) {
// 检查当前节点的子节点是否包含目标子节点 ID
if (node.children && node.children.some(child => child.id === targetNodeId)) {
// 如果当前节点的某个子节点的 ID 匹配目标子节点 ID则当前节点即为父节点
return node;
}
// 如果当前节点没有匹配的子节点,则递归检查当前节点的子节点
if (node.children) {
let parentNode = findParentByChildId(node.children, targetNodeId);
if (parentNode) {
return parentNode;
}
}
}
// 如果未找到匹配的父节点,则返回 null 或者适当的默认值
return null;
}
const transData = (data) => {
let ary = []
data.forEach(item => {
let obj = {}
// 根据当前教材ID 过滤出对应的单元、章节
if (item.rootid == curBookId) {
if(item.level == 1){
obj.label = item.itemtitle
obj.id = item.id
obj.itemtitle = item.itemtitle
obj.edudegree = item.edudegree
obj.edustage = item.edustage
obj.edusubject = item.edusubject
let ary2 = []
evaluationList.forEach(el => {
let obj2 = {}
if (item.id == el.parentid) {
obj2 = {
label: el.itemtitle,
id: el.id,
itemtitle : el.itemtitle,
edudegree : el.edudegree,
edustage : el.edustage,
edusubject : el.edusubject,
}
ary2.push(obj2)
}
obj.children = ary2
})
ary.push(obj)
}
}
})
return ary
}

View File

@ -71,7 +71,6 @@ const getHomeWorkList = async () => {
return await homeworklist({
entpcourseid: chapterId,
edituserid: userStore.user.userId,
orderby: "timestamp DESC",
pageSize: 100,
status: '10'
}).then((res) => {
@ -80,24 +79,6 @@ const getHomeWorkList = async () => {
for (var i = 0; i < res.rows.length; i++) {
res.rows[i].taskconfig = []
// 处理任务类型的UI
// if (res.rows[i].worktype == '学习目标定位') {
// res.rows[i].workclass = 'success';
// res.rows[i].workcodesList = JSON.parse(res.rows[i].workcodes);
// } else
// 课标研读 目标设定 教材研读 框架梳理 学科定位 TODO 后续接入在添加
if (res.rows[i].worktype == '课堂展示') {
res.rows[i].workclass = 'primary';
} else if (res.rows[i].worktype == '框架梳理') {
res.rows[i].workclass = 'warning';
} else if (res.rows[i].worktype == '常规作业') {
res.rows[i].workclass = 'info';
} else if (res.rows[i].worktype == '习题训练') {
res.rows[i].workclass = 'danger';
} else {
res.rows[i].workclass = 'primary';
}
// 找child
for (var j = 0; j < res.rows.length; j++) {
if (res.rows[j].parentid == res.rows[i].id) {

View File

@ -16,9 +16,8 @@ export const isJson = (str) => {
/**
* @description processList 格式化试题
* @param {*} row
* @param {*} aloneOption 选择题中选项是否为每行一个
*/
export const processList = (row, aloneOption=false) => {
export const processList = (row) => {
for (var i = 0; i < row.length; i++) {
if (isJson(row[i].workanalysis)) {
//1、先默认格式化 格式化各项内容(待优化, 后续界面显示的为format的值)
@ -56,25 +55,19 @@ export const processList = (row, aloneOption=false) => {
const workDescArr = element.split('#&')
let tmp = ''
let j = 0
for(; j<workDescArr.length; j++){
const char = String.fromCharCode(65+j);
if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
for (; j < workDescArr.length; j++) {
if (j % 2 == 0) {
tmp += `<div style='width:80%;display:flex;'>`
}
else {
if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`;
}
tmp += `<div style='padding-left:10px;width:50%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left:2%;width:35%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if(!aloneOption && j%2 == 1){
tmp += '</div>';
if (j % 2 == 1) {
tmp += '</div>'
}
// workDescArr为 [''] 表示为 判断题或者填空题,这里不需要选项
@ -143,36 +136,31 @@ export const processList = (row, aloneOption=false) => {
* ]
*/
let workDescArr = JSON.parse(row[i].workdesc)
let workDescHtml = `<div style='width:100%;display:flex;>`
let workDescHtml = `<div style='width:80%;display:flex;>`
workDescArr.map((item, index) => {
if (item.type == '单选题' || item.type == '多选题') {
workDescHtml += `<div style='width:100%;display:flex;'>${index + 1}. ${item.title}</div>`
workDescHtml += `<div style='width:80%;display:flex;'>${index + 1}. ${item.title}</div>`
let tmp = ''
let j = 0
let optionsArr = item.options
for(; j<optionsArr.length; j++){
const char = String.fromCharCode(65+j);
if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${optionsArr[j]}</div>`;
for (; j < optionsArr.length; j++) {
if (j % 2 == 0) {
tmp += `<div style='width:80%;display:flex;'>`
}
else {
if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`;
}
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${optionsArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${optionsArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if(!aloneOption && j%2 == 1){
tmp += '</div>';
if (j % 2 == 1) {
tmp += '</div>'
}
workDescHtml += tmp
} else if (item.type == '填空题' || item.type == '判断题' || item.type == '主观题') {
workDescHtml += `<div style='width:100%;display:flex;'>${index + 1}. ${item.title}</div>`
workDescHtml += `<div style='width:80%;display:flex;'>${index + 1}. ${item.title}</div>`
}
})
workDescHtml += '</div>'
@ -277,24 +265,18 @@ export const processList = (row, aloneOption=false) => {
// 处理[选项显示] - 拼接ABCD首序号
let tmp = ''
let j = 0
for(; j<workDescArr.length; j++){
const char = String.fromCharCode(65+j);
if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
for (; j < workDescArr.length; j++) {
if (j % 2 == 0) {
tmp += `<div style='width:80%;display:flex;'>`
}
else {
if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`;
}
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${workDescArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${workDescArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
if(!aloneOption && j%2== 0){
tmp += '</div>';
if (j % 2 == 0) {
tmp += '</div>'
}
row[i].workdescFormat = tmp
@ -323,11 +305,210 @@ export const processList = (row, aloneOption=false) => {
.join('、')
row[i].workanswerFormat = answer
}
// else {
// // 其余类型试题类型(因学科不同, 大多为主观题类型, 结构为数组)
// row[i].workanswerFormat = workAnswerArr.join('、')
// }
}
/*
//2、处理单选题
if(row[i].worktype == '单选题' || row[i].worktype == '多选题' ){
//1.选项前增加ABCD workdesc: "①②#&①③#&②④#&③④" || "<div>为了活着</div>#&<div>为了填报肚子</div>#&<div>为了吃饭而吃饭</div>"
let workDescArr = [];
if(row[i].workdesc.indexOf('[')!==-1 && row[i].workdesc.indexOf(']')!==-1) {
//123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workDescArr = JSON.parse(row[i].workdesc);
}
else if(row[i].workdesc.indexOf('#&')) {
workDescArr = row[i].workdesc.split('#&');
}
else if(row[i].workdesc.indexOf(',')){
workDescArr = row[i].workdesc.split(',');
}
else {
// 待考虑
workDescArr.push(item.workdesc)
}
//2.答案 - 数字转为ABCD
if(row[i].worktype == '单选题') {
const str2Char = String.fromCharCode(65+Number(row[i].workanswer));
row[i].workanswerFormat = str2Char;
} else if (row[i].worktype == '多选题') {
const answerArr = row[i].workanswer.split('#&');
let arr2Char = '';
for(let k=0; k<answerArr.length; k++){
arr2Char += String.fromCharCode(65+Number(answerArr[k]));
}
row[i].workanswerFormat = arr2Char;
}
}
else if(row[i].worktype == '填空题') {
// console.log(row[i].workanswer.replace(/<[^>]*>/g, "").split('#&'),'????')
// 填空题答案
row[i].workanswerFormat = row[i].workanswer.replace(/#&/g," ");
// 填空选项不需要展示,
row[i].workdescFormat = '';
}
else if(row[i].worktype == '判断题'){
// console.log(row[i].workanswer.replace(/<[^>]*>/g, "").split('#&'),'????')
// 判断题答案
row[i].workanswerFormat = row[i].workanswer.replace(/#&/g," ");
// 判断选项不需要展示,
row[i].workdescFormat = '';
}
else if(row[i].worktype == '复合题') {
// 1.选项解析替换
const options = JSON.parse(row[i].workdesc);
// 题目(背景材料+复合题目)
const bjTitle = row[i].title.split('!@#$%')[0];
const tmTitles = row[i].title.split('!@#$%').filter((it,ix)=>ix>0);
// console.log(bjTitle,'背景标题');
// console.log(tmTitles,'复合题目');
let titls = [];
options.forEach((element,index1) => {
const workDescArr = element.split('#&');
let tmp = '';
let j=0;
for(; j<jsonArr.length; j++){
if(j%2 == 0){
tmp += `<div style='width:80%;display:flex;'>`;
}
const char = String.fromCharCode(65+j);
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${jsonArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
}
if(j%2== 0){
tmp += '</div>';
}
workdesc = tmp;
}
row[i].workdescFormat = workdesc; // 题目选项
// 答案处理
let workanswer = '';
if(row[i].workanswer && row[i].workanswer != '') {
// 因答案内容存在多种格式: 1.["123","1234"] 2.123#&1234 3.123
if(row[i].workanswer.indexOf('[')!==-1 && row[i].workanswer.indexOf(']')!==-1) {
//123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
let json = JSON.parse(row[i].workanswer);
// 单选、多选 需要 数字转为ABCD
if(row[i].worktype == '单选题') {
const str2Char = String.fromCharCode(65+Number(json[0]));
workanswer = str2Char;
} else if (row[i].worktype == '多选题') {
// const answerArr = row[i].workanswer.split('#&');
let arr2Char = '';
for(let k=0; k<json.length; k++){
arr2Char += String.fromCharCode(65+Number(json[k]));
}
workanswer = arr2Char;
} else if(row[i].worktype == '主观题' ) {
let arr2Char = '';
for(let k=0; k<json.length; k++){
const itemArr = json[k];
arr2Char += '('+ (parseInt(k) + 1) +')'+ itemArr.join('、')+ '<br />';
}
workanswer = arr2Char;
row[i].titleFormat = row[i].titleFormat.replace(/!@#\$%/g, '');
} else {
workanswer = json.join('、');
}
} else if(row[i].workanswer.indexOf('#&')) {
// 意味着多个答案或者填空内容
let workanswerList = row[i].workanswer.split('#&');
if(row[i].worktype == '多选题') {
// 数字转为ABCD
let arr2Char = '';
for(let k=0; k<workanswerList.length; k++){
arr2Char += String.fromCharCode(65+Number(workanswerList[k]));
}
workanswer = arr2Char;
}else{
workanswer = workanswerList.join('、');
}
} else if(row[i].workanswer.indexOf(',')){
// 意味这同样多个答案或者填空内容
let workanswerList = row[i].workanswer.split(',');
if(row[i].worktype == '多选题') {
// 数字转为ABCD
let arr2Char = '';
for(let k=0; k<workanswerList.length; k++){
arr2Char += String.fromCharCode(65+Number(workanswerList[k]));
}
workanswer = arr2Char;
}else{
workanswer = workanswerList.join('、');
}
} else {
// 待考虑
workanswer = row[i].workanswer;
}
}
row[i].workanswerFormat = workanswer; // 题目正确答案
//2.答案 - 数字转为ABCD
const answerArr = JSON.parse(row[i].workanswer);
let indexLabel = 1;
let arr = [];
answerArr.forEach(item => {
const arrTmp = item.answer.split('#&');
let value = `(${indexLabel})`;
arrTmp.forEach((element,i) => {
if(item.type == '单选题' || item.type == '多选题'){
value += `${String.fromCharCode(65+Number(element))}`;
}
if(item.type == '判断题' || item.type == '填空题'){
// 去除下 html标签
value += `${element.replace(/<[^>]+>/g, '')}`+ (i==arrTmp.length-1?'':'、');
}
})
arr.push(value);
indexLabel++;
})
const answer = arr.join('<br />');
row[i].workanswerFormat = answer;
}
else if(row[i].worktype == '主观题') {
// 1.选项解析替换---主观题没选项
// 题目(背景材料+主观题目)
const bjTitle = row[i].title.split('!@#$%')[0];
const tmTitles = row[i].title.split('!@#$%').filter((it,ix)=>ix>0);
// console.log(bjTitle,'背景标题');
// console.log(tmTitles,'主观题目');
let titls = [];
const s = [];
tmTitles.map((it,ix)=>{
s.push(it);
})
// console.log(s,'?????????????????')
row[i].titleFormat = bjTitle + s.join('');
// 填空选项不需要展示,
row[i].workdescFormat = '';
//2.答案
// 填空题答案
const workanswerList = JSON.parse(row[i].workanswer);
let tmp='';
workanswerList&&workanswerList.map((item,index)=>{
tmp += '<div>'+(index+1)+'.'+item.replace(/#&/g, '')+'</div>';
})
row[i].workanswerFormat = tmp;
}
else {
//处理答案
row[i].workanswerFormat = '见试题解答内容';
}
*/
}
}
}

View File

@ -29,15 +29,11 @@
<div class="avatar-container">
<div class="avatar-wrapper flex">
<el-dropdown class="right-menu-item hover-effect" @command="handleCommand">
<el-image :src="dev_api + userStore.user.avatar" class="user-avatar" style="float: left">
<template #error>
<el-image :src="defaultUserImg" class="user-avatar" style="float: left" />
</template>
</el-image>
<img :src="dev_api + userStore.user.avatar" class="user-avatar" style="float: left" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
<!-- <el-dropdown-item @click="changePage('/classReserv')">课程预约</el-dropdown-item>-->
<el-dropdown-item @click="changePage('/classReserv')">课程预约</el-dropdown-item>
<el-dropdown-item @click="changePage('/class')">班级中心</el-dropdown-item>
<el-dropdown-item divided command="logout">
<span>退出登录</span>
@ -84,10 +80,9 @@ import { updateUserInfo } from '@/api/system/user'
import logoIco from '@/assets/images/logo.png'
import { listEvaluation } from '@/api/classManage/index'
import { sessionStore } from '@/utils/store'
import defaultUserImg from '@/assets/images/img-avatar.png'
// import Chat from '@/utils/chat' // im
// if (!Chat.imChat) Chat.init()
import { useToolState } from '@/store/modules/tool'
const toolState = useToolState();
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
const { ipcRenderer } = window.electron || {}
const userStore = useUserStore()
@ -121,12 +116,6 @@ const headerMenus = [
id: 4,
icon: '#icon-iconfontzhizuobiaozhunbduan3-1',
disabled: true
},
{
name: '实验室',
id: 5,
icon: '#icon-a-shiyanpingshiyanyanjiu',
path: '/experiment'
}
]
@ -156,7 +145,6 @@ function handleCommand(command) {
break
case 'logout':
logout()
// Chat?.logout() // im 退
break
default:
break
@ -165,16 +153,13 @@ function handleCommand(command) {
function logout() {
const hasClass = sessionStore.has('activeClass.id')
const hasTool = sessionStore.get('isToolWin')
if (hasClass || hasTool) return ElMessage.warning('当前正在上课,请先结束上课')
if (hasClass || toolState.isToolWin) return ElMessage.warning('当前正在上课,请先结结束上课')
ElMessageBox.confirm('确认退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
const Chat = (await import('@/utils/chat')).default
if (!!Chat.imChat) Chat.logout()
.then(() => {
userStore
.logOut()
.then(() => {
@ -185,7 +170,8 @@ function logout() {
// router.replace('/login')
ipcRenderer && ipcRenderer.send('openLoginWindow')
})
}).catch(()=>{})
})
.catch(() => { })
}
const emits = defineEmits(['setLayout'])

View File

@ -4,7 +4,14 @@
<Header />
</el-header>
<el-main>
<AppMain />
<template v-if="currentRoute.path != '/home'">
<el-page-header @back="goBack">
<template #content>
<span class="text-large mr-3"> {{ currentRoute.meta.title }} </span>
</template>
</el-page-header>
</template>
<AppMain :style="{ height: currentRoute.path == '/home' ? '100%' : 'calc(100% - 45px)'}" />
</el-main>
<Uploader v-if="uploaderStore.uploadList && uploaderStore.uploadList.length > 0" />
<AiChart/>
@ -12,18 +19,31 @@
</template>
<script setup>
import { ref } from 'vue'
import { watch } from 'vue'
import { useRouter } from 'vue-router'
import Header from './components/Header.vue'
import AppMain from './components/AppMain.vue'
import Uploader from './components/Uploader.vue'
import AiChart from '@/components/ai-chart/index.vue'
import uploaderState from '@/store/modules/uploader'
// import Chat from '@/utils/chat'
import { ref } from 'vue'
const router = useRouter()
const currentRoute = ref('')
watch(
() => router.currentRoute.value,
(newValue) => {
currentRoute.value = newValue
},
{ immediate: true }
)
let uploaderStore = ref(uploaderState())
// window.test = Chat
// Chat.init()
const goBack = () =>{
router.back()
}
</script>

View File

@ -70,10 +70,7 @@ export class ImChat {
// 日志监听
this.timChat.TIMSetLogCallback({
callback: data => {
const [type, log] = data
if (type == log_level) { // 打印对应日志
this.setConsole('%cchat-log ', log)
}
this.setConsole('%cchat-log ', data[1])
},
user_data: ''
})
@ -89,7 +86,7 @@ export class ImChat {
if (code == 0) { // 初始化成功
this.setConsole('%cim-chat: init', '初始化成功')
this.status.isConnect = true
// this.setConfig() // 设置日志级别
this.setConfig() // 设置日志级别
resolve(this)
} else { // 失败具体请看code
console.error('[im-chat]:初始化失败', code)
@ -230,11 +227,10 @@ export class ImChat {
})
}
// 删除群组
deleteGroup(timGroupId) {
const groupId = timGroupId || this.timGroupId
if (!groupId) return
deleteGroup() {
if (!this.timGroupId) return
return this.timChat.TIMGroupDelete({
groupId,
groupId: this.timGroupId,
data: '', // 用户自定义数据
})
}
@ -253,7 +249,7 @@ export class ImChat {
}
// 获取群组列表
getGroupList() {
return this.timChat.TIMGroupGetJoinedGroupList().then(res => {
return this.timChat.getGroupList().then(res => {
console.log('获取群组列表', res)
return res
}).catch(error => {
@ -285,11 +281,8 @@ export class ImChat {
// callback: (data) => {}
}
// console.log('发送消息', option)
this.setConsole('%cim-chat: 发送消息【req】', conv_id, msg)
return this.timChat.TIMMsgSendMessageV2(option).then(res => {
this.setConsole('%cim-chat: 发送消息【res】', JSON.stringify(res))
return res
})
this.setConsole('%cim-chat: 发送消息', option)
return this.timChat.TIMMsgSendMessageV2(option)
}
/**
* @description 发送群消息

View File

@ -10,8 +10,6 @@ import _ from 'lodash'
// import { diff } from 'jsondiffpatch'
// const Remote = isNode?require('@electron/remote'):{} // 远程模块
const exArrs = ['subject','env','curr'] // 不需要同步key-排除
export function shareStorePlugin({store}) {
store.$subscribe((mutation, state) => { // 自动同步
// mutation 变量包含了变化前后的状态
@ -56,45 +54,43 @@ function stateSync(storeName, key, value, state) {
// 同步数据-发送给主线程-单独($subscribe-监听使用)
function stateSyncWatch(storeName, newState) {
const oldState = sessionStore.store // 旧数据
exArrs.forEach(k => Object.keys(oldState).includes(k) && (delete oldState[k]))
const diffData = findDifferences(oldState, newState)
if(!_.keys(diffData).length) return // 没有变化就终止执行
// 数据处理: 找出差异
// console.log('state-change-diffData', diffData, newState)
console.log('state-change-diffData', diffData)
try {
let pinaValue = {} // store pina状态管理需要的数据格式
// 数据转换处理
for(const key in diffData) {
const value = diffData[key]
const value = diffData[key] || null
const newValue = {} // 重新组装pinia需要的数据 {a:{b:1}} 这种
const keyArr = key.split('.') || []
keyArr.reduce((o,c,i)=>{o[c] = i === keyArr.length-1 ? value : {};return o[c]}, newValue)
// 合并数据 loadsh _.merge() 函数
_.merge(pinaValue, newValue)
}
const piniaArr = _.toPairs(pinaValue) // 对象转换为数组 {a:1} toPairs [['a',1]]
const setData = (key, value) => {
// 无数据就终止执行
if (!key) return
// 更新本地数据-session
// 直接获取当前最新值(整体更新)上面获取到value是差异值并不能知道删除还是新增
const newValAll = _.get(newState, key)
const oldValAll = sessionStore.get(key)
// 没变化也终止执行
if (_.isEqual(oldValAll, newValAll)) return
// 更新本地数据-session
sessionStore.set(key, newValAll || null)
// 数据处理: pina-store
const jsonStr = JSON.stringify(pinaValue) // 从新组装-json数据
// 通知主线程更新
ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
// console.log('======',key, value, jsonStr )
}
// 数据处理: electron-store
for(let [key, value] of piniaArr) {
setData(key, value)
}
const [key, value] = _.toPairs(pinaValue)[0] // 对象转换为数组 {a:1} toPairs [['a',1]]
// 无数据就终止执行
if (!key || !value) return
// 更新本地数据-session
// 直接获取当前最新值(整体更新)上面获取到value是差异值并不能知道删除还是新增
const newValAll = _.get(newState, key)
const oldValAll = sessionStore.get(key)
// 没变化也终止执行
if (_.isEqual(oldValAll, newValAll)) return
// 更新本地数据-session
sessionStore.set(key, newValAll)
// 数据处理: pina-store
const jsonStr = JSON.stringify(pinaValue) // 从新组装-json数据
// 通知主线程更新
ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
// console.log('======',key, value, jsonStr )
} catch (error) {
console.log('state-change-error', error)
}
@ -120,7 +116,7 @@ function stateSyncInit(wid, store) {
function sessionWatch(store) {
const unsubscribe = sessionStore.onDidAnyChange((newV, oldV) => {
if (newV !== oldV) {
// console.log('session-change', newV, oldV)
console.log('session-change', newV, oldV)
// 通知主线程更新
// ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
}
@ -133,7 +129,7 @@ function stateChange(store) {
const storeName = store.$id
ipcRenderer?.on('pinia-state-set', (e, sName, jsonStr) => {
if (sName == storeName) { // 更新对应数据
// console.log('state-set', jsonStr, sName)
console.log('state-set', jsonStr, sName)
const curJson = circularSafeStringify(store.$state) // 当前数据
const isUp = curJson != jsonStr // 不同的时候才写入,不然会导致触发数据变化监听,导致死循环
if (!isUp) return

View File

@ -15,12 +15,6 @@ export const constantRoutes = [
// component: ()=> import('../login/index.vue'),
hidden: true
},
{
path: '/fullscreenpdf',
component: () => import('@/views/fullScreenPdf/index.vue'),
name: 'fullscreenpdf',
meta: {title: '全屏显示PDF'}
},
{
path: '/teachClassTask',
component: () => import('@/views/classTask/teachClassTask.vue'),
@ -49,12 +43,6 @@ export const constantRoutes = [
name: 'resource',
meta: {title: '资源库'}
},
{
path: '/experiment',
component: () => import('@/views/experiment/index.vue'),
name: 'experiment',
meta: {title: '实验室'}
},
{
path: '/prepare',
component: () => import('@/views/prepare/index.vue'),
@ -91,7 +79,6 @@ export const constantRoutes = [
name: 'testpdf',
meta: {title: '测试PDF'}
},
{
path: '/classReserv',
component: () => import('@/views/classManage/classReserv.vue'),

View File

@ -0,0 +1,28 @@
import { defineStore } from 'pinia'
import { } from '@/api/classTask/index.js'
import { listClassmain } from '@/api/classManage/index'
const useClassTaskStore = defineStore('classTask',{
state: () => ({
classListIds: [],
}),
actions: {
listClassmain(params) {
// 获取班级列表
return new Promise((resolve, reject) => {
listClassmain(params)
.then((res) => {
this.classListIds = res.rows&&res.rows.map((item) => item.id)
resolve(res)
})
.catch((error) => {
reject(error)
})
})
},
},
persist: true
})
export default useClassTaskStore

View File

@ -4,18 +4,13 @@ const overviewStore = defineStore(
{
state: () => {
return {
tableList:[],
allData:[]
tableList:[]
}
},
actions: {
getTableList(data){
this.tableList = [...data]
},
//整理的所有列表
getAllData(data){
this.allData = [...data]
}
}
}
})
export default overviewStore

View File

@ -6,7 +6,7 @@ import { sessionStore } from '@/utils/store'
// 默认数据
const defData = sessionStore.store || {}
const exArrs = ['subject','env','curr']
const exArrs = ['subject']
exArrs.forEach(k => Object.keys(defData).includes(k) && (delete defData[k]))
// 延时
@ -18,7 +18,7 @@ export const useToolState = defineStore('tool', {
showBoardAll: false, // 全屏画板-是否显示
isPdfWin: false, // pdf窗口是否打开
isToolWin: false, // 工具窗口是否打开
isTaskWin: false, // 批改窗口是否打开
isTaskWin: false, // 批改窗口是否打开
curSubjectNode: {
querySearch: {} // 查询资源所需参数
},

View File

@ -1,80 +0,0 @@
import axios from "axios";
import { getSignature } from "./index";
let appId = "01ec9aa3";
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
let timestamp = Math.floor(Date.now() / 1000);
let signature = getSignature(appId, secret, timestamp);
export function text2Text(data) {
return axios({
url: "v1/chat/completions",
method: "post",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer yjCwlZCeUtBYvjHAQZdk:FtWNmJSWcZMCmTBQZfoH",
Accept: "*/*",
},
data: {
model: "4.0Ultra",
messages: data,
stream: false,
},
});
}
export function uploadDoc(data) {
return axios({
url: "openapi/v1/file/upload",
method: "post",
headers: {
"Content-Type": "multipart/form-data",
appId: appId,
timestamp: timestamp,
signature: signature,
},
data: data,
});
}
export function queryDocStatus(data) {
return axios({
url: "openapi/v1/file/status",
method: "post",
headers: {
"Content-Type": "application/form-data",
appId: appId,
timestamp: timestamp,
signature: signature,
},
data: data
});
}
export function chatByDoc(fileId, data) {
const wsUrl = `wss://chatdoc.xfyun.cn/openapi/chat?fileId=${fileId}&appId=${appId}&timestamp=${timestamp}&signature=${signature}`;
const ws = new WebSocket(wsUrl);
ws.onopen = () => {
const messageBody = {
fileIds: [fileId],
messages: data,
chatExtends: {
wikiPromptTpl:
"请将以下内容作为已知信息:\n<wikicontent>\n请根据以上内容回答用户的问题。\n问题:<wikiquestion>\n回答:",
wikiFilterScore: 0.82,
temperature: 0.5,
sparkWhenWithoutEmbedding: false,
},
};
ws.send(JSON.stringify(messageBody));
};
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
console.log("WebSocket 消息:", response);
};
ws.onerror = (error) => {
console.error("WebSocket 错误:", error);
};
}

View File

@ -1,97 +0,0 @@
/**
* 实现单例模式
*/
import useUserStore from '@/store/modules/user'
import { ImChat } from '@/plugins/imChat'
import * as http from '@/api/apiService' // 自定义api service
export class Chat {
instance = null;
sdkAppId = 0; // 应用id
sign = ''; // 签名
imUserId = ''; // 用户id
imChat = null; // IM实例
constructor() {
if (!Chat.instance) { // 存在的时候
Chat.instance = this;
}
return Chat.instance;
}
/**
* 初始化 获取IM签名
* @param {*} isInit : 是否初始化IM
* @param {*} isLogin : 是否登录IM
* @param {*} callback: 监听消息回调函数
* @returns Promise<ImChat>
*/
async init(isInit = true, isLogin = true, callback) {
// 特殊处理只传1个参数且为函数则默认为callbackisInit和isLogin默认为true
if (typeof isInit == 'function'){
callback = isInit
isInit = true
isLogin = true
}
const userStore = useUserStore()
const { timuserid: imUserId } = userStore.user
// 获取腾讯云签名
const res = await http.imChat.getTxCloudSign({imUserId})
if (res && res.code == 200) {
const { sdkAppId, sign } = res.data
this.sdkAppId = sdkAppId
this.sign = sign
this.imUserId = imUserId
// 初始化IM
if (isInit) return await this.initIM(isLogin, callback)
}
}
// 初始化IM
async initIM(isLogin, callback) {
const imChat = new ImChat(this.sdkAppId, this.sign, this.imUserId)
this.imChat = imChat
await imChat.init() // 初始化IM
callback && this.listenMsg(callback) // 监听消息
if(isLogin) await imChat.login() // 登录IM
return imChat
}
// 监听消息
async listenMsg(callback) {
if (!callback) return
if (!this.imChat) return
await this.imChat?.watch(msg => callback(msg))
}
// 解散群
async dismissGroup(groupId) {
if (!this.imChat) return
await this.imChat?.deleteGroup(groupId)
}
// 退出登录
async logout() {
if (!this.imChat) return
await this.imChat?.logout()
this.imChat = null
}
// 发群消息
async sendMsg(conv_id, msg) {
if (!this.imChat) return
await this.imChat?.sendMsg(conv_id, msg)
}
// 发群消息
async sendMsgGroup(msg, head, type) {
if (!this.imChat) return
this.imChat?.sendMsgGroup(msg, head, type)
}
// 发群消息
async sendMsgGroupId(groupId, msg, head, type) {
if (!this.imChat) return
const msgObj = this.imChat?.getMsgObj(head, msg, type)
this.imChat?.sendMsg(groupId, msgObj)
}
// 获取群列表
async getGroupList() {
if (!this.imChat) return
return await this.imChat?.getGroupList()
}
}
export default new Chat()

View File

@ -127,10 +127,7 @@ export const removePropertyOf = function(obj){
export function removeTree(list) {
var this_ = this
for (var i in list) {
if (list[i].children == null) {
delete list[i].children;
}
else if (list[i].children.length == 0) {
if (list[i].children.length == 0) {
list[i].children = undefined
} else {
this_.removeTree(list[i].children)
@ -313,44 +310,3 @@ export function timeToStr(time,str = '时分秒', isPad = false) {
if (isPad) return `${h?toStr(h)+arr[0]:''}${m?toStr(m)+arr[1]:''}${toStr(s)}${arr[2]||''}`
return `${h?h+arr[0]:''}${m?m+arr[1]:''}${s?s+arr[2]||'':''}`
}
/**
* 防抖一定时间内如果函数被连续调用则只执行最后一次调用
* debounce(() => {
console.log('Input event handled');
}, 300);
* @param {*} func
* @param {*} wait
* @returns
*/
export function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
/**
* 节流一定时间内函数最多只执行一次
* throttle(() => {
console.log('Scroll event handled');
}, 300);
* @param {*} func
* @param {*} wait
* @returns
*/
export function throttle(func, wait) {
let lastTime = 0;
return function() {
const context = this;
const args = arguments;
const now = new Date();
if (now - lastTime >= wait) {
func.apply(context, args);
lastTime = now;
}
};
}

View File

@ -100,17 +100,6 @@ export const getCurrentTime = (format)=> {
if(format == 'MMDD'){
return `${month}${day}`;
}
if(format == 'HH'){
return `${hours}`;
}
if(format == 'HH+3'){
//往后延时3个小时
const hours = (now.getHours()+3).toString().padStart(2, '0');
return `${hours}`
}
if(format == 'mm'){
return `${minutes}`;
}
}
/**
*
@ -167,61 +156,3 @@ export function getTomorrow() {
return tomorrow;
}
/**
* 当前日期的 前几天
* @param {*} index 天数
* @param {*} format true 返回中国标准时间Wed Oct 02 2024 08:00:00 GMT+0800 (中国标准时间) 格式 false 返回标准时间格式 YYYY-MM-DD
* @returns
*/
export function getTheOtherDay(index, format=true) {
let date = new Date();
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate()- index < 10 ? '0' + (date.getDate()- index) : date.getDate()- index
// 前 index 天的时间
if(format){
let tomorrow = `${year}-${month}-${day}`;
return new Date(tomorrow);
}else{
let tomorrow = `${year}-${month}-${day}`;
return tomorrow;
}
}
/**
* 当前日期的 后几天
* @param {*} index 天数
* @param {*} format true 返回中国标准时间Wed Oct 02 2024 08:00:00 GMT+0800 (中国标准时间) 格式 false 返回标准时间格式 YYYY-MM-DD
* @returns
*/
export function getTheOtheNextDay(index, format=true) {
const date = new Date();
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate()+ index < 10 ? '0' + (date.getDate()+ index) : date.getDate()+ index
// 前 index 天的时间
if(format){
const tomorrow = `${year}-${month}-${day}`;
return new Date(tomorrow);
}else{
const tomorrow = `${year}-${month}-${day}`;
return tomorrow;
}
}
/**
* Wed Oct 02 2024 08:00:00 GMT+0800 (中国标准时间) 转为日期格式: YYYY-MM-DD
*
* @param {*} format
* @returns
*/
export const getDateFormatDate = (newDate)=> {
const now = newDate; // new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}

View File

@ -1,33 +0,0 @@
import CryptoJS from 'crypto-js';
// 生成签名的主方法
export function getSignature(appId, secret, ts) {
try {
const auth = md5(appId + ts);
return hmacSHA1Encrypt(auth, secret);
} catch (error) {
console.error('Error generating signature:', error);
return null;
}
}
// MD5 加密
function md5(cipherText) {
try {
return CryptoJS.MD5(cipherText).toString(); // 使用 CryptoJS 进行 MD5 加密
} catch (error) {
console.error('Error in MD5 hashing:', error);
return null;
}
}
// HMAC-SHA1 加密
function hmacSHA1Encrypt(encryptText, encryptKey) {
try {
// 使用 CryptoJS 进行 HMAC-SHA1 加密,并转换为 Base64 格式
return CryptoJS.HmacSHA1(encryptText, encryptKey).toString(CryptoJS.enc.Base64);
} catch (error) {
console.error('Error in HMAC-SHA1 encryption:', error);
return null;
}
}

View File

@ -1,224 +0,0 @@
import request from '@/utils/request'
import axios from 'axios';
import { ElMessage, ElMessageBox, ElNotification } from "element-plus";
import qs from 'qs';
import { uploadServer, getJSONFile } from '@/utils/common';
import { getEvaluationclueList, updateEvaluationclue, addEvaluationclue } from '@/api/pdf';
export const pdfCallBack = (pdfInfo) => {
if(pdfInfo.toString() !== '{}' || pdfInfo !== null){
if(pdfInfo.storageInfo){
//圈点勾画操作返回
// saveJSON(pdfInfo.storageInfo)
saveAnnotationStorage(pdfInfo.storageInfo)
return 'draw'
}else if(pdfInfo.quoteInfo){
//引用文本操作返回
quoteWords(pdfInfo.quoteInfo)
return 'quote'
}else if(pdfInfo.imgInfo){
//OCR截图返回
ocrCallBack(pdfInfo.imgInfo)
return 'ocr'
}
}else{
return ''
}
}
//百度OCR识别配置
const baidubceConfig = {
// Header
'Content-Type': "application/x-www-form-urlencoded",
// 格式
'Accept' : 'application/json',
// id(临时测试)
'client_id': "U0DrGBE6X92IXgV6cJMNON8F",
// 密钥(临时测试)
'client_secret': 'oWb0M0YWMmZPMQIhIUkJX99ddr7h61qf',
};
//获取百度token
const getBaiduToken = async () => {
let config = {
headers: {
'Content-Type': `${baidubceConfig['Content-Type']}`
},
url: `/baidubce/oauth/2.0/token?grant_type=client_credentials&client_id=${baidubceConfig['client_id']}&client_secret=${baidubceConfig['client_secret']}`,
method: 'POST'
}
return await axios(config)
}
//图片识别方法
const getBaiduOCR = async (token,params) => {
let config = {
headers: {
'Content-Type': `${baidubceConfig['Content-Type']}`,
'Accept': `${baidubceConfig['Accept']}`,
},
method: 'POST',
url: `/baidubce/rest/2.0/ocr/v1/doc_analysis?access_token=${token}`,
data: qs.stringify(params),
}
try{
return await axios(config)
}catch{
return null
}
}
const ocrCallBack = async (ocrInfo) => {
ElMessage({
type: 'info',
message: '正在识别请稍后',
grouping: true,
duration: 3000
})
const baseUrl = ocrInfo.base64;
const imgurl = baseUrl.split(",")[1];
//获取百度智能云token
const tokenData = await getBaiduToken();
console.log('----tokenData',tokenData);
if(tokenData.status !== 200){
ElMessage({
type: 'error',
message: '文字识别获取用户标识失败',
grouping: true,
showClose: true,
duration: 5000
})
return;
}
//获取到的token
const token = tokenData.data?.access_token;
const query = {
image: imgurl, //图片地址(base64)
line_probability: false, //是否返回每行识别结果的置信度。默认为false
disp_line_poly: false, //是否返回每行的四角点坐标。默认为false
words_type: 'handprint_mix', //文字类型。 默认:印刷文字识别 = handwring_only手写文字识别 = handprint_mix 手写印刷混排识别
layout_analysis: false, //是否分析文档版面包括layout图、表、标题、段落、目录attribute栏、页眉、页脚、页码、脚注的分析输出
recg_long_division: false, //是否检测并识别手写竖式
recg_formula: true, //控制是否检测并识别公式默认为false
}
const ocrData = await getBaiduOCR(token,query);
if(ocrData && ocrData.status === 200){
const ocrList = ocrData.data?.words_result;
let words = '';
ocrList.map(item => {
words += item.words
})
ElMessageBox.alert(words, '识别结果', {
confirmButtonText: '引用文本',
type: 'info',
cancelButtonText: '取消'
}).then(() => {
window.navigator.clipboard.writeText(words).then(() => {
ElMessage({
type: 'success',
message: '已复制到粘贴板',
grouping: true,
duration: 3000
})
}).catch(() => {
ElMessage({
type: 'error',
message: '复制信息出错',
grouping: true,
duration: 3000
})
})
})
}else{
ElMessage({
type: 'error',
message: '识别信息出错',
grouping: true,
duration: 3000
})
}
}
const saveAnnotationStorage = (storage) => {
localStorage.setItem('PDFJS_Annotation', JSON.stringify(storage));
}
//保存json文件
export const saveJSON = (data) => {
let filename = ''
if (!data) {
console.log('传入的data数据为null');
return;
}
if (!filename) {
filename = `json${Date.now()}.json`
console.log('未传入文件名,采用默认文件名' + filename);
}
let newdata = null;
if (typeof data === 'object') {
newdata = JSON.stringify(data, undefined, 4)
}
// 创建json文件blob流
const blob = new Blob([newdata], { type: 'text/json' });
// 创建file文件
const file = new File([blob],filename, {type: blob.type})
// 创建上传文件流
const formdata = new FormData();
formdata.append('file', file);
//其他参数待添加
//上传
uploadServer(formdata).then(res => {
console.log('+++++++++++++');
console.log(res.data);
})
//下载json文件
// let e = document.createEvent('MouseEvents');
// let a = document.createElement('a');
// a.download = filename;
// a.href = window.URL.createObjectURL(blob);
// a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
// e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
// a.dispatchEvent(e);
}
//引用文本
const quoteWords = (quoteInfo) => {
const { textStr, StartStr, EndStr } = quoteInfo;
if(textStr === StartStr || textStr === EndStr){
ElNotification({
title: '引用内容',
message: textStr,
duration: 0,
type: 'info',
offset: 120
})
//如果开头和结尾的文字跟内容相同,那么它要么是开头,要么是整段,不需要替换操作
console.log('无需替换------',textStr);
return
}
let midStr = ''
if(StartStr === '' && EndStr === '' && textStr === '') return
if(StartStr === '' && EndStr !== '') {
midStr = textStr.replace(EndStr,'eeeeee').split('eeeeee')[0]
}else if(StartStr !== '' && EndStr === ''){
midStr = textStr.replace(StartStr,'ssssss').split('ssssss')[1]
}else{
midStr = textStr.replace(StartStr, 'ssssss').replace(EndStr,'eeeeee').split('ssssss')[1].split('eeeeee')[0];
}
ElNotification({
title: '引用内容',
message: StartStr + midStr + EndStr,
duration: 0,
type: 'info',
offset: 120
})
console.log('中间文字------',midStr);
console.log('转换后整体文字------',StartStr + midStr + EndStr);
}
//获取分析列表
export const getAnalysisList = async (params) => {
return await getEvaluationclueList(params)
}
//新增分析数据
export const addAnalysis = async (params) => {
return await addEvaluationclue(params);
}
//修改分析数据
export const updateAnalusis = async (params) => {
return await updateEvaluationclue(params);
}

View File

@ -1,129 +0,0 @@
import axios from "axios";
import { getSignature } from "./index";
import { ElMessage } from "element-plus";
import request from '@/utils/request'
let appId = "a3c84da2";
let secret = "OTZkZDdhNDQ4MDk0NTZiYjcyY2RjZmYz";
let timestamp = Math.floor(Date.now() / 1000);
let signature = getSignature(appId, secret, timestamp);
let req = (url, type, data)=>{
let config = {
headers: {
"Content-Type": "application/json",
appId: appId,
timestamp: timestamp,
signature: signature,
},
url: url,
method: type,
}
if (type === "GET") {
config.params = data;
} else {
config.data = data;
}
return request(config)
}
/*const instance = axios.create({
// baseURL: import.meta.env.VITE_APP_ENV === "development"?"/parth":import.meta.env.VITE_APP_BASE_API,
baseURL: "/dev-api",
headers: {
"Content-Type": "application/json",
'Authorization': 'Bearer ' + getToken(),
appId: appId,
timestamp: timestamp,
signature: signature,
},
});*/
const createOutline = async (data) => {
console.log("createOutline data:", data);
try {
const response = await req(
"/api/aippt/createOutline",
"POST",
data
);
console.log("createOutline response:", response);
if (response.code == 81002) {
ElMessage.error("并发数量超过限制");
}
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const getBackGround = async () => {
try {
const response = await req("/api/aippt/themeList", "GET");
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const createPPT = async (data) => {
try {
const response = await req("/api/aippt/create", "POST", data);
console.log("createOutline response:", response);
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const createByOutline = async (data) => {
try {
const response = await req("/api/aippt/createByOutline","POST", data);
console.log("createByOutline response:", response);
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const getProgress = async (id) => {
try {
const response = await req(`/api/aippt/progress?sid=${id}`, "GET");
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const getBackGroundV2 = async () => {
try {
const response = await req("/api/aipptV2/themeListV2", "GET");
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const createPPTV2 = async (data) => {
try {
const response = await req("/api/aipptV2/createV2", "POST", data);
console.log("createOutline response:", response);
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const getProgressV2 = async (id) => {
try {
const response = await req(`/api/aipptV2/progressV2?sid=${id}`, "GET");
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
export { createOutline, getBackGround, createPPT, getProgress, getBackGroundV2, createPPTV2, getProgressV2, createByOutline };

View File

@ -117,8 +117,4 @@ export const coursewareTypeList = [
label:'素材',
value:6
},
{
label:'视频',
value:12
},
]

View File

@ -96,19 +96,3 @@ export const creatPPT = (name, uploadData) => {
})
})
}
export const creatAIPPT = (name, url, uploadData) => {
JSON.parse(JSON.stringify(uploadData))
return new Promise((resolve, reject) => {
let cookie = localStorage.getItem('Admin-Token')
ipcRenderer.send('creat-ai-file-default', {
name,
url,
uploadData: JSON.parse(JSON.stringify(uploadData)),
cookie
})
ipcRenderer.once('creat-ai-file-default-reply', (e, res) => {
resolve(res)
})
})
}

View File

@ -131,14 +131,14 @@ export const createWindow = async (type, data) => {
data.isConsole = true // 是否开启控制台
data.isWeb = false // 是否开启web安全
data.option = {...defOption, ...option}
wins_tool = await toolWindow(type, data)
wins_tool = await toolWindow(data)
wins_tool.type = type // 唯一标识
wins_tool.show()
wins_tool.setFullScreen(true) // 设置窗口为全屏
wins_tool.setIgnoreMouseEvents(true, {forward: true}) // 忽略鼠标事件但是事件继续传递给窗口
wins_tool.setAlwaysOnTop(true,'screen-saver') // 将窗口设置为顶层窗口
wins_tool.setVisibleOnAllWorkspaces(true) // 如果窗口在所有工作区都可见
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') wins_tool.webContents.openDevTools() // 打开调试工具
// wins_tool.webContents.openDevTools() // 打开调试工具
eventHandles(type, wins_tool) // 事件监听处理
return wins_tool
}
@ -161,11 +161,11 @@ export const createWindow = async (type, data) => {
}
data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option}
const win = await toolWindow(type, data)
const win = await toolWindow(data)
win.type = type // 唯一标识
win.show()
win.setFullScreen(true) // 设置窗口为全屏
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') win.webContents.openDevTools() // 打开调试工具
// win.webContents.openDevTools() // 打开调试工具
eventHandles(type, win) // 事件监听处理
winPdf=win
break
@ -193,32 +193,13 @@ export const createWindow = async (type, data) => {
}
data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option}
winChild = await toolWindow(type, data)
winChild = await toolWindow(data)
winChild.type = type // 唯一标识
winChild.show()
winChild.setFullScreen(false) // 设置窗口为全屏
eventHandles(type, winChild) // 事件监听处理
return winChild
}
case 'open-win': { // 创建-新窗口
const option = data.option||{}
const defOption = {
show: false,
frame: true, // 无边框
autoHideMenuBar: true,
maximizable: false,
}
data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option}
const win = await toolWindow(type, data)
win.type = type // 唯一标识
win.show()
win.maximize();
// win.setFullScreen(true) // 设置窗口为全屏
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') win.webContents.openDevTools() // 打开调试工具
eventHandles(type, win) // 事件监听处理
break
}
default:
break
}
@ -232,7 +213,7 @@ export const createWindow = async (type, data) => {
* @author: zdg
* @date 2021-07-05 14:07:01
*/
export function toolWindow(type, {url, isConsole, isWeb=true, option={}}) {
export function toolWindow({url, isConsole, isWeb=true, option={}}) {
// width = window.screen.width
let width = option?.width || 800
let height = option?.height || 600
@ -240,7 +221,7 @@ export function toolWindow(type, {url, isConsole, isWeb=true, option={}}) {
const devUrl = `${BaseUrl}${url}`
const buildUrl = path.join(__dirname, 'index.html')
const urlAll = isDev ? devUrl : buildUrl
return new Promise(async(resolve) => {
return new Promise((resolve) => {
const config = {
width, height,
icon: path.join(appPath, '/resources/logo2.ico'),
@ -255,9 +236,6 @@ export function toolWindow(type, {url, isConsole, isWeb=true, option={}}) {
}
// 创建-新窗口
let win = new Remote.BrowserWindow(config)
// 新窗口-创建事件(如:主进程加载远程服务)
await ipcMsgInvoke('new-window', {id:win.id, type})
console.log(`渲染进程 [${type}]: 窗口创建-成功${Date.now()}`)
if (!isDev) win.loadFile(urlAll,{hash: url}) // 加载文件
else win.loadURL(urlAll) // 加载url
win.once('ready-to-show', () => { // 窗口加载完成
@ -303,7 +281,7 @@ const eventHandles = (type, win) => {
})
// 新窗口-创建事件(如:主进程加载远程服务)
// ipcRenderer.send('new-window', {id:win.id, type})
ipcRenderer.send('new-window', {id:win.id, type})
}
switch(type) {
case 'tool-sphere': { // 创建-悬浮球
@ -362,7 +340,7 @@ const eventHandles = (type, win) => {
// 监听窗口的激活事件
win.on('focus', async () => {
toolState.isTaskWin=true
// win&&win.reload(); //刷新该窗口
win&&win.reload(); //刷新该窗口
});
// 监听窗口关闭事件
win.on('closed', function () {
@ -379,18 +357,6 @@ const eventHandles = (type, win) => {
publicMethods(on) // 加载公共方法
break;
}
case 'open-win': { // 打开新窗口
// 监听窗口关闭事件
win.on('closed', function () {
win&&win.destroy()
});
const on = {
onClosed: () => {
}
}
publicMethods(on) // 加载公共方法
break
}
default:
break
}
@ -412,22 +378,7 @@ export const toLinkWeb = (path) => {
cookieData: { ...config }
})
}
/**
* @description 外部跳转-web网页
* @param {*} path
* @param {*} params
*/
export const toRoter = (path) => {
const config = baseConfig()
// console.log(config)
// const fullPath = config.url + path
// 通知主进程
ipcRenderer.send('openWindow', {
key: `win-${Date.now()}`,
fullPath: path,
cookieData: { ...config }
})
}
// const taskHandles = () => {
// // 设置任务栏上下文菜单
// const contextMenu = new Remote.Menu()

View File

@ -1,27 +1,25 @@
<template>
<el-container class="class-reserv-wrap">
<!-- <div class="class-reserv-tabs">
<div class="class-reserv-tabs">
<el-segmented v-model="tabActive" block :options="tabOptions" size="large" />
</div>-->
</div>
<div class="class-reserv-body">
<template v-for="(item, index) in dataList" :key="index">
<reserv-item
:style="{'background-color': index%2==0?'#f5f5f5':''}"
:item="item"
v-if="item.bookImg"
@open-edit="reservDialog.openDialog(item)"
@delete-reserv="deleteReserv(item)"
@change="(...o) => emit('change', ...o)"
></reserv-item>
<reserv-item-apt
v-if="!item.bookImg"
:style="{'background-color': index%2==0?'#f5f5f5':''}"
:item="item"
@open-edit="reservDialog.openDialog(item)"
@delete-reserv="deleteReserv(item)"
@change="(...o) => emit('change', ...o)"
></reserv-item-apt>
</template>
<reserv-item
v-for="(item, index) in activeDataList"
v-show="tabActive === '进行中'"
:key="index"
:item="item"
@open-edit="reservDialog.openDialog(item)"
@delete-reserv="deleteReserv(item)"
></reserv-item>
<reserv-item
v-for="(item, index) in doneDataList"
v-show="tabActive === '已结束'"
:key="index"
:item="item"
@open-edit="reservDialog.openDialog(item)"
@delete-reserv="deleteReserv(item)"
></reserv-item>
</div>
<reserv ref="reservDialog"></reserv>
</el-container>
@ -30,66 +28,44 @@
<script setup>
import { ref, onMounted, computed, watch } from 'vue'
import { getSelfReserv } from '@/api/classManage'
import { listClasscourseNew } from '@/api/teaching/classcourse' // api
import ReservItem from '@/views/classManage/reserv-item.vue'
import Reserv from '@/views/prepare/container/reserv.vue'
import { useToolState } from '@/store/modules/tool'
import useUserStore from '@/store/modules/user'
import ReservItemApt from '@/views/classManage/reserv-item-apt.vue'
// import Chat from '@/utils/chat' // im
// if (!Chat.imChat) Chat.init()
const emit = defineEmits(['change'])
import { sessionStore } from '@/utils/tool'
const reservDialog = ref(null)
const tabOptions = ref(['进行中', '已结束'])
const tabActive = ref('进行中')
const dataList = ref([])
const toolStore = useToolState()
const userStore = useUserStore()
/*const activeDataList = computed(() => {
const activeDataList = computed(() => {
return dataList.value.filter((item) => {
return item.status !== '已结束'
})
})*/
const props = defineProps({
curNode: {
type: Object,
default: () => {}
}
})
const deleteReserv = (item) => {
dataList.value = dataList.value.filter((is) => {
return is.id !== item.id
})
}
/*const doneDataList = computed(() => {
const doneDataList = computed(() => {
return dataList.value.filter((item) => {
return item.status === '已结束'
})
})*/
})
//
const getData = () => {
Promise.all([listClasscourseNew({teacherid: userStore.id,evalid: props.curNode.id,pageSize:1000}), getSelfReserv({ex2:props.curNode.id})]).then(([res1,res2])=>{
let list = res2.data || []
let list2 = res1.rows || []
// list.sort((a,b) => { if(a.status=='') return -1; else return 0 })
list = list.concat(list2)
list.sort((a,b) => { return new Date(b.createTime) - new Date(a.createTime) })
dataList.value = list
})
/*getSelfReserv().then((res) => {
getSelfReserv().then((res) => {
const list = res.data || []
list.sort((a,b) => { if(a.status=='上课中') return -1; else return 0 })
dataList.value = list
})*/
})
}
const toolStore = useToolState()
watch(
() => [dataList,toolStore.isToolWin,props.curNode],
() => [dataList,toolStore.isToolWin],
() => {
console.log('====',toolStore)
setTimeout(()=>{
getData() //
},300)
@ -105,14 +81,13 @@ onMounted(() => {
height: 100%;
display: flex;
flex-direction: column;
//padding: 15px 10px;
padding: 15px 30px;
.class-reserv-tabs {
width: 30%;
text-align: left;
}
.class-reserv-body {
height: 100%;
font-size: 12px;
flex: 1;
overflow: auto;
padding: 10px 0;

View File

@ -1,99 +0,0 @@
<template>
<div class="class-reserv-item">
<div class="class-reserv-item-body">
{{ item.openDate }}&nbsp;{{ item.openTime }}
</div>
<div style="flex: 1;max-width: 400px">
<span>{{item.caption}}</span>
</div>
<div class="class-reserv-item-tool" style="width: 200px;max-width: 300px">
<el-tag v-if="item.status === 'closed'" style="margin-right: 5px" type="success">已结束</el-tag>
<el-tag v-if="item.status === 'open'" style="margin-right: 5px" type="danger">上课中</el-tag>
<el-button v-if="item.status === 'open'" :disabled="toolStore.isToolWin" size="small" type="primary" @click="startClassR(item)"
>继续上课</el-button
>
<!-- <el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
<el-button v-if="item.status === 'open'" :loading="loading" size="small" type="info" @click="endClassR(item)"
>下课{{ loading?'中...':'' }}</el-button
>
</div>
<div class="class-reserv-item-tool" style="width: 50px;">
<!-- <el-button v-if="item.status!='open'" size="small" type="danger" @click="deleteReserv">删除</el-button>-->
<el-tag>APT</el-tag>
</div>
<div style="min-width: 150px;"><span> 浏览25955 点赞26605</span></div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useToolState } from '@/store/modules/tool'
import { deleteSmartReserv } from '@/api/classManage'
import { ElMessage } from 'element-plus'
const emit = defineEmits(['openEdit', 'deleteReserv', 'change'])
const props = defineProps({
item: {
type: Object,
default: () => {}
}
})
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
const toolStore = useToolState() // -tool
const loading = ref(false) // loading
const openEdit = () => {
emit('openEdit', props.item)
}
const deleteReserv = () => {
deleteSmartReserv([props.item.id]).then((res) => {
if (res.data === true) {
ElMessage({
message: '删除成功',
type: 'success'
})
emit('deleteReserv', props.item)
}
})
}
//
const startClassR = (item) => {
emit('change', 'continue', item)
}
//
const endClassR = (item) => {
emit('change', 'close', item, { type: 2, loading })
}
</script>
<style scoped lang="scss">
.class-reserv-item {
display: flex;
background-color: white;
border-radius: 10px;
padding: 5px;
margin-bottom: 10px;
.class-reserv-item-img {
width: 60px;
img {
width: 100%;
}
}
.class-reserv-item-body {
display: flex;
align-items: center;
text-align: left;
padding-left: 5px;
width: 120px;
.class-reserv-item-title1 {
flex: 1;
label {
font-size: 20px;
font-weight: bold;
}
}
}
.class-reserv-item-tool {
margin-left: 15px;
display: flex;
align-items: center;
}
}
</style>

View File

@ -1,38 +1,44 @@
<template>
<div class="class-reserv-item">
<div class="class-reserv-item-img">
<img :src="basePath + item.bookImg" alt="封面" />
</div>
<div class="class-reserv-item-body">
{{ item.classDay }}&nbsp;{{ item.startTime }}
<div class="class-reserv-item-title1">
<label>{{ item.className }}</label>
<el-tag style="margin-left: 5px" type="primary"> {{ item.classType }}</el-tag>
<el-tag style="margin-left: 5px" type="primary"> {{ item.classSubject }}</el-tag>
</div>
<div class="class-reserv-item-title2">
{{ item.classDay }}&nbsp;{{ item.startTime }} ~ {{ item.classDay }}&nbsp;{{ item.endTime }}
{{ item.createUserName }}老师
</div>
<div class="class-reserv-item-title3">
<span v-for="(tag, index) in item.classItemList" :key="index" style="margin-left: 5px">
{{ index === 0 ? tag.name : '、' + tag.name }}</span
>
</div>
</div>
<div style="flex: 1;max-width: 400px">
<span v-for="(tag, index) in item.classItemList" :key="index">{{ index === 0 ? tag.name : '' + tag.name }}</span>
</div>
<div class="class-reserv-item-tool" style="width: 200px;max-width: 300px">
<el-tag v-if="item.status === '已结束'" style="margin-right: 5px" type="success">已结束</el-tag>
<el-tag v-if="item.status === '上课中'" style="margin-right: 5px" type="danger">上课中</el-tag>
<el-button v-if="item.status === '上课中'" :disabled="toolStore.isToolWin" size="small" type="primary" @click="startClassR(item)"
>继续上课</el-button
>
<!-- <el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
<el-button v-if="item.status === '上课中'" :loading="loading" size="small" type="info" @click="endClassR(item)"
>下课{{ loading?'中...':'' }}</el-button
<div class="class-reserv-item-tool">
<el-button v-if="item.status !== '已结束'" :disabled="toolStore.isToolWin" type="primary" @click="startClassR(item)"
>{{item.status == '上课中'?'上课中':'上课'}}</el-button
>
<el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>
<!-- <el-button v-if="item.status === '上课中'" type="info" @click="endClassR(item)"
>下课</el-button
>-->
<el-button v-if="item.status!='上课中'" type="danger" @click="deleteReserv">删除</el-button>
</div>
<div class="class-reserv-item-tool" style="width: 50px;">
<!-- <el-button v-if="item.status!='上课中'" size="small" type="danger" @click="deleteReserv">删除</el-button>-->
<el-tag type="success">PPT</el-tag>
</div>
<div style="min-width: 150px;"></div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useToolState } from '@/store/modules/tool'
import useUserStore from '@/store/modules/user'
import { createWindow } from '@/utils/tool'
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
import { ElMessage } from 'element-plus'
import { listEntpcourse } from '@/api/teaching/classwork'
const emit = defineEmits(['openEdit', 'deleteReserv','change'])
const emit = defineEmits(['openEdit', 'deleteReserv'])
const props = defineProps({
item: {
type: Object,
@ -41,7 +47,6 @@ const props = defineProps({
})
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
const toolStore = useToolState() // -tool
const loading = ref(false) // loading
const openEdit = () => {
emit('openEdit', props.item)
}
@ -82,29 +87,38 @@ const openLesson = () => {
})
}
const endClassR = (item) => {
emit('change', 'close', item, { type: 2, loading })
endClass(item.id).then((res) => {
if (res.data === true) {
ElMessage({
message: '下课成功',
type: 'success'
})
item.status = '已结束'
}
})
}
</script>
<style scoped lang="scss">
.class-reserv-item {
font-size: 13px;
display: flex;
background-color: white;
border-radius: 10px;
padding: 5px;
margin-bottom: 10px;
.class-reserv-item-img {
width: 60px;
width: 80px;
padding-left: 20px;
img {
width: 100%;
}
}
.class-reserv-item-body {
flex: 1;
display: flex;
align-items: center;
flex-direction: column;
text-align: left;
padding-left: 5px;
width: 120px;
padding-left: 30px;
font-size: 14px;
.class-reserv-item-title1 {
flex: 1;
label {

View File

@ -1,19 +1,28 @@
<!--
* @Author: 苦逼程序猿
* @Date: 2024-09-06 16:58:59
* @Warning: 千行代码Bug露锋芒
-->
<template>
<el-container class="class-reserv-wrap">
<div style="display: flex; justify-content: space-between;">
<div class="class-reserv-tabs">
<el-segmented v-model="tabActive" block :options="tabOptions" size="large" />
</div>
<div v-if="tabActive === '已批改'">
<div v-if="tabActive === '已结束'">
<div class="demo-date-picker">
<el-date-picker
v-model="startEndDate"
type="daterange"
start-placeholder="Start Date"
end-placeholder="End Date"
:default-time="defaultTime"
@change="changeStartEndDate"
/>
<div class="block">
<el-date-picker
v-model="EndDate"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择截止日期"
size="large"
:disabled-date="disabledDate"
@change="changeEndDate"
/>
</div>
</div>
</div>
</div>
@ -23,7 +32,7 @@
<div v-if="classWorkList.length > 0">
<task-item
v-for="(item, index) in activeDataList"
v-show="tabActive === '待批改'"
v-show="tabActive === '进行中'"
:key="index"
:item="item"
:tabactive="tabActive"
@ -32,7 +41,7 @@
></task-item>
<task-item
v-for="(item, index) in doneDataList"
v-show="tabActive === '已批改'"
v-show="tabActive === '已结束'"
:key="index"
:item="item"
:tabactive="tabActive"
@ -48,6 +57,7 @@
></el-empty>
</div>
</div>
<!-- <item-dialog ref="itemDialogRef" @cle-click="closeDialog"></item-dialog> -->
</el-container>
</template>
@ -55,30 +65,24 @@
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import { listByDeadDate, listClassworkdata } from '@/api/classTask'
import TaskItem from '@/views/classTask/container/classTask/task-item.vue'
import TaskItem from '@/views/classTask/container/task-item.vue'
// import ItemDialog from '@/views/classTask/container/item-dialog.vue'
import { useToolState } from '@/store/modules/tool'
import { getDateFormatDate, getTheOtherDay, getTheOtheNextDay } from '@/utils/date'
import { getCurrentTime, getTomorrow } from '@/utils/date'
import useUserStore from '@/store/modules/user'
import {createWindow} from '@/utils/tool'
import {debounce } from '@/utils/comm'
import useClassTaskStore from "@/store/modules/classTask";
import {sessionStore, createWindow} from '@/utils/tool'
const toolState = useToolState();
const classTaskStore = useClassTaskStore()
const userStore = useUserStore().user
const tabOptions = ref(['待批改', '已批改'])
const tabActive = ref('待批改')
// const itemDialogRef = ref(null)
const tabOptions = ref(['进行中', '已结束'])
const tabActive = ref('进行中')
const dataList = ref([])
// 23
const startEndDate = ref([
getTheOtherDay(3),
getTheOtheNextDay(3),
])
const defaultTime = ref<[Date, Date]>([
getTheOtherDay(3),
getTheOtheNextDay(3),
])
const EndDate = ref(getCurrentTime('YYYY-MM-DD'))
//
const classWorkList = ref([])
@ -90,18 +94,23 @@ const loading = ref(false)
const activeDataList = computed(() => {
return classWorkList.value
})
const doneDataList = computed(() => {
return classWorkList.value
})
const deleteReserv = (item) => {
console.log('删除待开发', item)
// dataList.value = dataList.value.filter((is) => {
// return is.id !== item.id
// })
}
const doneDataList = computed(() => {
return classWorkList.value
})
const changeStartEndDate = (val) => {
console.log('起止日期改变', val)
//
const disabledDate = (time) => {
return time.getTime() > Date.now()
}
//
const changeEndDate = (val) => {
console.log('截止日期改变', val)
getData() //
}
@ -109,202 +118,216 @@ const changeStartEndDate = (val) => {
const getData = async () => {
classWorkList.value = []
loading.value = true
// 1
// 1
getClassList()
// 2
await getClassWorkList()
// 2
// 3
getStudentClassWorkData()
loading.value = false
}
/**
* 1获取班级作业
* 1获取班级列表数据
* TODO 这里暂时取班级id的list后续需要在修改
*/
const getClassList = () => {
if(classTaskStore.classListIds.length==0){
// ids idlist
classTaskStore.listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' })
}
}
/**
* 2获取班级作业
*/
const getClassWorkList = async () => {
// homeworklist
const response = await listByDeadDate({
edituserid: userStore.userId, // id
edustage: userStore.edustage, //
edusubject: userStore.edusubject,//
startdate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[0]),
deaddate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[1]),//
status: tabActive.value === '待批改'? '1' : '2', // 1-
orderby: 'deaddate DESC',
pageSize: 100,
})
if(classTaskStore.classListIds.length>0){
// homeworklist
const response = await listByDeadDate({
classidarray: classTaskStore.classListIds.join(','),
edituserid: userStore.userId, // id
edustage: userStore.edustage, //
edusubject: userStore.edusubject,//
deaddate: tabActive.value === '进行中'? getTomorrow() : EndDate.value,//
status: '1', // 1-
// orderby: 'concat(deaddate,uniquekey) DESC',
orderby: 'deaddate DESC',
pageSize: 100,
})
let list = response.rows || [];
for (var i = 0; i < list.length; i++) {
//
list[i].workdatalist = [] //
list[i].workdatalistVisible = false
list[i].feedtimelength = 0 //
list[i].rightAnswerCount = 0
list[i].scoingRate = 0 + '%' //
list[i].averagetime = 0 //
for (var i = 0; i < response.rows.length; i++) {
//
response.rows[i].workdatalist = []
response.rows[i].workdatacount = 0 //
response.rows[i].workdatalistVisible = false
response.rows[i].workdatafeedbackcount = 0 //
response.rows[i].feedtimelength = 0
response.rows[i].rightAnswerCount = 0
response.rows[i].scoingRate = 0 + '%' //
response.rows[i].averagetime = 0 //
// ----------------------------------------------
// UI
if (list[i].worktype == '学习目标定位') {
list[i].workclass = 'success'
list[i].workcodesList = JSON.parse(list[i].workcodes)
} else if (list[i].worktype == '教材研读') {
list[i].workclass = 'primary'
} else if (list[i].worktype == '框架梳理') {
list[i].workclass = 'warning'
} else if (list[i].worktype == '学科定位') {
list[i].workclass = 'info'
} else if (list[i].worktype == '习题训练') {
list[i].workclass = 'danger'
} else {
list[i].workclass = ''
// ----------------------------------------------
// UI
if (response.rows[i].worktype == '学习目标定位') {
response.rows[i].workclass = 'success'
response.rows[i].workcodesList = JSON.parse(response.rows[i].workcodes)
} else if (response.rows[i].worktype == '教材研读') {
response.rows[i].workclass = 'primary'
} else if (response.rows[i].worktype == '框架梳理') {
response.rows[i].workclass = 'warning'
} else if (response.rows[i].worktype == '学科定位') {
response.rows[i].workclass = 'info'
} else if (response.rows[i].worktype == '习题训练') {
response.rows[i].workclass = 'danger'
} else {
response.rows[i].workclass = ''
}
//
if (response.rows[i].entpcourseworklist != '') {
response.rows[i].entpcourseworklistarray = JSON.parse(
'[' + response.rows[i].entpcourseworklist + ']'
)
} else {
response.rows[i].entpcourseworklistarray = []
}
// classworkdatastudentids
if (
response.rows[i].classworkdatastudentids != '' &&
response.rows[i].classworkdatastudentids != null &&
response.rows[i].classworkdatastudentids != 'null'
) {
const stuList = JSON.parse('[' + response.rows[i].classworkdatastudentids + ']')
response.rows[i].workdatacount = stuList.length
}
}
//
if (list[i].entpcourseworklist != '') {
list[i].entpcourseworklistarray = JSON.parse(
'[' + list[i].entpcourseworklist + ']'
)
} else {
list[i].entpcourseworklistarray = []
// (workdatacount)>0
if (response.rows && response.rows.length > 0) {
classWorkList.value = response.rows && response.rows.filter((item) => item.workdatacount > 0)
//TODO total
total.value = response.total
}else{
classWorkList.value = []
total.value = 0
}
loading.value = false
}
// (workdataresultsum)>0
if (list && list.length > 0) {
classWorkList.value = list && list.filter((item) => item.workdataresultsum > 0)
//TODO total
total.value = 0
}else{
classWorkList.value = []
total.value = 0
}
loading.value = false
}
/**
* 2获取多个班级学生作业数据
* 查询已交的列表
* @param workList 需要更新的作业list
* @param Refresh true 不用刷新false 需要刷新
* 3获取多个班级学生作业数据
*/
const getStudentClassWorkData = async(workList = [], Refresh = true) => {
// (workdataresultcount )
let SubmitClWorkList = [];
if(Refresh){
SubmitClWorkList = classWorkList.value.filter((item) => item.workdataresultcount > 0) ;
}else{
SubmitClWorkList = workList;
}
const getStudentClassWorkData = async() => {
// const { chapterId } = await useGetHomework(props.bookobj.node)
// this.entpcourseid = chapterId
if(classTaskStore.classListIds.length>0){
// listClassworkdataByDeadDate({
// edituserid: userStore.userId, // id
// classids: classTaskStore.classListIds.join(','),
// edusubject: userStore.edusubject,//
// deaddate: tabActive.value === ''? getTomorrow() : EndDate.value,//
// //status: '1', // 1-
// orderby: "deaddate DESC",// TODO deaddate
// pageSize: 1000,
// })
const ids = classWorkList.value.map((item) => item.id).join(',');
listClassworkdata({
classworkids: ids,
pageSize: 1000,
}).then((res) => {
for (var t = 0; t < classWorkList.value.length; t++) {
for (var i = 0; i < res.rows.length; i++) {
if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].finishtimelength != '0') {
console.log('==================')
// /
// resultcount0
classWorkList.value[t].workdatafeedbackcount++
console.log('有提交的作业', SubmitClWorkList)
const ids = SubmitClWorkList&&SubmitClWorkList.map((item) => item.id).join(',');
if (ids == '') {
return;
}
listClassworkdata({
classworkids: ids,
pageSize: 1000,
}).then((res) => {
for (var t = 0; t < classWorkList.value.length; t++) {
for (var i = 0; i < res.rows.length; i++) {
// finishtimelength != '0'
if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].finishtimelength != '0') {
console.log('==================')
// /
// resultcount0
//classWorkList.value[t].workdataresultcount++
//
classWorkList.value[t].feedtimelength += parseInt(res.rows[i].finishtimelength)
//
classWorkList.value[t].feedtimelength += parseInt(res.rows[i].finishtimelength)
//
if (
res.rows[i].classworkevallist != '' &&
res.rows[i].classworkevallist != null &&
res.rows[i].classworkevallist != 'null'
) {
let replacedString = res.rows[i].classworkevallist.replace(/""/g, '"')
// , : "{\"id\":172907, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358520, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172908, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358521, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172909, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363096, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172910, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363098, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist).replace(
/"(\[.*\])"/g,
'$1'
)
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist)
var evalarray
try {
evalarray = JSON.parse('[' + res.rows[i].classworkevallist + ']')
} catch {
evalarray = JSON.parse('[' + replacedString + ']')
}
for (var e = 0; e < evalarray.length; e++) {
if (res.rows[i].worktype == '常规作业') {
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent).replace(
/"(\[.*\])"/g,
'$1'
)
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent)
//
if (
res.rows[i].classworkevallist != '' &&
res.rows[i].classworkevallist != null &&
res.rows[i].classworkevallist != 'null'
) {
let replacedString = res.rows[i].classworkevallist.replace(/""/g, '"')
// , : "{\"id\":172907, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358520, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172908, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358521, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172909, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363096, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172910, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363098, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist).replace(
/"(\[.*\])"/g,
'$1'
)
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist)
var evalarray
try {
evalarray = JSON.parse('[' + res.rows[i].classworkevallist + ']')
} catch {
evalarray = JSON.parse('[' + replacedString + ']')
}
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
//
classWorkList.value[t].rightAnswerCount++
for (var e = 0; e < evalarray.length; e++) {
if (res.rows[i].worktype == '常规作业') {
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent).replace(
/"(\[.*\])"/g,
'$1'
)
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent)
}
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
//
classWorkList.value[t].rightAnswerCount++
}
}
}
}
// workdatacount
if (res.rows[i].classworkid == classWorkList.value[t].id) {
classWorkList.value[t].workdatalist.push(res.rows[i])
}
}
// workdataresultsum
if (res.rows[i].classworkid == classWorkList.value[t].id) {
classWorkList.value[t].workdatalist.push(res.rows[i])
// workdatacount0
if (
classWorkList.value[t].workdataresultcount > 0 &&
classWorkList.value[t].workdatacount > 0
) {
classWorkList.value[t].finishpercent = parseInt(
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100
)
} else {
classWorkList.value[t].finishpercent = 0
}
//
// 2024-04-12by jackyshen
//
if (classWorkList.value[t].workdatafeedbackcount > 0) {
classWorkList.value[t].averagetime = Math.ceil(classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount / 60).toFixed(0)
} else {
classWorkList.value[t].averagetime = 0
}
//
//
// /**100
if (
classWorkList.value[t].entpcourseworklistarray &&
classWorkList.value[t].entpcourseworklistarray.length > 0
) {
var dd =
(classWorkList.value[t].rightAnswerCount /
(classWorkList.value[t].entpcourseworklistarray.length *
classWorkList.value[t].workdatacount)) *
100
classWorkList.value[t].scoingRate = dd.toFixed(0) + '%'
} else {
classWorkList.value[t].scoingRate = '0%'
}
//
//
}
// : workdataresultcount ; workdataresultsum 0;
if (classWorkList.value[t].workdataresultcount > 0 && classWorkList.value[t].workdataresultsum > 0 ) {
classWorkList.value[t].finishpercent = parseInt(
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdataresultsum) * 100
)
} else {
classWorkList.value[t].finishpercent = 0
}
/** 计算 已批阅进度 */
// workdataresultsum teacherrationgcount
// (-) / * 100
if (classWorkList.value[t].workdataresultsum > 0) {
classWorkList.value[t].teacherCorrectionProgress = parseInt(
((classWorkList.value[t].teacherrationgcount) / classWorkList.value[t].workdataresultsum) * 100
)
} else {
classWorkList.value[t].teacherCorrectionProgress = 0
}
//
// 2024-04-12by jackyshen
// /
if (classWorkList.value[t].workdataresultcount > 0) {
classWorkList.value[t].averagetime = Math.ceil(classWorkList.value[t].feedtimelength / classWorkList.value[t].workdataresultcount / 60).toFixed(0)
} else {
classWorkList.value[t].averagetime = 0
}
// /**100
if (
classWorkList.value[t].entpcourseworklistarray &&
classWorkList.value[t].entpcourseworklistarray.length > 0
) {
var dd =
(classWorkList.value[t].rightAnswerCount /
(classWorkList.value[t].entpcourseworklistarray.length *
classWorkList.value[t].workdataresultsum)) *
100
classWorkList.value[t].scoingRate = dd.toFixed(0) + '%'
} else {
classWorkList.value[t].scoingRate = '0%'
}
}
})
})
}
}
@ -330,7 +353,6 @@ const getStudentClassWorkDataPolling = () => {
getStudentVisible()
//
pollingST.value = setInterval(() => {
console.log('轮询查询学生作业进度')
getStudentVisible()
}, 1000 * 10)
}
@ -340,10 +362,6 @@ const closeDialog = () => {
getStudentClassWorkDataPolling()
}
const debounceOpenWin = debounce(() => {
toolState.isTaskWin=true; //
createWindow('open-taskwin',{url:'/teachClassTask'}); //
}, 1000);
/**
* 开启新批改弹窗
* @param item 作业对象
@ -351,11 +369,14 @@ const debounceOpenWin = debounce(() => {
const onClickItem = (item) => {
console.log('开启弹窗,关闭作业进度轮询')
clearInterval(pollingST.value)
// itemDialogRef.value.openDialog(item)
console.log('防抖开启弹窗')
// sessionStore.set('teachClassWorkItem', item); // item
localStorage.setItem('teachClassWorkItem', JSON.stringify(item));
debounceOpenWin();
//
// 1item2 3item
// localStorage.setItem('teachClassWorkItem', JSON.stringify(item))
sessionStore.set('teachClassWorkItem', item)
toolState.isTaskWin=true //
createWindow('open-taskwin',{url:'/teachClassTask'})
}
@ -365,56 +386,60 @@ onUnmounted(() => {
// [] -
const getStudentVisible = async () => {
if(!classWorkList.value.length>0){
return;
if (classTaskStore.classListIds.length <= 0) {
return
}
//
const response = await listByDeadDate({
classidarray: classTaskStore.classListIds.join(','),
edituserid: userStore.userId, // id
edustage: userStore.edustage, //
edustage: userStore.edustage,//
edusubject: userStore.edusubject,//
startdate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[0]),
deaddate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[1]),//
status: tabActive.value === '待批改'? '1' : '2', // 1-
deaddate: tabActive.value === '进行中'? getTomorrow() : EndDate.value,//
status: '1', // 1-
// orderby: 'concat(deaddate,uniquekey) DESC',
orderby: 'deaddate DESC',
pageSize: 100,
pageSize: 100
})
const curWorkList = response.rows
let list = response.rows || [];
let newList = [];
for(let i = 0; i < classWorkList.value.length; i++){
// listidlistid listlist
const isList = list.filter((item) => item.id === classWorkList.value[i].id);
if(isList.length === 0){
// listidlistidlist
classWorkList.value.splice(i,1);
}
for(let j = 0; j < list.length; j++){
// workdataresultcount ;
if(classWorkList.value[i].id === list[j].id && classWorkList.value[i].workdataresultcount != list[j].workdataresultcount){
// =
newList.push(list[j]);
/**
* warn: 这里仅更新了finishpercent(进度条), 且当前作业布置推送新任务时, curWorkList中会查到新的任务与当前页面中this.classWorkList长度不一致,
* 故这里需循环this.classWorkList且只更新当前页面中的存在的任务进度
*/
for (let t = 0; t < classWorkList.value.length; t++) {
// []
// if( getDateTime > classWorkList.value[t].deaddate ){
// continue;
// }
// (index)
let curWork = curWorkList.find((work) => work.id === classWorkList.value[t].id)
// workdataresultcount workdatacount0
if (curWork && curWork.workdataresultcount > 0 && classWorkList.value[t].workdatacount > 0) {
classWorkList.value[t].workdataresultcount = curWork.workdataresultcount
//
classWorkList.value[t].finishpercent = parseInt(
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100
)
//
if (classWorkList.value[t].workdatafeedbackcount > 0) {
classWorkList.value[t].averagetime = Math.ceil(classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount / 60).toFixed(0)
} else {
classWorkList.value[t].averagetime = 0
}
// teacherrationgcount
if(classWorkList.value[i].id === list[j].id && classWorkList.value[i].teacherrationgcount != list[j].teacherrationgcount){
//
if (classWorkList.value[i].workdataresultsum > 0) {
//
classWorkList.value[i].teacherrationgcount = list[j].teacherrationgcount;
classWorkList.value[i].teacherCorrectionProgress = parseInt(
((list[j].teacherrationgcount) / list[j].workdataresultsum) * 100
)
} else {
classWorkList.value[i].teacherCorrectionProgress = 0
}
//
classWorkList.value[t].teacherrationgcount = curWork.teacherrationgcount
} else {
//
if(curWork && curWork.workdataresultcount == 0){
//
classWorkList.value[t].teacherrationgcount = curWork.teacherrationgcount
}
classWorkList.value[t].finishpercent = 0
}
}
if(newList.length>0){
//
const list = newList&&newList.filter((item) => item.workdataresultcount > 0) ;
getStudentClassWorkData(list,false);
}
return 1
}
@ -422,27 +447,16 @@ const getStudentVisible = async () => {
watch(
() => [dataList, toolState.isTaskWin],
() => {
console.log('监听--批改窗口是否打开===', toolState.isTaskWin)
console.log('=监听到批改窗口打开了===', toolState.isTaskWin)
if(!toolState.isTaskWin){
if(tabActive.value === '待批改'){
closeDialog();//
}
closeDialog();//
}
}
)
watch(tabActive, (newVal,oldVal)=>{
console.log('newVal',newVal);
getData() //
if(newVal === '待批改'){
//
console.log('监听---开启轮询')
closeDialog();
}else{
//
console.log('监听---关闭轮询')
clearInterval(pollingST.value);
}
})
</script>

View File

@ -15,11 +15,10 @@
<!-- 标题 -->
<el-row style="align-items: center; margin-bottom: 0px; flex: 0 0 auto">
<el-col :span="12" style="padding-left: 20px; text-align: left;">
<!-- <div class="unit-top-left" @click="isCollapse = !isCollapse"> -->
<div class="unit-top-left">
<!-- <i v-if="!isCollapse" class="iconfont icon-xiangzuo" style="color: blue;"></i> -->
<span>作业布置</span>
<!-- <i v-if="isCollapse" class="iconfont icon-xiangyou" style="color: blue;"></i> -->
<div class="unit-top-left" @click="isCollapse = !isCollapse">
<i v-if="!isCollapse" class="iconfont icon-xiangzuo" style="color: blue;"></i>
<span>课程目录</span>
<i v-if="isCollapse" class="iconfont icon-xiangyou" style="color: blue;"></i>
</div>
</el-col>
<el-col :span="12">
@ -37,7 +36,6 @@
<div style="flex: 1;overflow: hidden;">
<el-table
ref="taskTable"
v-loading="loading"
:data="taskList"
:tree-props="{checkStrictly: true}"
row-key="id"
@ -60,7 +58,7 @@
</div>
<div>
<div v-if="scope.row.status == '10'">
<el-button v-hasPermi="['teaching:classwork:edit']" text @click="handleWorkEditItem(scope.row)">编辑</el-button>
<el-button v-hasPermi="['teaching:classwork:edit']" text @click="newHandleWorkEdit2ClassWorkQuizAdd(scope.row, scope.$index)">编辑</el-button>
</div>
<div v-else>
<el-button v-hasPermi="['teaching:classwork:edit']" text @click="handleWorkEdit(scope.row, scope.$index)">查看详情</el-button>
@ -77,10 +75,10 @@
</div>
<div>
<div v-if="scope.row.status == '10'">
<el-button v-hasPermi="['teaching:classwork:edit']" text @click="handleWorkTitleEdit(scope.row, scope.$index,'edit')">编辑</el-button>
<el-button v-hasPermi="['teaching:classwork:edit']" text @click="handleWorkTitleEdit(scope.row, scope.$index)">编辑</el-button>
</div>
<div v-else>
<el-button v-hasPermi="['teaching:classwork:edit']" text @click="handleWorkTitleEdit(scope.row, scope.$index,'query')">查看详情</el-button>
<el-button v-hasPermi="['teaching:classwork:edit']" text @click="handleWorkTitleEdit(scope.row, scope.$index)">查看详情</el-button>
</div>
</div>
</div>
@ -94,21 +92,20 @@
<el-table-column v-if="props.initDataProps.queryType!=='single'" label="推送配置" align="left" min-width="16%">
<template #default="scope">
<div style="display: flex">
<el-button v-hasPermi="['teaching:classwork:edit']" link icon="Setting" :style="formatStyle(scope.row)" @click="scope.row.status == '10' ? openSet(scope.row,'item') : ''">{{scope.row.status == '10'? '推送' : '已推送'}}</el-button>
<el-button link icon="Setting" @click="scope.row.status == '10' ? openClassWorkConfigDialog(scope.row, -1,'item') : ''" :style="formatStyle(scope.row)" v-hasPermi="['teaching:classwork:edit']">{{scope.row.status == '10'? '推送' : '已推送'}}</el-button>
</div>
</template>
</el-table-column>
<el-table-column v-if="props.initDataProps.queryType!=='single'" label="历史推送" align="left" min-width="16%">
<template #default="scope">
<el-button @click="queryPushRecords(scope.row)">查看记录</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 作业内容详情 -->
<!-- 作业内容编辑 -->
<el-dialog v-model="workEdit" title="作业内容详情" width="90%" append-to-body>
<div v-if="currentTag=='学习目标定位'" style="display: flex;">
<degreeevolution :attainmentList="attainmentList" :show-class="true" :courseQualityList="courseQualityList"/>
</div>
<!-- 课标研读 目标设定 教材研读 框架梳理 学科定位 -->
<div v-if="currentTag=='习题训练'" :style="{'padding': '15px', 'overflow': 'auto'}">
<el-table :data="workConfObj.quizlist" style="width: 100%;">
<el-table-column type="index" width="60" />
@ -122,197 +119,52 @@
</el-table-column>
<el-table-column label="分值" align="center" width="180">
<template #default="scope">
<el-input-number v-model="scope.row.score" :min="1" :max="100" :disabled="currentWorkEdit.currentType == 'query'?true:false"></el-input-number >
<el-input-number v-model="scope.row.score" :min="1" :max="100" :disabled="checkTaskAssigned(currentWorkEdit.currentTask)"></el-input-number >
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template #default="scope">
<el-button :disabled="currentWorkEdit.currentType == 'query'?true:false" @click="handleWorkConfigQuizMinus(scope.$index)">删除</el-button>
<el-button :disabled="checkTaskAssigned(currentWorkEdit.currentTask)" @click="handleWorkConfigQuizMinus(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 框架梳理 -->
<div v-if="currentTag=='框架梳理'" :style="{'padding': '15px', 'overflow': 'auto'}">
<div style="margin: 5px; background-color: white">
<template v-for="(_item,index) in workConfObj.chooseWorkLists" :key="index">
<div v-if="_item.worktype=='框架梳理'">
<div style="color: silver; display: flex;align-items: center;">
<div style="flex: 1;">{{ _item.worktype }}</div>
<div style="flex: 1;">分值{{ _item.score }}</div>
<div style="display: flex;align-items: center;flex: 1;justify-content: flex-end;">
<el-button @click="prevRead(_item)">预览</el-button>
</div>
</div>
</div>
</template>
</div>
</div>
<!-- TODO 课堂展示 预览待开发-->
<!-- 常规作业包含多个格式图片略缩图展示点击放大其他附件跳转另外弹窗查看 -->
<div v-if="currentTag=='常规作业'" :style="{'padding': '15px', 'overflow': 'auto'}">
<div style="margin: 5px; background-color: white">
<div v-if="workConfObj.teacherFeedContentList.length > 0">
<div class="image_list">
<div v-if="workConfObj.teachImageList.length > 0">
<div style="margin-bottom: 5px;text-align: left;">
<span style="color: red">温馨提示点击图片可放大预览 </span>
</div>
<div style="display: flex; flex-wrap: nowrap; flex-direction: row;">
<div v-for="(imageItem, index) in workConfObj.teachImageList" :key="index" style="margin: 0 15px;">
<el-image
style="width: 100px; height: 100px"
:src="imageItem.url"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="
workConfObj.teachImageList
.filter(
(item) =>
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
)
.map((item) => item.url)
"
:initial-index="4"
fit="contain"
/>
</div>
<!-- 其他类型附件 -->
<div v-if="workConfObj.teachFileList.length > 0">
<div style="margin: 10px 0;text-align: left; margin: auto 0; cursor: pointer;">
<span style="color: #409eff" @click="openFile">预览其他类型附件</span>
</div>
</div>
</div>
</div>
<div v-else>
<div v-if="workConfObj.teachFileList.length > 0">
<div style="margin: 10px 0;text-align: left; margin: auto 0; cursor: pointer;">
<span style="color: #409eff" @click="openFile">预览其他类型附件</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer" style="text-align: right; margin-top: 20px;">
<div style="display: flex">
<el-button type="primary" style="margin-left: auto" @click="workEdit = false"> </el-button>
<!-- <el-button v-if="currentTag=='习题训练'" style="margin-right: auto" type="primary"
:disabled="checkTaskAssigned(currentWorkEdit.currentTask)" @click="handleWorkEdit2ClassWorkQuizAdd">添加作业</el-button> -->
<el-button type="primary" style="margin-left: auto" :disabled="checkTaskAssigned(currentWorkEdit.currentTask)"
@click="submitStudy('submit')"> </el-button>
</div>
</div>
</el-dialog>
<!-- 作业说明编辑 -->
<el-dialog v-model="currentWorkEdit.workTitleEdit" title="作业说明" width="70%" append-to-body>
<el-input v-model="currentWorkEdit.currentTitle" type="textarea" rows="5" placeholder="请输入作业说明" :disabled="currentWorkEdit.currentType == 'query'?true:false"/>
<el-dialog v-model="currentWorkEdit.workTitleEdit" title="作业说明编辑" width="70%" append-to-body>
<el-input v-model="currentWorkEdit.currentTitle" type="textarea" rows="5" placeholder="请输入作业说明" :disabled="checkTaskAssigned(currentWorkEdit.currentTask)"/>
<div slot="footer" class="dialog-footer" style="text-align: right; margin-top: 20px;">
<el-button type="primary" :disabled="checkTaskAssigned(currentWorkEdit.currentTask)"
@click="submitWorkTitle('submit')"> </el-button>
</div>
</el-dialog>
<!-- 已推送历史 -->
<el-dialog v-model="pushRecordsOpen" title="推送历史记录" width="80%" append-to-body>
<div style="flex: 1;overflow: hidden; min-height: 400px;">
<el-table
ref="pushRecordsRef"
:data="pushRecordsList"
v-loading="loading"
:tree-props="{checkStrictly: true}"
row-key="id"
style="width: 100%;height: 100%; border: 1px solid #dcdfe6;border-radius: 3px;flex:1"
:row-class-name="tableRowClassName"
>
<el-table-column type="index" label="序号" width="52" reserve-selection align="center" />
<el-table-column v-if="props.initDataProps.queryType!=='single'" label="创建时间" align="center" prop="timestamp" min-width="30%" sortable>
<template #default="scope">
<el-tag size="large">{{ scope.row.timestamp }}</el-tag>
</template>
</el-table-column>
<el-table-column label="作业名称" prop="uniquekey" min-width="18%" align="center">
</el-table-column>
<el-table-column label="作业类型'" align="center" prop="worktype" min-width="20%" sortable>
<template #default="scope">
<el-tag :type="scope.row.workclass" size="large">{{ scope.row.worktype }}</el-tag>
</template>
</el-table-column>
<el-table-column label="作业内容" align="center" min-width="20%">
<template #default="scope">
<div style="border: 1px solid #ccc; width: 100%; height: auto; display: flex; justify-content: space-between; padding-left: 10px">
<div style="display: flex; margin-top: 5px">
<el-tag v-if="scope.row.entpcourseworklistarray.length>0" effect="dark" type="warning" size="small" style="margin-left: 5px" round>{{ scope.row.entpcourseworklistarray.length }}</el-tag>
</div>
<div>
<el-button text @click="handleWorkEdit(scope.row, scope.$index)">查看详情</el-button>
</div>
</div>
</template>
</el-table-column>
<el-table-column v-if="props.initDataProps.queryType!=='single'" label="作业说明" align="center" min-width="36%">
<template #default="scope">
<div style="border: 1px solid #ccc; width: 100%; height: auto; display: flex; justify-content: space-between; padding-left: 10px">
<div style="display: flex; margin-top: 5px">
<div class="singe-line" style="max-width: 200px" v-html="scope.row.title"></div>
</div>
<div>
<el-button @click="handleWorkTitleEdit(scope.row, scope.$index, 'query')">查看详情</el-button>
</div>
</div>
</template>
</el-table-column>
<el-table-column v-if="props.initDataProps.queryType!=='single'" label="推送配置" align="left" min-width="16%">
<template #default="scope">
<div style="display: flex">
<el-button @click="onClickItem(scope.row)">批阅详情</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer" style="text-align: right; margin-top: 20px;">
<el-button type="primary" @click="pushRecordsOpen = false"> </el-button>
</div>
</el-dialog>
<!-- 推送作业的配置对话框 -->
<SetHomework v-model="setDialog" :entpcourseid="entpcourseid" :rows="rowsList" @on-close="closeHomework" @on-success="successHomework"/>
<!-- 预览框 -->
<prevReadMsgDialog ref="prevReadMsgDialogRef" :bookobj="courseObj"/>
<!-- 其他附件预览框 acceptParams-->
<prevReadImgFileDialog ref="prevReadImgFileDialogRef"/>
</div>
</div>
</template>
<script setup>
import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue'
import { onMounted, ref, toRaw,watch, reactive, getCurrentInstance } from 'vue'
import { useRouter } from 'vue-router'
import ChooseTextbook from '@/components/choose-textbook/index.vue'
import { homeworklist, delClasswork } from '@/api/teaching/classwork'
import { listEntpcoursework, listClassworkeval,updateClasswork,getEvaluationclue } from '@/api/classTask'
import { listEntpcoursework, listClassworkeval,updateClasswork } from '@/api/classTask'
import { useGetHomework } from '@/hooks/useGetHomework'
import { editListItem } from '@/hooks/useClassTask'
import { processList } from '@/hooks/useProcessList'
import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue'
import prevReadImgFileDialog from '@/views/classTask/container/classTask/prevReadImgFileDialog.vue'
import SetHomework from '@/components/set-homework/index.vue'
import {sessionStore} from '@/utils/store'
import {createWindow} from '@/utils/tool'
import { useToolState } from '@/store/modules/tool'
import {throttle,debounce } from '@/utils/comm'
const toolState = useToolState();
import { getCurrentTime } from '@/utils/date'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user
const { proxy } = getCurrentInstance()
@ -329,8 +181,7 @@ const props = defineProps({
// ---------------------------------------------------
const isCollapse = ref(false)
const prevReadMsgDialogRef = ref(null);// ref
const prevReadImgFileDialogRef = ref(null);// ref
const courseObj = reactive({
// : id,id,id,
@ -342,35 +193,23 @@ const courseObj = reactive({
//
})
const taskList = ref([]); //
const taskList = ref([]);
const loading = ref(false);
const pushRecordsList = ref([]); //
const pushRecordsOpen = ref(false); //
const pushRecordsLoading = ref(false); // loading
//
const workEdit = ref(false);
const currentWorkEdit = reactive({
workTitleEdit: false,
currentType:'query',
currentTask: {},
currentTitle: '',
currentIndex: 0,
})//
const currentTag = ref('');//
// -
const workConfObj = reactive({
quizlist: [], // list
chooseWorkLists: [], //
teacherFeedContentList: [],//
teachFileList: [], // list
teachImageList: [],// list
});
//
const setDialog = ref(false); //
const rowsList = ref([]) //
const entpcourseid = ref('') // id
// ---------------------------------------------------
@ -396,90 +235,6 @@ const getData = (data) => {
// ID
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId}))
//
initHomeWork();
}
/**
* 获取 entpcourseid 获取作业列表
*/
const initHomeWork = async()=> {
loading.value = true;
const { res, chapterId } = await useGetHomework(courseObj.node);
console.log('entpcourseid', chapterId);
console.log('res', res);
entpcourseid.value = chapterId;
taskList.value = res;
loading.value = false;
}
/**
* 查看推送历史
*
*/
const queryPushRecords = (row) => {
//
console.log(row,'查看该行推送历史')
pushRecordsOpen.value = true;
pushRecordsLoading.value = true;
homeworklist({
entpcourseid: entpcourseid.value,
edituserid: userStore.userId,
parentid: row.id,
orderby: "timestamp DESC",
pageSize: 100,
status: '1' // 1 10
}).then((res) => {
if(res.rows&& res.rows.length>0){
for (var i = 0; i < res.rows.length; i++) {
res.rows[i].taskconfig = []
// UI
// if (res.rows[i].worktype == '') {
// res.rows[i].workclass = 'success';
// res.rows[i].workcodesList = JSON.parse(res.rows[i].workcodes);
// } else
// TODO
if (res.rows[i].worktype == '课堂展示') {
res.rows[i].workclass = 'primary';
} else if (res.rows[i].worktype == '框架梳理') {
res.rows[i].workclass = 'warning';
} else if (res.rows[i].worktype == '常规作业') {
res.rows[i].workclass = 'info';
} else if (res.rows[i].worktype == '习题训练') {
res.rows[i].workclass = 'danger';
} else {
res.rows[i].workclass = 'primary';
}
//
if (res.rows[i].entpcourseworklist != '') {
res.rows[i].entpcourseworklistarray = JSON.parse('[' + res.rows[i].entpcourseworklist + ']')
} else {
res.rows[i].entpcourseworklistarray = []
}
res.rows[i].fileShowName = res.rows[i].uniquekey
}
pushRecordsList.value = res.rows;
pushRecordsLoading.value = false;
}
}).catch((err) => {
pushRecordsLoading.value = false;
})
}
const debounceOpenWin = debounce(() => {
toolState.isTaskWin=true; //
createWindow('open-taskwin',{url:'/teachClassTask'}); //
}, 1000);
//
const onClickItem = (item) => {
console.log('防抖开启弹窗')
// sessionStore.set('teachClassWorkItem', item); // item
localStorage.setItem('teachClassWorkItem', JSON.stringify(item));
debounceOpenWin();
}
/**
@ -496,7 +251,7 @@ const handleDelete =() => {
return delClasswork(ids.join(','));
}).then(() => {
setTimeout(() => {
initHomeWork();
getTaskList();
}, 1500);
proxy.$modal.msgSuccess("删除成功");
}).catch(() => {})
@ -509,146 +264,139 @@ const handleDelete =() => {
/**
* 1.获取作业列表
*/
// const getTaskList = async () => {
// //
// taskList.value = [];
// loading.value = true;
// homeworklist({entpcourseid: entpcourseid.value, orderby: "deaddate DESC" , edituserid: userStore.userId, pageSize: 500}).then(res => {
// let model = [];
// let mission = [];
const getTaskList = async () => {
const { chapterId } = await useGetHomework(courseObj.node)
// this.entpcourseid = chapterId
//
homeworklist({entpcourseid: chapterId, orderby: "deaddate DESC" , edituserid: userStore.userId, pageSize: 500}).then(res => {
let model = [];
let mission = [];
// if(res.rows&&res.rows.length > 0){
// //
// res.rows.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
// }
// for (let item of res.rows){
// item.taskconfig = [];
for (let item of res.rows){
item.taskconfig = [];
// //
// if (item.timelength == null) {
// item.timelength = 1;
// }
// if (item.weights == null) {
// item.weights = 1;
// }
//
if (item.timelength == null) {
item.timelength = 1;
}
if (item.weights == null) {
item.weights = 1;
}
// // UI
// if (item.worktype == '') {
// item.workclass = 'success';
// item.workcodesList = JSON.parse(item.workcodes);
// } else if (item.worktype == '') {
// item.workclass = 'primary';
// } else if (item.worktype == '') {
// item.workclass = 'warning';
// } else if (item.worktype == '') {
// item.workclass = 'info';
// } else if (item.worktype == '') {
// item.workclass = 'danger';
// } else {
// item.workclass = 'primary';
// }
// //
// if (item.entpcourseworklist != '') {
// item.entpcourseworklistarray = JSON.parse('['+item.entpcourseworklist+']');
// } else {
// item.entpcourseworklistarray = [];
// }
// UI
if (item.worktype == '学习目标定位') {
item.workclass = 'success';
item.workcodesList = JSON.parse(item.workcodes);
} else if (item.worktype == '教材研读') {
item.workclass = 'primary';
} else if (item.worktype == '框架梳理') {
item.workclass = 'warning';
} else if (item.worktype == '学科定位') {
item.workclass = 'info';
} else if (item.worktype == '习题训练') {
item.workclass = 'danger';
} else {
item.workclass = 'primary';
}
//
if (item.entpcourseworklist != '') {
item.entpcourseworklistarray = JSON.parse('['+item.entpcourseworklist+']');
} else {
item.entpcourseworklistarray = [];
}
// //
// if (item.status == '10') {
// // ,
// model.push(item);
// continue;
// }
// else if (item.status == '1') {
// // ,
// let ss = [];
// if (item.classworkdatastudentids != null) {
// ss = JSON.parse('['+ item.classworkdatastudentids+']');
// }
// const js = {
// id: item.id,
// classid: item.classid,
// classcaption: item.classcaption,
// parentid: 0,
// worktype: '',
// workkey: item.workkey,
// worktag: '',
// entpcourseid: 0,
// evalid: 0,
// edusubject: '',
// edudegree: '',
// workdate: '',
// title: '',
// workcodes: '',
// studentlist: ss,
// deaddate: item.deaddate,
// timelength: item.timelength,
// weights: item.weights,
// feedtype: item.feedtype
// }
// item.taskconfig.push(js);
// mission.push(item);
// }
// }
//
if (item.status == '10') {
// ,
model.push(item);
continue;
}
else if (item.status == '1') {
// ,
let ss = [];
if (item.classworkdatastudentids != null) {
ss = JSON.parse('['+ item.classworkdatastudentids+']');
}
const js = {
id: item.id,
classid: item.classid,
classcaption: item.classcaption,
parentid: 0,
worktype: '',
workkey: item.workkey,
worktag: '',
entpcourseid: 0,
evalid: 0,
edusubject: '',
edudegree: '',
workdate: '',
title: '',
workcodes: '',
studentlist: ss,
deaddate: item.deaddate,
timelength: item.timelength,
weights: item.weights,
feedtype: item.feedtype
}
item.taskconfig.push(js);
mission.push(item);
}
}
// //
// let list = [];
// for (let item of model){
// item.children = [];
// for (let child of mission) {
// if (item.id == child.parentid) {
// item.children.push(child);
// }
// }
// list.push(item);
// }
// //
// taskList.value = list;
// loading.value = false;
// })
// }
//
let list = [];
for (let item of model){
item.children = [];
for (let child of mission) {
if (item.id == child.parentid) {
item.children.push(child);
}
}
list.push(item);
}
taskList.value = list;
loading.value = false;
})
}
// /
const handleWorkTitleEdit = (row, index, type) => {
currentWorkEdit.currentType = type;
const handleWorkTitleEdit = (row, index) => {
currentWorkEdit.workTitleEdit = true;
currentWorkEdit.currentTask = row;
currentWorkEdit.currentTitle = row.title;
currentWorkEdit.currentIndex = index;
};
/**
* 作业内容-查看详情
* */
const handleWorkEdit = (row, index) =>{
// -
const handleWorkEdit = (row, index) =>{
console.log(row, index)
workEdit.value = true
currentWorkEdit.currentTask = row;
// currentWorkEdit.currentIndex = index;
currentTag.value = row.worktype;
// this.attainmentList = row.workcodesList?.attlist;
// this.courseQualityList = row.workcodesList?.qualist;
if (row.worktype == '框架梳理') {
// entpcourseworklistarray
var listCourseWork = [];
for (var i=0; i < row.entpcourseworklistarray.length; i++) {
listCourseWork.push(row.entpcourseworklistarray[i]);
}
nextTick(()=>{
// id
getEvaluationclue(listCourseWork[0].id).then(res => {
if ( res.data==null || res.data==undefined ) {
return;
}
res.data.worktype = '框架梳理';
res.data.score = listCourseWork[0].score;
console.log('框架梳理的列表', res.data);
// list
workConfObj.chooseWorkLists = [res.data];
});
this.$nextTick(()=>{
this.getFlowData()
})
}
// if (row.worktype == '') {
// // TODO
// rootid:entpcourseworkid rootid: row.entpcourseworklistarray[0].id,
// listEvaluationclue({ cluegroup: 'graph', edusubject: this.courseObj.edusubject, pageSize: 1000 }).then((res) => {
// var glist = [];
// for (var i = 0; i < res.rows.length; i++) {
// glist.push(res.rows[i]);
// }
// this.isEditable = false;
// this.preKnowList = glist;
// this.$refs.jsMind.updateFromParent(this.preKnowList, this.courseObj.edusubject);
// this.$refs.jsMind.initJsMindMap();
// })
// }
// //
if (row.worktype == '习题训练') {
@ -686,125 +434,136 @@ const handleWorkTitleEdit = (row, index, type) => {
})
}
//TODO
if(row.worktype == '常规作业'){
console.log(row,'常规作业');
workConfObj.teacherFeedContentList = [];
workConfObj.teachImageList = [];
workConfObj.teachFileList = [];
//
if(row.worktype == '常规作业' || row.worktype == '课堂展示'){
console.log(row,'常规作业-课堂展示');
// workcodes
if(row.workcodes != ''){
const teachWorkFileList = JSON.parse(row.workcodes);
teachWorkFileList&&teachWorkFileList.forEach(item => {
if(item.name.indexOf('jpg') > -1 || item.name.indexOf('jpeg') > -1 || item.name.indexOf('png') > -1){
workConfObj.teachImageList.push(item);
this.teachImageList.push(item);
}else{
workConfObj.teachFileList.push(item);
this.teachFileList.push(item);
}
})
workConfObj.teacherFeedContentList.push(teachWorkFileList);
this.teacherFeedContentList.push(teachWorkFileList);
}
}
};
/**
* 作业内容-前往作业设计 编辑页面
* @param row
*/
const handleWorkEditItem = (row) => {
editListItem(row, courseObj).then((obj) => {
if(obj){
//
router.push({
path: '/newClassTask',
query: {
classtaskObj: JSON.stringify(obj),
// --
let classtaskObj = reactive({
id: '', //
uniquekey: '', //
title: '', //
worktype: '', //
quizlist: [], //
})
// -
const newHandleWorkEdit2ClassWorkQuizAdd =(row, index) =>{
// this.newWorkSpace = true;
// this.newWorkSpaceEdit = true;
// this.currentTask = row;
// this.currentIndex = index;
// this.currentTag = row.worktype;
// this.attainmentList = row.workcodesList?.attlist;
// this.courseQualityList = row.workcodesList?.qualist;
//
// this.classWorkForm.uniquekey = this.currentTask.uniquekey;
// this.classWorkForm.title = this.currentTask.title;
//
// this.classWorkForm.worktype = this.currentTask.worktype;
//[]
var listCourseWork = [];
for (var i=0; i < row.entpcourseworklistarray.length; i++) {
listCourseWork.push(row.entpcourseworklistarray[i]);
}
if (listCourseWork.length > 0) {
classtaskObj.id= row.id; //
classtaskObj.uniquekey= row.uniquekey; //
classtaskObj.title= row.title; //
classtaskObj.worktype= row.worktype; //
classtaskObj.quizlist= []; //
if (row.worktype == '框架梳理') {
//
// let queryParams = {}
// queryParams.id = listCourseWork[0].id;
// queryParams.ppttype = '';
// queryParams.title = '';
// queryParams.filetype = 'draw';
// const res = await getEvaluationclue(listCourseWork[0].id);
// if ( res.data==null || res.data==undefined ) {
// return;
// }
// this.chooseWorkLists = [];
// res.data.worktype = '';
// res.data.score = listCourseWork[0].score;
// this.chooseWorkLists.push(res.data);
}
else if (row.worktype == '习题训练') {
const ids = listCourseWork.map(item => item.id).join(",");
listEntpcoursework({ids: ids, pageSize: 50}).then(idres => {
// for (var i=0; i<idres.rows.length; i++) {
// idres.rows[i].titletext = idres.rows[i].title.replace(/<[^>]+>/g, '');
// }
//
if(idres.rows&&idres.rows.length > 0){
processList(idres.rows);
//task
row.entpcourseworklistarray.forEach(item => {
const quizItem = idres.rows.find(quiz => quiz.id === item.id);
if (quizItem) {
quizItem.score = item.score;
quizItem.scoreOrigin = item.score;
}
});
classtaskObj.quizlist = idres.rows;
//
router.push({
path: '/newClassTask',
query: {
classtaskObj: JSON.stringify(classtaskObj),
}
})
}
})
}
});
}
else if (this.classWorkForm.worktype == '课堂展示') {
/**
* 推送作业配置
* //list
*/
//
const openSet=(row, type)=> {
if(type == 'list'){
// row rows
rowsList.value = row;
setDialog.value = true;
}else{
// row row,
rowsList.value = [row];
setDialog.value = true;
}
}
/**
* 关闭布置作业窗口
*/
const closeHomework = () => {
rowsList.value = [];
setDialog.value = false;
}
/**
* 推送布置作业成功
*/
const successHomework = () => {
rowsList.value = [];
setDialog.value = false;
//
nextTick(() => {
initHomeWork();
})
}
// const workcodes = JSON.parse(this.currentTask.workcodes);
// const jsonString = JSON.stringify(workcodes.json);
/**
* 一键推送
*/
const handleTaskAssignToAllClass = () => {
let rows = proxy.$refs.taskTable.getSelectionRows();
if (rows.length > 0) {
proxy.$modal.confirm('是否确认推送选中的学习任务?').then(()=> {
}).then(() => {
//
openSet(rows,'list');
}).catch(() => {})
}else{
return proxy.$modal.alertWarning("请选择需要推送的任务!");
}
}
/**
* 设计作业
*/
const handleNewClassWorkDialog = () => {
//
router.push({
path: '/newClassTask',
query: {
isBack: true,
// await this.$nextTick();
// this.isShowBoard = true
// this.whiteboardObj = jsonString;
}
else if (this.classWorkForm.worktype == '常规作业') {
// this.fileHomeworkList = [];
// if(isJson(this.currentTask.workcodes)){
// this.fileHomeworkList = JSON.parse(this.currentTask.workcodes);
// }
}
});
}
/**
* 预览框
*/
const prevRead = (item) => {
proxy.$refs.prevReadMsgDialogRef.openDialog(item);
}
/**
* 常规作业其他附件预览
*/
const openFile = () => {
const obj = {
teacherFeedContentList: workConfObj.teacherFeedContentList,//
teachFileList: workConfObj.teachFileList, // list
teachImageList: workConfObj.teachImageList,// list
}
proxy.$refs.prevReadImgFileDialogRef.acceptParams(obj);
}
};
// -
const submitWorkTitle = () => {
@ -818,7 +577,7 @@ const submitWorkTitle = () => {
proxy.$modal.msgSuccess("修改成功");
currentWorkEdit.workTitleEdit = false;
//
initHomeWork();
getTaskList();
});
};
@ -874,26 +633,26 @@ const selectable=(row, index)=>{
onMounted(() => {
})
watch(() => courseObj.node, (newVal,oldVal) => {
watch(() => courseObj.node, (newVal) => {
console.log(courseObj,'课程选择')
//
getTaskList();
})
</script>
<!--
<style>
.el-table .hidden-row {
display: none !important;
/* color: #ccc !important; */
}
.el-table .father-row {
&#45;&#45;el-table-tr-bg-color: #fff;
--el-table-tr-bg-color: #fff;
}
.el-table .son-row {
&#45;&#45;el-table-tr-bg-color: #f0f0f08a;
--el-table-tr-bg-color: #f0f0f08a;
}
</style>
-->
<style lang="scss" scoped>
.page-classTaskAssign {
@ -910,6 +669,7 @@ watch(() => courseObj.node, (newVal,oldVal) => {
min-height: 100%;
}
.unit-top-left {
cursor: pointer;
.icon-xiangzuo {
margin-right: 5px;
@ -937,4 +697,3 @@ watch(() => courseObj.node, (newVal,oldVal) => {
}
}
</style>
<style src="@/assets/styles/JYStyle.css"></style>

View File

@ -1,5 +1,5 @@
<template>
<div class="common-layout" style="width: 100%">
<div class="common-layout" style="width: 100%; height: 73vh;">
<el-container>
<el-container>
<el-header style="height: auto">
@ -7,7 +7,7 @@
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
等级分布
学情分布
</div>
</template>
<Distribution></Distribution>
@ -18,7 +18,7 @@
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
时分析
分析
</div>
</template>
<TimeAnalyse></TimeAnalyse>
@ -29,7 +29,7 @@
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
价值透析
知识点概览
</div>
</template>
<Konwledge></Konwledge>
@ -43,12 +43,12 @@
</template>
<script setup>
import {ref,watchEffect,provide } from 'vue'
import Distribution from './classOverview/distribution.vue'
import Konwledge from './classOverview/knowledge.vue'
import TimeAnalyse from './classOverview/timeAnalyse.vue'
import Distribution from '@/views/classTask/container/classOverview/distribution.vue'
import Konwledge from '@/views/classTask/container/classOverview/knowledge.vue'
import TimeAnalyse from '@/views/classTask/container/classOverview/timeAnalyse.vue'
import {defineProps,watch} from 'vue'
import overviewStore from "@/store/modules/overview";
// import {getBindlist} from "@/api/education/knowledgePoint";
const useOverview = overviewStore()
const props = defineProps({
tableList: {
@ -57,225 +57,12 @@ const props = defineProps({
return []
}
},
evalId:{
type: Number,
default: 0
},
activeData: { //
type: Object,
// required: true, //
default: () => ({
quizlist: [], //
studentList: [], // -
workFeedList: [] // -
})
},
// evalId:{
// type: Number,
// default: 0
// }
})
let studentList = ref([]) //
// -
const initData = () => {
// window.test = activeCourse
studentList.value = props.activeData.studentList || []
const activeWorkFeedList = props.activeData.workFeedList || []
const quizlist = props.activeData.quizlist || []
console.log(quizlist,'quizlist');
//
let data = quizlist.map(o => {
//
const workdesc = o.workdesc || ''
let accSum = 0 //
let activeIds = [] //
let rightIds = [] //
let hasAnswers= [] //
let timeAnalyse = [] //
// let subjectCourese = [] //
const quizFeedList = activeWorkFeedList.filter(f => f.entpcourseworkid == o.id) //
let children = []
const allStudents = [];
if (o.worktype == '单选题') { // '',''
const list = workdesc.includes('#&') ? workdesc.split('#&') : isJson(workdesc)?JSON.parse(workdesc):[];
children = list.map((v,i) => {
const code = toCode(i) // A-Z
const isOk = (isJson(workdesc)?JSON.parse(o.workanswer):o.workanswer||'').includes(i+'') // ()
// id
const studentIds = quizFeedList.filter(f => f.feedcontent==v&&f.finishtimelength!='0').map(f => f.studentid)||[];
accSum += studentIds.length;
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
if (isOk) {
activeIds.push(...studentIds)
}
hasAnswers.push(...studentIds)
return { def: v, code, isOk, studentIds }
})
}
else if (o.worktype == '多选题') {
//
rightIds = quizFeedList.filter(f => {
const workanswer = (isJson(o.workanswer)) ? JSON.parse(o.workanswer) : o.workanswer || [];
const res = isSame((f.feedcontent||'').split(','), workanswer);
return f.entpcourseworkid == o.id && f.finishtimelength!='0' && res;
});
const list = workdesc.includes('#&') ? workdesc.split('#&') : isJson(workdesc)?JSON.parse(workdesc):[];
children = list.map((v,i) => {
const isOne = o.worktype == '单选题'
const code = toCode(i) // A-Z
// const isOk = isOne ? i == o.workanswer : o.workanswer.includes(i) // ()
const isOk = (isJson(workdesc)?JSON.parse(o.workanswer):o.workanswer||'').includes(i+'') // ()
// id
const studentIds = quizFeedList.filter(f => f.feedcontent.includes(i)&&f.finishtimelength!='0').map(f => f.studentid)||[];
accSum += studentIds.length;
if (studentIds.length>0) {
allStudents.push(...studentIds);
}
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
if(isOk) {
activeIds=[...new Set(activeIds.concat(studentIds))] //
}
hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
return { def: v, code, isOk, studentIds }
})
}
else if (o.worktype == '填空题') { //
let title = o.title.replace(/_{3,}/g, '_____'); //3-10线5
let regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA-->
if (title.indexOf('_____') != -1) {
regex = /_{5}/g // <!--BA-->xxx<!--EA-->
}
children = (title||'').match(regex).map((v,i) => {
const def = `填空项 ${i+1}`
//const code = '(&emsp;)'
const code = '(略)', txt=v
// id
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'').split('#')[i] && f.finishtimelength!='0').map(f => f.studentid)||[]
activeIds=[...new Set(activeIds.concat(studentIds))] //
hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
accSum = activeIds.length
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
return { def, code, txt, isOk:true, studentIds }
})
} else if (o.worktype == '判断题') { //
const list = ['正确', '错误'];
children = list.map((v,i) => {
const workanswer = o.workanswer
.replace('×', '0')
.replace('√', '1')
.replace('错误', '0')
.replace('正确', '1')
.replace('正确。', '1')
.replace('F', '0')
.replace('T', '1')
.replace('错', '0')
.replace('对', '1');
const workanswerFormat = isJson(workanswer) ? JSON.parse(workanswer) : workanswer||''
const code = v=='正确' ? '1' : '0'
let isOk = (workanswerFormat).includes(code)
// warn:
// if (workanswerFormat == '0') {
// isOk = !isOk;
// }
// id
const studentIds = quizFeedList.filter(f => {
const feedcontent = f.feedcontent
.replace('×', '0')
.replace('√', '1')
.replace('错误', '0')
.replace('正确', '1')
.replace('正确。', '1')
.replace('F', '0')
.replace('T', '1')
.replace('错', '0')
.replace('对', '1');
if(feedcontent == code&&f.finishtimelength!='0'){
return f
}
}).map(f => f.studentid)||[];
accSum += studentIds.length;
if(isOk) activeIds.push(...studentIds)
hasAnswers.push(...studentIds)
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
return { def: v, code: v, isOk, studentIds }
})
} else { //
// code = '(&emsp;)'
const code = '(略)', def = '解答内容'
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'')&&f.finishtimelength!='0').map(f => f.studentid)||[]
activeIds=[...new Set(activeIds.concat(studentIds))] //
hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
accSum = activeIds.length
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
children = [{ def, code, isOk:true, studentIds }]
}
const studentSum = studentList.value.length || 0 //
let points = percent((activeIds.length / (studentSum||1)).toFixed(2)) //
let rightSum = activeIds.length; //
//
if (o.worktype == '多选题') {
//
const uniqueTmpStuents = [...new Set(allStudents)];
accSum = uniqueTmpStuents.length;
//
points = percent((rightIds.length / (studentSum||1)).toFixed(2)) //
//
rightSum = rightIds.length;
}
// def: type active: points: , accSum
return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, rightSum, children,hasAnswers,timeAnalyse,score:o.workScore }
})
if (data.length === 0) return
useOverview.getAllData([...data])
}
// 0-100
const percent = v => v > 1 ? 1 : v < 0 ? 0 : Math.round(v * 100)
// Unicode 65
const toCode = (v, b) => b ? v.charCodeAt() - 65 : String.fromCharCode(v + 65)
// json
const isJson = str => {if(typeof str == 'string'){
try {
const res = JSON.parse(str)
if(typeof res == 'object' && res) return true
} catch (error) {}}return false
}
//
const groupByField = (array, field) => {
const groupedMap = {};
array.forEach(item => {
const key = item[field];
if (!groupedMap[key]) {
groupedMap[key] = [];
}
groupedMap[key].push(item);
});
//
return Object.values(groupedMap);
}
watch(() => props.tableList,() => {
useOverview.getTableList(props.tableList)
},{deep:true})
// === ===
watchEffect(() => { initData() })
</script>

View File

@ -14,8 +14,8 @@
</template>
<script setup>
import Echarts from './distribution/echarts.vue'
import StuList from "./distribution/stuList.vue";
import Echarts from '@/views/classTask/container/classOverview/distribution/echarts.vue'
import StuList from "@/views/classTask/container/classOverview/distribution/stuList.vue";
</script>
<style scoped>

View File

@ -1,127 +1,99 @@
<template>
<div class="chart-container">
<div ref="chartRef" class="chart"></div>
<div className="chart-container">
<div ref="chartRef" className="chart"></div>
</div>
</template>
<script setup>
import { ref, nextTick, watch } from 'vue';
import {ref,nextTick,watch} from 'vue';
import * as echarts from 'echarts';
import overviewStore from '@/store/modules/overview';
const useOverview = overviewStore();
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
//
const chartRef = ref(null);
//
const dataList = ref([
{ name: '完美', value: 0, rating: 1, max: 100, min: 100 },
{ name: '优秀', value: 0, rating: 2, max: 99, min: 80 },
{ name: '良', value: 0, rating: 3, max: 79, min: 70 },
{ name: '及格', value: 0, rating: 4, max: 69, min: 60 },
{ name: '不及格', value: 0, rating: 5, max: 59, min: 0 },
{name: '优', value: 0,rating:1},
{name: '优-', value: 0,rating:2},
{name: '良', value: 0,rating:3},
{name: '良-', value: 0,rating:4},
{name: '差', value: 0,rating:5},
]);
//
const hasStudents = ref([])
//
function getColor(name) {
const colorMap = {
'完美': '#d14a61',
'优秀': '#675bba',
'良好': '#e89110',
'及格': '#008c8c',
'不及格': '#5793f3'
};
return colorMap[name];
function getColor(index) {
//
const colors = ['#d14a61','#675bba', '#e89110','#008c8c','#5793f3'];
return colors[index];
}
//
function initChart() {
const myChart = echarts.init(chartRef.value);
const total = dataList.value.reduce((acc, cur) => acc + cur.value, 0); //
// 0
const filteredData = dataList.value.filter(item => item.value > 0);
const options = {
tooltip: {
trigger: 'item',
formatter: params => {
const value = params.value;
const percentage = value ? ((value / total) * 100).toFixed(2) : 0; //
return `${params.name}: ${value}人 (${percentage}%)`; //
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: dataList.value.map(item => item.name),
axisTick: {
alignWithLabel: true
}
},
yAxis: {
type: 'value'
},
series: [{
name: '数据',
type: 'pie',
radius: '90%', //
data: filteredData.map(item => ({
name: item.name,
value: item.value,
itemStyle: {
color: getColor(item.name)
type: 'bar',
barWidth: '30%',
data: dataList.value.map(item => item.value),
itemStyle: {
color: function (params) {
//
return getColor(params.dataIndex);
}
})),
},
//
label: {
show: true,
position: 'inside', //
formatter: params => {
const value = params.value;
const percentage = value ? ((value / total) * 100).toFixed(2) : 0; //
return `${params.name}\n${value}人 (${percentage}%)`; //
},
position: 'top',
formatter: '{c}人',
color: '#333',
fontSize: 12
},
labelLine: {
show: false // 线
}
}]
};
myChart.setOption(options);
}
//
const showEcharts = () => {
hasStudents.value.forEach((item, index) => {
if (item.rating === 0) {
dataList.value.forEach((item1, index1) => {
if (item1.min <= Number(item.scoingRate) && Number(item.scoingRate) <= item1.max) {
item1.value++;
}
});
} else {
dataList.value.forEach((item1, index1) => {
if (item1.rating === item.rating) {
item1.value++;
}
});
}
});
const showEcharts =() => {
useOverview.tableList.forEach(item => {
const index = dataList.value.findIndex(item1 => item1.rating === item.rating)
if(index !== -1)
dataList.value[index].value ++
})
}
//
watch(() => useOverview.tableList, () => {
//
const subType = useOverview.allData.map(item => item.type)
const objectiveQuestion = ['单选题','多选题','判断题']
if( !subType.every(item => objectiveQuestion.includes(item)) ){
hasStudents.value = useOverview.tableList.filter(item => {
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
return item
}
})
}else{
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
}
showEcharts();
watch(() => useOverview.tableList,() => {
showEcharts()
nextTick(() => {
initChart();
});
},{deep: true})
})
})
</script>
<style scoped>

View File

@ -1,10 +1,10 @@
<template>
<el-tabs :tab-position="tabPosition" style="height: 100%" class="demo-tabs" @tabChange="handelChange">
<template v-for="(item,index) in leftList" :key="index">
<el-tab-pane :label="item.label" style="text-align:left" stretch="true">
<el-tab-pane :label="item.label" style="text-align:left">
<template v-if="item.stuList.length > 0">
<template v-for="(stuItem,stuIndex) in item.stuList" :key="stuIndex">
<el-tag style="margin:5px 10px 0 0" type="primary">{{ stuItem.studentname }}:{{ stuItem.getScore }}</el-tag>
<el-tag style="margin:5px 10px 0 0" type="primary">{{stuItem.studentname}}</el-tag>
</template>
</template>
<template v-else>
@ -16,50 +16,36 @@
</template>
<script setup>
import {ref, watch} from 'vue'
import {nextTick, ref, watch} from 'vue'
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
const tabPosition = ref('left')
//
const hasStudents = ref([])
const leftList = ref([
{
label:'完美',
label:'优',
stuList:[],
rating:1,
max:100,
min:100,
rating:1
},
{
label:'优',
label:'优-',
stuList:[],
rating:2,
max:99,
min:80,
rating:2
},
{
label:'良',
label:'良',
stuList:[],
rating:3,
max:79,
min:70,
rating:3
},
{
label:'及格',
label:'良-',
stuList:[],
rating:4,
max:69,
min:60,
rating:4
},
{
label:'不及格',
label:'',
stuList:[],
rating:5,
max:59,
min:0,
rating:5
},
])
//
@ -68,33 +54,14 @@ const handelChange = (item) => {
}
//
const showStudents = (index) => {
leftList.value[index].stuList = hasStudents.value.filter(item => {
if(item.rating === 0){
if(leftList.value[index].min <= Number(item.scoingRate || 0) && Number(item.scoingRate || 0) <= leftList.value[index].max ){
return item
}
}else{
if(item.rating == leftList.value[index].rating){
return item
}
}
console.log(useOverview.tableList,'lef')
leftList.value[index].stuList = useOverview.tableList.filter(item => {
if(item.rating == leftList.value[index].rating) return item
})
}
watch(() => useOverview.tableList, () => {
//
const subType = useOverview.allData.map(item => item.type)
const objectiveQuestion = ['单选题','多选题','判断题']
if( !subType.every(item => objectiveQuestion.includes(item)) ){
hasStudents.value = useOverview.tableList.filter(item => {
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
return item
}
})
}else{
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
}
watch(() => useOverview.tableList,() => {
showStudents(0)
},{deep: true})
})
</script>
<style scoped>
@ -140,9 +107,9 @@ watch(() => useOverview.tableList, () => {
:deep(.el-tabs__item.is-active){
background-color: rgb(238, 241, 246);
}
:deep(.el-tabs--left .el-tabs__item.is-left, .el-tabs--right .el-tabs__item.is-left){
:deep(.el-tabs--left .el-tabs__item.is-left){
text-align: left;
justify-content: flex-start !important;
justify-content: flex-start;
}
</style>

View File

@ -20,71 +20,82 @@
<script setup>
import {ref, watch} from 'vue'
import overviewStore from '@/store/modules/overview'
import {listEntpcoursework} from '@/api/education/entpCourseWork'
const useOverview = overviewStore()
const tableData = ref([])
//id
const ids = ref('')
//
const allScore = ref(0)
//
const konwledge = ref([])
const hasStudents = ref([])
//
const allScore = ref(0)
//
const avatarScore = ref()
//
const getKonwledge = () => {
const getScoreRate = []
//
const ledges = []
hasStudents.value.forEach((item,index) => {
//
useOverview.tableList.forEach(item => {
if(item.knowledgePoint){
const title = JSON.parse(item.knowledgePoint)
//
if(!ledges.includes(title.id)){
ledges.push(title.id)
// 0
konwledge.value.push({title:title.title,allPoint:allScore.value,id:title.id,point:avatarScore.value})
}
//
if(useOverview.allData[0].hasAnswers.includes(item.studentid))
getScoreRate.push({rate:item.scoingRate,id:title.id})
konwledge.value.push({...JSON.parse(item.knowledgePoint),...{scoingRate:Number(item.scoingRate),point:item.point,allPoint:allScore.value}})
}
})
//
konwledge.value.forEach(item => {
tableData.value.push({
scoingRate:(item.point / item.allPoint * 100).toFixed(2),
tableData.value = getTableList(konwledge.value)
tableData.value = tableData.value.map(item => {
return{
...item,
})
allPoint: allScore.value
}
})
console.log(tableData.value,'tableData.value')
}
//
const getScore = async () => {
const scoreId = useOverview.tableList[0].entpcourseworklist
const fixedJsonString = `[${scoreId}]`;
const objects = JSON.parse(fixedJsonString);
const id = objects.map(obj => obj.id);
ids.value = id.join(',')
const res = await listEntpcoursework({ids: ids.value, pageSize: 500})
if(res.code === 200){
allScore.value = res.rows.reduce((acc, cur) => acc + cur.workScore, 0);
getKonwledge()
}
}
//tableList
const getTableList = (data) => {
const result = [];
data.forEach(item => {
const existingItem = result.find(i => i.id === item.id);
if (existingItem) {
// pointscoingRate
existingItem.pointTotal += parseInt(item.point);
existingItem.scoingRateTotal += parseFloat(item.scoingRate);
existingItem.count++;
} else {
//
result.push({
id: item.id,
title: item.title,
pointTotal: item.point,
scoingRateTotal: parseFloat(item.scoingRate),
count: 1
});
}
});
//
result.forEach(item => {
item.point = Math.round(item.pointTotal / item.count);
// item.scoingRate = Math.round((item.scoingRateTotal / item.count) * 100) / 100;
item.scoingRate = Math.round((item.point / allScore.value) * 100);
delete item.pointTotal;
delete item.scoingRateTotal;
delete item.count;
});
return result;
}
watch(() => useOverview.tableList,() => {
//
const subType = useOverview.allData.map(item => item.type)
const objectiveQuestion = ['单选题','多选题','判断题']
if( !subType.every(item => objectiveQuestion.includes(item)) ){
hasStudents.value = useOverview.tableList.filter(item => {
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
return item
}
})
}else{
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
}
//
allScore.value = useOverview.allData.reduce((acc, cur) => {
return acc + Number(cur.score)
},0)
//
avatarScore.value = hasStudents.value.reduce((acc, cur) => {
return acc + Number(cur.getScore)
},0) / hasStudents.value.length
//
getKonwledge()
console.log(useOverview.tableList,'useOverview.tableList')
getScore()
})
</script>

View File

@ -9,160 +9,166 @@ import * as echarts from 'echarts';
import {ref, nextTick, watch} from 'vue'
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore();
const useOverview = overviewStore()
//
const chartRef = ref(null);
//
const expectedDuration = ref([]);
const estimateTime = ref([]);
const avaterTime = ref([]);
// x
const xAxisData = ref([]);
// y
const getyAxisData = () => {
estimateTime.value = [];
avaterTime.value = [];
useOverview.tableList.forEach(item => {
if (item.rating !== 0) {
estimateTime.value.push({
name: item.scoingRate ? item.scoingRate + '%' : 0 + '%',
value: Number(item.timelength)
});
avaterTime.value.push({
name: item.scoingRate ? item.scoingRate + '%' : 0 + '%',
value: Number(item.finishtimelength)
});
}
});
// x
xAxisData.value.sort((a, b) => {
const aPercentage = parseInt(a.replace('%', ''));
const bPercentage = parseInt(b.replace('%', ''));
return aPercentage - bPercentage;
});
// x
generateXAxisData();
};
// x
function generateXAxisData() {
// 8x
if(estimateTime.value.length > 8){
const minScoreRate = 0;
const maxScoreRate = 100;
const numPoints = 6; // x
const step = (maxScoreRate - minScoreRate) / (numPoints - 1);
xAxisData.value = [];
for (let i = 0; i < numPoints; i++) {
const scoreRate = minScoreRate + i * step;
xAxisData.value.push(scoreRate + '%');
}
}else{
let uniqueXAxisData = new Set();
estimateTime.value.forEach(item => {
// Set
uniqueXAxisData.add(item.name);
});
// Set
xAxisData.value = Array.from(uniqueXAxisData);
//
xAxisData.value.sort((a, b) => {
const aPercentage = parseInt(a.replace('%', ''));
const bPercentage = parseInt(b.replace('%', ''));
return aPercentage - bPercentage;
});
}
}
//
function initChart() {
const myChart = echarts.init(chartRef.value);
//
let option = {
const options = {
tooltip: {
trigger: "axis",
trigger: 'axis',
axisPointer: {
type: "shadow", // 线'line' | 'shadow'
},
formatter: function (parms) {
let str = "";
parms.forEach(param => {
if (param.seriesType === 'bar') {
str += param.axisValue + "</br>" + param.marker + "平均用时:" + param.value + 's' + "</br>";
} else if (param.seriesType === 'line') {
str += param.marker + "预计用时:" + param.value + 's';
}
});
return str;
},
type: 'cross'
}
},
textStyle: {
color: "#333",
legend: {
data: ['预估时长', '平均用时']
},
color: ["#7BA9FA", "#4690FA"],
grid: {
containLabel: true,
left: "10%",
top: "20%",
bottom: "10%",
right: "10%",
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: "category",
data: getXValue(),
axisLine: {
lineStyle: {
color: "#333",
},
type: 'category',
boundaryGap: false,
name: '得分率',
nameTextStyle: {
color: '#999',
fontSize: 12,
padding: [0, 0, 10, 0]
},
axisTick: {
show: false,
},
axisLabel: {
margin: 20, //线
textStyle: {
color: "#000",
},
},
name: '题目编号'
data: xAxisData.value
},
yAxis: {
type: "value",
axisLine: {
show: true,
lineStyle: {
color: "#B5B5B5",
},
type: 'value',
name: '作业时长',
nameTextStyle: {
color: '#999',
fontSize: 12,
padding: [0, 0, 10, 0]
},
name: '平均时长',
splitLine: {
lineStyle: {
// 使
color: ["#B5B5B5"],
type: "dashed",
opacity: 0.5,
},
},
axisLabel: {},
},
series: [
{
data: getYValue(),
stack: "zs",
type: "bar",
barMaxWidth: "auto",
barWidth: 60,
itemStyle: {
color: {
x: 0,
y: 0,
x2: 0,
y2: 1,
type: "linear",
global: false,
colorStops: [
{ offset: 0, color: "#5EA1FF" },
{ offset: 1, color: "#90BEFF" },
],
},
},
label: {
show: true,
position: 'top',
formatter: '{c}s',
color: '#333',
},
},
//线
{
data: expectedDuration.value,
type: "line",
name: `预估时长`,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 8,
symbolSize: 10,
lineStyle: {
color: '#FF7F50',
width: 2,
},
itemStyle: {
color: '#FF7F50',
color: '#5793f3'
},
data: estimateTime.value.map(item => ({
name: item.name,
value: item.value
}))
},
],
{
name: `平均用时`,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 10,
lineStyle: {
color: '#d14a61'
},
data: avaterTime.value.map(item => ({
name: item.name,
value: item.value
}))
}
]
};
myChart.setOption(option);
myChart.setOption(options);
}
//
const getAvaterTime = () => {
return useOverview.tableList.reduce((acc, cur) => acc + cur.finishtimelength, 0) / useOverview.tableList.length;
}
const getEstimateTime = () => {
return useOverview.tableList.reduce((acc, cur) => acc + cur.timelength, 0) / useOverview.tableList.length;
}
//
const getYValue = () => {
const arr = useOverview.allData.map(item => item.timeAnalyse)
const num = useOverview.allData[0].hasAnswers.length;
if (arr.length === 0) return [];
return arr.map(item => (item ? (item / num).toFixed(2) : 0));
};
//
const getXValue = () => {
return useOverview.allData.map((item, index) => `${index + 1}`);
};
watch(() => useOverview.tableList, () => {
const time = useOverview.tableList.map(item => Number(item.timelength))
if(time.length === 0) return;
const avatarTime = time.reduce((acc, cur) => {
return acc + cur
},0) / time.length
expectedDuration.value = useOverview.allData.map(() => (Number(avatarTime) * 60 / useOverview.allData.length).toFixed(2));
//
watch(() => useOverview.tableList,() => {
getyAxisData()
nextTick(() => {
initChart();
});
});
})
})
</script>
<style scoped>

View File

@ -1,179 +0,0 @@
<template>
<el-dialog
v-model="fileReadopen"
title="文件预览"
width="80%"
:style="{ height: '72vh' }"
append-to-body
>
<div class="file-read-dialog">
<div>
<!-- 老师附件 -->
<div v-if="teachFileList.length > 0">
<el-card style="max-width: 480px">
<template #header>
<div class="card-header">
<span>文件列表</span>
</div>
</template>
<div
v-for="item in teachFileList"
:key="item"
style="margin: 10px; display: flex; align-items: center"
>
<span style="margin-right: 10px">{{ item.name }}</span>
<el-button type="primary" @click="onFileRead(item)">预览</el-button>
</div>
</el-card>
</div>
</div>
<div style="width: 100%" :style="{ height: '72vh' }">
<div style="margin-left: 10px">
预览展示区域<span style="color: red; margin-left: 10px">
温馨提示若预览失败<span style="margin-left: 10px">{{ props.name }}</span
>可点击此处<a
:href="fileitem.url ? fileitem.url : ''"
target="_blank"
style="color: blue"
>下载</a
></span
>
</div>
<ReFilePreview
:name="fileitem.name"
:type="fileitem.type"
:file-type="fileitem.type"
:file-path="fileitem.url"
:text-content="textContent"
/>
</div>
</div>
</el-dialog>
</template>
<script setup name="prevReadImgFileDialogRef">
import { ref, reactive } from 'vue'
import ReFilePreview from '@/components/refile-preview/index.vue'
const props = defineProps({})
const fileReadopen = ref(false)
// list
const teacherFeedContentList = ref([])
const teachImageList = ref([])
const teachFileList = ref([])
// 线
//#region
const fileitem = reactive({
name: '',
type: '',
url: ''
})
//
const onFileRead = (file) => {
textContent.value = '1'
//
fileitem.type = file.name.split('.').pop().toLowerCase()
fileitem.url = file.url
fileitem.name = file.name
// txt
if (fileitem.type == 'txt') {
loadFileTextContent(fileitem.url)
}
}
// txt
const textContent = ref('')
const loadFileTextContent = async (url) => {
try {
const response = await fetch(url)
if (!response.ok) {
textContent.value = '文件读取失败,您可以点击上方链接跳到另外页面查看'
throw new Error('文件读取失败')
}
textContent.value = await response.text()
} catch (error) {
console.error('读取文件时出错:', error)
textContent.value = '文件读取失败,您可以点击上方链接跳到另外页面查看'
}
}
//
const acceptParams = (params) => {
console.log(params)
fileReadopen.value = true;
teacherFeedContentList.value = params.teacherFeedContentList
teachImageList.value = params.teachImageList
teachFileList.value = params.teachFileList
}
//
// onMounted(() => {})
// ()
defineExpose({
acceptParams
})
</script>
<style scoped>
.card-header{
text-align: left;
}
.image_list {
display: flex;
flex-wrap: nowrap;
flex-direction: column;
}
.file-read-dialog {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
}
.score-container {
display: flex;
justify-content: center;
align-items: center;
/* margin-bottom: 20px; */
}
.score-circle {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: pink;
color: red;
display: flex;
justify-content: center;
align-items: center;
font-size: 13px;
margin: 0 10px;
cursor: pointer;
transition: background-color 0.3s;
}
.score-circle.active {
background-color: red;
color: white;
}
.card-header{
align-items: left;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<el-form ref="classWorkFormScoreRef" :model="classWorkFormScore" style="height: 100%">
<!-- <div class="teacher_content" :style="{ height: dialogProps.maxheight + 'px' }"> -->
<div class="teacher_content" :style="{ height: '100%',fontSize: '18px' }">
<div class="teacher_content" :style="{ height: '100%' }">
<div style="font-size: 18px; width: 100%; padding: 5px 10px; flex: 0 0 auto;">
{{ classWorkFormScore.name }} 答题详情
</div>
@ -90,7 +90,7 @@
<span>学生答案
<span
v-if="stuItem.feedcontent !=''"
:style="{backgroundColor: `${formatWorkAnswer(quItem) == formatFeedContent(stuItem, quItem)? '#0ed116' : 'red'}`,color: 'white', padding: '0 5px', borderRadius: '5px'}"
style="background-color: red; color: white; padding: 0 5px; border-radius: 5px;"
v-html="formatFeedContent(stuItem, quItem)"
>
</span>
@ -121,7 +121,9 @@
:max="classWorkFormScore.teacherRating[sIndex].maxScore"
size="small"
:disabled="
(quItem.worktype == '单选题' || quItem.worktype == '多选题' || quItem.worktype == '判断题')
(quItem.worktype == '单选题' || quItem.worktype == '多选题') &&
stuItem.feedcontent == stuItem.rightanswer &&
stuItem.feedcontent != ''
? true
: false
"
@ -196,7 +198,7 @@
</div>
<div v-if="teachFileList.length > 0">
<div style="margin: 10px 0;text-align: left; cursor: pointer;">
<div style="margin: 10px 0;text-align: left;">
<span style="color: red" @click="openFile"
>温馨提示点击此处 可预览其他类型附件
</span>
@ -245,7 +247,7 @@
</div>
<div v-if="fileList.length > 0">
<div style="margin: 10px 0;text-align: left; cursor: pointer;">
<div style="margin: 10px 0;text-align: left;">
<span style="color: red" @click="openFile"
>温馨提示点击此处 可预览其他类型附件
</span>
@ -284,27 +286,58 @@
<!-- 批改评价与评语 -->
<div class="tacher_conten_foot">
<el-row style=" padding: 1% 4%; border: 2px dotted;">
<el-col :span="24" style="display: flex;flex-direction: column;">
<el-row style="padding: 1% 4%; border: 2px dotted">
<el-col :span="24" style="display: flex; flex-direction: column">
<el-row>
<el-col :span="24">
<div class="greenLine" style="text-align: left;" v-if="dialogProps.studentObj.worktype == '习题训练'">
<span style="font-weight: bold;">老师点评</span>
<span style="margin: 0;">{{ classWorkFormScore.teacherRating.reduce((a, b) => a + b.score, 0).toFixed(2)}}</span>
</div>
<div style="display: flex; margin: 10px auto;align-items: center;justify-content: space-between;">
<el-col :span="14">
<div style="display: flex; margin: 10px auto">
<span style="display: flex; align-items: center">
<span v-if="dialogProps.studentObj.worktype == '习题训练'">
<span>得分 </span>
<span style="margin: 0; color: red">{{
classWorkFormScore.teacherRating.reduce((a, b) => a + b.score, 0).toFixed(2)
}}</span>
<span></span>
</span>
<span v-else>
<span>得分 </span>
<span v-if="classWorkFormScore.teacherRating.length > 0">
<el-input-number
v-model="classWorkFormScore.teacherRating[0].score"
:controls="false"
type="number"
:min="0"
:max="classWorkFormScore.teacherRating[0].maxScore"
size="small"
style="width: 60px"
@change="handleChange"
></el-input-number>
</span>
<span></span>
</span>
</span>
<div class="score-container">
<div
v-for="(score, index) in teacherRatingList"
:key="index"
style="white-space: nowrap;"
:class="['score-circle', { 'active': classWorkFormScore.rating == score.ratingKey }]"
:class="[
'score-circle',
{ active: classWorkFormScore.rating == score.ratingKey }
]"
@click="selectScore(score)"
>
<el-text :style="{fontWeight:'bold', color: classWorkFormScore.rating == score.ratingKey ? 'rgb(225,12,8)':'rgb(131,131,131)' }">{{ score.ratingValue }}</el-text>
{{ score.ratingValue }}
</div>
</div>
<el-select v-model="value" placeholder="常用评语" style="width: 240px" @change="onSelectOption">
</div>
</el-col>
<el-col :span="10" style="display: flex; align-items: center">
<el-select
v-model="value"
placeholder="常用评语"
style="width: 240px"
@change="onSelectOption"
>
<el-option
v-for="item in cities"
:key="item.value"
@ -322,21 +355,23 @@
placeholder="输入新的常用语"
size="small"
/>
<el-button type="primary" size="small" @click="onConfirm">
确定
</el-button>
<el-button type="primary" size="small" @click="onConfirm"> 确定 </el-button>
<el-button size="small" @click="clear">取消</el-button>
</template>
</template>
</el-select>
</div>
</el-col>
</el-row>
</el-col>
<el-col :span="24" style="display: flex;flex-direction: column;">
<el-form-item>
<el-col :span="24" style="padding: 0px">
<el-input row="5" type="textarea" v-model="classWorkFormScore.teacherremark" rows="1" placeholder="输入评语" />
<el-col :span="24" style="display: flex; flex-direction: column">
<el-form-item label="评语说明">
<el-col :span="15" style="padding: 0px">
<el-input
v-model="classWorkFormScore.teacherremark"
type="textarea"
rows="1"
placeholder="请输入评语说明"
/>
</el-col>
</el-form-item>
</el-col>
@ -427,13 +462,12 @@ import useUserStore from '@/store/modules/user'
import { ref, reactive } from 'vue'
// import { Plus } from '@element-plus/icons-vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import { updateClassworkeval,updateClasswork, updateClassWorkDataAutoFinish, getClassworkdata, updateClassworkevalList } from '@/api/classTask'
import { updateClassworkeval, updateClassworkdata } from '@/api/classTask'
import { getTimeDate } from '@/utils/date'
import ReFilePreview from '@/components/refile-preview/index.vue'
import { quizStrToList } from '@/utils/comm';
const userStore = useUserStore()
const router = useRouter()
const route = useRoute()
@ -468,11 +502,11 @@ const classWorkFormScore = reactive({
teacherremark: '' //
})
const teacherRatingList = ref([
{ ratingKey: '1', ratingValue: '完美' },
{ ratingKey: '2', ratingValue: '优' },
{ ratingKey: '3', ratingValue: '良' },
{ ratingKey: '4', ratingValue: '及格' },
{ ratingKey: '5', ratingValue: '不及格' }
{ ratingKey: '1', ratingValue: '' },
{ ratingKey: '2', ratingValue: '优-' },
{ ratingKey: '3', ratingValue: '良' },
{ ratingKey: '4', ratingValue: '良-' },
{ ratingKey: '5', ratingValue: '' }
])
// 线
//#region
@ -610,35 +644,9 @@ const selectScore = (score) => {
console.log(score, 'score----')
classWorkFormScore.rating = score.ratingKey
}
const checkWorkType = (params) => {
//
const subType = params.quizlist.map(item => item.worktype)
const objectiveQuestion = ['单选题','多选题','判断题']
//
if(subType.every(item => objectiveQuestion.includes(item))){
//
const score = extractedNumber(params.studentObj.scoingRate)
if(0<=score && score<=59){
classWorkFormScore.rating = 5
}else if(60<=score && score<=69){
classWorkFormScore.rating = 4
}else if(70<=score && score<=79){
classWorkFormScore.rating = 3
}else if(80<=score && score<=99){
classWorkFormScore.rating = 2
}else{
classWorkFormScore.rating = 1
}
}
}
//
const extractedNumber = (score) => {
const match = score.match(/\d+/);
return match ? parseInt(match[0], 10) : null;
}
//
const acceptParams = async (params) => {
const acceptParams = (params) => {
console.log(params)
console.log(dialogProps, 'dialogProps')
//
@ -657,7 +665,7 @@ const acceptParams = async (params) => {
teacherFeedContentList.value = []
teachImageList.value = []
teachFileList.value = []
checkWorkType(params)
// -----------------
dialogProps.value = params
classWorkFormScore.name = params.studentObj.studentname
@ -703,9 +711,8 @@ const acceptParams = async (params) => {
if (params.studentObj.worktype == '常规作业') {
try {
// datacontent TODO
const res = await getClassworkdata(params.studentObj.id);
if(res.data.datacontent != ''){
const teachWorkFileList = JSON.parse(res.data.datacontent);
if (params.studentObj.datacontent != '') {
const teachWorkFileList = JSON.parse(params.studentObj.datacontent)
console.log(teachWorkFileList, '老师filelist-------------')
teachWorkFileList &&
teachWorkFileList.forEach((item) => {
@ -722,6 +729,7 @@ const acceptParams = async (params) => {
teacherFeedContentList.value.push(teachWorkFileList)
}
dialogProps.value.studentObj.datacontent = dialogProps.value.studentObj.datacontent
} catch (error) {
console.error('Invalid JSON:', error)
}
@ -795,7 +803,7 @@ const acceptParams = async (params) => {
console.log(params.studentQuizAllList[0].rating, '----------------------------')
// null 0 0
classWorkFormScore.rating =
params.studentQuizAllList[0].rating == 0 ? classWorkFormScore.rating : params.studentQuizAllList[0].rating
params.studentQuizAllList[0].rating == 0 ? 0 : params.studentQuizAllList[0].rating
}
analysisScoreOpen.value = true
@ -827,10 +835,8 @@ const formatFeedContent = (stuItem, quItem) => {
const list = quizStrToList(quItem.workdesc);
format = list.map((item,index) =>{
if (quItem.worktype == '单选题') {
// const workdesc = item.replace(/<[^>]*>/g,'');
// const feedcontent = stuItem.feedcontent.replace(/<[^>]*>/g,'');
const workdesc = item;
const feedcontent = stuItem.feedcontent;
const workdesc = item.replace(/<[^>]*>/g,'');
const feedcontent = stuItem.feedcontent.replace(/<[^>]*>/g,'');
if(workdesc == feedcontent){
return (String.fromCharCode(65+Number(index)))
}
@ -871,13 +877,6 @@ const onSubmit = () => {
})
return
}
/** 1、 更新当前作业是否已经批阅完成 */
// TODO updateClasswork
/** 2、 更新每个学生的批阅 */
var formd = {
id: dialogProps.value.studentObj.id, // this.activeClassWork.id;
@ -885,7 +884,7 @@ const onSubmit = () => {
updatedate: getTimeDate(),// = year+'-'+month+'-'+day+' '+hh+':'+mm;
};
//
updateClassWorkDataAutoFinish(formd).then(res => {
updateClassworkdata(formd).then(res => {
})
//
@ -905,21 +904,6 @@ const onSubmit = () => {
// }
})
})
// let queryList = [];
// classWorkFormScore.teacherRating && classWorkFormScore.teacherRating.map((item, index) => {
// const queryParams = {
// id: item.id,
// teacherRating: item.score, //
// rating: classWorkFormScore.rating, //
// teacherremark: classWorkFormScore.teacherremark, //
// timestamp: getTimeDate() //
// }
// //console.log(queryParams);
// queryList.push(queryParams);
// })
// //console.log(queryList);
// updateClassworkevalList(queryList).then((res) => {
// })
ElMessage({
type: 'success',
message: '提交成功!'
@ -989,23 +973,23 @@ defineExpose({
}
.score-circle {
border: 1px solid rgb(131,131,131,.5);
padding: 5px 0;
background-color: #fff;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: pink;
color: red;
display: flex;
justify-content: center;
align-items: center;
font-size: 13px;
margin: 0 10px;
cursor: pointer;
margin-right: 5px;
width: 60px;
text-align: center;
transition: background-color 0.3s;
}
.score-circle.active {
background-color: rgb(253, 236, 224);
background-color: red;
color: white;
border: 1px solid rgb(253, 236, 224);
}
.greenLine{
border-left: 5px solid rgb(14, 209, 22);
padding-left: 5px
}
.card-header{

View File

@ -0,0 +1,762 @@
<template>
<el-dialog
v-model="classWorkAnalysis.open"
:modal-append-to-body="false"
class="clwk_dialog"
style="width: 90%; height: 85vh"
:show-close="false"
top="8vh"
append-to-body
destory-on-close
:before-close="onBeforeClose"
>
<template #header>
<div style="font-size: 18px; display: flex; flex-wrap: nowrap">
<div style="flex: 1">
{{ classWorkAnalysis.title }}答题情况
<el-tag :type="classWorkAnalysis.workclass" size="large" style="height: 25px">{{
classWorkAnalysis.worktype
}}</el-tag>
</div>
<!-- classWorkAnalysis.entpcourseworklistarray 当前学习任务所包含的试题ID -->
<el-row
v-if="classWorkAnalysis.entpcourseworklistarray.length > 0"
style="margin: 0 auto; flex: 1"
>
<el-button-group style="margin-bottom: 10px">
<el-button
:type="classWorkAnalysis.view == 'studentview' ? 'success' : ''"
@click="classWorkAnalysis.view = 'studentview'"
>作业批阅</el-button
>
<el-button
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'quizStats' ? 'success' : ''"
@click="workHandle('quizStats')"
>作业概况</el-button
>
<el-button
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'report' ? 'success' : ''"
@click="handleClassOverviewOpen('report')"
>作业报告</el-button
>
</el-button-group>
</el-row>
<div style="flex: 1">
<div
style="float: right; padding: 0 10px; cursor: pointer"
icon="el-icon-close"
@click="closeDialog"
>
x
</div>
</div>
</div>
</template>
<!-- 如果当前学习没有试题 :height="mainHeight"-->
<div
v-if="classWorkAnalysis.view == 'studentview'"
style="width: 100%; height:73vh; "
class="clwk_dialog_view"
>
<div class="view_table">
<el-radio-group
v-model="tableRadio.value"
style="margin-bottom: 1px"
@change="tableRadioChange"
>
<el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''" />
<el-radio-button :value="0" :label="'未交' + '' + tableRadio.num0 + ''" />
</el-radio-group>
<!-- 学生列表classWorkAnalysis.classworkdata; 已交未交tableRadio.list -->
<el-table
v-loading="loading_dt_table"
:data="tableRadio.list"
row-key="id"
style="height: 69vh;"
highlight-current-row
@row-click="getStudentClassWorkDataDetail"
>
<el-table-column type="index" label="序号" width="52" reserve-selection align="center" />
<el-table-column label="姓名" prop="studentname" width="100" align="center" />
<el-table-column label="提交时间" prop="updatedate" width="170" align="center" />
<el-table-column label="批阅状态" prop="teacherRating" align="center" width="120" sortable>
<template #default="scope">
<template v-if="scope.row.teacherRating == 0"
><span style="color: #2196f3">待批阅</span></template
>
<!-- 1- 2-优减 3- 4-良减 5- -->
<template v-if="scope.row.teacherRating == 1"
><el-tag type="danger"></el-tag></template
>
<template v-if="scope.row.teacherRating == 2"
><el-tag type="danger">-</el-tag></template
>
<template v-if="scope.row.teacherRating == 3"
><el-tag type="warning"></el-tag></template
>
<template v-if="scope.row.teacherRating == 4"
><el-tag type="info">-</el-tag></template
>
<template v-if="scope.row.teacherRating == 5"
><el-tag type="info"></el-tag></template
>
</template>
</el-table-column>
</el-table>
</div>
<div class="view_teachrting">
<div class="classwork-score">
<div v-if="classWorkAnalysis.activeStudentQuizlist.length == 0">
<el-empty
description="点击左侧表格学生信息可查看批阅详情"
style="width: 100%; height: 500px"
></el-empty>
</div>
<div v-else>
<div v-if="isopen_dtwk_table">
<div v-show="classWorkAnalysis.activeStudentQuizlist.length > 0">
<item-dialog-score
ref="classWorkAnalysisScoreDialogRef"
@class_work_score_submit="onClassWorkScoreSubmit"
/>
</div>
</div>
<div v-else>
<el-empty
description="点击左侧表格学生信息可查看批阅详情"
style="width: 100%; height: 500px"
></el-empty>
</div>
</div>
</div>
</div>
</div>
<!-- 作业概况 -->
<div v-else-if="classWorkAnalysis.view == 'quizStats'">
<quiz-stats :active-data="classWorkActiveData" />
</div>
<!-- 作业报告-->
<div v-else-if="classWorkAnalysis.view == 'report'" style="overflow-y: scroll">
<!-- <ClassOverview :table-list="overviewData" :eval-id="courseObj.evalid"></ClassOverview> -->
<ClassOverview :table-list="overviewData"></ClassOverview>
</div>
<!-- <template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="classWorkAnalysis.open=false"> </el-button>
</div>
</template> -->
</el-dialog>
</template>
<script setup name="itemDialogRef">
import { ref, defineExpose, onMounted, reactive, computed, watch, onUnmounted, nextTick, getCurrentInstance } from 'vue'
import { addSmartClassReserv, updateSmartClassReserv, listClassmain } from '@/api/classManage'
import { listClassworkdata, listEntpcoursework, listClassworkeval } from '@/api/classTask'
import useUserStore from '@/store/modules/user'
import { ElMessage } from 'element-plus'
import { getCurrentTime, getAfterMinutes } from '@/utils/date'
import { processList } from '@/hooks/useProcessList'
import ItemDialogScore from '@/views/classTask/container/item-dialog-score.vue'
// zdg:
import quizStats from '@/views/classTask/container/quizStats.vue'
import ClassOverview from '@/views/classTask/container/classOverview.vue'
const { proxy } = getCurrentInstance()
const emit = defineEmits(['cle-click'])
const props = defineProps({
bookId: {
type: Number,
default: 0
},
})
const mainHeight = ref(document.documentElement.clientHeight - 110)
const classWorkAnalysis = reactive({
open: false
})
const tableRadio = reactive({
value: '1', //
list: [], // list
num1: 0, //
num0: 0 //
}) //
const loading_dt_table = ref(false)
const isopen_dtwk_table = ref(false)
// zdg:
const classWorkActiveData = reactive({
quizlist: [], //
studentList: [], // -
workFeedList: [], // -
timerId: 0 // id
})
//-
const classWorkAnalysisScore = reactive({
studentObj: {}, //
studentQuizAllList: [], // list
quizlist: [] // list
})
//
const overviewData = ref([])
// watch(
// // () => props.currentNode,
// (newValue, oldValue) => {
// form.name = newValue.label
// }
// )
const openDialog = (data) => {
console.log(data, '点击的item答题情况')
classWorkAnalysis.title = data.uniquekey ? data.uniquekey + '--' : ''
classWorkAnalysis.worktype = data.worktype
classWorkAnalysis.workclass = data.workclass
//
tableRadio.list = []
tableRadio.value = '1'
tableRadio.num0 = 0
tableRadio.num1 = 0
classWorkAnalysis.open = true
//
classWorkAnalysis.view = 'studentview'
// ID
classWorkAnalysis.entpcourseworklistarray = data.entpcourseworklistarray
//
classWorkAnalysis.activeStudentQuizlist = []
//
classWorkAnalysis.activeQuizAnalysisData = []
classWorkAnalysis.row = data
// window.test = this
// zdg:
const studentArr = data.classworkdatastudentids
? JSON.parse(`[${data.classworkdatastudentids}]`)
: []
classWorkActiveData.studentList = studentArr
/** 学生完成情况分析--获取作业学生list数据 */
getClassWorkStudentList(data.id)
// idlist
var ids = []
for (var i = 0; i < data.entpcourseworklistarray.length; i++) {
ids.push(data.entpcourseworklistarray[i].id)
}
//
listEntpcoursework({ ids: ids.join(','), pageSize: 500 }).then((idres) => {
for (var i = 0; i < idres.rows.length; i++) {
// // + .replace(/!@#\$%/g,'')
idres.rows[i].titletext = idres.rows[i].title.replace(/!@#\$%/g, '')
}
classWorkAnalysis.quizlist = idres.rows
classWorkActiveData.quizlist = idres.rows // zdg: 使
//
// + , pageSize: 100
listClassworkeval({ workid: data.id, pageSize: 1000 }).then((wevalres) => {
for (var i = 0; i < classWorkAnalysis.quizlist.length; i++) {
//
var scoingCount = 0
var feedcount = 0
//
var evalCount = 0
for (var w = 0; w < wevalres.rows.length; w++) {
if (wevalres.rows[w].entpcourseworkid == classWorkAnalysis.quizlist[i].id) {
evalCount++
//
if (wevalres.rows[w].feedcontent != '') {
//
feedcount++
//
if (wevalres.rows[w].feedcontent == wevalres.rows[w].rightanswer) {
wevalres.rows[w].scoingStatus = true
scoingCount++
// =
wevalres.rows[w].teacherRating = wevalres.rows[w].score
} else {
wevalres.rows[w].scoingStatus = false
}
}
}
}
classWorkAnalysis.quizlist[i].evalCount = evalCount
//
classWorkAnalysis.quizlist[i].feedcount = feedcount
// NaN% scoingRate
if (scoingCount == 0 && feedcount == 0) {
classWorkAnalysis.quizlist[i].scoingRate = '0%'
} else {
classWorkAnalysis.quizlist[i].scoingRate =
((scoingCount / feedcount) * 100).toFixed(0) + '%'
}
}
// zdg:
const getStudentid = (workdataid) => {
// id
const classworkdata = (classWorkAnalysis.classworkdata || []).find(
(o) => o.id === workdataid
)
return classworkdata ? classworkdata.studentid : ''
}
wevalres.rows.forEach((o) => {
o.studentid = getStudentid(o.workdataid)
})
classWorkActiveData.workFeedList = wevalres.rows
})
})
console.log(classWorkAnalysis, '点击进度后获得的数据')
}
//#region
/** 1、获取作业学生列表 */
const getClassWorkStudentList = (rowId) => {
// rowid使
localStorage.setItem('activeClassWorkRowId', rowId)
//
classWorkAnalysis.classworkdata = []
// _
loading_dt_table.value = true
// classworkdata
listClassworkdata({ classworkid: rowId, pageSize: 100 })
.then((response) => {
for (var i = 0; i < response.rows.length; i++) {
if (response.rows[i].entpcourseworklist != '') {
response.rows[i].entpcourseworkarray = JSON.parse(
'[' + response.rows[i].entpcourseworklist + ']'
)
} else {
response.rows[i].entpcourseworkarray = []
}
// 0
response.rows[i].teacherRating = 0
//
if (
response.rows[i].classworkevallist != '' &&
response.rows[i].classworkevallist != null &&
response.rows[i].classworkevallist != 'null'
) {
// , : "{\"id\":172910, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
// .replace(/"(\[.*\])"/g, '$1'); eg: "feedcontent\":\"[{\"name\":\"Bliss.jpg\",\"url\":\"https://wzyzoss.3b8daa474.jpg\"}]\",
// json .replace(/""/g, '"') eg: """"
response.rows[i].classworkevallist = escapeHtmlQuotes(response.rows[i].classworkevallist)
console.log('学生完成情况分析classworkevallist', response.rows[i].classworkevallist)
const evalarray = JSON.parse('[' + response.rows[i].classworkevallist + ']')
var scoingCount = 0
var feedcount = 0
for (var e = 0; e < evalarray.length; e++) {
if (evalarray[e].feedcontent != '') {
feedcount++
//
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
scoingCount++
}
}
}
console.log(evalarray, 'evalarray------------------------------------')
if (feedcount > 0) {
// : /*100
response.rows[i].scoingRate = ((scoingCount / feedcount) * 100).toFixed(0) + '%'
} else {
response.rows[i].scoingRate = '0%'
}
// :
if (evalarray[0].rating != '') {
response.rows[i].teacherRating = evalarray[0].rating
}
} else {
response.rows[i].scoingRate = '0%'
}
}
classWorkAnalysis.classworkdata = response.rows
loading_dt_table.value = false
//
tableRadio.list =
classWorkAnalysis.classworkdata &&
classWorkAnalysis.classworkdata.filter((item) => item.finishtimelength != '0')
tableRadio.value = '1'
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length
tableRadio.num1 = tableRadio.list.length
})
.catch(() => {
loading_dt_table.value = false
})
}
/** 2、查看某一个学生的学习任务完成详情*/
const getStudentClassWorkDataDetail = (row) => {
//
// this.classWorkAnalysis.quizlist
console.log(row, '点击了左侧学生')
//
classWorkAnalysisScore.studentObj = row
listClassworkeval({ workdataid: row.id, pageSize: 100 })
.then((wevalres) => {
for (var i = 0; i < classWorkAnalysis.quizlist.length; i++) {
//
for (var w = 0; w < wevalres.rows.length; w++) {
if (wevalres.rows[w].entpcourseworkid == classWorkAnalysis.quizlist[i].id) {
wevalres.rows[w].quiztitle = classWorkAnalysis.quizlist[i].title
wevalres.rows[w].quiztitletext = classWorkAnalysis.quizlist[i].title.replace(
/<[^>]*>/g,
''
)
wevalres.rows[w].score = wevalres.rows[w].score ? wevalres.rows[w].score : 0
// html
wevalres.rows[w].rightanswer =
wevalres.rows[w].rightanswer != '' && wevalres.rows[w].rightanswer != null
? wevalres.rows[w].rightanswer.replace(/<[^>]+>/g, '')
: wevalres.rows[w].rightanswer
// html
wevalres.rows[w].feedcontent =
wevalres.rows[w].feedcontent != '' && wevalres.rows[w].feedcontent != null
? wevalres.rows[w].feedcontent.replace(/<[^>]+>/g, '')
: wevalres.rows[w].feedcontent
if (classWorkAnalysis.row.worktype == '常规作业') {
wevalres.rows[w].feedcontent = JSON.parse(wevalres.rows[w].feedcontent)
}
if (wevalres.rows[w].feedcontent != '') {
if (wevalres.rows[w].feedcontent == wevalres.rows[w].rightanswer) {
wevalres.rows[w].scoingStatus = true
// =
wevalres.rows[w].teacherRating = wevalres.rows[w].score
} else {
wevalres.rows[w].scoingStatus = false
}
} else {
wevalres.rows[w].scoingStatus = ''
}
//
}
// "" prop="feedcontent" width="200" align="center"></el-table-column>
// <el-table-column label="" prop="rightanswer"
// +
wevalres.rows[w].worktitle = wevalres.rows[w].worktitle.replace(/!@#\$%/g, '')
// feedcontent\r<br />
wevalres.rows[w].feedcontent = wevalres.rows[w].feedcontent.replace(/(?<!\\)\n/g, '<br />'); //\n\\n \\n
}
}
classWorkAnalysis.activeStudentQuizlist = wevalres.rows
//
isopen_dtwk_table.value = true
//
if (wevalres.rows.length > 0) {
handleClassWorkAnalysissScoreOpen(row)
} else {
ElMessage({
type: 'warning',
message: '未获取到答题信息,请稍后再看,或者联系管理员查看情况!'
})
}
})
.catch(() => {
console.log('获取答题情况失败')
ElMessage({
type: 'warning',
message: '未获取到答题信息!'
})
})
}
/** 3、教师批改后返回的方法*/
const onClassWorkScoreSubmit = () => {
console.log('批改后返回的方法')
loading_dt_table.value = true
isopen_dtwk_table.value = false
// 1table- classWorkAnalysis.classworkdata- classWorkAnalysis.activeStudentQuizlist
// -
classWorkAnalysis.classworkdata = []
classWorkAnalysis.activeStudentQuizlist = []
// 2
const rowid = localStorage.getItem('activeClassWorkRowId')
getClassWorkStudentList(rowid)
}
// ()
const handleClassWorkAnalysissScoreOpen = (row) => {
console.log(row, '所选点击的信息')
// list
classWorkAnalysisScore.studentQuizAllList = classWorkAnalysis.activeStudentQuizlist
// list
classWorkAnalysisScore.quizlist = classWorkAnalysis.quizlist
//
processList(classWorkAnalysisScore.quizlist)
//
classWorkAnalysisScore.maxheight = mainHeight.value - 100
//
nextTick(() => {
proxy.$refs.classWorkAnalysisScoreDialogRef.acceptParams(classWorkAnalysisScore)
})
}
//#endregion
/** 批阅:已交未交事件 */
const tableRadioChange = (e) => {
// ui
isopen_dtwk_table.value = false;
console.log(e,'??????')
console.log("学生列表:", classWorkAnalysis.classworkdata)
if(e=='1'){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength != '0')
tableRadio.value = '1';
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
tableRadio.num1 = tableRadio.list.length;
}else if(e=='0'){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength == '0')
tableRadio.value = '0';
tableRadio.num0 = tableRadio.list.length;
tableRadio.num1 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
}
}
//
const escapeHtmlQuotes = (str) => {
// replace,
const regex1 = /\\+/g; //
let result = str.replace(regex1, '\\');
result = str.replace(/(?<!\\)\n/g, '<br />'); //\n\\n \\n
return result;
}
//#region
// -
const workHandle = (type) => {
// ui
isopen_dtwk_table.value = false;
classWorkAnalysis.view = type
const isClose = type != 'quizStats' && !! classWorkActiveData.timerId
const isOpen = type == 'quizStats' && !classWorkActiveData.timerId
//
if(type == 'quizStats') {
getWorkFeedList();
}
if (isClose) clearInterval(classWorkActiveData.timerId) //
if (isOpen) {
//
classWorkActiveData.timerId = setInterval(() => {
console.log('zdg: 定时执行')
getWorkFeedList()
}, 20 * 1000);
}
}
// -
const getWorkFeedList = async() =>{
const workid = classWorkAnalysis.row.id
const res = await listClassworkeval({workid, isFinish: 1, pageSize: 1000})
const getStudentid = (workdataid) => { // id
const classworkdata = (classWorkAnalysis.classworkdata||[]).find(o => o.id === workdataid)
return classworkdata ? classworkdata.studentid : ''
}
res.rows.forEach(o => { o.studentid = getStudentid(o.workdataid) })
classWorkActiveData.workFeedList = res.rows
}
//#endregion
//#regin
/*
author: yangws
time: 2024-8-06 16:35:33
function:作业报告的处理
*/
const handleClassOverviewOpen = (type) =>{
// ui
isopen_dtwk_table.value = false;
classWorkAnalysis.view = type
const data = classWorkAnalysis.row
//
listClassworkdata({classworkid: data.id, pageSize: 100}).then((response) => {
if(response.code === 200){
response.rows.forEach(item => {
let rightAnswer = 0
let answers = 0
if(!item.classworkevallist) return
// 使
let replacedString = item.classworkevallist.replace(/""/g, "\"");
// , : "{\"id\":172907, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358520, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172908, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358521, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172909, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363096, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172910, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363098, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
replacedString = escapeHtmlQuotes(item.classworkevallist);
let allTopic
try{
allTopic = JSON.parse(`[${item.classworkevallist}]`)
}catch{
allTopic = JSON.parse(`[${replacedString}]`)
}
if(item.classworkevallist != ''){
allTopic.forEach(itemTopic => {
if(itemTopic.feedcontent != ''){
answers ++
//
if(itemTopic.feedcontent === itemTopic.rightanswer){
rightAnswer ++
}
}
})
rightAnswer > 0?item.scoingRate = (rightAnswer/answers * 100).toFixed(0):item.scoingRate = ''
}else{
item.scoingRate = ''
}
//
const point = allTopic.reduce((acc, cur) => {
if(cur.rating !== 0){
return acc + cur.teacherRating;
}
},0)
// item.chapter = this.courseObj.evalid
item.point = point || 0
item.rating = allTopic[0].rating
})
overviewData.value = [...response.rows]
}
})
}
//#endregion
const onBeforeClose = () =>{
console.log('非正常关闭dialog?esc、dialog外部区域')
closeDialog()
}
const closeDialog = () => {
classWorkAnalysis.open = false
emit('cle-click')
}
watch(classWorkAnalysis, (newVal, oldVal) => {
if(newVal.view != 'quizStats'){
console.log('关闭zdg: 定时执行')
clearInterval(classWorkActiveData.timerId) //
}
})
onUnmounted(() => {
clearInterval(classWorkActiveData.timerId) //
})
defineExpose({
openDialog,
})
</script>
<style src="@/assets/styles/JYStyle.css"></style>
<style scoped lang="scss">
/*:deep(.reserv-date-pick) {
width: 140px;
}
:deep(.reserv-time-pick) {
width: 240px;
}*/
.clwk_dialog {
.clwk_dialog_view {
display: flex;
flex-direction: row;
justify-content: flex-start;
overflow: hidden;
}
.view_table {
flex: 0 0 auto;
height: 100%;
overflow: hidden;
}
.view_teachrting {
flex: 1;
height: 100%;
/*overflow-y: auto; */
overflow: hidden;
}
}
.clwk_dialog {
display: flex;
justify-content: center;
overflow: hidden;
}
.clwk_dialog .el-dialog {
margin: 0 auto !important;
height: 85%!important;
overflow: hidden;
}
.clwk_dialog .el-dialog__header {
/* position: absolute;
top: 0;
left: 0; */
width: 100%!important;
}
.clwk_dialog .el-dialog__body {
position: absolute;
left: 0;
top: 15px;
bottom: 1px;
right:0;
padding:5px;
z-index:1;
display: flex;
flex-direction: column;
overflow: hidden;
/* overflow:hidden;
overflow-y: auto; */
}
.clwk_dialog .el-dialog__footer{
position: absolute;
bottom: 10px;
right: 10px;
}
.clwk_dialog .classwork-score{
overflow-y: auto;
}
</style>
<style scoped>
.clwk_dialog {
display: flex;
justify-content: center;
overflow: hidden;
}
.clwk_dialog .el-dialog {
margin: 0 auto !important;
height: 85%!important;
overflow: hidden;
}
.clwk_dialog .el-dialog__header {
/* position: absolute;
top: 0;
left: 0; */
width: 100%!important;
}
.clwk_dialog .el-dialog__body {
position: absolute;
left: 0;
top: 15px;
bottom: 1px;
right:0;
padding:5px;
z-index:1;
display: flex;
flex-direction: column;
overflow: hidden;
/* overflow:hidden;
overflow-y: auto; */
}
.clwk_dialog .el-dialog__footer{
position: absolute;
bottom: 10px;
right: 10px;
}
.clwk_dialog .classwork-score{
overflow-y: auto;
}
</style>

View File

@ -1,148 +0,0 @@
<template>
<!-- 预览框 -->
<el-dialog v-if="prevReadMsg.visible" v-model="prevReadMsg.visible" class="prev-read-zy-wrap" width="90%" style="height: 80vh" append-to-body>
<!-- <div v-if="prevReadMsg.type=='课标研读'" style="height: 100%;">
<standard book-type="课标研读" :show-cata="true" :show-tools="false" :course-obj="courseObj" :bookdatahtml="versionObj.bookdata" :teachResObj="activeTeachResOfStandard"></standard>
</div>
<div v-if="prevReadMsg.type=='目标设定'" style="height: 100%;display: flex;">
<degreeevolution :courseObj="courseObj" :show-class="true" :teachResObj="activeTeachResOfStandard" :attainmentList="attainmentList" :courseQualityList="courseQualityList"/>
</div>
<div v-if="prevReadMsg.type=='教材研读'" style="height: 100%;">
<standard book-type="教材研读" :course-obj="courseObj" :show-tools="false" :bookdatahtml="versionObj.bookdata" :teachResObj="activeTeachResOfStandard"></standard>
</div> -->
<div v-if="prevReadMsg.type=='框架梳理'" style="height: 100%;">
<FlowChart ref="flowref" :flowHeight="mainHeight" :dataSource="flowData"/>
</div>
<!-- <div v-if="prevReadMsg.type=='学科定位'" style="height: 100%;">
<teachJsMind :course-obj="courseObj" :teachResObj="activeTeachResOfStandard"></teachJsMind>
</div> -->
<!-- <div v-if="prevReadMsg.type=='习题训练'">习题训练</div> -->
</el-dialog>
</template>
<script setup name="prevReadMsgDialogRef">
import { onMounted, ref, watch, reactive, getCurrentInstance } from 'vue'
import { listEntpcoursefile } from '@/api/education/entpcoursefile'
import { useGetHomework } from '@/hooks/useGetHomework'
import FlowChart from "@/components/Flowchart/index.vue";
// import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork'
// import { addClassworkReturnId } from '@/api/teaching/classwork'
// import { updateClasswork, listEvaluationclue,readFile, listClassworkeval,delClassworkeval,addClassworkeval,updateClassworkeval } from '@/api/classTask'
// import { listEvaluation } from '@/api/subject'
// import { listKnowledgePoint } from "@/api/knowledge/knowledgePoint";
import FileUpload from "@/components/FileUpload/index.vue";
import whiteboard from '@/components/whiteboard/whiteboard.vue'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user
const { proxy } = getCurrentInstance()
const props = defineProps({
bookobj: {
type: Object,
default: () => ({})
},
})
const mainHeight = ref(document.documentElement.clientHeight - 110)
//
const prevReadMsg = reactive({
visible: false,
type: ""
});// msg
// ----------
const flowData = ref({})//
// 1
const openDialog = async (item) => {
prevReadMsg.visible = true;
prevReadMsg.type = item.worktype;
// // if (item.worktype===''){
// // //
// // listEvaluation({itemkey: 'subject', edusubject: userStore.edusubject, edustage: userStore.edustage}).then(res => {
// // // TODO -
// // console.log("-",res);
// // // this.versionObj = res.rows[0];
// // // //
// // // if (this.versionObj.fileurl.length > 0) {
// // // readFile({cluelink: this.versionObj.fileurl}).then(fileres => {
// // // this.versionObj.bookdata = fileres;
// // // this.activeTeachResOfStandard = item;
// // // })
// // // }
// // })
// // }
// // if (item.worktype===''){
// // // TODO -
// // // this.activeTeachResOfStandard = item;
// // }
// if (item.worktype===''){
// // TODO -
// // getEvaluation(this.courseObj.evalrootid).then(bookres => {
// // this.versionObj = bookres.data;
// // if (this.versionObj.fileurl.length > 0) {
// // readFile({cluelink: this.versionObj.fileurl}).then(fileres => {
// // this.versionObj.bookdata = fileres;
// // this.activeTeachResOfStandard = item;
// // })
// // }
// // })
// }
if (item.worktype==='框架梳理'){
flowData.value = {};
const { chapterId } = await useGetHomework(props.bookobj.node)
// this.entpcourseid = chapterId
let queryParams = {
entpcourseid: chapterId,
ppttype: '教材分析',
parentid: item.id,
title: '逻辑框架建构',
filetype: 'draw'
}
listEntpcoursefile(queryParams).then(response=>{
if (response.rows.length == 0) {
return;
}
flowData.value = JSON.parse(response.rows[0].datacontent)
})
}
// if (item.worktype===''){
// // TODO -
// // this.activeTeachResOfStandard = item;
// }
}
onMounted(() => {
})
watch(() => props.bookobj.levelSecondId, (newVal) => {
// console.log(props.bookobj,'')
})
defineExpose({
openDialog,
})
</script>
<style>
.prev-read-zy-wrap .el-dialog__header{
padding: 0!important;
}
.prev-read-zy-wrap .el-dialog__header button{
z-index: 99;
}
.prev-read-zy-wrap .el-dialog__body{
padding: 0!important;
height: 100%;
}
</style>

View File

@ -7,9 +7,9 @@
<el-row class="top">
<el-col :span="24">
<el-form-item label="作业类型:">
<el-radio-group v-model="classWorkForm.worktype" @change="changeFormType">
<el-radio-group v-model="formType" @change="changeFormType">
<template v-for="(item) in listWorkType" :key="item">
<el-radio :value="item" :disabled="props.isedit">{{ item }}</el-radio>
<el-radio :value="item" >{{ item }}</el-radio>
</template>
</el-radio-group>
</el-form-item>
@ -21,12 +21,12 @@
<div v-if="classWorkForm.worktype == '课堂展示'" style="height: 100%; display: flex; flex-direction: column;">
<div style="flex: 0 0 auto;">
<el-form-item label="问题">
<el-input v-model="classWorkForm.question" type="text" placeholder="请输入问题" />
<el-input v-model="question" type="text" placeholder="请输入问题" />
</el-form-item>
</div>
<div v-loading="boardLoading" class="board-wrap" style="height: 100%; flex: 1; overflow: hidden;">
<!-- <whiteboard v-if="isShowBoard" ref="boardref" :height="mainHeight - 150" :isShowSave="false" :data="whiteboardObj"/> -->
<whiteboard ref="boardref" :height="mainHeight - 150" :isShowSave="false" :data="classWorkForm.whiteboardObj"/>
<whiteboard ref="boardref" :height="mainHeight - 150" :isShowSave="false" :data="whiteboardObj"/>
</div>
</div>
<div v-else class="el-form-work-list">
@ -55,7 +55,6 @@
<el-col :span="11">
<el-form-item label="知识点" label-width="70">
<el-cascader
disabled
v-model="entpCourseWorkQueryParams.point"
clearable
style="width: 100%"
@ -90,13 +89,8 @@
</el-col>
</el-row>
<!-- 习题表格 -->
<div class="infinite-list-wrapper" >
<el-table
:data="workResource.entpCourseWorkList"
style="width: 100%; height: 100%;"
v-loading="pageParams.loading"
ref="tableRef"
>
<div class="middle" >
<el-table :data="workResource.entpCourseWorkList" style="width: 100%; height: 100%;">
<el-table-column type="index" width="60" />
<el-table-column align="left" >
<template #header>
@ -105,7 +99,7 @@
</div>
</template>
<template #default="scope">
<div @click="showExamAnalyseDrawer(scope.row)">
<div>
<div style="overflow: hidden; text-overflow: ellipsis" v-html="scope.row.titleFormat"></div>
<div style="overflow: hidden; text-overflow: ellipsis; font-size: 0.9em; margin-top: 6px;" v-html="scope.row.workdescFormat"></div>
<el-col :span="24" style="display: flex">
@ -121,54 +115,17 @@
</template>
</el-table-column>
</el-table>
<!-- <ul
v-infinite-scroll="pageLoad"
class="infinite-list"
infinite-scroll-immediate="false"
infinite-scroll-distance='100'
infinite-scroll-delay="1000"
:infinite-scroll-disabled="pageDisabled"
>
<li v-for="(item,index) in workResource.entpCourseWorkList" :key="item" class="infinite-list-item">
<div align="left" style="width: 100%;" >
<div @click="showExamAnalyseDrawer(item)">
<div>
<span style="width: 20px;">{{ index +1 }}. </span>
<span style="overflow: hidden; text-overflow: ellipsis" v-html="item.titleFormat"></span>
</div>
<div style="overflow: hidden; text-overflow: ellipsis; font-size: 0.9em; margin-top: 6px;" v-html="item.workdescFormat"></div>
<el-col :span="24" style="display: flex">
<div style="font-size: 1em; color: silver; padding-top: 5px">{{ item.entpname }} {{ item.editusername }}</div>
<div style="margin-left: 30px; font-size: 1em; color: silver; padding-top: 5px">{{ item.worktag }}</div>
</el-col>
</div>
</div>
<div align="right" style="width: 72px;">
<el-button type="primary" @click="handleClassWorkQuizAdd('entpcourseworklist', item.id)">添加</el-button>
</div>
</li>
</ul>
<p class="infinite-list-loading" v-if="pageParams.loading">加载中...</p>
<p class="infinite-list-noMove" v-if="pageNoMore">无更多试题...</p>
<div v-if="workResource.entpCourseWorkList.length == 0 && !pageParams.loading">
<el-empty
description="未找到相关试题"
style="width: 100%; height: 200px; margin-top: 20px;"
></el-empty>
</div> -->
</div>
<!-- 分页 这里不用-->
<div style="height: 55px;">
<el-pagination
v-show="pageParams.total > 0"
<!-- <div style="height: 55px;">
<pagination
v-show="entpCourseWorkTotal > 0"
v-model:page="paginationParams.pageNum"
v-model:limit="paginationParams.pageSize"
:total="pageParams.total"
:total="entpCourseWorkTotal"
:style="{ position: 'relative', 'margin-top': '5px' }"
@change="getPaginationList" />
</div>
@pagination="getPaginationList" />
</div> -->
</div>
<!-- 非习题训练常规作业 -->
<div v-if="classWorkForm.worktype!='习题训练'">
@ -197,7 +154,7 @@
</template>
<template v-if="classWorkForm.worktype =='常规作业'">
<div v-loading="fileLoading" class="upload-homework">
<FileUpload v-model="classWorkForm.fileHomeworkList" :fileSize="800" :fileType="['mp3','mp4','doc','docx','xlsx','xls','pdf','ppt','pptx','jpg','jpeg','gif','png','txt']"/>
<FileUpload v-model="fileHomeworkList" :fileSize="800" :fileType="['mp3','mp4','doc','docx','xlsx','xls','pdf','ppt','pptx','jpg','jpeg','gif','png','txt']"/>
</div>
</template>
</div>
@ -223,7 +180,7 @@
<div v-if="classWorkForm.worktype!='习题训练'" :style="{'overflow': 'auto', 'border':'1px dotted blue','border-radius':'5px', 'background-color': '#f7f7f7'}">
<div style="margin: 5px; background-color: white">
<template v-for="(item) in classWorkForm.chooseWorkLists" :key="item.id">
<template v-for="(item) in chooseWorkLists" :key="item.id">
<div v-if="item.worktype==classWorkForm.worktype">
<div class="choose-work">
<div class="choose-work-title">{{ item.worktype }}</div>
@ -265,46 +222,54 @@
</div>
</el-form>
<!-- 预览框 -->
<prevReadMsgDialog ref="prevReadMsgDialogRef" :bookobj="props.bookobj"/>
<!-- 试题详细信息 -->
<examDetailsDrawer ref="examDetailsDrawerRef"></examDetailsDrawer>
<!-- 预览框 -->
<el-dialog v-if="prevReadMsg.visible" v-model="prevReadMsg.visible" class="prev-read-zy-wrap" width="90%" style="height: 80vh" append-to-body>
<!-- <div v-if="prevReadMsg.type=='课标研读'" style="height: 100%;">
<standard book-type="课标研读" :show-cata="true" :show-tools="false" :course-obj="courseObj" :bookdatahtml="versionObj.bookdata" :teachResObj="activeTeachResOfStandard"></standard>
</div>
<div v-if="prevReadMsg.type=='目标设定'" style="height: 100%;display: flex;">
<degreeevolution :courseObj="courseObj" :show-class="true" :teachResObj="activeTeachResOfStandard" :attainmentList="attainmentList" :courseQualityList="courseQualityList"/>
</div>
<div v-if="prevReadMsg.type=='教材研读'" style="height: 100%;">
<standard book-type="教材研读" :course-obj="courseObj" :show-tools="false" :bookdatahtml="versionObj.bookdata" :teachResObj="activeTeachResOfStandard"></standard>
</div> -->
<div v-if="prevReadMsg.type=='框架梳理'" style="height: 100%;">
<FlowChart ref="flowref" :flowHeight="mainHeight" :dataSource="flowData"/>
</div>
<!-- <div v-if="prevReadMsg.type=='学科定位'" style="height: 100%;">
<teachJsMind :course-obj="courseObj" :teachResObj="activeTeachResOfStandard"></teachJsMind>
</div> -->
<!-- <div v-if="prevReadMsg.type=='习题训练'">习题训练</div> -->
</el-dialog>
</div>
</template>
<script setup>
import { onMounted, ref, nextTick, watch, reactive, getCurrentInstance, computed } from 'vue'
import { onMounted, ref, nextTick, watch, reactive, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'
import { cloneDeep } from 'lodash'
import { useRouter } from 'vue-router'
import { Search } from '@element-plus/icons-vue'
import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork'
import { addClassworkReturnId } from '@/api/teaching/classwork'
import { updateClasswork, listEvaluationclue, listClassworkeval,delClassworkeval,addClassworkeval,updateClassworkeval } from '@/api/classTask'
import { updateClasswork, listEvaluationclue,readFile, listClassworkeval,delClassworkeval,addClassworkeval } from '@/api/classTask'
import { listEvaluation } from '@/api/subject'
import { listEntpcoursefile } from '@/api/education/entpcoursefile'
import { listKnowledgePoint } from "@/api/knowledge/knowledgePoint";
import { isJson } from "@/utils/comm";
import { useGetHomework } from '@/hooks/useGetHomework'
import { processList } from '@/hooks/useProcessList'
import { getCurrentTime } from '@/utils/date'
import FlowChart from "@/components/Flowchart/index.vue";
import FileUpload from "@/components/FileUpload/index.vue";
import whiteboard from '@/components/whiteboard/whiteboard.vue'
import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue'
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
import { JYApiListCT, JYApiListOriginYear, JYApiListSO} from "@/utils/examQuestion/jyeoo"
import {throttle,debounce } from '@/utils/comm'
import { useToolState } from '@/store/modules/tool'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user
const { proxy } = getCurrentInstance()
const router = useRouter()
const toolStore = useToolState()
const props = defineProps({
bookobj: {
@ -314,20 +279,21 @@ const props = defineProps({
propsformobj: {
type: Object,
default: () =>({})
},
isedit: {
type: Boolean,
default: false
},
isback:{
type: Boolean,
default: false
}
})
const prevReadMsgDialogRef = ref(null);// ref
const isDialogOpen = ref(false)
const toolStore = useToolState()
const openDialog = () => {
isDialogOpen.value = true
}
const classWorkFormRef = ref(null);
const formType = ref('习题训练')
const newWorkSpaceEdit = ref(false); //
// ---------------------------------------------------
const listWorkType = ref(['习题训练', '框架梳理', '课堂展示', '常规作业']); //
const entpCourseWorkTypeList = ref([
{value: 0, label: "不限"},
{value: 1, label: "单选题"},
@ -394,49 +360,37 @@ const workResource = reactive({
}); //
let classWorkForm = reactive({
id: cloneDeep(props.propsformobj.id),
uniquekey: props.propsformobj.uniquekey?cloneDeep(props.propsformobj.uniquekey):'', //
uniquekey: cloneDeep(props.propsformobj.uniquekey), //
worktype: props.propsformobj.worktype?cloneDeep(props.propsformobj.worktype): '习题训练', //
title: props.propsformobj.title?cloneDeep(props.propsformobj.title):'',//
quizlist: props.propsformobj.quizlist?cloneDeep(props.propsformobj.quizlist):[], //
chooseWorkLists: props.propsformobj.chooseWorkLists?cloneDeep(props.propsformobj.chooseWorkLists):[], // list
fileHomeworkList: props.propsformobj.fileHomeworkList?cloneDeep(props.propsformobj.fileHomeworkList):[], //
whiteboardObj: props.propsformobj.whiteboardObj?cloneDeep(props.propsformobj.whiteboardObj):'', // -
question: props.propsformobj.question?cloneDeep(props.propsformobj.question):'', // -
quizlist: props.propsformobj.quizlist?cloneDeep(props.propsformobj.quizlist):[] //
}); //
// ---------------------------------------------------
// const listWorkType = ref(['', '', '', '']); //
const listWorkType = ref(['习题训练', '课堂展示', '常规作业']); //
const chooseWorkLists = ref([]); //
const whiteboardObj = ref(''); // -
// -------
const boardLoading = ref(false);
const isShowBoard = ref(false); //
const question = ref(''); // --
const prevReadMsg = reactive({
visible: false,
type: ""
});// msg
// ----------
const flowData = ref({})//
//----------
const fileLoading = ref(false); // loading
//
// const BASE_LIMIT_COUT = 50; //
// const pageNoMore = computed( () => {
// if (pageParams.value.total < 1) {
// return false;
// }
// let count = BASE_LIMIT_COUT >= pageParams.value.total ? pageParams.value.total : pageParams.value.originCount+BASE_LIMIT_COUT;
// return workResource.entpCourseWorkList.length >= count;
// });
// const pageDisabled = computed(() => pageParams.value.loading || pageNoMore.value);
const pageParams = ref({
loading: false, //
originCount: 0, //
isFirst: true, //
total: 0,
})
const fileHomeworkList = ref([]);//
/***
* 作业类型切换
*/
const changeFormType = (val) => {
console.log(val)
classWorkForm.worktype = val;
}
console.log(props.propsformobj)
console.log(classWorkForm,'==============zizujian===================')
/**
* @desc: 根据查询参数查询试题
* @return: {*}
@ -447,33 +401,9 @@ const changeFormType = (val) => {
const handleQueryParamFromEntpCourseWork = (queryType) => {
//
// this.paginationParams = {pageNum: 1,pageSize: 10};
//
initPageParams();
handleQueryFromEntpCourseWork(queryType);
};
let obj = {};
function Apis(key) {
obj[key] = [];
return function(task) {
return new Promise((resolve, reject) => {
obj[key].push(task);
Promise.all([...obj[key]]).then(res => {
const i = obj[key].findIndex(item => {
return item == task;
});
resolve(obj[key][i]);
//arr.splice(i, 1);
})
})
}
}
const client = new Apis('/paht');
/**
* @desc: 1习题训练 - 新查询试题
* @return: {*}
@ -482,39 +412,34 @@ function Apis(key) {
* 1 - 按条件查询
* 2 - 按关键词查询
*/
const t = function(name, time) {
return new Promise(resolve => {
const evalId = props.bookobj.levelSecondId=='' ? props.bookobj.levelFirstId : props.bookobj.levelSecondId;
const queryForm = {
//
currentPage: paginationParams.pageNum,
pageSize: paginationParams.pageSize,
//
eid: evalId,
sectionName: props.bookobj.coursetitle,
edusubject: userStore.edusubject,
edustage: userStore.edustage,
//
//
worktype: entpCourseWorkQueryParams.worktype.label,
workTypeId: entpCourseWorkQueryParams.worktype.value,
//
workgroup: entpCourseWorkQueryParams.workgroup,
//
yearStr: entpCourseWorkQueryParams.yearStr !== '-1' ? entpCourseWorkQueryParams.yearStr:'',
//
thirdId: entpCourseWorkQueryParams.point && entpCourseWorkQueryParams.point.length > 0 ? entpCourseWorkQueryParams.point[0]:'',
//
keyword: entpCourseWorkQueryParams.keyWord && entpCourseWorkQueryParams.keyWord !== '' ? entpCourseWorkQueryParams.keyWord:'',
const handleQueryFromEntpCourseWork= (queryType) => {
//queryForm.pageNum = this.paginationParams.pageNum;
//queryForm.pageSize = this.paginationParams.pageSize;
}
const entpcourseworkres = listEntpcourseworkNew(queryForm);
const queryForm = {
//
eid: props.bookobj.levelSecondId,
sectionName: props.bookobj.coursetitle,
edusubject: userStore.edusubject,
edustage: userStore.edustage,
//
//
worktype: entpCourseWorkQueryParams.worktype.label,
workTypeId: entpCourseWorkQueryParams.worktype.value,
//
workgroup: entpCourseWorkQueryParams.workgroup,
//
yearStr: entpCourseWorkQueryParams.yearStr !== '-1' ? entpCourseWorkQueryParams.yearStr:'',
//
thirdId: entpCourseWorkQueryParams.point.length > 0 ? entpCourseWorkQueryParams.point[0]:'',
//
keyword: entpCourseWorkQueryParams.keyWord && entpCourseWorkQueryParams.keyWord !== '' ? entpCourseWorkQueryParams.keyWord:'',
resolve(entpcourseworkres);
})
}
const handleQueryFromEntpCourseWork= async (queryType) => {
pageParams.value.loading = true;
//
// pageNum: paginationParams.pageNum,
// pageSize: paginationParams.pageSize,
// ( warn: )
// if (this.courseObj.edusubject=='' && this.courseObj.edustage=='') {
@ -522,45 +447,35 @@ const handleQueryFromEntpCourseWork= async (queryType) => {
// queryForm.edusubject = '';
// }
client(t('任务1', 1500)).then(res => {
//console.log("",res);
// if(paginationParams.pageNum == 1){
// workResource.entpCourseWorkList = [];
// workResource.entpCourseWorkTotal = 0;
// //
// // pageParams.value.loading = false;
// // pageParams.value.isFirst = true;
// // pageParams.value.originCount = 0;
listEntpcourseworkNew(queryForm).then(entpcourseworkres => {
// if (queryType == 1 && this.entpCourseWorkQueryParams.worktype == '') {
// // ,
// const allowedWorkTypes = ['', '', '', '', ''];
// workResource.entpCourseWorkList = entpcourseworkres.rows.filter(item => {
// return !allowedWorkTypes.includes(item.worktype);
// });
// } else {
// workResource.entpCourseWorkList = entpcourseworkres.rows;
// }
const data = res.data || [];
if(data && data.length>0){
// workResource.entpCourseWorkList = entpcourseworkres.data;
// workResource.entpCourseWorkTotal = entpcourseworkres.data.length;
data.forEach(item=> {
if(entpcourseworkres.data&&entpcourseworkres.data.length>0){
workResource.entpCourseWorkList = entpcourseworkres.data;
workResource.entpCourseWorkTotal = entpcourseworkres.data.length;
workResource.entpCourseWorkList.forEach(item=> {
if (item.worktype == '选择题') {
item.worktype = '单选题'
}
})
//
processList(data);
//workResource.entpCourseWorkList.push(...data);
workResource.entpCourseWorkList = data;
//
if (pageParams.value.isFirst) {
pageParams.value.isFirst = false;
pageParams.value.originCount = workResource.entpCourseWorkList.length;
pageParams.value.total = parseInt(res.msg);
paginationParams.pageNum = Math.ceil(parseInt(res.msg)/paginationParams.pageSize);
//console.log('first->', pageParams.value, paginationParams);
}
//
processList(workResource.entpCourseWorkList);
}else{
workResource.entpCourseWorkList = [];
workResource.entpCourseWorkTotal = 0
}
pageParams.value.loading = false;
});
})
}
//
@ -585,14 +500,10 @@ const getQueryFromEvaluationclue = () => {
}
if (clueres.rows[i].childlist != '') {
const tmpJson = '['+clueres.rows[i].childlist+']';
if (isJson(tmpJson)){
clueres.rows[i].childArray = JSON.parse(tmpJson);
for (var j=0; j<clueres.rows[i].childArray.length; j++) {
clueres.rows[i].childArray[j].title = clueres.rows[i].childArray[j].title.replace(/(<([^>]+)>)/ig, '');
}
clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']');
for (var j=0; j<clueres.rows[i].childArray.length; j++) {
clueres.rows[i].childArray[j].title = clueres.rows[i].childArray[j].title.replace(/(<([^>]+)>)/ig, '');
}
} else {
clueres.rows[i].childArray = {};
}
@ -655,7 +566,61 @@ const handleClassWorkQuizAdd = (fromsrc, entpcourseworkid) => {
* 预览资源
*/
const prevRead = async (item) => {
proxy.$refs.prevReadMsgDialogRef.openDialog(item);
prevReadMsg.visible = true;
prevReadMsg.type = item.worktype;
if (item.worktype==='课标研读'){
//
listEvaluation({itemkey: 'subject', edusubject: userStore.edusubject, edustage: userStore.edustage}).then(res => {
// TODO -
console.log("课标研读-还未接入",res);
// this.versionObj = res.rows[0];
// //
// if (this.versionObj.fileurl.length > 0) {
// readFile({cluelink: this.versionObj.fileurl}).then(fileres => {
// this.versionObj.bookdata = fileres;
// this.activeTeachResOfStandard = item;
// })
// }
})
}
if (item.worktype==='目标设定'){
// TODO -
// this.activeTeachResOfStandard = item;
}
if (item.worktype==='教材研读'){
// TODO -
// getEvaluation(this.courseObj.evalrootid).then(bookres => {
// this.versionObj = bookres.data;
// if (this.versionObj.fileurl.length > 0) {
// readFile({cluelink: this.versionObj.fileurl}).then(fileres => {
// this.versionObj.bookdata = fileres;
// this.activeTeachResOfStandard = item;
// })
// }
// })
}
if (item.worktype==='框架梳理'){
flowData.value = {};
const { chapterId } = await useGetHomework(props.bookobj.node)
// this.entpcourseid = chapterId
let queryParams = {
entpcourseid: chapterId,
ppttype: '教材分析',
parentid: item.id,
title: '逻辑框架建构',
filetype: 'draw'
}
listEntpcoursefile(queryParams).then(response=>{
if (response.rows.length == 0) {
return;
}
flowData.value = JSON.parse(response.rows[0].datacontent)
})
}
if (item.worktype==='学科定位'){
// TODO -
// this.activeTeachResOfStandard = item;
}
};
/**
* 添加到作业
@ -663,7 +628,7 @@ const prevRead = async (item) => {
//
const handleClassWorkAddOfResource = (work) => {
//
let arrSole = classWorkForm.chooseWorkLists.filter((item) => {
let arrSole = chooseWorkLists.value.filter((item) => {
return item.worktype === classWorkForm.worktype
})
//
@ -672,29 +637,25 @@ const handleClassWorkAddOfResource = (work) => {
return;
}
//
let arr = classWorkForm.chooseWorkLists.filter((item) => {
let arr = chooseWorkLists.value.filter((item) => {
return item.id === work.id
})
if (arr.length===0) {
work.score = 1;
classWorkForm.chooseWorkLists.push(work);
chooseWorkLists.value.push(work);
}
};
/**
* 右侧资源删除作业 非习题list
* 删除作业
*/
const deleteClassWorkAddOfResource = (work) => {
classWorkForm.chooseWorkLists.filter((item, index) => {
chooseWorkLists.value.filter((item, index) => {
if (item.id === work.id) {
classWorkForm.chooseWorkLists.splice(index, 1);
chooseWorkLists.value.splice(index, 1);
return;
}
})
}
/** 右侧资源删除按钮 习题list */
const handleClassWorkFormQuizRemove = (index) =>{
classWorkForm.quizlist.splice(index, 1);
}
@ -752,8 +713,8 @@ const handleClassWorkSave = async () => {
let canvasJson = proxy.$refs.boardref.getCanvasJson()
let canvasBase64 = await proxy.$refs.boardref.getCanvasBase64()
//
cform.worktag = classWorkForm.question;
cform.title = classWorkForm.title;
// cform.worktag = question.value;
cform.title = question.value;
cform.workcodes = JSON.stringify({json: canvasJson, base64: canvasBase64});
cform.entpcourseworklist = JSON.stringify([{'id':-1, 'score': '10'}]);
try {
@ -763,13 +724,21 @@ const handleClassWorkSave = async () => {
classWorkForm.worktype = "课堂展示";
classWorkForm.uniquekey = props.propsformobj.uniquekey, //
classWorkForm.title = "";
classWorkForm.question = "";
classWorkForm.quizlist = [], //
//
classWorkForm.chooseWorkLists = []; // list
classWorkForm.whiteboardObj = ''; // ? //
chooseWorkLists.value = [];
whiteboardObj.value = ''; // ? //
// refresh the list
//
// this.getClassWorkAllList();
// TODO
//TODO 3
// this.newWorkSpace = false;
// this.newWorkSpaceEdit = false;
// this.workEdit = false;
boardLoading.value = false
})
} finally {
@ -778,7 +747,7 @@ const handleClassWorkSave = async () => {
}
else if(classWorkForm.worktype === "常规作业"){
fileLoading.value = true
cform.workcodes = JSON.stringify(classWorkForm.fileHomeworkList);
cform.workcodes = JSON.stringify(fileHomeworkList.value);
cform.entpcourseworklist = JSON.stringify([{'id':-2, 'score': '10'}]);
try {
addClassworkReturnId(cform).then(() => {
@ -790,9 +759,19 @@ const handleClassWorkSave = async () => {
classWorkForm.quizlist = [], //
//
classWorkForm.chooseWorkLists = []; // list
classWorkForm.whiteboardObj = ''; // ? //
classWorkForm.fileHomeworkList = []; // list
chooseWorkLists.value = [];
whiteboardObj.value = ''; // ? //
fileHomeworkList.value = []; // list
// refresh the list
//
// this.getClassWorkAllList();
// TODO
//TODO 3
// this.newWorkSpace = false;
// this.newWorkSpaceEdit = false;
// this.workEdit = false;
fileLoading.value = false
})
@ -809,7 +788,7 @@ const handleClassWorkSave = async () => {
ll.push({'id': classWorkForm.quizlist[i].id, 'score': classWorkForm.quizlist[i].score});
}
}else if( classWorkForm.worktype === "框架梳理") {
classWorkForm.chooseWorkLists.filter((item) => {
chooseWorkLists.value.filter((item) => {
if (item.worktype === classWorkForm.worktype) {
ll.push({'id':item.id, 'score': item.score});
}
@ -833,29 +812,24 @@ const handleClassWorkSave = async () => {
classWorkForm.quizlist = [], //
//
classWorkForm.chooseWorkLists = [];
classWorkForm.whiteboardObj = ''; // ? //
chooseWorkLists.value = [];
whiteboardObj.value = ''; // ? //
// refresh the list
//
// this.getClassWorkAllList();
})
}
// TODO
if(props.isback){
//
router.back();
}else{
//
router.push({ path: '/classTaskAssign' });
//TODO 3
// this.newWorkSpace = false;
// this.newWorkSpaceEdit = false;
// this.workEdit = false;
})
}
}
});
};
/**
* 编辑作业内容
* @param cform 表单数据
*/
const editWork = async (cform) =>{
//
cform.id= classWorkForm.id;
@ -870,7 +844,7 @@ const editWork = async (cform) =>{
//
}else {
if (classWorkForm.chooseWorkLists.length == 0) {
if (chooseWorkLists.value.length == 0) {
//
ElMessage.error('请先添加作业资源!');
return;
@ -919,178 +893,91 @@ const editWork = async (cform) =>{
}
}
// 3.-
}
else if (classWorkForm.worktype=='框架梳理') {
// 1.workidid
const wevalres = await listClassworkeval({'workid': classWorkForm.id});
if (wevalres.rows.length == 0) {
ElMessage.error('未找到原框架梳理任务,请或退出重试');
return;
// 3.
// const evalid = this.homeworkLesson.length==0 ? '' : this.homeworkLesson.length==1 ? this.homeworkLesson[0] : this.homeworkLesson[1];
// const formObj = {
// id: this.currentTask.id,
// uniquekey: this.classWorkForm.uniquekey, // []
// // warn:
// //evalid = evalid, // []
// }
// let res = await updateClasswork(cform);
let res = await updateClasswork(cform);
if (res.code == 200) {
ElMessage.success('更新成功');
//
router.back()
}
// 2.
let needUplEval = false;
if (classWorkForm.chooseWorkLists.length !== props.propsformobj.chooseWorkLists.length) {
needUplEval = true;
}else {
//
needUplEval = classWorkForm.chooseWorkLists.some(cur =>
!props.propsformobj.chooseWorkLists.some(last =>
last.id === cur.id && last.score === cur.score
)
);
}
if (needUplEval) {
const uplParams = {
id: wevalres.rows[0].id,
entpcourseworkid: classWorkForm.chooseWorkLists[0].id,
score: classWorkForm.chooseWorkLists[0].score,
}
//
let res = await updateClassworkeval(uplParams);
}
}
else if (classWorkForm.worktype=='课堂展示') {
let canvasJson = proxy.$refs.boardref.getCanvasJson()
let canvasBase64 = await proxy.$refs.boardref.getCanvasBase64()
cform.workcodes = JSON.stringify({json: canvasJson, base64: canvasBase64});
cform.worktag = classWorkForm.question;
}
else if (classWorkForm.worktype=='常规作业') {
// 1. (, )
cform.workcodes = JSON.stringify(classWorkForm.fileHomeworkList);
}
// else if (classWorkForm.worktype=='') {
// // 1.workidid
// const wevalres = await listClassworkeval({'workid': this.currentTask.id});
// if (wevalres.rows.length == 0) {
// ElMessage.error('退');
// return;
// }
// 3.
let res = await updateClasswork(cform);
if (res.code == 200) {
ElMessage.success('更新成功');
//
router.back()
}
}
// // 2.
// let needUplEval = false;
// if (this.chooseWorkLists.length !== this.currentTask.entpcourseworklistarray.length) {
// needUplEval = true;
// }else {
// //
// needUplEval = this.chooseWorkLists.some(cur =>
// !this.currentTask.entpcourseworklistarray.some(last =>
// last.id === cur.id && last.score === cur.score
// )
// );
// }
// if (needUplEval) {
// const uplParams = {
// id: wevalres.rows[0].id,
// entpcourseworkid: this.chooseWorkLists[0].id,
// score: this.chooseWorkLists[0].score,
// }
// let res = await updateClassworkeval(uplParams);
// }
/**
* 查看试题详细信息
* @param row 单题数据
*/
const showExamAnalyseDrawer = (row) => {
nextTick(() => {
const activeParams = {
activeExam: row,
}
proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams);
})
}
// }else if (classWorkForm.worktype=='') {
const tableRef = ref();
const getPaginationList = ( page, limit ) => {
paginationParams.pageNum = page;
paginationParams.pageSize = limit;
//console.log(page, limit)
handleQueryFromEntpCourseWork(0);
//
tableRef.value.setScrollTop(0);
}
const pageLoad = async() => {
console.log("加载中...")
paginationParams.pageNum ++ ,
//paginationParams.pageSize = 2,
await handleQueryFromEntpCourseWork(0);
}
// }else if (classWorkForm.worktype=='') {
// // 1. (, )
// classWorkParams.workcodes = JSON.stringify(this.fileHomeworkList);
// }
const initPageParams = () => {
//
workResource.entpCourseWorkList = [];
workResource.entpCourseWorkTotal = 0
// // 3.
// let res = await updateClasswork(cform);
// this.fileHomeworkList = [];
// this.chooseWorkLists = [];
//
pageParams.value.loading = false;
pageParams.value.isFirst = true;
pageParams.value.originCount = 0;
pageParams.value.total = 0;
// // 4.
// //
// // this.getClassWorkAllList();
//
paginationParams.pageNum = 1;
paginationParams.pageSize = 10;
// this.classWorkForm.worktype = "";
// this.newWorkSpace = false;
// this.newWorkSpaceEdit = false;
// this.workEdit = false;
}
onMounted(async() => {
//
const name = userStore.edustage + userStore.edusubject;
const jyCT = await JYApiListCT(proxy, name);
if (jyCT.length == 0) {
ElMessage.error('获取题型失败!');
return;
}
entpCourseWorkTypeList.value = jyCT;
//
entpCourseWorkYearList.value = JYApiListOriginYear();
entpCourseWorkGroupList.value = await JYApiListSO(proxy, name);
onMounted(() => {
})
// const refreshData = () => {
// console.log("")
// //
// initPageParams();
// //
// handleQueryFromEntpCourseWork(0);
// //
// getQueryFromEvaluationclue();
// //
// getEntpCourseWorkPointList();
// }
// //
// const debounceQueryData = debounce(throttle(refreshData, 1000), 1000);
//
const debounceQueryData = debounce(() => {
console.log("防抖 加载数据中...")
//
initPageParams();
watch(() => props.bookobj.levelSecondId, (newVal) => {
console.log(props.bookobj,'课程选择')
//
handleQueryFromEntpCourseWork(0);
//
getQueryFromEvaluationclue();
//
getEntpCourseWorkPointList();
}, 1000);
watch(() => props.propsformobj.uniquekey, (newVal) => {
console.log(props.propsformobj,'propsformobj')
if(props.propsformobj.uniquekey){
classWorkForm.uniquekey = props.propsformobj.uniquekey?cloneDeep(props.propsformobj.uniquekey):''; //
}
})
watch(
[
() => props.bookobj.levelSecondId,
() => props.bookobj.levelFirstId
],
([newLevelSecondId, newLevelFirstId], [oldLevelSecondId, oldLevelFirstId]) => {
if(props.bookobj.node.edusubject == '英语' && props.bookobj.node.edustage == '高中'){
if(newLevelFirstId != oldLevelFirstId){
console.log(props.bookobj,'高中英语-课程选择')
debounceQueryData();
}
else{
//
workResource.entpCourseWorkList = [];
return;
}
}
else{
console.log(props.bookobj,'课程选择')
debounceQueryData();
}
})
</script>
<style lang="scss" scoped>
@ -1178,47 +1065,6 @@ watch(
box-sizing: border-box;
background-color: rgb(231, 231, 231)
}
.infinite-list-wrapper{
height: 100%;
text-align: center;
overflow: auto;
.infinite-list {
padding: 0;
margin: 0;
list-style: none;
.infinite-list-item {
display: flex;
align-items: center;
//justify-content: center;
//height: 50px;
//background: var(--el-color-primary-light-9);
padding: 10px;
border-top: 1px solid #eee;
//color: var(--el-color-primary);
}
.infinite-list-item:hover {
background-color: #F3F5F8;
}
.infinite-list-item + .list-item {
margin-top: 10px;
}
}
.infinite-list-loading{
padding: 10px 0;
border-top: 1px solid #eee;
color: red;
}
.infinite-list-noMove{
padding: 10px 0;
border-top: 1px solid #eee;
color: #999;
}
}
}
</style>
<style src="@/assets/styles/JYStyle.css"></style>

View File

@ -1,9 +1,9 @@
<template>
<el-row class="c-warp" :gutter="10">
<el-col class="left" :span="10">
<el-col class="left" :span="12">
<el-collapse class="c-item" v-model="activeTopic" accordion>
<template v-for="(item, index) in dataList">
<el-collapse-item class="collapse-item" :name="index+1" :id="'collapse-'+(index+1)" @click="clickItem(index)">
<el-collapse-item class="collapse-item" :name="index+1" :id="'collapse-'+(index+1)">
<template #title>
<el-popover :width="500" placement="right">
<p>{{item.def?.titletext}}</p>
@ -54,13 +54,13 @@
</template>
</el-collapse>
</el-col>
<el-col class="right" :span="14">
<el-col class="right" :span="12">
<div class="c-item">
<!-- <div class="title">提交情况</div>
<div class="title">答题情况</div>
<div class="respond">
<el-space wrap> -->
<el-space wrap>
<!-- <template v-for="it in 11"> -->
<!-- <template v-for="(item, index) in dataList">
<template v-for="(item, index) in dataList">
<el-card shadow="hover" class="card-warp">
<div class="card-body">
<el-progress type="dashboard" :color="colorArr" :width="80" :percentage="ratio_2(item)" />
@ -70,41 +70,7 @@
</el-card>
</template>
</el-space>
</div> -->
<div class="title">试题详情</div>
<!-- 习题训练 -->
<el-card class="item-card">
<el-row>
<el-col :span="24" style="padding: 10px">
<!-- 题源题目标题题目选项 -->
<span>{{ activeExam.worktag }}</span>
<span v-html="activeExam.titleFormat"></span>
<div :span="24" style="padding: 12px 6px 2px" v-html="activeExam.workdescFormat"></div>
<!-- 折叠 详情分析解答 -->
<div class="demo-collapse">
<el-collapse v-model="activeExamFlag" class="custom-collapse">
<el-collapse-item title="详情分析解答" name="1">
<el-row style=" padding: 4px; border: 2px dotted;">
<template #default="scope">
<el-col :span="3" style="padding: 4px 0px"><em>答案</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.workanswerFormat"></el-col>
<el-col :span="3" style="padding: 4px 0px"><em>分析</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.method"></el-col>
<el-col :span="3" style="padding: 4px 0px"><em>解答</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.analyse"></el-col>
<el-col :span="3" style="padding: 4px 0px"><em>点评</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.discuss"></el-col>
<!-- <el-col :span="21" style="padding: 4px 0px" v-html="dataList[activeTopic-1].def.discuss"></el-col> -->
</template>
</el-row>
</el-collapse-item>
</el-collapse>
</div>
</el-col>
</el-row>
</el-card>
</div>
</div>
</el-col>
</el-row>
@ -118,9 +84,7 @@ import { ref, defineExpose, onMounted, reactive, computed, watch, nextTick, watc
// import * as elementPlus from 'element-plus' // ElMessage ElMessageBox
let colorArr = [] // --
// const attrs = useAttrs() // props
const activeTopic = ref(1) //
const activeExam = ref({}) //
const activeExamFlag = ref(['1']) //
const activeTopic = ref(0) //
let dataList = ref([]) //
let studentList = ref([]) //
const props = defineProps({ // defineProps
@ -150,12 +114,7 @@ colorArr = [
]
// === ===
onMounted(() => {
//activeTopic.value = dataList.value.map((_, index) => index + 1);
if (dataList.value[activeTopic.value-1].def != null && dataList.value[activeTopic.value-1].def != undefined) {
activeExam.value = dataList.value[activeTopic.value-1].def;
}
})
onMounted(() => {})
// === (methods) ===
@ -224,13 +183,8 @@ const initData = () => {
})
}
else if (o.worktype == '填空题') { //
//console.log('->', o.title);
let title = o.title.replace(/_{3,}/g, '_____'); //3-10线5
let regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA-->
if (title.indexOf('_____') != -1) {
regex = /_{5}/g // <!--BA-->xxx<!--EA-->
}
children = (title||'').match(regex).map((v,i) => {
const regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA-->
children = (o.title||'').match(regex).map((v,i) => {
const def = `填空项 ${i+1}`
//const code = '(&emsp;)'
const code = '(略)', txt=v
@ -323,24 +277,12 @@ const getStudentName = id => studentList.value.length && (studentList.value.find
// -
const getActive = ind => activeTopic.value != ind
// -
// -
const clickInfo = async ind => {
activeTopic.value = activeTopic.value != ind ? ind : 0
setTimeout(() => {scrollToElement('collapse-' + ind)}, 300);
// elementPlus.ElMessage.warning('!')
}
//
const clickItem = async (index) => {
if (index > dataList.length-1 ) {
return
}
if (dataList.value[index].def == null || dataList.value[index].def == undefined) {
return;
}
activeExam.value = dataList.value[index].def;
}
// === ===
//
const scrollToElement = id => {
@ -369,14 +311,8 @@ watchEffect(() => { initData() })
background: #F2F3F5;
height: 100%;
margin: 0 !important;
.left{
padding-left: 0 !important;
height: 100%;
}
.right{
padding-right: 0 !important;
height: 100%;
}
.left{padding-left: 0 !important;}
.right{padding-right: 0 !important;}
.c-item{
padding: 10px;
background: #fff;
@ -409,7 +345,6 @@ watchEffect(() => { initData() })
}
.t-left{width: 160px;text-align: left;}
.c-respond{
text-align: left;
.el-tag{margin: 0 5px;}
}
}
@ -424,7 +359,7 @@ watchEffect(() => { initData() })
margin-bottom: 10px;
}
.respond{
/* height: calc(100% - 65px);*/
height: calc(70vh - 65px);
overflow: auto;
.el-space{padding: 5px;}
.card-warp{
@ -438,17 +373,6 @@ watchEffect(() => { initData() })
flex-direction: column;
}
}
.item-card {
max-width: 100%;
margin-bottom: 10px;
text-align: left;
font-size: 18px;
:deep(.el-collapse-item__content){
font-size: 18px;
}
}
}
}
</style>

View File

@ -14,40 +14,31 @@
&nbsp;|&nbsp; 截止时间{{ item.deaddate }} &nbsp;|&nbsp;{{ tabactive }}
</div>
</div>
<div v-if=" tabactive == '待批改' " class="class-reserv-item-progress">
<el-progress :text-inside="true" :stroke-width="26" :percentage="item.finishpercent" :color="'#000fff'" style="cursor: pointer"></el-progress>
<span>
已交(
<span>
<span v-if="item.workdataresultcount!=0" style="color:#000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultcount }}</span>
<span v-if="item.workdataresultcount==0">{{ item.workdataresultcount }}</span>
/{{ item.workdataresultsum }}
</span>
)
</span>
</div>
<div v-if=" tabactive == '待批改' " class="class-reserv-item-progress">
<el-progress :text-inside="true" :stroke-width="26" :percentage="item.teacherCorrectionProgress" :color="'#ff7f00'" style="cursor: pointer"></el-progress>
<span>
已批阅(<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherrationgcount}}</span>)
</span>
</div>
<!-- TODO 练习次数引用次数 这里随便的假数据-->
<div v-if=" tabactive == '已批改' " class="class-reserv-item-tool">
<span style="color:#000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultsum }}</span>
<span>练习次数</span>
</div>
<div v-if=" tabactive == '已批改' " class="class-reserv-item-tool">
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherrationgcount?item.workdataresultsum - item.teacherrationgcount:item.workdataresultsum }}</span>
<span>引用次数</span>
</div>
<div class="class-reserv-item-tool">
<span>
<span v-if="item.workdataresultcount!=0" style="color:#000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultcount }}</span>
<span v-if="item.workdataresultcount==0">{{ item.workdataresultcount }}</span>
/{{ item.workdatacount }}</span>
<span>已交</span>
</div>
<div class="class-reserv-item-tool">
<!-- 总人数-已批阅人数 -->
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherrationgcount?item.workdatacount - item.teacherrationgcount:item.workdatacount }}</span>
<span>待批阅</span>
</div>
<div class="class-reserv-item-tool">
<span>
<!-- {{ item.averagetime?item.averagetime:0 }} -->
<!-- <span v-if=" item.averagetime<60 ">
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ item.averagetime }}</span>分钟
</span>
<span v-if=" item.averagetime==60 ">
<span style="color: #007fff; font-weight: 900; font-size: 15px">1</span>小时
</span>
<span v-if=" item.averagetime>60 ">
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ Math.floor(item.averagetime / 60)}}</span>小时
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ Math.floor(item.averagetime % 60)}}</span>分钟
</span> -->
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ item.averagetime }}</span>分钟
</span>
<span>平均用时</span>
@ -86,7 +77,6 @@ const props = defineProps({
border-radius: 10px;
padding: 10px 5px;
margin-bottom: 10px;
cursor: pointer;
.class-reserv-item-body {
flex: 1;
@ -106,11 +96,6 @@ const props = defineProps({
}
}
}
.class-reserv-item-progress {
width: 200px;
padding: 0 10px;
font-size: 14px;
}
.class-reserv-item-tool {
margin-left: 10px;
display: flex;

View File

@ -15,16 +15,10 @@
<!-- 标题 -->
<el-row style="align-items: center; margin-bottom: 0px; flex: 0 0 auto">
<el-col :span="12" style="padding-left: 20px; text-align: left;">
<div v-if="!isOpenLeftBook" class="unit-top-left" @click="onOpenLeftBook">
<i v-if="isback" class="iconfont icon-xiangzuo cursor-pointer" style="color: blue;" @click="goBack">返回上页</i>
<!-- <i v-if="!isCollapse" class="iconfont icon-xiangzuo" style="color: blue;"></i> -->
<span>作业设计</span>
<!-- <i v-if="isCollapse" class="iconfont icon-xiangyou" style="color: blue;"></i> -->
</div>
<div v-else class="unit-top-left">
<i class="iconfont icon-xiangzuo cursor-pointer" style="color: blue;" @click="goBack">返回上页</i>
<span>课程</span>
<span>{{bookTitle}}</span>
<div class="unit-top-left" @click="isCollapse = !isCollapse">
<i v-if="!isCollapse" class="iconfont icon-xiangzuo" style="color: blue;"></i>
<span>课程目录</span>
<i v-if="isCollapse" class="iconfont icon-xiangyou" style="color: blue;"></i>
</div>
</el-col>
<el-col :span="12">
@ -36,13 +30,13 @@
</el-col>
</el-row>
<!-- 作业类型:内容 -->
<task-type-view :bookobj="courseObj" :isback="isback" :propsformobj="classWorkForm" :isedit="isOpenLeftBook" style="flex: 1; overflow: hidden;"/>
<task-type-view :bookobj="courseObj" :propsformobj="classWorkForm" style="flex: 1; overflow: hidden;"/>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, toRaw,watch, reactive, getCurrentInstance } from 'vue'
import { onMounted, ref, toRaw,watch, reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import ChooseTextbook from '@/components/choose-textbook/index.vue'
@ -51,14 +45,8 @@ import { getCurrentTime } from '@/utils/date'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user
const route = useRoute();
const router = useRouter()
const { proxy } = getCurrentInstance()
const isback = ref(route.query.isBack?true:false);
const classtaskObj = route.query.classtaskObj;//
const bookTitle = ref(classtaskObj? JSON.parse(classtaskObj).bookName: '');//
const isOpenLeftBook = ref(classtaskObj? JSON.parse(classtaskObj).id ? true : false: false ); //
const isCollapse = ref(isOpenLeftBook.value?true:false); // : false
// ---------------------------------------------------
const classWorkForm = reactive({
id: classtaskObj? JSON.parse(classtaskObj).id : '', // id
@ -66,12 +54,9 @@ const classWorkForm = reactive({
title: classtaskObj? JSON.parse(classtaskObj).title : '', //
worktype: classtaskObj? JSON.parse(classtaskObj).worktype : '', //
quizlist: classtaskObj? JSON.parse(classtaskObj).quizlist : [], //
chooseWorkLists: classtaskObj? JSON.parse(classtaskObj).chooseWorkLists : [],// list
fileHomeworkList: classtaskObj? JSON.parse(classtaskObj).fileHomeworkList : [],// list
whiteboardObj: classtaskObj? JSON.parse(classtaskObj).whiteboardObj : '',//
question: classtaskObj? JSON.parse(classtaskObj).question : '',//
})
const isCollapse = ref(false)
const courseObj = reactive({
// : id,id,id,
@ -82,6 +67,7 @@ const courseObj = reactive({
node: null, //
//
})
// ---------------------------------------------------
@ -108,32 +94,16 @@ const getData = (data) => {
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId}))
}
const onOpenLeftBook = () => {
if(isOpenLeftBook.value){
//
proxy.$modal.msgError(`当前为编辑状态不可选择课程!`);
}else{
//
// isCollapse.value = !isCollapse.value
}
}
// ---
const goBack = () =>{
router.back()
}
onMounted(() => {
//
if(classtaskObj&&JSON.parse(classtaskObj).bookObj){
const bookobj = JSON.parse(classtaskObj).bookObj;
courseObj.textbookId = bookobj.bookObj //
courseObj.levelFirstId = bookobj.levelFirstId //
courseObj.levelSecondId = bookobj.levelSecondId //
courseObj.coursetitle = bookobj.node.itemtitle // (/)
courseObj.node = bookobj.node; //
}
// init()
// sourceStore.getCreate()
})
// const init = () => {
// classWorkForm.uniquekey = userStore.edusubject+'-' + getCurrentTime('MMDD')+'-'+(1);
// }
</script>
<style lang="scss" scoped>
@ -150,10 +120,8 @@ onMounted(() => {
width: 300px;
min-height: 100%;
}
.cursor-pointer {
cursor: pointer;//
}
.unit-top-left {
cursor: pointer;
.icon-xiangzuo {
margin-right: 5px;
@ -186,4 +154,3 @@ onMounted(() => {
}
}
</style>
<style src="@/assets/styles/JYStyle.css"></style>

View File

@ -3,7 +3,7 @@
<div class="teachClassTask_header">
<div style="font-size: 18px; display: flex; flex-wrap: nowrap">
<div style="flex: 1">
{{ classWorkAnalysis.title }}完成情况
{{ classWorkAnalysis.title }}答题情况
<el-tag :type="classWorkAnalysis.workclass" size="large" style="height: 25px">{{
classWorkAnalysis.worktype
}}</el-tag>
@ -23,13 +23,13 @@
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'quizStats' ? 'success' : ''"
@click="workHandle('quizStats')"
>逐题讲评</el-button
>作业概况</el-button
>
<el-button
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'report' ? 'success' : ''"
@click="handleClassOverviewOpen('report')"
>训练报告</el-button
>作业报告</el-button
>
</el-button-group>
</div>
@ -57,8 +57,8 @@
style="margin-bottom: 1px"
@change="tableRadioChange"
>
<el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''"/>
<el-radio-button :value="0" :label="'未交' + '' + tableRadio.num0 + ''"/>
<el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''" />
<el-radio-button :value="0" :label="'未交' + '' + tableRadio.num0 + ''" />
</el-radio-group>
<!-- 学生列表classWorkAnalysis.classworkdata; 已交未交tableRadio.list -->
<el-table
@ -71,37 +71,27 @@
>
<el-table-column type="index" label="序号" width="52" reserve-selection align="center" />
<el-table-column label="姓名" prop="studentname" width="100" align="center" />
<el-table-column :label="tableRadio.value==0?'提交状态':'提交时间'" prop="updatedate" width="170" align="center">
<el-table-column label="提交时间" prop="updatedate" width="170" align="center" />
<el-table-column label="批阅状态" prop="teacherRating" align="center" width="120" sortable>
<template #default="scope">
<span v-if="tableRadio.value==0" style="color: #2196f3">未提交</span>
<span v-if="tableRadio.value==1">{{ scope.row.updatedate }}</span>
</template>
</el-table-column>
<el-table-column :label="tableRadio.value==0?'':'得分'" prop="score" width="80" align="center" >
<template #default="scope" v-if="tableRadio.value==1">
<span style="color: #2196f3">{{scope.row.getScore || 0}}</span>
</template>
</el-table-column>
<el-table-column label="批阅状态" prop="rating" align="center" width="120" sortable>
<template #default="scope">
<template v-if="scope.row.rating == 0">
<span v-if="tableRadio.value==1" style="color: #2196f3">待批阅</span>
</template>
<template v-if="scope.row.teacherRating == 0"
><span style="color: #2196f3">待批阅</span></template
>
<!-- 1- 2-优减 3- 4-良减 5- -->
<template v-if="scope.row.rating == 1"
><el-tag type="danger">完美</el-tag></template
<template v-if="scope.row.teacherRating == 1"
><el-tag type="danger"></el-tag></template
>
<template v-if="scope.row.rating == 2"
><el-tag type="danger"></el-tag></template
<template v-if="scope.row.teacherRating == 2"
><el-tag type="danger">-</el-tag></template
>
<template v-if="scope.row.rating == 3"
><el-tag type="warning"></el-tag></template
<template v-if="scope.row.teacherRating == 3"
><el-tag type="warning"></el-tag></template
>
<template v-if="scope.row.rating == 4"
><el-tag type="info">及格</el-tag></template
<template v-if="scope.row.teacherRating == 4"
><el-tag type="info">-</el-tag></template
>
<template v-if="scope.row.rating == 5"
><el-tag type="info">不及格</el-tag></template
<template v-if="scope.row.teacherRating == 5"
><el-tag type="info"></el-tag></template
>
</template>
</el-table-column>
@ -136,15 +126,15 @@
</div>
</div>
<!-- 逐题讲评 -->
<!-- 作业概况 -->
<div v-else-if="classWorkAnalysis.view == 'quizStats'" style="width: 100%;">
<quiz-stats :active-data="classWorkActiveData" style="width: 100%;height: 100%;"/>
</div>
<!-- 训练报告-->
<!-- 作业报告-->
<div v-else-if="classWorkAnalysis.view == 'report'" style="width: 100%;overflow-y: scroll">
<!-- <ClassOverview :table-list="overviewData" :eval-id="courseObj.evalid"></ClassOverview> -->
<ClassOverview :active-data="classWorkActiveData" :table-list="overviewData" style="width: 100%;"></ClassOverview>
<ClassOverview :table-list="overviewData" style="width: 100%;"></ClassOverview>
</div>
</div>
@ -157,13 +147,11 @@ import useUserStore from '@/store/modules/user'
import { ElMessage } from 'element-plus'
import { getCurrentTime, getAfterMinutes } from '@/utils/date'
import { processList } from '@/hooks/useProcessList'
import ItemDialogScore from '@/views/classTask/container/classTask/item-dialog-score.vue'
import ItemDialogScore from '@/views/classTask/container/item-dialog-score.vue'
// zdg:
import quizStats from '@/views/classTask/container/quizStats.vue'
import ClassOverview from '@/views/classTask/container/classOverview.vue'
import {sessionStore} from '@/utils/store'
import Chat from '@/utils/chat' // im
import {sessionStore} from '@/utils/tool'
const { proxy } = getCurrentInstance()
const emit = defineEmits(['cle-click'])
@ -179,7 +167,7 @@ const classWorkAnalysis = reactive({
entpcourseworklistarray: [], // ID
})
const tableRadio = reactive({
value: 1, //
value: '1', //
list: [], // list
num1: 0, //
num0: 0 //
@ -187,7 +175,7 @@ const tableRadio = reactive({
const loading_dt_table = ref(false)
const isopen_dtwk_table = ref(false)
// zdg:
// zdg:
const classWorkActiveData = reactive({
quizlist: [], //
studentList: [], // -
@ -210,38 +198,35 @@ const classWorkAnalysisScore = reactive({
// form.name = newValue.label
// }
// )
const openDialog = (data, isInit=true) => {
console.log(data, '点击的item完成情况')
const openDialog = (data) => {
console.log(data, '点击的item答题情况')
if (isInit) {
classWorkAnalysis.title = data.uniquekey ? data.uniquekey + '--' : ''
classWorkAnalysis.worktype = data.worktype
classWorkAnalysis.workclass = data.workclass
//
tableRadio.list = []
tableRadio.value = 1
tableRadio.num0 = 0
tableRadio.num1 = 0
classWorkAnalysis.title = data.uniquekey ? data.uniquekey + '--' : ''
classWorkAnalysis.worktype = data.worktype
classWorkAnalysis.workclass = data.workclass
//
tableRadio.list = []
tableRadio.value = '1'
tableRadio.num0 = 0
tableRadio.num1 = 0
classWorkAnalysis.open = true
//
classWorkAnalysis.view = 'studentview'
// ID
classWorkAnalysis.entpcourseworklistarray = data.entpcourseworklistarray
//
classWorkAnalysis.activeStudentQuizlist = []
//
classWorkAnalysis.activeQuizAnalysisData = []
classWorkAnalysis.row = data
window.test = this
// zdg:
const studentArr = data.classworkdatastudentids
? JSON.parse(`[${data.classworkdatastudentids}]`)
: []
classWorkActiveData.studentList = studentArr
}
classWorkAnalysis.open = true
//
classWorkAnalysis.view = 'studentview'
// ID
classWorkAnalysis.entpcourseworklistarray = data.entpcourseworklistarray
//
classWorkAnalysis.activeStudentQuizlist = []
//
classWorkAnalysis.activeQuizAnalysisData = []
classWorkAnalysis.row = data
window.test = this
// zdg:
const studentArr = data.classworkdatastudentids
? JSON.parse(`[${data.classworkdatastudentids}]`)
: []
classWorkActiveData.studentList = studentArr
/** 学生完成情况分析--获取作业学生list数据 */
getClassWorkStudentList(data.id)
@ -258,7 +243,6 @@ const openDialog = (data, isInit=true) => {
}
classWorkAnalysis.quizlist = idres.rows
classWorkActiveData.quizlist = idres.rows // zdg: 使
processList(classWorkActiveData.quizlist);
//
// + , pageSize: 100
@ -345,7 +329,7 @@ const getClassWorkStudentList = (rowId) => {
}
// 0
response.rows[i].rating = 0
response.rows[i].teacherRating = 0
//
if (
@ -354,44 +338,35 @@ const getClassWorkStudentList = (rowId) => {
response.rows[i].classworkevallist != 'null'
) {
// , : "{\"id\":172910, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
// .replace(/"(\[.*\])"/g, '$1'); eg: "feedcontent\":\"[{\"name\":\"Bliss.jpg\",\"url\":\"https://wzyzoss.3b8daa474.jpg\"}]\",
// .replace(/"(\[.*\])"/g, '$1'); eg: "feedcontent\":\"[{\"name\":\"Bliss.jpg\",\"url\":\"https://wzyzoss.3b8daa474.jpg\"}]\",
// json .replace(/""/g, '"') eg: """"
response.rows[i].classworkevallist = escapeHtmlQuotes(response.rows[i].classworkevallist)
//console.log('classworkevallist', response.rows[i].classworkevallist)
console.log('学生完成情况分析classworkevallist', response.rows[i].classworkevallist)
const evalarray = JSON.parse('[' + response.rows[i].classworkevallist + ']')
var scoingCount = 0
var feedcount = 0
let score = 0
for (var e = 0; e < evalarray.length; e++) {
if (evalarray[e].feedcontent != '') {
feedcount++
//
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
scoingCount++
score += evalarray[e].score;
evalarray[e].teacherRating = evalarray[e].score
}
}
}
const allTeacherRating = evalarray.reduce((acc, cur) => acc + cur.teacherRating, 0) //
//console.log(evalarray, 'evalarray------------------------------------')
console.log(evalarray, 'evalarray------------------------------------')
if (feedcount > 0) {
// : /*100
response.rows[i].scoingRate = ((score / allTeacherRating) * 100).toFixed(0) + '%'
response.rows[i].getScore = allTeacherRating
response.rows[i].scoingRate = ((scoingCount / feedcount) * 100).toFixed(0) + '%'
} else {
response.rows[i].scoingRate = '0%'
response.rows[i].getScore = 0
}
// :
if (evalarray[0].rating != '') {
response.rows[i].rating = evalarray[0].rating
response.rows[i].teacherRating = evalarray[0].rating
}
} else {
response.rows[i].scoingRate = '0%'
response.rows[i].getScore = 0
}
}
classWorkAnalysis.classworkdata = response.rows
@ -401,61 +376,14 @@ const getClassWorkStudentList = (rowId) => {
tableRadio.list =
classWorkAnalysis.classworkdata &&
classWorkAnalysis.classworkdata.filter((item) => item.finishtimelength != '0')
tableRadio.value = 1
tableRadio.value = '1'
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length
tableRadio.num1 = tableRadio.list.length
//
teacherCriticism();
})
.catch(() => {
loading_dt_table.value = false
})
}
/**
* 自动批阅判断
* 已交 作业类型为习题训练
*/
const teacherCriticism = ()=>{
// list
if(tableRadio.value == 1 && classWorkAnalysis.worktype == '习题训练'){
//
tableRadio.list = tableRadio.list.map((item) => {
return {
...item,
rating : item.rating || checkWorkType(item)
}
})
}
}
const checkWorkType = (item) => {
//
const subType = classWorkActiveData.quizlist.map(item => item.worktype)
const objectiveQuestion = ['单选题','多选题','判断题']
let rating = 0
//
if(subType.every(item => objectiveQuestion.includes(item))){
// scoingRate
const score = extractedNumber(item.scoingRate)
if(0<=score && score<=59){
rating = 5
}else if(60<=score && score<=69){
rating = 4
}else if(70<=score && score<=79){
rating = 3
}else if(80<=score && score<=99){
rating = 2
}else{
rating = 1
}
}
return rating
}
//
const extractedNumber = (score) => {
const match = score.match(/\d+/);
return match ? parseInt(match[0], 10) : null;
}
/** 2、查看某一个学生的学习任务完成详情*/
const getStudentClassWorkDataDetail = (row) => {
//
@ -477,15 +405,15 @@ const getStudentClassWorkDataDetail = (row) => {
wevalres.rows[w].score = wevalres.rows[w].score ? wevalres.rows[w].score : 0
// html
// wevalres.rows[w].rightanswer =
// wevalres.rows[w].rightanswer != '' && wevalres.rows[w].rightanswer != null
// ? wevalres.rows[w].rightanswer.replace(/<[^>]+>/g, '')
// : wevalres.rows[w].rightanswer
// // html
// wevalres.rows[w].feedcontent =
// wevalres.rows[w].feedcontent != '' && wevalres.rows[w].feedcontent != null
// ? wevalres.rows[w].feedcontent.replace(/<[^>]+>/g, '')
// : wevalres.rows[w].feedcontent
wevalres.rows[w].rightanswer =
wevalres.rows[w].rightanswer != '' && wevalres.rows[w].rightanswer != null
? wevalres.rows[w].rightanswer.replace(/<[^>]+>/g, '')
: wevalres.rows[w].rightanswer
// html
wevalres.rows[w].feedcontent =
wevalres.rows[w].feedcontent != '' && wevalres.rows[w].feedcontent != null
? wevalres.rows[w].feedcontent.replace(/<[^>]+>/g, '')
: wevalres.rows[w].feedcontent
if (classWorkAnalysis.row.worktype == '常规作业') {
wevalres.rows[w].feedcontent = JSON.parse(wevalres.rows[w].feedcontent)
@ -526,7 +454,7 @@ const getStudentClassWorkDataDetail = (row) => {
}
})
.catch(() => {
console.log('获取完成情况失败')
console.log('获取答题情况失败')
ElMessage({
type: 'warning',
message: '未获取到答题信息!'
@ -538,7 +466,7 @@ const onClassWorkScoreSubmit = () => {
console.log('批改后返回的方法')
loading_dt_table.value = true
isopen_dtwk_table.value = false
// 1table- classWorkAnalysis.classworkdata- classWorkAnalysis.activeStudentQuizlist
// 1table- classWorkAnalysis.classworkdata- classWorkAnalysis.activeStudentQuizlist
// -
classWorkAnalysis.classworkdata = []
classWorkAnalysis.activeStudentQuizlist = []
@ -572,16 +500,14 @@ const tableRadioChange = (e) => {
isopen_dtwk_table.value = false;
console.log(e,'??????')
console.log("学生列表:", classWorkAnalysis.classworkdata)
if(e==1){
if(e=='1'){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength != '0')
tableRadio.value = 1;
tableRadio.value = '1';
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
tableRadio.num1 = tableRadio.list.length;
//
teacherCriticism();
}else if(e==0){
}else if(e=='0'){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength == '0')
tableRadio.value = 0;
tableRadio.value = '0';
tableRadio.num0 = tableRadio.list.length;
tableRadio.num1 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
}
@ -597,7 +523,7 @@ const escapeHtmlQuotes = (str) => {
return result;
}
//#region
//#region
// -
const workHandle = (type) => {
// ui
@ -605,7 +531,6 @@ const workHandle = (type) => {
classWorkAnalysis.view = type
const isClose = type != 'quizStats' && !! classWorkActiveData.timerId
const isOpen = type == 'quizStats' && !classWorkActiveData.timerId
//
if(type == 'quizStats') {
getWorkFeedList();
}
@ -632,17 +557,13 @@ const getWorkFeedList = async() =>{
//#endregion
//#regin
//#regin
/*
author: yangws
time: 2024-8-06 16:35:33
function: 训练报告的处理
function:作业报告的处理
*/
const handleClassOverviewOpen = (type) =>{
//
if(type == 'report') {
getWorkFeedList();
}
// ui
isopen_dtwk_table.value = false;
classWorkAnalysis.view = type
@ -653,7 +574,6 @@ const handleClassOverviewOpen = (type) =>{
response.rows.forEach(item => {
let rightAnswer = 0
let answers = 0
let score = 0
if(!item.classworkevallist) return
// 使
let replacedString = item.classworkevallist.replace(/""/g, "\"");
@ -672,18 +592,12 @@ const handleClassOverviewOpen = (type) =>{
//
if(itemTopic.feedcontent === itemTopic.rightanswer){
rightAnswer ++
score += itemTopic.score
itemTopic.teacherRating = itemTopic.score
}
}
})
const allTeacherRating = allTopic.reduce((acc, cur) => acc + cur.teacherRating, 0)
rightAnswer > 0?item.scoingRate = (score/allTeacherRating * 100).toFixed(0):item.scoingRate = ''
item.getScore = allTeacherRating
rightAnswer > 0?item.scoingRate = (rightAnswer/answers * 100).toFixed(0):item.scoingRate = ''
}else{
item.scoingRate = ''
item.getScore = 0
}
//
const point = allTopic.reduce((acc, cur) => {
@ -712,79 +626,33 @@ const closeDialog = () => {
emit('cle-click')
}
// im
const msgHandle = (msg) => {
const { type, data } = msg
switch(type) {
case 'TIMAddRecvNewMsgCallback': // data=[]
{
(data||[]).forEach(o => {
const msgArr = o?.message_elem_array||[]
msgArr.forEach(info => {
const msgType = info?.elem_type // TIMElemType
const msgData = !!info.text_elem_content ? JSON.parse(info.text_elem_content)||'' : ''
//
//console.log('msgData->', msgData);
if (msgData.msgKey == "finishHomework"){
//
const data = JSON.parse(localStorage.getItem('teachClassWorkItem'));
//console.log('data->', data);
openDialog(data, false);
}
})
})
}
break
}
}
const reloadTimer = ref(0); // id
const cutid = ref(0); // id
onMounted(() => {
const data = JSON.parse(localStorage.getItem('teachClassWorkItem'));
// const data = sessionStore.get('teachClassWorkItem');
// const data = JSON.parse(localStorage.getItem('teachClassWorkItem'));
const data = sessionStore.get('teachClassWorkItem');
// console.log(data,'????????????????????' )
if(data){
openDialog(data)
}
//
cutid.value = data.id;
isReloadTimer();
// im
if (!Chat.imChat) {
Chat.init(true, true, msgHandle);
} else {
Chat.listenMsg(msgHandle);
}
})
const isReloadTimer = () =>{
clearInterval(reloadTimer.value) //
// id
reloadTimer.value = setInterval(() => {
const data = JSON.parse(localStorage.getItem('teachClassWorkItem'));
if(cutid.value != data.id){
cutid.value = data.id;
openDialog(data)
}
}, 1000)
}
watch(classWorkAnalysis, (newVal, oldVal) => {
if(newVal.view != 'quizStats'){
console.log('关闭zdg: 定时执行')
clearInterval(classWorkActiveData.timerId) //
clearInterval(classWorkActiveData.timerId) //
}
})
onUnmounted(() => {
clearInterval(classWorkActiveData.timerId) //
clearInterval(reloadTimer.value) // id
clearInterval(classWorkActiveData.timerId) //
})
// defineExpose({
// })
</script>
<style src="@/assets/styles/JYStyle.css"></style>
<style scoped lang="scss">
.teachClassTask{
height: 100%;
@ -824,12 +692,100 @@ onUnmounted(() => {
overflow: hidden;
.classwork-score{
/* overflow-y: auto; */
// overflow-y: auto;
height: 100%;
}
}
}
}
// .clwk_dialog {
// }
// .clwk_dialog {
// display: flex;
// justify-content: center;
// overflow: hidden;
// }
// .clwk_dialog .el-dialog {
// margin: 0 auto !important;
// height: 85%!important;
// overflow: hidden;
// }
// .clwk_dialog .el-dialog__header {
// /* position: absolute;
// top: 0;
// left: 0; */
// width: 100%!important;
// }
// .clwk_dialog .el-dialog__body {
// position: absolute;
// left: 0;
// top: 15px;
// bottom: 1px;
// right:0;
// padding:5px;
// z-index:1;
// display: flex;
// flex-direction: column;
// overflow: hidden;
// /* overflow:hidden;
// overflow-y: auto; */
// }
// .clwk_dialog .el-dialog__footer{
// position: absolute;
// bottom: 10px;
// right: 10px;
// }
// .clwk_dialog .classwork-score{
// overflow-y: auto;
// }
</style>
<style src="@/assets/styles/JYStyle.css"></style>
<style scoped>
/* .clwk_dialog {
display: flex;
justify-content: center;
overflow: hidden;
}
.clwk_dialog .el-dialog {
margin: 0 auto !important;
height: 85%!important;
overflow: hidden;
}
.clwk_dialog .el-dialog__header {
/* position: absolute;
top: 0;
left: 0;
width: 100%!important;
}
.clwk_dialog .el-dialog__body {
position: absolute;
left: 0;
top: 15px;
bottom: 1px;
right:0;
padding:5px;
z-index:1;
display: flex;
flex-direction: column;
overflow: hidden;
/* overflow:hidden;
overflow-y: auto;
}
.clwk_dialog .el-dialog__footer{
position: absolute;
bottom: 10px;
right: 10px;
}
.clwk_dialog .classwork-score{
overflow-y: auto;
} */
</style>

View File

@ -14,15 +14,13 @@
<div class="class-left flex">
<div class="class-name flex">
<span class="name">{{ item.uniquekey }}</span>
<!-- <el-tag class="tag" round :type="tagType(item.deaddate)" effect="dark" size="small">{{
getCurrentTime('YYYY-MM-DD HH:mm') > item.deaddate ? '已批改' : '待批改' }}</el-tag> -->
<el-tag class="tag" round :type="tagType(item.deaddate)" effect="dark" size="small">待批改</el-tag>
<el-tag :type="item.workclass" size="large">{{ item.worktype }}</el-tag>
<el-tag class="tag" round :type="tagType(item.deaddate)" effect="dark" size="small">{{
getCurrentTime('YYYY-MM-DD HH:mm') > item.deaddate ? '已结束' : '进行中' }}</el-tag>
</div>
<div class="class-time">{{ item.classcaption }} | 截止时间{{ item.deaddate }} </div>
</div>
<div class="class-right">
<div><span class="num">{{ item.workdataresultcount }}</span> / {{ item.workdataresultsum }}</div>
<div><span class="num">{{ item.workdataresultcount }}</span> / {{ item.workdatacount }}</div>
<div>已交</div>
</div>
</li>
@ -37,10 +35,8 @@ import { ref, onMounted, watch } from 'vue'
import useUserStore from '@/store/modules/user'
import { homeworklist } from '@/api/teaching/classwork'
import { getCurrentTime, getTomorrow } from '@/utils/date'
import {sessionStore} from '@/utils/store'
import {createWindow} from '@/utils/tool'
import {sessionStore, createWindow} from '@/utils/tool'
import { useToolState } from '@/store/modules/tool'
import {throttle,debounce } from '@/utils/comm'
const user = useUserStore().user
const toolState = useToolState();
@ -52,28 +48,12 @@ const getHomework = async () => {
loading.value = true
const { edustage, edusubject } = user
try {
const { rows } = await homeworklist({ edituserid: user.userId, edustage, edusubject, deaddate: getTomorrow(), status: '1', orderby: 'deaddate DESC', pageSize: 500 })
//
//homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getCurrentTime('YYYY-MM-DD HH:mm') < item.deaddate) //
// homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getTomorrow() <= item.deaddate) //
homeworkList.value = rows || [];
const { rows } = await homeworklist({ edituserid: user.userId, edustage, edusubject, deaddate: getTomorrow(), status: '1', orderby: 'uniquekey DESC', pageSize: 500 })
//
// homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getCurrentTime('YYYY-MM-DD HH:mm') < item.deaddate)
homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getTomorrow() <= item.deaddate) //
homeworkList.value.forEach((item) => {
// UI
if (item.worktype == '学习目标定位') {
item.workclass = 'success'
item.workcodesList = JSON.parse(item.workcodes)
} else if (item.worktype == '教材研读') {
item.workclass = 'primary'
} else if (item.worktype == '框架梳理') {
item.workclass = 'warning'
} else if (item.worktype == '学科定位') {
item.workclass = 'info'
} else if (item.worktype == '习题训练') {
item.workclass = 'danger'
} else {
item.workclass = ''
}
item.workdatacount = JSON.parse('[' + item.classworkdatastudentids + ']').length
//
if (item.entpcourseworklist != '') {
item.entpcourseworklistarray = JSON.parse(
@ -87,24 +67,18 @@ const getHomework = async () => {
loading.value = false
}
}
const debounceOpenWin = debounce(() => {
toolState.isTaskWin=true; //
createWindow('open-taskwin',{url:'/teachClassTask'}); //
}, 1000);
//
const onClickItem = (item) => {
console.log('防抖开启弹窗')
// sessionStore.set('teachClassWorkItem', item); // item
localStorage.setItem('teachClassWorkItem', JSON.stringify(item));
debounceOpenWin();
console.log('开启弹窗')
// itemDialogRef.value.openDialog(item)
//
sessionStore.set('teachClassWorkItem', item)
toolState.isTaskWin=true //
createWindow('open-taskwin',{url:'/teachClassTask'})
}
const tagType = (time) => {
return 'warning';
//return getCurrentTime('YYYY-MM-DD HH:mm') > time ? 'info' : 'warning'
return getCurrentTime('YYYY-MM-DD HH:mm') > time ? 'info' : 'warning'
}
@ -156,7 +130,6 @@ watch(
border-radius: 5px;
margin-bottom: 10px;
padding: 10px;
cursor: pointer;
.class-left {
flex-direction: column;

View File

@ -113,17 +113,17 @@ const menuList = [{
{
name: '作业设计',
icon: '#icon-zuoyesheji',
// isOuter: true,
// path: '/teaching/classtaskassign?titleName=&openDialog=newClassTask',
path: '/newClassTask',
isOuter: true,
path: '/teaching/classtaskassign?titleName=作业布置&openDialog=newClassTask',
// path: '/newClassTask',
id: '2-1'
},
{
name: '作业布置',
icon: '#icon-zuoyebuzhi',
// isOuter: true,
// path: '/teaching/classtaskassign?titleName=',
path: '/classTaskAssign',
isOuter: true,
path: '/teaching/classtaskassign?titleName=作业布置',
// path: '/classTaskAssign',
id: '2-2'
},
{
@ -183,10 +183,10 @@ const clickMenu = ({isOuter, path, disabled, id}) =>{
// ID
const { id, rootid } = sessionStore.get('subject.curNode')
if(fullPath.indexOf('?') == -1){
fullPath += `?unitId=${id}&bookId=${rootid}`
fullPath += `?unitId=${id}&bookeId=${rootid}`
}
else{
fullPath += `&unitId=${id}&bookId=${rootid}`
fullPath += `&unitId=${id}&bookeId=${rootid}`
}
}
fullPath = fullPath.replaceAll('//', '/')

View File

@ -17,40 +17,29 @@
<el-table-column align="center" prop="worktag" width="120"></el-table-column>
</el-table>
<!-- 试题详细信息 -->
<examDetailsDrawer ref="examDetailsDrawerRef"></examDetailsDrawer>
<!-- <el-drawer v-model="activeExamInfoDrawer" title="题目详情" :with-header="false" direction="rtl" size="60%">
<div style="height: calc(100% - 50px);">
<el-scrollbar style="background: #F3F5F8;border-radius: 8px;">
<el-row class="drawer-main">
<el-col :span="24">
<span>{{activeExam.worktag}}</span>
<span style="margin-left: 4px" v-html="activeExam.titleFormat" ></span>
</el-col>
<el-col :span="24" style="padding: 4px" v-html="activeExam.workdescFormat">
</el-col>
<el-col :span="3" class="drawer-main-col"><em>答案</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.workanswerFormat"></el-col>
<el-col :span="3" class="drawer-main-col"><em>分析</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.method"></el-col>
<el-col :span="3" class="drawer-main-col"><em>解答</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.analyse"></el-col>
<el-col :span="3" class="drawer-main-col" ><em>点评</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.discuss"></el-col>
</el-row>
</el-scrollbar>
</div>
<div class="drawer-footer">
<el-button type="primary" @click="activeExamInfoDrawer = false">关闭</el-button>
</div>
</el-drawer> -->
<!-- 试题详情 -->
<el-drawer v-model="activeExamInfoDrawer" title="题目详情" :with-header="false" direction="rtl" size="60%">
<el-row class="drawer-main">
<el-col :span="24">
<span>{{activeExam.worktag}}</span>
<span style="margin-left: 4px" v-html="activeExam.titleFormat" ></span>
</el-col>
<el-col :span="24" style="padding: 4px" v-html="activeExam.workdescFormat">
</el-col>
<el-col :span="3" class="drawer-main-col"><em>答案</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.workanswerFormat"></el-col>
<el-col :span="3" class="drawer-main-col"><em>分析</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.method"></el-col>
<el-col :span="3" class="drawer-main-col"><em>解答</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.analyse"></el-col>
<el-col :span="3" class="drawer-main-col" ><em>点评</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.discuss"></el-col>
</el-row>
</el-drawer>
</template>
<script setup>
import {ref, reactive, getCurrentInstance, nextTick} from 'vue'
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
const { proxy } = getCurrentInstance()
import {ref, reactive} from 'vue'
const props = defineProps({
listExamQuestion: {type: Array},
@ -61,12 +50,8 @@ const activeExamInfoDrawer = ref(false);
const activeExam = ref({});
const showExamAnalyseDrawer = (row) => {
nextTick(() => {
const activeParams = {
activeExam: row,
}
proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams);
})
activeExam.value = row;
activeExamInfoDrawer.value = true;
}
@ -104,17 +89,13 @@ const showExamAnalyseDrawer = (row) => {
.drawer-main{
margin: 1%;
padding: 2%;
padding: 1% 2%;
border: 2px dotted;
display: flex;
text-align: left;
.drawer-main-col{
padding: 10px 0px;
}
}
.drawer-footer{
padding-top: 15px;
display: flex;
justify-content: flex-end;
box-sizing: border-box;
}
</style>

View File

@ -58,8 +58,8 @@
v-else-if="curTask.viewkey=='考点分析' "
/>
<examMocks
v-else
<examMocks v-else
/>
</div>
</div>
@ -75,13 +75,12 @@ import { ArrowRight } from '@element-plus/icons-vue'
import useResoureStore from '@/views/resource/store'
import ChooseTextbook from '@/components/choose-textbook/index.vue'
import {listEntpcoursework, listEntpcourseworkNew} from '@/api/education/entpCourseWork'
import { processList } from '@/hooks/useProcessList'
import {processExamQuestion} from '@/utils/examQuestion/tool'
import { JYApiListCT} from "@/utils/examQuestion/jyeoo"
import examReview from './container/examReview.vue'
import pointAnalysis from './container/pointAnalysis.vue'
import examMocks from './container/examMocks.vue'
import { ElMessage } from 'element-plus'
const {proxy} = getCurrentInstance();
const sourceStore = useResoureStore();
@ -107,19 +106,6 @@ const listWorkType = ref([{
value: 0,
}]);
const getCourseWorkList = async (params) => {
const res = await listEntpcourseworkNew(params);
if(res.data == null) {
listExamQuestion.value = [];
// queryParams.total = 0
return;
}
listExamQuestion.value = res.data;
// queryParams.total = res.total;
//
processList(listExamQuestion.value);
}
/**
* @desc: 选中单元章节后的回调, 获取单元章节信息
* @return: {*}
@ -148,26 +134,27 @@ const getData = async (data) => {
// const res = await listEntpcoursework(params);
// listExamQuestion.value = res.rows;
// id,
// : id[/evaluation/bind]
if (curNode.value.bookId == null || curNode.value.bookId == '' || curNode.value.bookId == '0') {
listExamQuestion.value = [];
loading.value = false;
ElMessage.error("当前单元/章节下无试题");
return;
}
// +()
const params = {
eid: curNode.value.id,
workgroup: '1',
worktype: '999', // 使
worktype: '不限',
workTypeId: '0',
edusubject: curNode.value.edusubject,
edustage: curNode.value.edustage,
sectionName: curNode.value.itemtitle,
}
await getCourseWorkList(params);
const res = await listEntpcourseworkNew(params);
if(res.data == null) {
listExamQuestion.value = [];
// queryParams.total = 0
loading.value = false;
return;
}
listExamQuestion.value = res.data;
// queryParams.total = res.total;
//
processExamQuestion(listExamQuestion.value);
loading.value = false;
}
@ -215,13 +202,6 @@ const queryExamQuestionByParams = async () => {
// const res = await listEntpcoursework(params);
// listExamQuestion.value = res.rows;
if (curNode.value.bookId == null || curNode.value.bookId == '' || curNode.value.bookId == '0') {
listExamQuestion.value = [];
loading.value = false;
ElMessage.error("当前单元/章节下无试题");
return;
}
// +()
const params = {
eid: curNode.value.id,
@ -232,7 +212,17 @@ const queryExamQuestionByParams = async () => {
edustage: curNode.value.edustage,
sectionName: curNode.value.itemtitle,
}
await getCourseWorkList(params);
const res = await listEntpcourseworkNew(params);
if(res.data == null) {
listExamQuestion.value = [];
// queryParams.total = 0
loading.value = false;
return;
}
listExamQuestion.value = res.data;
// queryParams.total = res.total;
//
processExamQuestion(listExamQuestion.value);
loading.value = false;
}

View File

@ -1,333 +0,0 @@
<template>
<div class="book-wrap">
<el-scrollbar height="100%">
<div class="book-name flex" @click="dialogVisible = true">
<span>{{ curBook.data.itemtitle }}</span>
<i class="iconfont icon-xiangyou"></i>
</div>
<div class="book-list" v-loading="treeLoading">
<el-tree :data="treeData" accordion :props="defaultProps" node-key="id"
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
@node-click="handleNodeClick">
<template #default="{ node }">
<span :title="node.label" class="tree-label">{{ node.label }}</span>
</template>
</el-tree>
</div>
</el-scrollbar>
</div>
<!--弹窗 选择教材-->
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550"
style="border-radius: 10px; padding: 10px 15px;">
<template #header>
<div class="choose-book-header flex">
<span>切换教材</span>
<i class="iconfont icon-guanbi" @click="dialogVisible = false"></i>
</div>
</template>
<div class="textbook-container">
<el-scrollbar height="450px">
<div class="textbook-item flex" v-for="item in subjectList" :class="curBook.data.id == item.id ? 'active-item' : ''"
:key="item.id" @click="changeBook(item)">
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
<div v-else class="textbook-img">
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
</div>
<span class="book-name">{{ item.itemtitle }}</span>
</div>
</el-scrollbar>
</div>
</el-dialog>
</template>
<script setup>
import { onMounted, ref, nextTick, toRaw, reactive } from 'vue';
import { cloneDeep } from 'lodash'
import { listEvaluation } from '@/api/subject'
import { sessionStore } from '@/utils/store'
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
// emit
const emit = defineEmits(['nodeClick', 'changeBook'])
// List
const unitList = ref([])
const subjectList = ref([])
const dialogVisible = ref(false)
//
const treeData = ref([])
const defaultProps = {
children: 'children',
label: 'itemtitle',
class: 'textbook-tree'
}
//
const subjectParams = reactive(
{
edusubject: '科学',
edustage:'小学',
itemkey: 'version',
orderby: 'orderidx asc',
pageSize: 10000
}
)
//
const unitParams = reactive({
edusubject:'科学',
edustage:'小学',
itemgroup: 'textbook',
orderby: 'orderidx asc',
pageSize: 10000
})
//
const curBook = reactive({
data: {}
})
//
const curNode = reactive({
data:{}
})
const treeLoading = ref(false)
//
const defaultExpandedKeys = ref([])
//
const changeBook = (data) => {
curBook.data = data
treeData.value = getTreeData(data.id)
//
nextTick(() =>{
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
handleNodeClick(curNode.data)
})
//
setTimeout(() => {
dialogVisible.value = false
}, 100);
}
const getLastLevelData = (tree) => {
let lastLevelData = [];
//
function traverseTree(nodes) {
nodes.forEach((node) => {
//
if (node.children && node.children.length > 0) {
traverseTree(node.children);
} else {
//
lastLevelData.push(node);
}
});
}
//
traverseTree(tree);
//
return lastLevelData;
}
// id
const findParentByChildId = (treeData, targetNodeId) => {
//
//
for (let node of treeData) {
// ID
if (node.children && node.children.some(child => child.id === targetNodeId)) {
// ID ID
return node;
}
//
if (node.children) {
let parentNode = findParentByChildId(node.children, targetNodeId);
if (parentNode) {
return parentNode;
}
}
}
// null
return null;
}
const handleNodeClick = (data) => {
/**
* data : 当前节点数据
*/
let nodeData = cloneDeep(toRaw(data));
//label label
nodeData.label = nodeData.itemtitle
// null
let parent = {
id: nodeData.parentid,
label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle
}
const parentNode = nodeData.parentid ? parent : null
nodeData.parentNode = parentNode
let curData = {
textBook: {
curBookId: curBook.data.id,
curBookName: curBook.data.itemtitle,
curBookImg: BaseUrl + curBook.data.avartar,
curBookPath: curBook.data.fileurl
},
node: nodeData
}
// :electron-store
emit('nodeClick', curData)
}
//
const getTreeData = (bookId) =>{
// id
let data = unitList.value.filter(item => item.rootid == bookId && item.level == 1)
data.forEach( item => {
item.children = unitList.value.filter( item2 => item2.parentid == item.id && item2.level == 2)
})
return data
}
onMounted( async () => {
treeLoading.value = true
try{
//
const { rows } = await listEvaluation(subjectParams)
//
subjectList.value = rows
const res = await listEvaluation(unitParams)
unitList.value = [...res.rows]
//
curBook.data = rows[0]
// ""rows
treeData.value = getTreeData(rows[0].id)
nextTick(() =>{
//
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
handleNodeClick(curNode.data)
})
} finally{
treeLoading.value = false
}
})
</script>
<style lang="scss" scoped>
.book-wrap {
width: 300px;
height: 100%;
background: #ffffff;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
display: flex;
flex-direction: column;
position: relative;
.book-name {
background-color: #ffffff;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 45px;
padding: 0 15px;
z-index: 1;
justify-content: space-between;
align-items: center;
color: #3b3b3b;
cursor: pointer;
border-bottom: solid #f4f5f7 1px;
font-size: 15px;
font-weight: 600;
border-radius: 10px 10px 0 0;
}
.book-list {
padding: 45px 10px 0 10px;
flex: 1;
}
}
:deep(.choose-dialog) {
border-radius: 10px;
}
.choose-book-header {
justify-content: space-between;
font-size: 15px;
font-weight: bold;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
.textbook-container {
.textbook-item {
padding: 10px 20px;
align-items: center;
border-radius: 5px;
cursor: pointer;
.book-name {
margin-left: 20px;
color: #3b3b3b;
font-size: 13px;
}
&:hover {
background: #f4f7f9;
}
}
.active-item {
background-color: #f4f7f9;
.book-name {
color: #368fff;
font-weight: bold
}
}
.textbook-img {
width: 55px;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
}
}
:deep(.el-tree-node) {
.el-tree-node__content {
height: 40px;
border-radius: 10px;
&:hover {
background-color: #eaf3ff;
}
}
}
.tree-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
background-color: #eaf3ff !important;
color: #409EFF
}
</style>

View File

@ -1,25 +0,0 @@
<template>
<el-dialog v-model="model" class="preview-drawer" :title="row.fileShowName" :modal="true" :destroy-on-close="true" :with-header="false" :append-to-body="true"
width="60%">
<video style="margin: 0 auto;" :src="row.fileFullPath" controls autoplay></video>
</el-dialog>
</template>
<script setup>
const model = defineModel()
const props = defineProps({
row: {
type: Object,
default(){
return {}
}
},
})
</script>
<style scoped>
.header-close {
padding: 0;
cursor: pointer;
text-align: right;
}
</style>

View File

@ -1,259 +0,0 @@
<template>
<div class="page-resource flex">
<!-- 左侧 教材 目录 -->
<experimentBook @node-click="getData"/>
<div class="page-right">
<!-- 排序 -->
<div style="margin-left: 5px;margin-top: 10px;height: 45px;">
<el-form size="large">
<el-form-item label="排序:">
<div
:class="['score-circle', { 'active': active == item.active }]"
v-for="(item,index) in screenList" :key="index" @click="chooseItem(item)">
<el-text
:style="{fontWeight:'bold', color: active == item.active ? 'rgb(57, 184, 244)':'rgb(131,131,131)' }"
size="large">{{ item.title }}</el-text>
</div>
</el-form-item>
</el-form>
</div>
<div class="list-content">
<div class="list-container" v-loading="loading">
<div v-for="(item, index) in experimentList" :key="index" class="content">
<div class="content-list">
<!-- 封面 -->
<el-image style="width: 100%;border-radius: 8px;" :src="item.coverPic" fit="contain" @click="chooseVedio(item)"/>
</div>
<!-- 标题 -->
<div style="text-align: left;">
<el-text>{{ item.fileShowName }}</el-text>
</div>
<!-- 观看人数 -->
<!-- <div style="text-align: left;display: flex;align-items: center;">
<el-icon type="info"><View /></el-icon><el-text size="small" type="info">{{ item.nums }}</el-text>
</div> -->
</div>
</div>
<div class="pagination-box">
<el-pagination
v-model:current-page="query.pageNum"
v-model:page-size="query.pageSize"
:page-sizes="[20, 30, 50, 100]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="resultTotal"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</div>
<!-- 播放视频 -->
<VideoLog v-model="isShow" :row="curRow"></VideoLog>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import experimentBook from './components/experimentBook.vue';
import { View } from '@element-plus/icons-vue'
import { getSmarttalkPage } from '@/api/file/index'
import VideoLog from './components/VideoLog.vue'
//
const screenList = ref([
{
title: '最新发布',
active: 1,
}
])
const active = ref(1)
//
const resultTotal = ref(0)
//
const isShow = ref(false)
const curRow = ref({})
// loading
const loading = ref(false)
//
const experimentList = ref([])
//
const query = ref({
textbookId: '',
fileSource: '平台',
// mp3 ppt ...
fileSuffix: 'mp4',
fileFlags: "'素材'",
fileRoot: '资源',
fileName: '',
orderByColumn: 'uploadTime',
isAsc: 'desc',
pageNum: 1,
pageSize: 20,
levelFirstId: 0,
levelSecondId: 0
})
const getData = (data) => {
const { textBook, node } = data
if (node.parentNode) {
query.value.levelFirstId = node.parentNode.id
query.value.levelSecondId = node.id
} else {
query.value.levelFirstId = node.id
query.value.levelSecondId = ''
}
query.value.textbookId = node.rootid
getVideoList()
}
const chooseItem = (item) => {
active.value = item.active
}
//
const getVideoList = async () => {
loading.value = true
const res = await getSmarttalkPage(query.value)
loading.value = false
experimentList.value = [...res.rows]
resultTotal.value = res.total
}
const handleSizeChange = (limit) => {
query.pageNum = limit
getVideoList()
}
const handleCurrentChange = (page) => {
query.pageSize = page
getVideoList()
}
const chooseVedio = (item) => {
isShow.value = true
curRow.value = item
}
</script>
<style lang="scss" scoped>
.page-resource {
height: 100%;
padding: 10px 15px 0;
.page-right {
min-width: 0;
display: flex;
flex-direction: column;
flex: 1;
margin-left: 20px;
height: 100%;
background: #ffffff;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
}
.icon-jiahao {
font-size: 12px;
margin-right: 3px;
font-weight: bold;
}
}
.create-btn {
font-size: 13px;
padding: 5px 13px;
}
.list-content {
border-radius: 8px;
height: 90%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.list-container {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
}
.content {
border-radius: 8px;
// box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: calc(20%);
cursor: pointer;
transition: all 0.3s ease;
padding: 5px;
display: flex;
flex-direction: column;
// justify-content: space-between;
}
.content:hover {
transform: translateY(-4px);
// box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
}
.content-list{
height: 150px;
display: flex;
align-items: center
}
.item-content {
display: flex;
align-items: center;
}
.item-icon {
font-size: 24px;
color: #409eff;
margin-right: 16px;
}
.item-text {
flex: 1;
}
.item-title {
font-size: 16px;
font-weight: 500;
color: #303133;
margin-bottom: 4px;
font-weight: bold;
}
.title-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-bottom {
text-align: right;
}
/* 过渡动画 */
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
.score-circle {
background-color: #fff;
cursor: pointer;
margin-right: 5px;
width: auto;
text-align: center;
padding: 0 10px;
}
.score-circle.active {
background-color: rgb(218, 236, 255);
color: white;
}
.pagination-box {
display: flex;
justify-content: center;
height: 65px;
}
</style>

View File

@ -1,38 +0,0 @@
<template>
<PDF :url="pdfUrl" :isWin="true" v-if="pdfUrl" />
</template>
<script setup>
import { onMounted, ref } from 'vue'
import PDF from '@/components/PdfJs/index.vue'
import { useRoute } from 'vue-router';
import { getStaticUrl } from '@/utils/tool'
const route = useRoute();
const pdfUrl = ref('');
const loadPdfAnimation = (path) => {
console.log('书本地址====',path);
const timer = setTimeout(() => {
pdfUrl.value = path
clearTimeout(timer);
},2000)
}
onMounted(() => {
const pdfUrl1 = localStorage.getItem('PDF-TOOL-PATH')
const pdfUrl2 = localStorage.getItem('PDF-LOCAL-PATH')
console.log('tool-pdf 地址: ',pdfUrl1, pdfUrl2)
const bookpath = pdfUrl1 || pdfUrl2
// const filepath = import.meta.env.VITE_APP_RES_FILE_PATH + bookpath
// const isDev = process.env.NODE_ENV == 'development'
// if (isDev)
// pdfUrl.value = getStaticUrl(bookpath, 'user', 'selfFile', true) //
// else
// pdfUrl.value = getStaticUrl(bookpath, 'user', 'selfFile', true) //线
// const newpath = getStaticUrl(bookpath, 'user', 'selfFile', true)
loadPdfAnimation(bookpath)
// pdfUrl.value = filepath
// console.log('',bookpath);
})
</script>
<style>
</style>

View File

@ -28,7 +28,7 @@
<el-input v-model="ruleForm.phoneNumber" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="验证码" prop="Code" v-if="activeIndex==1">
<el-input style="width:60%" v-model="ruleForm.Code" :disabled="false" placeholder="请输入验证码" />
<el-input style="width:60%" v-model="ruleForm.Code" :disabled="true" placeholder="请输入验证码" />
<el-button type="primary" style="margin-left:10px" @click="sendcaptchaImg">发送验证码</el-button>
</el-form-item>
<el-form-item label="设置密码" prop="password" v-if="activeIndex==1">
@ -409,13 +409,11 @@ const sbmitImg=()=>{
type: type.value
}
sendCode(params).then(res=>{
isImg.value=false
if(res.code==200){
// ruleForm.Code=res.data
ElMessage.success(res.msg||'验证码-已发送')
ruleForm.Code=res.data
isImg.value=false
}
}).catch(err=>{
isImg.value=false
})
}else{
ElMessage.error('请根据图片输入验证码')

View File

@ -1,264 +0,0 @@
<template>
<div class="login-container">
<div class="box-item desc">
<div class="welcome">
<p>欢迎登录 {{ homeTitle }}</p>
</div>
<img class="welcome-img" :src="leftBg2" />
</div>
<div class="box-item login">
<WindowTools :is-has-max="false" />
<div class="login-title">账号登录</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item>
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="RegisterModel(1)">注册账号</a>
|
<a class="hover:text-sky-500" style="margin-left: 10px;" @click="RegisterModel(2)">忘记密码</a>
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import useUserStore from '@/store/modules/user'
import leftBg2 from '@/assets/images/login/left-bg2.png'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script>
<style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
padding: 23px 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 42px;
position: relative;
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 35px;
margin-top: 50px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style>

View File

@ -1,13 +1,273 @@
<template>
<ycLogin v-if="buildMode === 'yc'">
</ycLogin>
<defultLogin v-else>
</defultLogin>
<div class="login-container">
<div class="box-item desc">
<div class="welcome">
<p>欢迎登录 {{homeTitle}}</p>
</div>
<img class="welcome-img" :src="leftBg2" />
</div>
<div class="box-item login">
<WindowTools :is-has-max="false" />
<div class="login-title">账号登录</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input
v-model="loginForm.password"
autocomplete="on"
type="password"
placeholder="请输入密码"
/>
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe" >记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item>
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)"
>登录</el-button
>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="RegisterModel(1)">注册账号</a>
|
<a class="hover:text-sky-500" style="margin-left: 10px;" @click="RegisterModel(2)">忘记密码</a>
</div>
</el-form>
</div>
</div>
<el-dialog
v-model="showDownLoading"
width="500"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
align-center
>
<el-progress
:text-inside="true"
:stroke-width="22"
:percentage="downloadProp"
:show-text="false"
status="success"
/>
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import ycLogin from './yc-login.vue'
import defultLogin from './defult-login.vue'
const buildMode = import.meta.env.MODE
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import useUserStore from '@/store/modules/user'
import leftBg2 from '@/assets/images/login/left-bg2.png'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
//
const RegisterModel = type =>{
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
onMounted(() => {
localStorage.clear()
getCookie()
})
</script>
<style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
padding: 23px 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 42px;
position: relative;
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 35px;
margin-top: 50px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style>

View File

@ -1,287 +0,0 @@
<template>
<div class="login-container">
<div class="login-yc">
<img class="welcome-img" :src="buildMode === 'yc2?'?leftBg2:leftBg1" />
</div>
<div class="box-item login">
<WindowTools :is-has-max="false" />
<div style="display: flex;justify-content: center;"><img class="title-logo" :src="yclogo" /></div>
<div class="login-title">永川中小学</div>
<div class="login-title2">{{buildMode === 'yc2?'?'重庆永川虚拟仿真AI实训教学管理系统':'人工智能赋能科学素养与劳动技能系统'}}</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item style="margin-bottom: 20px;">
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="RegisterModel(1)">注册账号</a>
|
<a class="hover:text-sky-500" style="margin-left: 10px;" @click="RegisterModel(2)">忘记密码</a>
</div>
<div class="title-bottom">
重庆市永川区教育委员会
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import useUserStore from '@/store/modules/user'
import yclogo from '@/assets/images/login/yc-logo.png'
import leftBg1 from '@/assets/images/login/ycpeitu.png'
import leftBg2 from '@/assets/images/login/ycpeitu2.jpg'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
const buildMode = import.meta.env.MODE
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script>
<style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.login-yc{
width: 100%;
height: 100%;
img{
width: 100%;
height: 100%;
}
}
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 15px;
position: relative;
.title-logo{
width: 50px;
}
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
/*.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}*/
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 10px;
margin-top: 5px;
font-width: bold;
}
.login-title2 {
margin-bottom: 20px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
.title-bottom{
text-align: center;
width: 100%;
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style>

View File

@ -1,308 +0,0 @@
<template>
<div class="book-wrap">
<el-scrollbar height="100%">
<div class="book-name flex" @click="dialogVisible = true">
<span>{{ curBook.data.itemtitle }}</span>
<i class="iconfont icon-xiangyou"></i>
</div>
<div class="book-list" v-loading="treeLoading">
<el-tree :data="treeData" accordion :props="defaultProps" node-key="id"
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
@node-click="handleNodeClick">
<template #default="{ node }">
<span :title="node.label" class="tree-label">{{ node.label }}</span>
</template>
</el-tree>
</div>
</el-scrollbar>
</div>
<!--弹窗 选择教材-->
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550"
style="border-radius: 10px; padding: 10px 15px;">
<template #header>
<div class="choose-book-header flex">
<span>切换教材</span>
<i class="iconfont icon-guanbi" @click="dialogVisible = false"></i>
</div>
</template>
<div class="textbook-container">
<el-scrollbar height="450px">
<div class="textbook-item flex" v-for="item in subjectList" :class="curBook.data.id == item.id ? 'active-item' : ''"
:key="item.id" @click="changeBook(item)">
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
<div v-else class="textbook-img">
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
</div>
<span class="book-name">{{ item.itemtitle }}</span>
</div>
</el-scrollbar>
</div>
</el-dialog>
</template>
<script setup>
import { onMounted, ref, nextTick, toRaw, reactive } from 'vue';
import { cloneDeep } from 'lodash'
import { sessionStore } from '@/utils/store'
import { useGetSubject } from '@/hooks/useGetSubject'
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
// emit
const emit = defineEmits(['nodeClick', 'changeBook'])
let useSubject = null
const subjectList = ref([])
const dialogVisible = ref(false)
//
const treeData = ref([])
const defaultProps = {
children: 'children',
label: 'itemtitle',
class: 'textbook-tree'
}
//
const curBook = reactive({
data: {}
})
//
const curNode = reactive({
data:{}
})
const treeLoading = ref(false)
//
const defaultExpandedKeys = ref([])
//
const changeBook = (data) => {
curBook.data = data
treeData.value = useSubject.getTreeData(data.id)
//
nextTick(() =>{
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
handleNodeClick(curNode.data)
})
//
setTimeout(() => {
dialogVisible.value = false
}, 100);
}
const getLastLevelData = (tree) => {
let lastLevelData = [];
//
function traverseTree(nodes) {
nodes.forEach((node) => {
//
if (node.children && node.children.length > 0) {
traverseTree(node.children);
} else {
//
lastLevelData.push(node);
}
});
}
//
traverseTree(tree);
//
return lastLevelData;
}
// id
const findParentByChildId = (treeData, targetNodeId) => {
//
//
for (let node of treeData) {
// ID
if (node.children && node.children.some(child => child.id === targetNodeId)) {
// ID ID
return node;
}
//
if (node.children) {
let parentNode = findParentByChildId(node.children, targetNodeId);
if (parentNode) {
return parentNode;
}
}
}
// null
return null;
}
const handleNodeClick = (data) => {
/**
* data : 当前节点数据
*/
let nodeData = cloneDeep(toRaw(data));
//label label
nodeData.label = nodeData.itemtitle
// null
let parent = {
id: nodeData.parentid,
label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle
}
const parentNode = nodeData.parentid ? parent : null
nodeData.parentNode = parentNode
let curData = {
textBook: {
curBookId: curBook.data.id,
curBookName: curBook.data.itemtitle,
curBookImg: BaseUrl + curBook.data.avartar,
curBookPath: curBook.data.fileurl
},
node: nodeData
}
// :electron-store
emit('nodeClick', curData)
}
onMounted( async () => {
treeLoading.value = true
try{
useSubject = await useGetSubject()
subjectList.value = sessionStore.get('subject.bookList')
//
if(sessionStore.get('subject.curBook')){
curBook.data = sessionStore.get('subject.curBook')
}
else{
curBook.data = subjectList.value[0]
}
// ""
treeData.value = useSubject.getTreeData(curBook.data.id)
nextTick(() =>{
//
if(sessionStore.get('subject.curNode')){
defaultExpandedKeys.value = sessionStore.get('subject.defaultExpandedKeys')
curNode.data = sessionStore.get('subject.curNode')
}else{
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
}
handleNodeClick(curNode.data)
})
} finally{
treeLoading.value = false
}
})
</script>
<style lang="scss" scoped>
.book-wrap {
width: 300px;
height: 100%;
background: #ffffff;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
display: flex;
flex-direction: column;
position: relative;
.book-name {
background-color: #ffffff;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 45px;
padding: 0 15px;
z-index: 1;
justify-content: space-between;
align-items: center;
color: #3b3b3b;
cursor: pointer;
border-bottom: solid #f4f5f7 1px;
font-size: 15px;
font-weight: 600;
border-radius: 10px 10px 0 0;
}
.book-list {
padding: 45px 10px 0 10px;
flex: 1;
}
}
:deep(.choose-dialog) {
border-radius: 10px;
}
.choose-book-header {
justify-content: space-between;
font-size: 15px;
font-weight: bold;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
.textbook-container {
.textbook-item {
padding: 10px 20px;
align-items: center;
border-radius: 5px;
cursor: pointer;
.book-name {
margin-left: 20px;
color: #3b3b3b;
font-size: 13px;
}
&:hover {
background: #f4f7f9;
}
}
.active-item {
background-color: #f4f7f9;
.book-name {
color: #368fff;
font-weight: bold
}
}
.textbook-img {
width: 55px;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
}
}
:deep(.el-tree-node) {
.el-tree-node__content {
height: 40px;
border-radius: 10px;
&:hover {
background-color: #eaf3ff;
}
}
}
.tree-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
background-color: #eaf3ff !important;
color: #409EFF
}
</style>

View File

@ -1,64 +0,0 @@
<template>
<div style="padding: 10px;">
<el-dialog
v-model="dialogVisible"
width="350"
append-to-body
>
<div style="display: flex;justify-content: center;">
<ChooseTextbook @node-click="nodeClick" />
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click.stop="save">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, defineExpose,defineEmits } from 'vue'
import ChooseTextbook from './chooseTextbook.vue'
const emit = defineEmits(['onsuccess'])
const dialogVisible = ref(false)
const getNodeInfo = ref({})
const openDialog = () => {
dialogVisible.value = true
}
const getFullObj = (node) => {
const obj = []
const recursive = (currentNode) => {
//
if (currentNode.parentNode) {
obj.unshift({id: currentNode.id,title:currentNode.itemtitle})
recursive(currentNode.parentNode)
} else {
obj.unshift({id: currentNode.id,title:currentNode.itemtitle})
}
}
recursive(node)
return obj
}
const nodeClick = (data) => {
getNodeInfo.value = {
textbookId:data.node.rootid,
cataList:getFullObj(data.node)
}
console.log(getNodeInfo.value,'log')
}
const save = () => {
dialogVisible.value = false
emit('onsuccess', getNodeInfo.value)
}
defineExpose({
openDialog
})
</script>

View File

@ -1,719 +0,0 @@
<template>
<div class="ai-container">
<el-steps style="max-width:100% " :active="activeStep" align-center>
<el-step title="开始创作" />
<el-step title="输入主题" />
<el-step title="编辑大纲" />
<el-step title="选择模板" />
<el-step title="制作PPT" />
</el-steps>
<div class="card-box">
<el-card class="card1" v-if="activeStep == 0">
<el-tabs v-model="activeName" type="card" class="demo-tabs">
<el-tab-pane label="输入主题与要求" name="first">
<div style="padding: 20px;">输入主题</div>
<el-input type="textarea" v-model="inputTheme" :rows="3" placeholder="在此输入您的PPT主题..."
@keydown.enter.exact.prevent="addMessage" @keydown.enter.shift.exact.prevent="inputTheme += '\n'" />
<div style="padding: 20px;">具体生成要求</div>
<el-input type="textarea" v-model="inputRequire" :rows="3" placeholder="请输入对生成大纲的具体要求,比如要包含那些内容"
@keydown.enter.exact.prevent="addMessage" @keydown.enter.shift.exact.prevent="inputRequire += '\n'" />
<div>
<el-button style="margin:15px 0" type="primary" @click="addMessage">生成大纲</el-button>
</div>
</el-tab-pane>
<!-- <el-tab-pane label="上传文件并解析" name="second">
<el-upload action="#" :on-change="onFileChange" :before-upload="beforeUpload" :show-file-list="false">
<el-button type="primary">点击上传并解析文件</el-button>
<text>(支持 doc/docxpdfmdtxt 格式文档,不超过 20M,不超过 100W 字符)</text>
</el-upload>
<br/>
<div v-if="enableButton">{{docName}}文档已解析完毕</div>
<br/>
<div style="padding: 20px;">输入主题</div>
<textarea style="width:50vw" v-model="inputTheme" :rows="3" placeholder="在此输入您的PPT主题..."
@keydown.enter.shift.exact.prevent="inputTheme += '\n'">
</textarea>
<div style="padding: 20px;">具体生成要求</div>
<textarea style="width:50vw; margin:20px" v-model="docRequire" :rows="3"
placeholder="请输入对生成大纲的具体要求,比如要包含那些内容" @keydown.enter.shift.exact.prevent="inputRequire += '\n'">
</textarea>
<div>
<el-button style="padding:15px" type="primary" :disabled="!enableButton"
@click="chatDoc">生成大纲</el-button>
</div>
</el-tab-pane> -->
</el-tabs>
</el-card>
<el-card class="card2" v-if="activeStep == 1">
<div class="paragraphs">
{{ outputText }}
</div>
</el-card>
<el-card class="card3" v-if="activeStep == 2">
<div class="outline">
<el-scrollbar height="250px">
<el-row :gutter="20" class="outline-row">
<el-col :span="8">
<div v-for="item in firstArray" :key="item" class="item-with-dash">{{ item }}</div>
</el-col>
<el-col :span="16">
<div v-for="(item, index) in secondArray" :key="index">
<el-input v-model="item.value"></el-input>
</div>
</el-col>
</el-row>
</el-scrollbar>
</div>
<el-input type="textarea" v-model="fixRequire" :rows="3" placeholder="与AI对话告诉AI您想如何修改"
@keydown.enter.exact.prevent="fixOutline" @keydown.enter.shift.exact.prevent="inputRequire += '\n'" />
<br />
<el-button v-if="fixRequire.length > 0" style="padding:15px" type="primary"
@click="fixOutline()">发送修改</el-button>
<el-button style="margin-top:15px" type="primary" @click="combineOutline()">下一步</el-button>
</el-card>
<el-card v-if="activeStep == 3">
<div style="padding-bottom: 10px">ppt模板选择</div>
<div class="themes">
<div v-for="item in backGroundList" :key="item.key" :style="{
padding: '20px',
paddingRight: '30px',
paddingLeft: '30px',
margin: '10px',
backgroundColor: getBackgroundColor(item.key),
borderRadius: '10px',
borderBlock: '10px solid #e6e6e6'
}" @click="chooseBackground(item.key)" @mouseenter="changeCursor('pointer')" @mouseleave="changeCursor('default')">
{{ item.name }}
<br />
<img style="width: 150px; height: auto" :src="item.thumbnail" alt="" />
</div>
</div>
<el-row class="el-row">
<el-col :span="6" class="el-col">
<div class="grid-content-1">
<div>演讲备注</div>
<el-switch v-model="outlineData.is_card_note" />
</div>
</el-col>
<el-col :span="6" class="el-col">
<div class="grid-content-2">
<div>生成封面</div>
<el-switch v-model="outlineData.is_cover_img" />
</div>
</el-col>
<el-col :span="6" class="el-col">
<div class="grid-content-1">
<div>自动配图</div>
<el-switch v-model="outlineData.is_figure" />
</div>
</el-col>
<el-col :span="6" class="el-col">
<div class="grid-content-2">
<div>PPT作者名</div>
<el-input v-model="outlineData.author" style="width: 50%" />
</div>
</el-col>
</el-row>
<div>
<el-button style="margin-bottom: 5px;" type="primary" @click="outlineCreatePPT()">生成PPT</el-button>
</div>
</el-card>
<el-card v-if="activeStep == 4">
<el-progress :percentage="30" type="circle" v-if="percentage == 30"></el-progress>
<el-progress :percentage="70" type="circle" v-if="percentage == 70"></el-progress>
<el-progress :percentage="100" type="circle" v-if="percentage == 100"></el-progress>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { creatAIPPT } from '@/utils/talkFile'
import { ElMessage } from 'element-plus'
import {
getBackGround,
createPPT,
getProgress,
} from "@/utils/ppt-request.js";
import { uploadDoc, queryDocStatus } from "@/utils/aichat.js";
import CryptoJS from "crypto-js"
import { getSignature } from "@/utils/index.js";
let appId = "01ec9aa3";
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
let apikey = "39d05b269fa229f431a56c21794a8ea5"
let timestamp = Math.floor(Date.now() / 1000);
let signature = getSignature(appId, secret, timestamp);
const { ipcRenderer } = window.electron || {}
const outputText = ref(""); //
const stagingData = ref([]); //
const stagOutputText = ref(""); //
let extractedParts = ref([]) //
let firstArray = ref([]); //
let secondArray = ref([]); //
const backGroundList = ref([]);
const inputTheme = ref(""); //
const inputRequire = ref("") //
const activeStep = ref(0); //
const fixRequire = ref(""); //
const combined = ref('') // ppt
const treeData = ref([]);
const status = ref("init");
const percentage = ref(0);
const activeName = ref("first");
const getBackground = () => {
treeData.value = [];
getBackGround().then((res) => {
console.log(res);
backGroundList.value = res;
});
};
const getBackgroundColor = (key) => {
return outlineData.value.theme === key ? '#83e2b6' : '#f5f5f5';
};
const outlineData = ref({
query: '', // 8000
theme: 'auto', // ppt
author: 'AIX平台',
is_card_note: false, // ppt
is_cover_img: false, //
is_figure: false, //
}
)
const emit = defineEmits(['addSuccess'])
const props = defineProps({
currentNode: {
type: Object,
default: () => {}
},
uploadData: {
type: Object,
default: () => {}
}
})
//
function updateStagingData(role, newData) {
stagingData.value.push({ role: role, content: newData });
}
//ppt
const outlineCreatePPT = () => {
const newOutlineData = { ...outlineData.value, };
newOutlineData.query = combined.value;
createPPT(newOutlineData).then((res) => {
console.log(res, "正在生成中");
activeStep.value = 4
const checkProgress = () => {
getProgress(res.sid).then((response) => {
percentage.value = response.process;
if (response && response.pptUrl && response.pptUrl.length > 4) {
console.log('PPT',response)
// window.location.href = response.data.pptUrl;
//URLURL
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFiledf28bf990a4c40ffb7477ed4b65392c27232357022409613439/%E3%80%8A%E9%9D%99%E5%A5%B3%E3%80%8B%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BB%E4%B8%8E%E7%A0%94%E7%A9%B6.pptx"
creatAIPPT(props.currentNode.itemtitle + '.pptx',response.pptUrl, props.uploadData).then((res) => {
emit('addSuccess',res)
})
ElMessage.success("生成成功");
} else {
const sleepTime = 2000;
let remainingTime = sleepTime;
const intervalId = setInterval(() => {
remainingTime -= 100;
if (remainingTime <= 0) {
clearInterval(intervalId);
checkProgress();
}
}, 100);
}
});
};
checkProgress();
})
};
//
const addMessage = () => {
const themeValue = inputTheme.value;
const requireValue = inputRequire.value;
firstArray.value = []
secondArray.value = []
extractedParts.value = []
stagOutputText.value = ''
const combinedString = `请帮我生成一个ppt大纲主题为${themeValue}。具体内容要求为:${requireValue}。注意用三个等级大纲展示如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
updateStagingData("user", combinedString);
connectWebSocket(stagingData.value);
activeStep.value = 1
};
//ai
const fixOutline = () => {
outputText.value = '';
firstArray.value = [];
secondArray.value = [];
extractedParts.value = []
stagOutputText.value = ''
const fixValue = fixRequire.value;
updateStagingData('user', fixValue)
activeStep.value = 1
if (enableButton.value) {
uploadAndAskMainContent(stagingData.value)
} else { connectWebSocket(stagingData.value); }
fixRequire.value = ''
};
//
function extractAndRemove() {
let startIndex = -1;
let endIndex = -1;
for (let i = 0; i < stagOutputText.value.length; i++) {
const char = stagOutputText.value[i];
if (!isNaN(parseInt(char))) {
startIndex = i;
break;
}
}
if (startIndex !== -1) {
for (let j = startIndex; j < stagOutputText.value.length; j++) {
const char2 = stagOutputText.value[j];
if (char2 === '\n') {
endIndex = j;
break;
}
}
}
let extractedPart = '';
if (startIndex !== -1 && endIndex !== -1) {
extractedPart = stagOutputText.value.slice(startIndex, endIndex).replace(/\n/g, '');
extractedParts.value.push(extractedPart);
stagOutputText.value = stagOutputText.value.replace(extractedPart, '');
return true;
} else {
return false;
}
}
//
function startExtraction() {
stagOutputText.value = outputText.value
while (extractAndRemove()) { }
//
extractedParts.value.forEach(item => {
const parts = item.split(' ');
if (parts.length === 2) {
firstArray.value.push(parts[0]);
secondArray.value.push({ value: parts[1] });
}
})
}
//
function combineOutline() {
let tempCombined = '';
for (let i = 0; i < Math.max(firstArray.value.length, secondArray.value.length); i++) {
tempCombined += firstArray.value[i] || '';
tempCombined += secondArray.value[i] ? secondArray.value[i].value : '';
tempCombined += i < Math.max(firstArray.value.length, secondArray.value.length) - 1 ? ',' : '';
}
combined.value = tempCombined;
fixRequire.value = ''
activeStep.value = 3
}
let ttsWS
function connectWebSocket(data) {
outputText.value = ""; //
status.value = "ttsing";
return getWebsocketUrl().then((url) => {
ttsWS = new WebSocket(url);
ttsWS.onopen = () => {
webSocketSend(ttsWS, data);
};
ttsWS.onmessage = (e) => {
result1(e.data);
};
ttsWS.onerror = (e) => {
status.value = "error";
console.log("WebSocket error:", e);
};
ttsWS.onclose = () => {
status.value = "init";
};
});
}
function getWebsocketUrl() {
return new Promise((resolve, reject) => {
var apiKey = apikey;
var apiSecret = secret;
var url = "wss://spark-api.xf-yun.com/v4.0/chat";
var host = "spark-api.xf-yun.com";
var date = new Date().toGMTString();
var algorithm = "hmac-sha256";
var headers = "host date request-line";
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v4.0/chat HTTP/1.1`;
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
var authorization = CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(authorizationOrigin)
);
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
console.log(url);
resolve(url);
});
}
function webSocketSend(ws, data) {
const params = {
header: {
app_id: appId,
},
parameter: {
chat: {
domain: "4.0Ultra",
temperature: 0.5,
max_tokens: 1024,
},
},
payload: {
message: {
text: data,
},
},
};
ws.send(JSON.stringify(params));
}
function result1(resultData) {
let jsonData = JSON.parse(resultData);
outputText.value += jsonData.payload.choices.text[0].content;
const div = document.querySelector('.paragraphs');
if (div) {
div.scrollTop = div.scrollHeight;
}
if (jsonData.payload && jsonData.payload.usage) {
startExtraction() //
console.log(firstArray.value, secondArray.value)
activeStep.value = 2
updateStagingData("assistant", outputText.value) //
}
if (jsonData.header.code !== 0) {
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
}
}
const selectedFile = ref(null);
const maxSize = 1024 * 1024 * 20;
const allowedTypes = [
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"text/markdown",
"text/plain",
];
const fileType = "wiki";
const parseType = "AUTO";
const upfileId = ref('');
const docName = ref('')
const docTheme = ref('') // ppt
const docRequire = ref('') //
const onFileChange = (file) => {
console.log(file);
if (!allowedTypes.includes(file.raw.type)) {
console.error("不支持的文件类型");
return;
}
if (file.size > maxSize) {
console.error("文件过大");
return;
}
docName.value = file.raw.name
selectedFile.value = file.raw;
uploadFile();
};
const beforeUpload = (file) => {
return false;
};
const uploadFile = async () => {
if (!selectedFile.value) {
console.error("请先选择文件");
return;
}
const formData = new FormData();
formData.append("file", selectedFile.value);
formData.append("fileType", fileType);
formData.append("parseType", parseType);
for (const pair of formData.entries()) {
console.log(pair[0], pair[1]);
}
try {
const response = await uploadDoc(formData);
const upfileData = new FormData();
upfileId.value = response.data.data.fileId;
upfileData.append("fileIds", upfileId.value);
askdoc(upfileData);
} catch (error) {
console.error(error);
}
};
//
const askdoc = async (data) => {
const response = await queryDocStatus(data);
if (response.data.data && response.data.data.length > 0) {
let foundVectored = false;
for (const item of response.data.data) {
if (item.fileStatus === 'vectored') {
foundVectored = true;
break;
}
}
if (foundVectored) {
enableButton.value = true
} else {
const sleepTime = 2000;
let remainingTime = sleepTime;
const intervalId = setInterval(() => {
remainingTime -= 100;
if (remainingTime <= 0) {
clearInterval(intervalId);
askdoc(data);
}
}, 100);
}
}
};
//
function chatDoc() {
const docthemeValue = docTheme.value;
const docrequireValue = docRequire.value;
firstArray.value = []
secondArray.value = []
extractedParts.value = []
stagOutputText.value = ''
const combinedString = `请帮我生成一个ppt大纲主题为${docthemeValue}。具体内容要求为:${docrequireValue}。注意用三个等级大纲展示如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
updateStagingData("user", combinedString);
activeStep.value = 1
uploadAndAskMainContent(stagingData.value);
}
function chatByDoc(fileId, data) {
const wsUrl = `wss://chatdoc.xfyun.cn/openapi/chat?fileId=${fileId}&appId=${appId}&timestamp=${timestamp}&signature=${signature}`;
const ws = new WebSocket(wsUrl);
ws.onopen = () => {
const messageBody = {
fileIds: [fileId],
messages: data,
chatExtends: {
wikiPromptTpl:
"请将以下内容作为已知信息:\n<wikicontent>\n请根据以上内容回答用户的问题。\n问题:<wikiquestion>\n回答:",
wikiFilterScore: 0.82,
temperature: 0.5,
sparkWhenWithoutEmbedding: false,
},
};
ws.send(JSON.stringify(messageBody));
};
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
result2(response)
console.log("WebSocket 消息:", response);
};
ws.onerror = (error) => {
console.error("WebSocket 错误:", error);
};
}
function result2(resultData) {
const div = document.querySelector('.paragraphs');
if (div) {
div.scrollTop = div.scrollHeight;
}
if (resultData.status == 1) {
outputText.value += resultData.content
}
if (resultData.status == 2) {
startExtraction() //
activeStep.value = 2
updateStagingData("assistant", outputText.value) //
}
if (resultData.code !== 0) {
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
}
}
//
const uploadAndAskMainContent = async (data) => {
try {
// const formData = new FormData();
// formData.append(
// "url",
// "https://bjcdn.openstorage.cn/xinghuo/chatdocs/2024-09-13/dad35a4f-e7c1-4efc-b6df-cae763cb984b/39052b09-d154-419f-9832-20884adeb2f41726226211177.pdf"
// );
// formData.append("fileName", "test.pdf");
// formData.append("appId", "2ff2cc26");
// formData.append("secret", "YTMyZWFiOGVlYTc5ZGM5NGIwOTU3NWMx");
// // formData.append("fileType", "wiki");
// // formData.append("parseType", "AUTO");
// const uploadResp = await uploadDoc(formData);
// const fileId = uploadResp.data.data.fileId;
const fileId = upfileId.value;
chatByDoc(fileId, data);
} catch (error) {
console.error("上传或提问过程中发生错误:", error);
}
};
const enableButton = ref(false);
const chooseBackground = (data) => {
outlineData.value.theme = data
}
const changeCursor = (cursorStyle) => {
document.documentElement.style.cursor = cursorStyle;
};
onMounted(() => {
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx"
// creatAIPPT(props.currentNode.itemtitle + '.pptx',url, props.uploadData).then((res) => {
// emit('addSuccess',res)
// })
connectWebSocket("");
getBackground();
});
</script>
<style scoped>
.ai-container {
width: 100%;
background-color: #f5f7f6;
padding: 20px
}
.card-box {
margin-top: 20px;
}
.card1 {
padding: 0;
width: 100%;
}
.paragraphs {
white-space: pre-wrap;
text-align: left;
max-height: 60vh;
overflow-y: auto;
border: 1px solid #409EFF;
padding: 10px;
margin: 5px
}
.themes {
display: flex;
flex-wrap: wrap;
height: 250px;
overflow-y: auto;
}
.outline {
white-space: pre-wrap;
text-align: left;
border: 1px solid #409EFF;
padding: 10px;
outline-style: none;
/* margin: 5px */
}
.outline-row {
display: flex;
}
.outline-row>.el-col {
display: flex;
flex-direction: column
}
.outline-row>.el-col>div,
.outline-row>.el-col>div>.el-input {
flex: 1;
display: flex;
align-items: center;
padding: 3px;
}
.item-with-dash {
margin-left: 100px
}
.item-with-dash::after {
content: "";
border-bottom: 1px dashed #000;
flex-grow: 1;
margin-left: 4px;
}
.grid-content-1 {
border-radius: 4px;
background-color: #c2dbf3;
}
.grid-content-2 {
border-radius: 4px;
background-color: #f5f5f5;
}
.el-row {
padding: 20px
}
:deep(.el-card__body){
padding: 10px 15px;
}
</style>

Some files were not shown because too many files have changed in this diff Show More