Compare commits

..

No commits in common. "076a51010979f86f3c055aacdb952b06f19b9d42" and "cfac2086e6169a2a3d1091c2b7ec50569fe42ca6" have entirely different histories.

81 changed files with 299 additions and 7475 deletions

View File

@ -10,7 +10,6 @@ VITE_APP_BASE_API = '/dev-api'
VITE_APP_DOMAIN = 'file.ysaix.com'
VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api'
#VITE_APP_UPLOAD_API = 'http://192.168.2.52:7863'
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = AIx数字平台(测试版)
VITE_APP_TITLE = AIx数字平台
# 生产环境配置
VITE_APP_ENV = 'production'

View File

@ -10,7 +10,6 @@ module.exports = {
],
rules: {
'vue/require-default-prop': 'off',
'vue/multi-word-component-names': 'off',
'prettier/prettier': 'off'
'vue/multi-word-component-names': 'off'
}
}

View File

@ -25,7 +25,6 @@ export default defineConfig({
proxy: {
'/dev-api': {
target: 'http://27.128.240.72:7865',
// target: 'http://36.134.181.164:7863',
// target: 'http://192.168.2.52:7863',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '')

View File

@ -1,6 +1,6 @@
{
"name": "aix-win",
"version": "1.1.8",
"version": "1.1.6",
"description": "An Electron application with Vue",
"main": "./out/main/index.js",
"author": "example.com",
@ -25,18 +25,13 @@
"@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",
"@vueuse/core": "^10.11.0",
"cropperjs": "^1.6.2",
"crypto-js": "^4.2.0",
"echarts": "^5.5.1",
"electron-dl-manager": "^3.0.0",
"electron-log": "^5.1.7",
"electron-store": "8.0.0",
"electron-updater": "^6.1.7",
"element-china-area-data": "^6.1.0",
"element-plus": "^2.7.6",
"fabric": "^5.3.0",
"im_electron_sdk": "^8.0.5904",
@ -69,6 +64,5 @@
"vite-plugin-windicss": "^1.9.3",
"vue": "^3.4.30",
"windicss": "^3.5.6"
},
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1"
}
}

View File

@ -42,23 +42,19 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
let filePath = appRootFilePath + fileNewName
let uploadId = null
let isOn = false
let lastMTime = fs.statSync(filePath).mtime.getTime()
console.log(lastMTime)
setInterval(() => {
getFileMsg(filePath).then((msg) => {
if (msg !== lastMTime) {
lastMTime = msg
getFileMD5(filePath).then((md5New) => {
if (md5New !== md5) {
md5 = md5New
if (uploadId) {
clearTimeout(uploadId)
}
if (isOn === false) {
console.log(fileNewName)
e.reply('listen-file-change-on' + fileNewName)
isOn = true
}
//倒数十秒提交更改,十秒之内有继续修改则重置倒数
uploadId = setTimeout(() => {
console.log(223)
//执行更新,上传文件
let formData = new FormData()
formData.append('id', id)
@ -81,19 +77,12 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
console.error('Error uploading file:', err)
}
})
}, 5000)
}, 20000)
}
})
}, 1000)
}, 10000)
})
function getFileMsg(path) {
return new Promise((resolve, reject) => {
const stats = fs.statSync(path)
return resolve(stats.mtime.getTime())
})
}
function getFileMD5(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, dataFile) => {
@ -132,14 +121,13 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
return
}
getFileMsg(filePath).then((msg) => {
let time = new Date(lastModifyTime).getTime();
msg = parseInt(msg/1000)*1000;
if (msg == time) {
getFileMD5(filePath).then((localMd5) => {
if (localMd5 === md5) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: false, type: '' })
} else {
const stats = fs.statSync(filePath)
//如果线上时间大于线下时间,就需要从线上下载,否则则需要上传
let time = new Date(lastModifyTime)
if (time > stats.mtime.getTime()) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
} else if (time < stats.mtime.getTime()) {

View File

@ -141,11 +141,6 @@ async function createLinkWin(data) {
.then(() => {})
.catch((error) => {})
data.fullPath = data.fullPath.replaceAll('//', '/')
if (data.fullPath.indexOf('?') !== -1) {
data.fullPath += '&urlSource=smarttalk'
}else {
data.fullPath += '?urlSource=smarttalk'
}
linkWin[data.key].loadURL(data.fullPath)
linkWin[data.key].once('ready-to-show', () => {

View File

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8" />
<title>%VITE_APP_TITLE%</title>
<title>AIx智慧教育</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<!-- <meta
http-equiv="Content-Security-Policy"

View File

@ -187,14 +187,3 @@ export function getClassInfo(id) {
params: {id}
})
}
//加入班级
export function addClasses(data) {
return request({
url: '/smarttalk/audit/applyAddClass',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}

View File

@ -1,49 +0,0 @@
// 查询evaluation列表
import request from '@/utils/request'
// 查询反馈列表
// 查询classworkdata列表 班级作业列表
export function listClassworkdata(query) {
return request({
url: '/education/classworkdata/list',
method: 'get',
params: query
})
}
// 查询entpcoursework列表 课程作业列表
export function listEntpcoursework(query) {
return request({
url: '/education/entpcoursework/list',
method: 'get',
params: query
})
}
// 查询classworkeval列表 课堂作业列表
export function listClassworkeval(query) {
return request({
url: '/education/classworkeval/list',
method: 'get',
params: query
})
}
// 修改classworkeval
export function updateClassworkeval(data) {
return request({
url: '/education/classworkeval',
method: 'put',
data: data
})
}
// 修改classworkdata
export function updateClassworkdata(data) {
return request({
url: '/education/classworkdata',
method: 'put',
data: data
})
}

View File

@ -1,111 +0,0 @@
import request from '@/utils/request'
// 查询entpcoursework列表
export function listEntpcoursework(query) {
return request({
url: '/education/entpcoursework/list',
method: 'get',
params: query
})
}
// 查询entpcoursework详细
export function getEntpcoursework(id) {
return request({
url: '/education/entpcoursework/' + id,
method: 'get'
})
}
// 新增entpcoursework
export function addEntpcoursework(data) {
return request({
url: '/education/entpcoursework',
method: 'post',
data: data
})
}
// 修改entpcoursework
export function updateEntpcoursework(data) {
return request({
url: '/education/entpcoursework',
method: 'put',
data: data
})
}
// 删除entpcoursework
export function delEntpcoursework(id) {
return request({
url: '/education/entpcoursework/' + id,
method: 'delete'
})
}
// xuekubaoapi
export function xuekubaoAPI(data) {
return request({
url: '/education/entpcoursework/xuekubaoapi',
method: 'post',
data: data
})
}
// PPT文件上传
export function uploadEntpcourseworkFile(data) {
return request({
url: '/education/entpcoursework/uploadWord',
method: 'post',
data: data
})
}
// 查询entpcoursework列表
export function listEntpcourseworkNew(query) {
return request({
url: '/education/entpcoursework/new/list',
method: 'get',
params: query
})
}
/**
* @desc: 学科网接口api
* @return: {*}
* @param {*} path 请求路径 /xopqbm/questions(无需全拼, 后端学科网sdk自动处理)
* @param {*} method 请求方式 post/get
* @param {*} params 请求参数 {key: value,}
*/
export function xkwAPI(path, method, isPostBody, params) {
return request({
url: '/xkw/post',
method: 'post',
data: {
path: path,
method: method,
isPostBody: isPostBody,
params: params,
}
})
}
/**
* @desc: 图文识别接口 python_OCR_api
* @return: {*}
* @param {*} path 请求路径 /ocrApi/data
* @param {*} method 请求方式 post
* @param {*} params 请求参数 {key: value,}
*/
export function pyOCRAPI(path) {
return request({
url: '/ocrApi/data',
method: 'post',
data: {
imageBas64: path,
}
})
}

View File

@ -1,162 +0,0 @@
import request from '@/utils/request'
// 查询entpcoursefile列表
export function listEntpcoursefile(query) {
return request({
url: '/education/entpcoursefile/list',
method: 'get',
params: query
})
}
// zdg:查询entpcoursefile列表-新
export function listEntpcoursefileNew(query) {
return request({
url: '/education/entpcoursefile/new/list',
method: 'get',
params: query
})
}
// 查询entpcoursefile详细
export function getEntpcoursefile(id) {
return request({
url: '/education/entpcoursefile/' + id,
method: 'get'
})
}
// 新增entpcoursefile
export function addEntpcoursefile(data) {
return request({
url: '/education/entpcoursefile',
method: 'post',
data: data
})
}
// 新增entpcoursefile
export function addEntpcoursefileReturnId(data) {
return request({
url: '/education/entpcoursefile/addReturnId',
method: 'post',
data: data
})
}
// addFromId
export function addFromId(fromid, toid, entpid, entpcourseid, edituserid) {
return request({
url: '/education/entpcoursefile/addFromId/'+fromid+'/'+toid+'/'+entpid+'/'+entpcourseid+'/'+edituserid,
method: 'post'
})
}
// 修改entpcoursefile
export function updateEntpcoursefile(data) {
return request({
url: '/education/entpcoursefile',
method: 'put',
data: data
})
}
// 新增 修改接口
export function updateEntpcoursefileNew(data) {
return request({
url: '/education/entpcoursefile/newUpdateFile',
method: 'post',
data: data
})
}
// updateFileByIds
export function updateFileByIds(data) {
return request({
url: '/education/entpcoursefile/updateFileByIds',
method: 'post',
data: data
})
}
// updateFileByArray
export function updateFileByArray(data) {
return request({
url: '/education/entpcoursefile/updateFileByArray',
method: 'post',
data: data
})
}
// 修改entpcoursefile
export function updateFile2Redis(data) {
return request({
url: '/education/entpcoursefile/updateFile2Redis',
method: 'post',
data: data
})
}
// 删除entpcoursefile
export function delEntpcoursefile(id) {
return request({
url: '/education/entpcoursefile/' + id,
method: 'delete'
})
}
// 保存base64图片返回url
export function saveEntpCourseBase64File(data) {
return request({
url: '/education/entpcoursefile/saveBase64File',
method: 'post',
data: data
})
}
// 文件上传
export function saveEntpCourseBase64File2(data) {
return request({
url: '/education/entpcoursefile/saveBase64File2',
method: 'post',
data: data
})
}
// 保存PPT页面预览base64图片返回url
export function savePPTPreviewBase64File(data) {
return request({
url: '/education/entpcoursefile/savePreviewBase64',
method: 'post',
data: data
})
}
// PPT文件上传
export function saveEntpCoursePPT(data) {
return request({
url: '/education/entpcoursefile/importPPT',
method: 'post',
data: data
})
}
// PPT文件解析
export function parsePPT(data) {
return request({
url: '/education/entpcoursefile/parsePPT',
method: 'post',
data: data
})
}
// 修改ppt.slide.index
export function updateSlideIndex(data) {
return request({
url: '/education/entpcoursefile/saveSlideOrder',
method: 'post',
data: data
})
}

View File

@ -9,14 +9,6 @@ export const getSmarttalkPage = (params) => {
})
}
export const creatAPT = (params) => {
return request({
url: '/smarttalk/file/createApt',
method: 'post',
params
})
}
export const getPrepareById = (id) => {
return request({
url: '/smarttalk/file/' + id,

View File

@ -57,67 +57,3 @@ export function getCodeImg() {
timeout: 20000
})
}
// 注册模块-生成人机验证
export function captchaImg(data) {
return request({
url: '/captchaImg',
headers: {
isToken: false
},
method: 'get',
params: data
})
}
//注册模块-发送验证码
export function sendCode(data) {
return request({
url: '/smarttalk/register/authSendCode',
method: 'post',
data:data
})
}
//注册模块-申请注册
export function signIn(data) {
return request({
url: '/smarttalk/register/authSignIn',
method: 'post',
data:data
})
}
//注册模块-获取学校
export function deptTree(data) {
return request({
url: '/smarttalk/register/authDeptTree',
method: 'get',
params:data
})
}
// 查询部门详细
export function getDept(query) {
return request({
url: '/system/dept/detail',
method: 'get',
params: query
})
}
// 查询classmain列表
export function listClassmain(query) {
return request({
url: '/education/classmain/list',
method: 'get',
params: query
})
}
// 查询evaluation列表
export function listEvaluation(query) {
return request({
url: '/smarttalk/register/authEvaluationList',
method: 'get',
params: query
})
}

View File

@ -43,12 +43,3 @@ export function delClasswork(id) {
method: 'delete'
})
}
// 新增classwork
export function addClassworkReturnId(data) {
return request({
url: '/education/classwork/saveAndReturnId',
method: 'post',
data: data
})
}

View File

@ -1,9 +1,9 @@
@font-face {
font-family: "iconfont"; /* Project id 2794390 */
src: url('iconfont.woff2?t=1725691484835') format('woff2'),
url('iconfont.woff?t=1725691484835') format('woff'),
url('iconfont.ttf?t=1725691484835') format('truetype'),
url('iconfont.svg?t=1725691484835#iconfont') format('svg');
src: url('iconfont.woff2?t=1724212790213') format('woff2'),
url('iconfont.woff?t=1724212790213') format('woff'),
url('iconfont.ttf?t=1724212790213') format('truetype'),
url('iconfont.svg?t=1724212790213#iconfont') format('svg');
}
.iconfont {
@ -14,34 +14,6 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-saoyisao:before {
content: "\e691";
}
.icon-jiaoxuezhiliangfenxi:before {
content: "\e690";
}
.icon-jiaoxuejihua:before {
content: "\e7e9";
}
.icon-tongji:before {
content: "\e68f";
}
.icon-pigai:before {
content: "\e68d";
}
.icon-jiaoxuefansi:before {
content: "\e6b2";
}
.icon-kaoshi:before {
content: "\e68a";
}
.icon-yiwen:before {
content: "\e687";
}

File diff suppressed because one or more lines are too long

View File

@ -5,55 +5,6 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "12657402",
"name": "资源库",
"font_class": "saoyisao",
"unicode": "e691",
"unicode_decimal": 59025
},
{
"icon_id": "6513175",
"name": "教学质量分析",
"font_class": "jiaoxuezhiliangfenxi",
"unicode": "e690",
"unicode_decimal": 59024
},
{
"icon_id": "38447338",
"name": "教学计划",
"font_class": "jiaoxuejihua",
"unicode": "e7e9",
"unicode_decimal": 59369
},
{
"icon_id": "8455509",
"name": "统计",
"font_class": "tongji",
"unicode": "e68f",
"unicode_decimal": 59023
},
{
"icon_id": "5969226",
"name": "批改",
"font_class": "pigai",
"unicode": "e68d",
"unicode_decimal": 59021
},
{
"icon_id": "36295514",
"name": "教学反思",
"font_class": "jiaoxuefansi",
"unicode": "e6b2",
"unicode_decimal": 59058
},
{
"icon_id": "21088705",
"name": "考试",
"font_class": "kaoshi",
"unicode": "e68a",
"unicode_decimal": 59018
},
{
"icon_id": "20574719",
"name": "疑问",

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 352 KiB

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,99 +0,0 @@
body{font-family: "微软雅黑", Arial,"宋体"; color: #333;}
a{ text-decoration: none; color: #2489f6;}
dl, ul, ol, ul { list-style: none; padding: 0; margin: 0; }
.wrapper{ width: 1200px; margin: 0 auto; }
.ques-detail{}
.ques-detail ul li{margin-bottom: 20px;border: 1px solid #dadada;background: #fff;border-radius: 10px;}
.ques-detail ul li:last-child{ margin-bottom: 0; }
table.edittable{ border-collapse: collapse; text-align: center; margin: 2px; }
table.edittable th, table.edittable td{ line-height: 30px; padding: 5px; white-space: normal; word-break: break-all; border: 1px solid #000; vertical-align: middle; }
table.composition{ border-collapse: collapse; text-align: left; margin: 2px; width: 98%; }
table.composition th, table.composition td{ line-height: 30px; white-space: normal; word-break: break-all; border-width: 0px; vertical-align: middle; }
table.composition2{ border-collapse: collapse;width:auto }
table.composition2 th, table.composition2 td{text-align:left;line-height:30px; white-space:normal;word-break:break-all;border:none;border-width: 0px;vertical-align: middle; }
.MathJye{ border: 0 none; direction: ltr; line-height: normal; display: inline-block; float: none; font-family: 'Times New Roman','宋体'; font-size: 15px; font-style: normal; font-weight: normal; letter-spacing: 1px; line-height: normal; margin: 0; padding: 0; text-align: left; text-indent: 0; text-transform: none; white-space: nowrap; word-spacing: normal; word-wrap: normal; -webkit-text-size-adjust: none; }
.MathJye div, .MathJye span{ border: 0 none; margin: 0; padding: 0; line-height: normal; text-align: left; height: auto; _height: auto; white-space: normal; }
.MathJye table{ border-collapse: collapse; margin: 0; padding: 0; text-align: center; vertical-align: middle; line-height: normal; font-size: inherit; *font-size: 100%; _font-size: 100%; font-style: normal; font-weight: normal; border: 0; float: none; display: inline-block; *display: inline; zoom: 0; }
.MathJye table td{ padding: 0; font-size: inherit; line-height: normal; white-space: nowrap; border: 0 none; width: auto; _height: auto; }
.MathJye_mi{ font-style: italic; }
.flipv{-ms-transform: scaleX(-1);-moz-transform: scaleX(-1);-webkit-transform: scaleX(-1);-o-transform: scaleX(-1);transform: scaleX(-1);filter: FlipH;}
.fliph{-ms-transform: scaleY(-1);-moz-transform: scaleY(-1);-webkit-transform: scaleY(-1);-o-transform: scaleY(-1);transform: scaleY(-1);filter: FlipV;}
.mathjye-bold{font-weight:800}
.mathjye-del{text-decoration:line-through}
.mathjye-underline{border-bottom:1px solid #000;padding-bottom:2px;}
@-moz-document url-prefix() {.mathjye-underline{padding-bottom:0px;}}
.mathjye-underpline{border-bottom:2px dotted #000; padding-bottom:3px;}
@-moz-document url-prefix() {.mathjye-underpline {padding-bottom:1px;}}
.mathjye-underpoint{background: url(http://img.jyeoo.net/images/formula/point.png) no-repeat center bottom; padding-bottom:4px;}
.mathjye-underpoint2{border-bottom:2px dotted #000; padding-bottom:3px;}
@-moz-document url-prefix() {.mathjye-underpoint{padding-bottom:1px;}}
.mathjye-underwave{background: url(http://img.jyeoo.net/images/formula/wave.png) bottom repeat-x; padding-bottom:4px;}
@-moz-document url-prefix() {.mathjye-underwave {padding-bottom:1px;}}
.mathjye-alignleft{display:block;text-align:left;}
.mathjye-aligncenter{display:block;text-align:center;}
.mathjye-alignright{display:block;text-align:right;}
/*试题*/
.artpreview fieldset { padding-top: 10px; font-size: 14px; clear: both; overflow: hidden; zoom: 1; line-height: 24px; font-family: 'Times New Roman',,sans-serif; position: relative; }
.artpreview fieldset legend { padding: 5px 0; display: block; margin: 5px; background: #f1f1f1; color: #000; overflow: hidden; zoom: 1; }
.queserror { border: 1px dotted #f00; padding: 2px; }
fieldset.quesborder {display: block;padding: 0;line-height: 25px;letter-spacing: 1px;word-break: break-all;margin: 0;}
fieldset.queserror { border: 1px solid #f00; font-size: 12px; padding: 2px; margin-bottom: 1px; }
fieldset.quesborder td, fieldset.queserror td { line-height: 16px; }
fieldset.quesborder em, fieldset.queserror em { font-style: normal; font-weight: bold; position: absolute; left: 20px; }
fieldset.thiserror1 { border: 1px solid #f00; }
fieldset.thiserror1 legend { border: 4px solid #f00; }
fieldset.thiserror2 { border: 1px solid #ADCD3C; }
fieldset.thiserror2 legend { border: 4px solid #ADCD3C; }
fieldset.thisques { border: 1px solid blue; }
fieldset.thison { border: 1px solid #A9C9E2; }
fieldset.thison div.border { border: 1px solid #ADCD3C; background-color: #F2FDDB; }
fieldset, img { border: 0 none; }
table.thison { border: 1px solid #00F; }
table.thiserr { border: 1px solid #F00; }
fieldset.thisvip1 { border: 1px solid #00F; }
fieldset.thisvip1 legend { border: 4px solid #00F; }
fieldset.status17 { border: 1px solid #ff00ff; }
fieldset.status17 legend { border: 4px solid #ff00ff; }
.selectoption { vertical-align: middle; font-size: 14px; padding: 2px; }
.selectoption:hover { color: #EA8511; }
.selectoption label { padding: 4px; line-height: 24px; }
fieldset.quesbordere { border: 2px dotted #f00; }
.answer { border: 1px dotted #ffffff; }
ol.answer li, ul.answer li { padding: 1px; font-size: 14px; }
ol.answer li:hover { background: #f2f2f2; }
.collapseContainerPanel { border: 0; }
.collapsePanelHeader { height: 30px; font-weight: bold; padding: 6px 0 0 0; }
.collapseHeaderContent { float: left; padding-left: 5px; }
.collapseContent { margin: 0; padding: 0; border: 1px solid #ccc; border-top: 0; }
.pt0 { padding: 2px 0 5px 0; font-size: 14px; font-family: "黑体",sans-serif; font-weight: 700; }
.pt1 {overflow: hidden;zoom: 1;clear: both;line-height: 25px;font-size: 14px;padding: 20px;position: relative;word-break: break-word;}
fieldset.quesborder .pt1 em { position: static; }
.pt1 img { position: relative; }
.pt2 {padding: 20px;padding-top: 0;}
.pt3, .pt4, .pt5, .pt6, .pt7 { clear: both; zoom: 1; position: relative; padding: 0px 20px 20px 80px; }
.pt8 a:link, .pt8 a:visited { margin-right: 10px; padding: 2px 5px; }
.pt8 a:hover { background: #fc0; }
.pt9 { padding: 20px; border: 0 none; color: #999999; font-size: 12px; }
.fieldtip {height: 36px;line-height: 36px;background-color: #f4f4f4;border-top: 1px solid #dadada;padding: 0 20px;color: #666666;position: relative;font-size: 12px;border-radius: 0 0 10px 10px;}
.newFieldtip .pt1, .newFieldtip .pt2, .newFieldtip .pt3, .newFieldtip .pt4, .newFieldtip .pt5, .newFieldtip .pt6, .newFieldtip .pt7, .newFieldtip .pt8, .newFieldtip.pt9, .newFieldtip + .fieldtip { padding: 0; }
fieldset img { max-width: 100%; }
.fieldtip-left {float: left;}
.fieldtip-left >* {margin-right: 20px;}
.fieldtip-right { float: right; }
.fieldtip-right>* { margin-left: 20px; display: inline-block; color: #666666; }
.fieldtip .btn {display: inline-block;margin-bottom: 0;font-weight: normal;text-align: center;vertical-align: middle;-ms-touch-action: manipulation;touch-action: manipulation;cursor: pointer;background-image: none;border: 1px solid transparent;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;font-size: 14px;border-radius: 4px;color: #ffffff;background-color: #ff8a00;line-height: 18px;min-width: 28px;padding: 0 5px;}
.fieldtip .btn:hover, .fieldtip .btn:active, .fieldtip .btn:active:hover, .fieldtip .btn:hover { color: #ffffff; background-color: #faad4a; }
/*填空题*/
div.quizPutTag { display: inline-block; *display: inline; padding: 3px 10px 1px 10px; margin: 0 3px; font-size: 14px; min-width: 1em; min-height: 16px; line-height: 18px; height: auto; border-bottom: 1px solid #0033FF; text-decoration: none; zoom: 1; color: #127176; word-break: break-all; }
div.quizPutTag:hover { color: #f60; }
div.quizPutTag img { cursor: pointer; width: 200px; margin-left: 10px; }
.sanwser { padding: 4px 10px; margin: 0px; border: 1px solid #ADCD3C; background-color: #F2FDDB; color: #000; display: none; }
/*答案*/
.selectoption label.s, div.s { border: 1px solid #91cbed; background-color: #deeeff; display: inline-block; }
.selectoption label.s.sh, div.s.sh { margin: 1px; border: none; background: none; }
del { text-decoration: none; color: #f00; font-style: normal; font-weight: normal; }

View File

@ -75,8 +75,6 @@ const curBookImg = ref('')
const curBookPath = ref('')
//
const volumeOne = ref([])
//
const volumeTwo = ref([])
//
const currentNode = reactive({
@ -92,15 +90,14 @@ const defaultExpandedKeys = ref([])
const refTree = ref(null)
// +
//
const getSubjectContent = async () => {
treeLoading.value = true
const params = {
edusubject,
edustage,
// entpcourseedituserid: userId,
itemgroup: 'textbook',
pageSize: 10000
entpcourseedituserid: userId,
pageSize: 500
}
let data;
@ -117,8 +114,7 @@ const getSubjectContent = async () => {
* 不区分上下册
* 2024/08/20调整
*/
// volumeOne.value = data.filter(item => item.level == 1 && item.semester == '')
// volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '')
volumeOne.value = data.filter(item => item.level == 1)
getTreeData()
}
@ -136,9 +132,7 @@ const changeBook = ({ id, itemtitle, avartar, fileurl }) => {
const getTreeData = () => {
//
let upData = transData(evaluationList.value)
let upData = transData(volumeOne.value)
if(upData.length){
treeData.value = [...upData]
}
@ -228,9 +222,7 @@ const transData = (data) => {
let ary = []
data.forEach(item => {
let obj = {}
// ID
if (item.rootid == curBookId.value) {
if(item.level == 1){
obj.label = item.itemtitle
obj.id = item.id
obj.itemtitle = item.itemtitle
@ -255,7 +247,6 @@ const transData = (data) => {
})
ary.push(obj)
}
}
})
return ary
}
@ -263,10 +254,8 @@ const transData = (data) => {
//
const getSubject = async () => {
const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 10000 })
// subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject)
subjectList.value = rows
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id))
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
//

View File

@ -1,5 +1,5 @@
<template>
<el-drawer v-model="model" class="preview-drawer" title="title" :modal="true" :destroy-on-close="true" :with-header="false" :append-to-body="true"
<el-drawer v-model="model" class="preview-drawer" title="title" :modal="false" :with-header="false" append-to-body
size="50%">
<div class="flex drawer-header">
<div>
@ -16,23 +16,19 @@
</div>
<div class="drawer-content">
<!-- <iframe src="./aaa.pdf" width="600px" height="600px"></iframe> -->
<!--<div ref="playerRef" class="video-box"></div> -->
<video v-if="showPrev(row) === 'video'" :src="row.fileFullPath" controls autoplay></video>
<el-image v-if="showPrev(row) === 'img'" style="width: 100%;" :src="row.fileFullPath" />
<template v-if="showPrev(row) === 'office'">
<template v-for="item in row.prevImgList">
<el-image :key="item.targetFileId" v-if="item.targetFileType === '预览图'" style="width: 100%;" :src="item.targetFilePath" />
</template>
</template>
<!-- <div ref="playerRef" class="video-box"></div> -->
<!-- <video src="" controls autoplay></video> -->
<el-image style="width: 100%;" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" />
</div>
</el-drawer>
</template>
<script setup>
import { watch, ref, nextTick, onMounted } from 'vue'
import { watch, ref, nextTick } from 'vue'
import Player from 'xgplayer'
import 'xgplayer/dist/index.min.css'
import FileImage from '@/components/file-image/index.vue'
const model = defineModel()
const props = defineProps({
row: {
@ -46,26 +42,11 @@ const playerRef = ref();
const handleClose = () => {
}
onMounted(()=>{
})
//
const onClose = () => {
model.value = false
}
const showPrev = (row)=>{
if(row.fileSuffix === 'mp4' || row.fileSuffix === 'mp3'){
return 'video'
}else if(row.fileType.indexOf('image') !== -1){
return 'img'
}else if(row.fileSuffix === 'doc' || row.fileSuffix === 'docx'|| row.fileSuffix === 'ppt'|| row.fileSuffix === 'pptx'|| row.fileSuffix === 'xls'|| row.fileSuffix === 'xlsx'){
return 'office'
}else {
return 'other'
}
}
const init = () => {
nextTick(() => {
//

View File

@ -113,8 +113,7 @@ const getSubjectContent = async () => {
const params = {
edusubject,
edustage,
// entpcourseedituserid: userId,
itemgroup: 'textbook',
entpcourseedituserid: userId,
pageSize: 500
}
let data;
@ -130,7 +129,7 @@ const getSubjectContent = async () => {
* 不区分上下册
* 2024/08/20调整
*/
// volumeOne.value = data.filter(item => item.level == 1)
volumeOne.value = data.filter(item => item.level == 1)
getTreeData()
}
@ -142,8 +141,8 @@ const getSubject = async () => {
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
}
else {
const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 500 })
subjectList.value = rows
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id))
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
}
@ -161,7 +160,7 @@ const isHaveUnit = (id) => {
const getTreeData = () => {
//
let upData = transData(evaluationList.value)
let upData = transData(volumeOne.value)
if(upData.length){
treeData.value = [...upData]
}
@ -249,28 +248,20 @@ const handleNodeClick = (data, node) => {
const transData = (data) => {
let ary = []
data.forEach(item => {
let obj = {}
// ID
if (item.rootid == curBookId.value) {
if(item.level == 1){
obj.label = item.itemtitle
obj.id = item.id
obj.itemtitle = item.itemtitle
obj.edudegree = item.edudegree
obj.edustage = item.edustage
obj.edusubject = item.edusubject
let ary2 = []
evaluationList.value.forEach(el => {
let obj2 = {}
if (item.id == el.parentid) {
obj2 = {
label: el.itemtitle,
id: el.id,
itemtitle : el.itemtitle,
edudegree : el.edudegree,
edustage : el.edustage,
edusubject : el.edusubject,
id: el.id
}
ary2.push(obj2)
}
@ -278,7 +269,6 @@ const transData = (data) => {
})
ary.push(obj)
}
}
})
return ary
}

View File

@ -110,7 +110,7 @@ const savaDataStore = () => {
if(!toolState.isToolWin){
toolState.isPdfWin=false
toolState.showBoardAll=true //
// ipcRenderer.invoke('tool-sphere:reset') //tool
ipcRenderer.invoke('tool-sphere:reset') //tool
ipcRenderer.send('open-PDF:minimize')
return
}
@ -145,7 +145,7 @@ const savaDataStore = () => {
Promise.all(promises).then(res=>{
toolState.isPdfWin=false
toolState.showBoardAll=true //
// ipcRenderer.invoke('tool-sphere:reset') //tool
ipcRenderer.invoke('tool-sphere:reset') //tool
ipcRenderer.send('open-PDF:minimize')
})
}

View File

@ -100,14 +100,12 @@ const renderPage = async (canvasobj) => {
})
}
//
const savaDataStore = async (type) => {
const savaDataStore = (type) => {
if(!toolState.isToolWin){
toolState.isPdfWin = false
await sleep(20) //
toolState.showBoardAll = true //
await sleep(50) //
toolState.isPdfWin=false
toolState.showBoardAll=true //
if(type=='rest'){
// ipcRenderer.invoke('tool-sphere:reset') //tool-
ipcRenderer.invoke('tool-sphere:reset') //tool
ipcRenderer.send('open-PDF:close')
}else{
ipcRenderer.invoke('open-PDF:minimize')
@ -143,24 +141,20 @@ const savaDataStore = async (type) => {
}
})
Promise.all(promises).then(async res=>{
Promise.all(promises).then(res=>{
toolState.isPdfWin=false
await sleep(20) //
toolState.showBoardAll=true //
await sleep(50) //
// ipcRenderer.send('open-PDF:minimize')
if(type=='rest'){
// ipcRenderer.invoke('tool-sphere:reset') //tool-
ipcRenderer.invoke('tool-sphere:reset') //tool
ipcRenderer.send('open-PDF:close')
}else{
ipcRenderer.invoke('open-PDF:minimize')
ipcRenderer.send('open-PDF:minimize')
}
// ipcRenderer.send('open-PDF:close')
})
}
//
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const updatePage = (canvasobj) => {
renderPage(canvasobj)
}

View File

@ -1,118 +0,0 @@
<script setup name="ReFilePreview">
import "@vue-office/docx/lib/index.css";
import "@vue-office/excel/lib/index.css";
import { defineAsyncComponent, defineProps, onMounted } from "vue";
// import type { FileProps } from "@/components/RefilePreview/types";
import { useHooks } from "@/components/refile-preview/useReadFile";
import VueOfficeDocx from '@vue-office/docx'
import VueOfficeExcel from '@vue-office/excel'
import VueOfficePdf from '@vue-office/pdf'
// const VueOfficeDocx = defineAsyncComponent(() => import("@vue-office/docx"));
// const VueOfficeExcel = defineAsyncComponent(() => import("@vue-office/excel"));
// const VueOfficePdf = defineAsyncComponent(() => import("@vue-office/pdf"));
// const RePlayer = defineAsyncComponent(
// () => import("@/components/RePlayer/index.vue")
// );
const props = defineProps({
name: {
type: String,
default: ""
},
type: {
type: String,
default: ""
},
fileType: {
type: String,
default: ""
},
raw: () => new File([], ""),
filePath: {
type: String,
default: ""
},
textContent:{
type: String,
default: ""
}
});
const {
excelOptions,
src,
filePreviewRef,
renderedHandler,
errorHandler,
renderTheFile,
isImage,
isVideo,
isText,
isAudio
} = useHooks(props);
onMounted(() => {
renderTheFile(); //
});
defineExpose({
filePreviewRef
});
</script>
<template>
<div ref="filePreviewRef" class="file-preview">
<vue-office-docx
v-if="props.fileType === 'docx' || props.fileType === 'doc'"
:src="props.filePath"
@rendered="renderedHandler"
@error="errorHandler"
/>
<vue-office-excel
v-if="props.fileType === 'xlsx' || props.fileType === 'xls'"
:src="props.filePath"
width="100%"
height="100%"
:auto-resize="true"
:enable-scrollbars="true"
:options="excelOptions"
@rendered="renderedHandler"
@error="errorHandler"
/>
<vue-office-pdf
v-if="props.fileType === 'pdf'"
:src="props.filePath"
@rendered="renderedHandler"
@error="errorHandler"
/>
<el-image
v-if="isImage(props.fileType)"
:preview-teleported="true"
fit="cover"
class="w-[200px] align-left"
:src="props.filePath"
title="点击查看大图"
:preview-src-list="[src]"
/>
<video v-if="isVideo(props.fileType)" :src="props.filePath" style="width: 400px; height: 400px;" controls/>
<div v-if="isText(props.fileType)">
<pre v-html="props.textContent" />
</div>
<audio v-if="isAudio(props.fileType)" :src="props.filePath" controls />
</div>
</template>
<style lang="scss" scoped>
.file-preview {
width: 90%;
height: 60vh;
overflow: auto;
text-align: left;
margin-left: 10px;
}
</style>

View File

@ -1,7 +0,0 @@
export interface FileProps {
id?: number;
type?: string;
fileType?: string;
raw?: File;
filePath?: string;
}

View File

@ -1,261 +0,0 @@
import type { FileProps } from "@/components/refile-preview/types";
import { onUnmounted, ref } from "vue";
import axios from "axios";
export function useHooks(props: FileProps) {
const excelOptions = {
xls: props.fileType !== "xlsx", //预览xlsx文件设为false预览xls文件设为true
minColLength: 0, // excel最少渲染多少列如果想实现xlsx文件内容有几列就渲染几列可以将此值设置为0.
minRowLength: 0, // excel最少渲染多少行如果想实现根据xlsx实际函数渲染可以将此值设置为0.
widthOffset: 10, //如果渲染出来的结果感觉单元格宽度不够,可以在默认渲染的列表宽度上再加 Npx宽
heightOffset: 10, //在默认渲染的列表高度上再加 Npx高
beforeTransformData: workbookData => {
return workbookData;
}, //底层通过exceljs获取excel文件内容通过该钩子函数可以对获取的excel文件内容进行修改比如某个单元格的数据显示不正确可以在此自行修改每个单元格的value值。
transformData: workbookData => {
return workbookData;
} //将获取到的excel数据进行处理之后且渲染到页面之前可通过transformData对即将渲染的数据及样式进行修改此时每个单元格的text值就是即将渲染到页面上的内容
};
const src = ref();
const filePreviewRef = ref();
/**
*
*/
const renderedHandler = () => {
console.log("渲染完成");
};
/**
*
* @param e
*/
const errorHandler = e => {
console.log("渲染失败", e);
};
/**
*
*/
async function renderTheFile() {
if (props.type === "local") {
console.log("本地文件" + props.fileType);
isImage(props.fileType) && localImagePreview(props.raw);
isDoc(props.fileType) && localOfficePreview(props.raw);
isText(props.fileType) && localTextPreview(props.raw);
isVideo(props.fileType) && localVideoPreview(props.raw);
isAudio(props.fileType) && localAudioPreview(props.raw);
} else {
if (isVideo(props.fileType)) {
src.value =
import.meta.env.VITE_APP_BASE_URL +
"/upload/attachments/getTeamOfVideo?id=" +
props.id;
return;
}
if (isText(props.fileType)) {
const response = await axios.get(
import.meta.env.VITE_STATIC_URL + props.filePath,
{
responseType: "text"
}
);
src.value = response.data;
return;
}
src.value = import.meta.env.VITE_STATIC_URL + props.filePath;
}
}
/**
*
* @param type
*/
function isImage(type: string) {
const types = [
"jpg",
"png",
"gif",
"jpeg",
"bmp",
"webp",
"svg",
"tiff",
"tif",
"jpeg",
"jfif",
"pjpeg",
"pjp"
];
return types.includes(type);
}
/**
*
* @param type
*/
function isDoc(type: string) {
const types = ["docx", "doc", "xlsx", "xls", "pdf"];
return types.includes(type);
}
/**
*
* @param type
*/
function isText(type: string) {
const types = [
"txt",
"md",
"log",
"json",
"xml",
"html",
"css",
"js",
"java",
"c",
"cpp",
"h",
"hpp",
"py",
"rb",
"go",
"sh",
"bat",
"ps1",
"psm1",
"ps1xml",
"psc1",
"psd1",
"psm1",
"ps1xml",
"psc1",
"psd1",
"ps1xml",
"psc1",
"ps1xml",
"psc1",
"psd1"
];
return types.includes(type);
}
// 检验视频格式
function isVideo(type: string) {
const types = [
"mp4",
"avi",
"rmvb",
"mkv",
"flv",
"wmv",
"mov",
"webm",
"m4v",
"mpg",
"mpeg",
"3gp",
"3g2",
"vob",
"ogv",
"ogg",
"mts",
"m2ts",
"ts",
"m2v",
"mpe",
"mpv",
"m4p",
"m4v",
"mpv2",
"m4v",
"m4p",
"m4v",
"m4p"
];
return types.includes(type);
}
// 检验音频文件
function isAudio(type: string) {
const types = ["mp3", "wav", "ogg", "flac", "aac", "wma", "m4a", "wma"];
return types.includes(type);
}
/**
* Office文件
* @param file
*/
function localOfficePreview(file: File) {
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = loadEvent => {
const arrayBuffer = loadEvent.target.result;
const blob = new Blob([arrayBuffer], {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
src.value = URL.createObjectURL(blob);
};
}
/**
*
* @param file
*/
function localImagePreview(file: File) {
const reader = new FileReader();
reader.onload = e => {
src.value = e.target.result;
};
reader.readAsDataURL(file);
}
/**
*
* @param file
*/
function localTextPreview(file: File) {
const reader = new FileReader();
reader.onload = e => {
src.value = e.target.result;
};
reader.readAsText(file);
}
/**
*
* @param file
*/
function localVideoPreview(file: File) {
src.value = URL.createObjectURL(file);
}
function localAudioPreview(file: File) {
const reader = new FileReader();
reader.onloadend = () => {
const blob = new Blob([reader.result], { type: "audio/mpeg" });
src.value = URL.createObjectURL(blob);
};
reader.readAsArrayBuffer(file);
}
// 清理工作当组件卸载时释放URL对象
onUnmounted(() => {
if (src.value) {
isVideo(props.fileType) && URL.revokeObjectURL(src.value);
isAudio(props.fileType) && URL.revokeObjectURL(src.value);
}
});
return {
excelOptions,
src,
filePreviewRef,
isImage,
renderedHandler,
errorHandler,
renderTheFile,
isVideo,
isText,
isAudio
};
}

View File

@ -1,514 +0,0 @@
export 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 processList 格式化试题
* @param {*} row
*/
export const processList = (row) => {
for (var i = 0; i < row.length; i++) {
if (isJson(row[i].workanalysis)) {
//1、先默认格式化 格式化各项内容(待优化, 后续界面显示的为format的值)
row[i].titleFormat = row[i].title // 题目
row[i].examdateFormat = row[i].examdate // ?考试日期 eg 2024-07-11 14:39:27"
row[i].workdescFormat = row[i].workdesc // 题目选项
row[i].workanswerFormat = row[i].workanswer // 题目正确答案
if (row[i].workanswerFormat == null || row[i].workanswerFormat == '') {
row[i].workanswerFormat = '见试题解答内容'
}
// workanalysis 解析内容analyse解答 method分析 discuss点评
var jjj = JSON.parse(row[i].workanalysis)
row[i].analyse = jjj.analyse
row[i].method = jjj.method
row[i].discuss = jjj.discuss
//row[i].discusscollapse = false;
if (row[i].examdate !== null && row[i].examdate !== undefined) {
row[i].examdate = row[i].examdate.substring(0, 10)
}
// 具体题型数据结构处理
if (row[i].worktype == '复合题') {
// 旧类型
if (row[i].title.indexOf('!@#$%') !== -1) {
// 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 < workDescArr.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:35%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if (j % 2 == 1) {
tmp += '</div>'
}
// workDescArr为 [''] 表示为 判断题或者填空题,这里不需要选项
if (workDescArr[0] != '') {
titls.splice(index1, 1, tmp)
} else {
titls.splice(index1, 1, '')
}
})
const s = []
tmTitles.map((it, ix) => {
s.push(it)
titls.map((it2, ix2) => {
if (ix == ix2) {
s.push(it2)
}
})
})
// console.log(s,'?????????????????')
row[i].titleFormat = bjTitle + s.join('')
row[i].workdescFormat = ''
//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 ? '' : '、')
}
if (item.type == '主观题') {
if (element) {
console.log(element, 'element')
value += item.answer
} else {
value += '答案不唯一,请参考分析解答点评!'
}
}
})
arr.push(value)
indexLabel++
})
const answer = arr.join('<br />')
row[i].workanswerFormat = answer
} else {
// 处理[题干显示] - 不再需要处理
// row[i].titleFormat = row[i].title; // 仅占位提示
/**
* 处理[选项显示] - 特殊结构
* [
* {type: '单选题', title: '题目1', options: ['ABC123','ABC123']},
* {type: '多选题', title: '题目1', options: ['ABC123','ABC123']},
* {type: '填空题', title: '题目1', options: []},
* {type: '判断题', title: '题目1', options: []},
* {type: '主观题', title: '题目1', options: []},
* ]
*/
let workDescArr = JSON.parse(row[i].workdesc)
let workDescHtml = `<div style='width:80%;display:flex;>`
workDescArr.map((item, index) => {
if (item.type == '单选题' || item.type == '多选题') {
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++) {
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}.${optionsArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if (j % 2 == 1) {
tmp += '</div>'
}
workDescHtml += tmp
} else if (item.type == '填空题' || item.type == '判断题' || item.type == '主观题') {
workDescHtml += `<div style='width:80%;display:flex;'>${index + 1}. ${item.title}</div>`
}
})
workDescHtml += '</div>'
row[i].workdescFormat = workDescHtml
/**
* 处理[答案显示] - 特殊结构
* [
* {type: '单选题', answer: ['0']},
* {type: '多选题', answer: ['0','1']},
* {type: '填空题', answer: ['填空1','填空2']},
* {type: '判断题', answer: ['0'/'1']},
* {type: '主观题', answer: [xxxx]},
* ]
*/
let workAnswerArr = JSON.parse(row[i].workanswer)
let workAnswerHtml = ``
workAnswerArr.map((item, index) => {
const answerArr = item.answer //JSON.parse(item.answer);
if (item.type == '单选题' || item.type == '多选题') {
const answer = answerArr
.map((item) => {
return String.fromCharCode(65 + Number(item))
})
.join('')
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else if (item.type == '填空题') {
const answer = answerArr.join('、')
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else if (item.type == '判断题') {
const answer = answerArr
.map((item) => {
return item === '1' ? '正确' : '错误'
})
.join('、')
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else if (item.type == '主观题') {
// 复合题里面的主观题只有一个答案,或没填
const answer = answerArr.join('、')
if (answerArr[0]) {
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}</div>`
} else {
workAnswerHtml += `<div style='display:flex;'>${index + 1}. ${answer}答案不唯一,请参考分析解答点评!</div>`
}
}
})
row[i].workanswerFormat = workAnswerHtml
}
} else if (
row[i].worktype == '主观题' ||
(row[i].worktype !== '单选题' &&
row[i].worktype !== '多选题' &&
row[i].worktype !== '填空题' &&
row[i].worktype !== '判断题')
) {
// 处理[选项显示] - 主观题中无选项, 故置空
row[i].workdescFormat = ''
row[i].workanswerFormat = ''
// 答案处理- eg: "\"不唯一的答案,参考\""
if (row[i].workanswer && row[i].workanswer != '') {
row[i].workanswerFormat = JSON.parse(row[i].workanswer)
}
} else {
// 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
// 通用选项结构 ['ABC123','ABC123'] | ['ABC123','ABC123'] | [](填空题无选项) | [](判断题无选项)
let workDescArr = []
if (
row[i].workdesc.charAt(0) === '[' &&
row[i].workdesc.charAt(row[i].workdesc.length - 1) === ']'
) {
//123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workDescArr = JSON.parse(row[i].workdesc)
} else if (row[i].workdesc.indexOf('#&') !== -1) {
workDescArr = row[i].workdesc.split('#&')
} else if (row[i].workdesc.indexOf(',') !== -1) {
workDescArr = row[i].workdesc.split(',')
} else {
// 单字符串直接添加至空数组(待考虑确认)
workDescArr.push(row[i].workdesc)
}
// 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
// 通用答案结构 ['0'] | ['0','1'] | ['填空1','填空2'] | ['0'/'1']
let workAnswerArr = []
if (
row[i].workanswer.charAt(0) === '[' &&
row[i].workanswer.charAt(row[i].workanswer.length - 1) === ']'
) {
// 123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workAnswerArr = JSON.parse(row[i].workanswer)
} else if (row[i].workanswer.indexOf('#&') !== -1) {
workAnswerArr = row[i].workanswer.split('#&')
} else if (row[i].workanswer.indexOf(',') !== -1) {
workAnswerArr = row[i].workanswer.split(',')
} else {
// 单字符串直接添加至空数组(待考虑确认)
workAnswerArr.push(row[i].workanswer)
}
// 具体题型处理
if (row[i].worktype == '单选题' || row[i].worktype == '多选题') {
// 处理[选项显示] - 拼接ABCD首序号
let tmp = ''
let j = 0
for (; j < workDescArr.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}.${workDescArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
}
}
if (j % 2 == 0) {
tmp += '</div>'
}
row[i].workdescFormat = tmp
// 处理[答案显示] - 转换ABCD
let arr2Char = workAnswerArr
.map((item) => {
return String.fromCharCode(65 + Number(item))
})
.join('')
row[i].workanswerFormat = arr2Char
} else if (row[i].worktype == '填空题') {
// 处理[选项显示] - 填空题中无选项, 故置空
row[i].workdescFormat = ''
// 处理[答案显示] - 逗号连接
row[i].workanswerFormat = workAnswerArr.join('、')
} else if (row[i].worktype == '判断题') {
// 处理[选项显示] - 判断题中无选项, 故置空
row[i].workdescFormat = ''
// 处理[答案显示] - 1-正常 0-错误
const answer = workAnswerArr
.map((item) => {
return item === '1' ? '正确' : '错误'
})
.join('、')
row[i].workanswerFormat = answer
}
}
/*
//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

@ -1,13 +1,13 @@
<template>
<section class="app-main">
<!-- <div class="app-main-left no-select">
<div class="app-main-left no-select">
<div v-for="(item, index) in title" :key="index" :class="item.active?'active':''" class="app-main-left-item" @click="active(index)">
<div class="app-main-left-item-icon">
<i :class="item.img"></i>
</div>
<div class="app-main-left-item-text">{{item.name}}</div>
</div>
</div> -->
</div>
<transition mode="out-in" name="fade-transform">
<div v-show="$route != null" style="height: 100%; flex: 1">
<router-view v-slot="{ Component, route }">
@ -72,9 +72,8 @@ const title = reactive([
child1: []
},
{
name: '考试分析',
url: '/examReport',
type: 'hash',
name: '高考研究',
url: '/education/colentrance',
img: 'iconfont icon-icon_kaoshifenxi',
child1: []
}
@ -121,7 +120,7 @@ const active = (index) => {
})
}
onMounted(()=>{
// active(0)
active(0)
})
</script>

View File

@ -1,19 +1,10 @@
<template>
<div class="title-bar flex">
<div class="left-section">
<div class="flex title-box">
<el-image style="width: 23px; height: 23px" :src="logoIco" />
<span class="title" @click="changeTab">AIX智慧课堂</span>
</div>
<h3 class="title" @click="changeTab">AIX智慧课堂</h3>
<div class="change-tab">
<ul class="flex">
<li class="flex" :class="[activeId == menu.path ? 'active-li' : '', menu.disabled ? 'disabled' : '']"
v-for="menu in headerMenus" :key="menu.id" @click="clickMenu(menu)">
<i class="iconfont" :class="menu.icon"></i>
<span class="text">{{ menu.name }}</span>
</li>
<!-- <li
<li
v-for="(item, index) in routeHeader.nowRouter"
:key="index"
class="flex"
@ -23,7 +14,7 @@
>
<i :class="item.img"></i>
<span class="text">{{ item.name }}</span>
</li> -->
</li>
</ul>
</div>
</div>
@ -67,8 +58,6 @@ import useUserStore from '@/store/modules/user'
import routerStore from '@/store/modules/route'
import outLink from '@/utils/linkConfig'
import logoIco from '@/assets/images/logo.png'
const routeHeader = routerStore()
const { ipcRenderer } = window.electron || {}
const userStore = useUserStore()
@ -87,7 +76,7 @@ const handleOutLink = (path, type, name) => {
fullPath = fullPath.replaceAll('//', '/')
const { levelFirstId, levelSecondId } = JSON.parse(localStorage.getItem('unitId'))
let unitId = levelSecondId ? levelSecondId :levelFirstId
if(name == '教材分析' || name == '考试分析'){
if(name == '教材分析' || name == '高考研究'){
fullPath += `?unitId=${unitId}`
}
//
@ -98,41 +87,6 @@ const handleOutLink = (path, type, name) => {
})
}
}
const activeId = ref('/home')
const headerMenus = [
{
name: '工作台',
id: 1,
icon: 'icon-gongzuotai',
path: '/home'
},
{
name: '研究室',
id: 2,
icon: 'icon-yanjiushi',
disabled: true
},
{
name: '资源库',
id: 3,
icon: 'icon-saoyisao',
path: '/resource'
},
{
name: '朋友圈',
id: 4,
icon: 'icon-pengyouquan1',
disabled: true
}
]
const clickMenu = ({ id, disabled, path }) =>{
if(disabled) return
activeId.value = id
router.push(path)
}
/*const menus = ref([
{
icon: 'icon-zhuye2 icon-homepage',
@ -160,8 +114,7 @@ const clickMenu = ({ id, disabled, path }) =>{
watch(
() => router.currentRoute.value,
(newValue) => {
currentRoute.value = newValue
activeId.value = newValue.path
currentRoute.value = newValue.path
},
{ immediate: true }
)
@ -212,6 +165,8 @@ function setLayout() {
<style lang="scss" scoped>
.title-bar {
height: 80px;
background: #ebf0f9;
justify-content: space-between;
align-items: center;
-webkit-app-region: drag;
@ -219,38 +174,27 @@ function setLayout() {
.left-section {
display: flex;
align-items: flex-start;
flex-direction: column;
width: 50%;
.title-box{
padding-top: 8px;
box-sizing: border-box;
align-items: center;
padding-left: 20px;
.title {
color: #4b73df;
font-size: 13px;
padding: 0 5px;
}
}
width: 50%;
.change-tab {
-webkit-app-region: no-drag;
margin-left: 20px;
ul {
li {
padding: 3px 13px;
cursor: pointer;
flex-direction: column;
border-radius: 8px;
margin: 0 10px;
margin: 0 5px;
.text {
font-size: 13px;
font-weight: bold;
}
.iconfont {
font-size: 26px;
font-size: 22px;
}
.icon-resource {
@ -270,17 +214,12 @@ function setLayout() {
}
&:hover {
color: #409eff;
}
}
.disabled{
cursor: not-allowed;
color: #bfbfbf;
&:hover{
color: #bfbfbf;
background: #d3e3fb;
}
}
.active-li {
background: #d3e3fb;
color: #409eff;
}
}
@ -290,7 +229,11 @@ function setLayout() {
-webkit-app-region: no-drag;
}
.title {
color: #4b73df;
font-size: 18px;
padding: 0 20px;
}
}
.right-section {

View File

@ -4,45 +4,20 @@
<Header />
</el-header>
<el-main>
<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)'}" />
<AppMain />
</el-main>
<Uploader v-if="uploaderStore.uploadList && uploaderStore.uploadList.length > 0" />
</el-container>
</template>
<script setup>
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 uploaderState from '@/store/modules/uploader'
import { ref } from 'vue'
const router = useRouter()
const currentRoute = ref('')
watch(
() => router.currentRoute.value,
(newValue) => {
currentRoute.value = newValue
},
{ immediate: true }
)
let uploaderStore = ref(uploaderState())
const goBack = () =>{
router.back()
}
</script>
<style lang="scss" scoped>
@ -57,15 +32,6 @@ const goBack = () =>{
height: 80px;
}
.el-main {
--el-main-padding: 0 20px 20px 20px;
box-sizing: border-box;
}
.el-page-header{
margin-top: 20px;
display: flex;
align-items: center;
.text-large{
font-size: 16px;
}
--el-main-padding: 0 20px 0 0;
}
</style>

View File

@ -8,7 +8,6 @@ import 'element-plus/dist/index.css'
import './assets/iconfont/iconfont.css'
import './assets/iconfont/iconfont'
import 'virtual:windi.css'
import request from "@/utils/request";
import { store } from '@/store'
import App from './App.vue'
@ -21,20 +20,6 @@ if(process.env.NODE_ENV != 'development') { // 非开发环境,将日志打印
const app = createApp(App)
//专为菁优网配置的请求转发
app.config.globalProperties.$requestGetJYW = (url,config)=>{
config.params = config.params?config.params:{}
config.params["getjypath"] = url;
return request({
url: "/jy/proxy",
method: config.method||"get",
params: config.params
})
}
app.use(router)
.use(store)
.use(ElementPlus, { locale: zhLocale }).mount('#app')

View File

@ -13,7 +13,6 @@ import IMListeners from './imLiseners' // im消息-监听器
const API = window.api
// TIM生成签名
// import * as GenerateUserSig from './userSig' // 引入签名生成器
const isDev = process.env.NODE_ENV == 'development' // 环境
export class ImChat {
timChat // imChat对象
SDKAppID // sdkID
@ -49,11 +48,10 @@ export class ImChat {
}
// 设置配置
async setConfig() {
const log_level = TYPES.TIMLogLevel.kTIMLog_Error
await this.timChat.TIMSetConfig({ // TIMSetConfigParam
json_config: { // JSONCongfig
set_config_log_level: log_level,
set_config_callback_log_level: log_level,
set_config_log_level: TYPES.TIMLogLevel.kTIMLog_Test,
set_config_callback_log_level: TYPES.TIMLogLevel.kTIMLog_Error,
set_config_is_log_output_console: true,
// set_config_user_config: { // 用户配置
// user_config_is_read_receipt: true, // true表示要收已读回执事件

View File

@ -4,9 +4,7 @@
const isNode = typeof require !== 'undefined' // 是否支持node函数
const { ipcRenderer } = isNode?require('electron'):{} // app使用
import { sessionStore } from '@/utils/store'
// import { diff } from 'jsondiffpatch'
// const Remote = isNode?require('@electron/remote'):{} // 远程模块
export function shareStorePlugin({store}) {
store.$subscribe((mutation, state) => { // 自动同步
// mutation 变量包含了变化前后的状态
@ -21,8 +19,8 @@ export function shareStorePlugin({store}) {
if (names.includes(storeName)) {
const { storeId: storeName, payload, events, type } = mutation // direct
// if (!Object.keys(payload).length) return
// if (type != 'direct' || !events || Array.isArray(events) || !events.key) return
stateSyncWatch(storeName, state) // 需要同步
if (type != 'direct' || !events || Array.isArray(events) || !events.key) return
stateSync(storeName, events.key, events.newValue, state) // 需要同步
}
})
@ -53,28 +51,6 @@ function stateSync(storeName, key, value, state) {
console.log('state-change-error', error)
}
}
// 同步数据-发送给主线程-单独($subscribe-监听使用)
function stateSyncWatch(storeName, newState) {
const oldState = sessionStore.store // 旧数据
const diffData = findDifferences(oldState, newState)
// console.log('state-change-diffData', diffData)
try {
for(const key in diffData) {
const value = diffData[key]
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)
const jsonStr = JSON.stringify(newValue) // 从新组装-json数据
// // 更新本地数据-session
sessionStore.set(key, value)
// // 通知主线程更新
ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
// console.log('======',key, value, jsonStr )
}
} catch (error) {
console.log('state-change-error', error)
}
}
// 同步数据-发送给主线程-全量更新
function stateSyncAll(store) {
@ -92,24 +68,12 @@ function stateSyncInit(wid, store) {
ipcRenderer.invoke('pinia-state-init', wid, storeName, curJson)
}
// 监听session数据变化
function sessionWatch(store) {
const unsubscribe = sessionStore.onDidAnyChange((newV, oldV) => {
if (newV !== oldV) {
console.log('session-change', newV, oldV)
// 通知主线程更新
// ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
}
})
// unsubscribe() 取消监听
}
// 同步数据-接收主线程消息
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
@ -160,48 +124,5 @@ const filterByKey = (obj, key, value) => {
}
return res;
}
// 获取对象值
const getObjValue = (obj, key) => {
}
// 找出两个对象之间的差异
const findDifferences = (obj1, obj2) => {
const differences = {};
function compareObjects(o1, o2, path = '') {
for (const key in o1) {
if (o1.hasOwnProperty(key)) {
const newPath = path ? `${path}.${key}` : key;
if (o2.hasOwnProperty(key)) {
const v1 = toJsonStr(o1[key])
const v2 = toJsonStr(o2[key])
if (typeof o1[key] === 'object' && typeof o2[key] === 'object' && !Array.isArray(o1[key])) {
compareObjects(o1[key], o2[key], newPath);
} else if (v1 !== v2) {
differences[newPath] = o2[key];
}
} else {
differences[newPath] = o2[key];
}
}
}
for (const key in o2) {
if (o2.hasOwnProperty(key) && !o1.hasOwnProperty(key)) {
const newPath = path ? `${path}.${key}` : key;
differences[newPath] = o2[key];
}
}
}
compareObjects(objClone(obj1), objClone(obj2));
// 特殊处理
return differences;
}
// 对象克隆
const objClone = (obj) => JSON.parse(JSON.stringify(obj))
// 转换为json
const toJsonStr = (obj) => JSON.stringify(obj)

View File

@ -1,8 +1,3 @@
/*
* @Author: 苦逼程序猿
* @Date: 2024-09-06 16:15:32
* @Warning: 千行代码Bug露锋芒
*/
import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '../layout/index.vue'
@ -18,14 +13,8 @@ export const constantRoutes = [
{
path: '/',
component: Layout,
redirect: '/home',
redirect: '/homepage',
children: [
{
path: '/home',
component: () => import('@/views/desktop/index.vue'),
name: 'desktop',
meta: {title: '主页'}
},
{
path: '/homepage',
component: () => import('@/views/homePage/index.vue'),
@ -68,18 +57,6 @@ export const constantRoutes = [
name: 'class',
meta: {title: '班级中心'},
},
{
path: '/classTask',
component: () => import('@/views/classTask/classTask.vue'),
name: 'class',
meta: {title: '作业批改'},
},
{
path: '/examReport',
component: () => import('@/views/examReport/index.vue'),
name: 'examReport',
meta: {title: '考试分析'}
},
]
},
...toolRouters

View File

@ -1,18 +0,0 @@
import { defineStore } from 'pinia'
const overviewStore = defineStore(
'overview',
{
state: () => {
return {
tableList:[]
}
},
actions: {
getTableList(data){
this.tableList = [...data]
}
}
})
export default overviewStore

View File

@ -2,12 +2,10 @@
* 工具类-窗口-状态管理
*/
import { defineStore } from 'pinia'
import { sessionStore } from '@/utils/store'
import { sessionStore } from '@/utils/tool'
// 默认数据
const defData = sessionStore.store || {}
// 延时
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
export const useToolState = defineStore('tool', {
state: () => ({
@ -22,12 +20,5 @@ export const useToolState = defineStore('tool', {
...defData // 默认数据-覆盖上面的配置(不要删除, 会导致新窗口-获取状态失败)
}),
actions: {
async resetDef() { // 重置数据-下课
this.model = 'select' // 悬浮球-当前模式
await sleep(20) // 休眠20ms
this.showBoardAll = false // 全屏画板-是否显示
await sleep(20) // 休眠20ms
this.isToolWin = false // 工具窗口是否打开
}
}
})

View File

@ -109,28 +109,3 @@ export const getAfterMinutes = (m) => {
minutes = minutes < 10 ? ('0' + minutes) : minutes
return `${hours}:${minutes}`;
}
/**
* 表格时间格式化
*/
export function formatDate(cellValue) {
if (cellValue == null || cellValue == "") return "";
var date = new Date(cellValue)
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}
export function getTimeDate() {
var date = new Date()
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}

View File

@ -1,262 +0,0 @@
const JY_TOKEN = 'CA82641DA86072DEFD39E287335E035FDA6AEEC0549B58F54F4408734C8683FFAF0585CFA3B25091E588A03A65C66A80F5FF613F539D600954007A35DFFBFDC3C7BB982771C5E13F0918642CFD7596CE3718F06E5579238D92EC809AC6F4C82A9FE4B0E232A67DD3594D4DAC1C219CCBC4A7A093344446107EB11DC317526D0594249DEBBD82B740C794CF5A7065E1982B7779AF16AD25D7';
const JY_SUBJECT = [
{id: 10, subject: 'math3', name: '小学数学'},
{id: 11, subject: 'chinese3', name: '小学语文'},
{id: 12, subject: 'english3', name: '小学英语'},
{id: 14, subject: 'science3', name: '小学科学'},
{id: 15, subject: 'politics3', name: '小学道德与法治'},
{id: 20, subject: 'math', name: '初中数学'},
{id: 21, subject: 'physics', name: '初中物理'},
{id: 22, subject: 'chemistry', name: '初中化学'},
{id: 23, subject: 'bio', name: '初中生物'},
{id: 24, subject: 'science', name: '初中科学'},
{id: 25, subject: 'geography', name: '初中地理'},
{id: 26, subject: 'chinese', name: '初中语文'},
{id: 27, subject: 'english', name: '初中英语'},
{id: 28, subject: 'politics', name: '初中道德与法治'},
{id: 29, subject: 'history', name: '初中历史'},
{id: 30, subject: 'math2', name: '高中数学'},
{id: 31, subject: 'physics2', name: '高中物理'},
{id: 32, subject: 'chemistry2', name: '高中化学'},
{id: 33, subject: 'bio2', name: '高中生物'},
{id: 35, subject: 'geography2', name: '高中地理'},
{id: 36, subject: 'chinese2', name: '高中语文'},
{id: 37, subject: 'english2', name: '高中英语'},
{id: 38, subject: 'politics2', name: '高中政治'},
{id: 39, subject: 'history2', name: '高中历史'},
];
export const JYApiListCT = async (_this, name = '高中历史') => {
if (name === '初中政治') {
name = '初中道德与法治';
}
const obj = JY_SUBJECT.filter(item => item.name == name);
if(obj.length < 1) {
return [];
}
const res = await _this.$requestGetJYW(`/${obj[0].subject}/common`, {
headers: {
authorization: `Token ${JY_TOKEN}`
},
params: {
tp: 1,
},
});
if (res.code !== 200) {
return [];
}
let arrCT = [{
label: "不限",
value: 0
}]
res.data.forEach(item=> {
if (item.Value === '选择题') {
item.Value = '单选题';
}
const tmp = {
label: item.Value,
value: item.Key,
}
arrCT.push(tmp);
})
return arrCT;
}
export const JYApiListOriginYear = () => {
const arrYear = [{label: '不限', value: '-1'}];
let i = 0;
for( ; i < 10; i++) {
const year = new Date().getFullYear();
const s ={
label: `${year - i}`,
value: `${year - i}`,
}
arrYear.push(s);
};
//arrYear.push({label: '更早', value: '0'})
return arrYear;
}
export const JYApiListSO = async (_this, name = '高中历史') => {
if (name === '初中政治') {
name = '初中道德与法治';
}
const obj = JY_SUBJECT.filter(item => item.name == name);
if(obj.length < 1) {
return [];
}
const res = await _this.$requestGetJYW(`/${obj[0].subject}/common`, {
headers: {
authorization: `Token ${JY_TOKEN}`
},
params: {
tp: 2,
},
});
if (res.code !== 200) {
return [];
}
const arrSO = [{"Value": "不限", "Key": 0}, ...res.data];
return arrSO;
}
export const JYApiListPoint = async (_this, name = '高中历史') => {
if (name === '初中政治') {
name = '初中道德与法治';
}
const obj = JY_SUBJECT.filter(item => item.name == name);
if(obj.length < 1) {
return [];
}
const res = await _this.$requestGetJYW(`/${obj[0].subject}/point`, {
headers: {
authorization: `Token ${JY_TOKEN}`
},
});
if (res.code !== 200) {
return [];
}
return res.data;
}
/**
* @desc: 获取菁优网的版本内容
* @return: {*}
*/
export const JYApiListVersion = async (_this, query, hasPoints=true) => {
const listVersion = {
status: 0,
msg: '',
data: [],
};
if (query.stage === '' || query.subject === '') {
listVersion.msg = '未选中学段或学科!';
return listVersion;
}
let name = `${query.stage}${query.subject}`;
if (name === '初中政治') {
name = '初中道德与法治';
}
const result = JY_SUBJECT.filter( item => item.name===name);
if (result.length === 0) {
listVersion.msg = `[${name}]未找到对应菁优网教材版本, 请检查学段或学科是否匹配!`;
return listVersion;
}
const JYBook = await _this.$requestGetJYW(`/${result[0].subject}/book2`, {
headers: {
// JYToken仅占位, 实际后续已未使用该token
authorization: `Token ${JY_TOKEN}`
},
});
if (JYBook.code !== 200) {
listVersion.msg = `[${name}]获取菁优网教材版本失败!`;
return listVersion;
}
query.list.forEach(ele => {
if (ele.thirdkey == null) {
listVersion.msg = `[${name}-${ele.itemtitle}]菁优网教材字段标识未设置!`;
return;
}
let nPos = ele.thirdkey.indexOf('-');
if (nPos === -1) {
listVersion.msg = `[${name}-${ele.itemtitle}-${ele.thirdkey}]菁优网教材字段标识设置有误!`;
return;
}
let editionName = ele.thirdkey.substring(0, nPos);
let typeName = ele.thirdkey.substring(nPos+1);
const resVersion = JYBook.data.filter( item => {
// 高中以typeName为标识, 而小学/初中以Name为标识
const JYTypeName = query.stage=='高中' ? item.TypeName : item.Name;
if (item.EditionName.trim() === editionName.trim() && JYTypeName.trim() === typeName.trim()) {
return true;
}
})
if (resVersion.length === 0) {
listVersion.msg = `[${name}-${ele.thirdkey}]菁优网教材字段标识查询不存在!`;
return;
}
if (resVersion[0].Children.length === 0) {
listVersion.msg = `[${name}]菁优网教材字段标识查询单元内容为空!`;
return;
}
const children = JYApiListLession(resVersion[0], hasPoints);
// 高中以typeName为标识, 而小学/初中以Name为标识
let JYTypeName = query.stage=='高中' ? resVersion[0].TypeName : resVersion[0].Name;
JYTypeName = JYTypeName.trim();
const reset = {
id: resVersion[0].ID,
label: resVersion[0].Name.trim(),
nodeType: 'version',
children: children,
editionName: resVersion[0].EditionName.trim(),
typeName: JYTypeName,
}
listVersion.data.push(reset);
})
if(listVersion.data.length > 0){
listVersion.status = 1;
}
return listVersion;
}
const JYApiListLession = (item, hasPoints) => {
const list = [];
item.Children.forEach(element => {
const tempList = formatCurJYLession(element, hasPoints);
list.push(tempList);
});
return list;
}
const formatCurJYLession = (item, hasPoints) => {
const obj = {
id: item.ID,
label: item.Name.trim(),
nodeType: 'unit',
children: [],
}
if(hasPoints) {
if (item.Children.length === 0 && !item.hasOwnProperty('Points')) {
obj.nodeType = 'points';
obj.id = item.No;
return obj;
}
else if (item.Children.length > 0 ){
for(let i=0; i<item.Children.length; i++){
const child = formatCurJYLession(item.Children[i], hasPoints);
obj.children.push(child);
}
}
else if (item.Children.length === 0 && item.Points.length > 0){
obj.nodeType = 'lession';
for(let i=0; i<item.Points.length; i++){
const child = formatCurJYLession(item.Points[i], hasPoints);
obj.children.push(child);
}
}
}
else{
if (item.Children.length === 0) {
obj.nodeType = 'lession';
obj.id = item.ID;
return obj;
}
else if (item.Children.length > 0 ){
for(let i=0; i<item.Children.length; i++){
const child = formatCurJYLession(item.Children[i], hasPoints);
obj.children.push(child);
}
}
}
return obj;
}

View File

@ -1,310 +0,0 @@
export 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 processExamQuestion 格式化试题
* @param {*} row
*/
export const processExamQuestion = (row) => {
for (var i=0; i<row.length; i++) {
if (isJson(row[i].workanalysis)) {
//1、先默认格式化 格式化各项内容(待优化, 后续界面显示的为format的值)
row[i].titleFormat = row[i].title; // 题目
row[i].examdateFormat = row[i].examdate; // ?考试日期 eg 2024-07-11 14:39:27"
row[i].workdescFormat = row[i].workdesc; // 题目选项
row[i].workanswerFormat = row[i].workanswer; // 题目正确答案
if (row[i].workanswerFormat == null || row[i].workanswerFormat == '') {
row[i].workanswerFormat = '见试题解答内容';
}
// workanalysis 解析内容analyse解答 method分析 discuss点评
var jjj = JSON.parse(row[i].workanalysis);
row[i].analyse = jjj.analyse;
row[i].method = jjj.method;
row[i].discuss = jjj.discuss;
//row[i].discusscollapse = false;
if(row[i].examdate !== null && row[i].examdate !== undefined ){
row[i].examdate = row[i].examdate.substring(0, 10);
}
// 具体题型数据结构处理
if (row[i].worktype == '复合题') {
// 旧类型
if(row[i].title.indexOf('!@#$%') !== -1) {
// 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<workDescArr.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:35%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if(j%2 == 1){
tmp += '</div>';
}
// workDescArr为 [''] 表示为 判断题或者填空题,这里不需要选项
if(workDescArr[0] != ''){
titls.splice(index1, 1, tmp);
}else{
titls.splice(index1, 1, '');
}
});
const s = [];
tmTitles.map((it,ix)=>{
s.push(it);
titls.map((it2,ix2)=>{
if(ix == ix2){
s.push(it2);
}
})
})
// console.log(s,'?????????????????')
row[i].titleFormat = bjTitle + s.join('');
row[i].workdescFormat = '';
//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?'':'、');
}
if(item.type == '主观题') {
if(element){
console.log(element,'element')
value += item.answer;
}else{
value += '答案不唯一,请参考分析解答点评!';
}
}
})
arr.push(value);
indexLabel++;
})
const answer = arr.join('<br />');
row[i].workanswerFormat = answer;
}
else {
// 处理[题干显示] - 不再需要处理
// row[i].titleFormat = row[i].title; // 仅占位提示
/**
* 处理[选项显示] - 特殊结构
* [
* {type: '单选题', title: '题目1', options: ['ABC123','ABC123']},
* {type: '多选题', title: '题目1', options: ['ABC123','ABC123']},
* {type: '填空题', title: '题目1', options: []},
* {type: '判断题', title: '题目1', options: []},
* {type: '主观题', title: '题目1', options: []},
* ]
*/
let workDescArr = JSON.parse(row[i].workdesc);
let workDescHtml = `<div style='width:80%;display:flex;>`;
workDescArr.map( (item, index) => {
if(item.type == '单选题' || item.type == '多选题'){
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++){
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}.${optionsArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
}
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if(j%2 == 1){
tmp += '</div>';
}
workDescHtml += tmp;
}
else if(item.type == '填空题' || item.type == '判断题' || item.type == '主观题'){
workDescHtml += `<div style='width:80%;display:flex;'>${index+1}. ${item.title}</div>`;
}
})
workDescHtml += '</div>';
row[i].workdescFormat = workDescHtml;
/**
* 处理[答案显示] - 特殊结构
* [
* {type: '单选题', answer: ['0']},
* {type: '多选题', answer: ['0','1']},
* {type: '填空题', answer: ['填空1','填空2']},
* {type: '判断题', answer: ['0'/'1']},
* {type: '主观题', answer: [xxxx]},
* ]
*/
let workAnswerArr = JSON.parse(row[i].workanswer);
let workAnswerHtml = ``;
workAnswerArr.map( (item, index) => {
const answerArr = item.answer; //JSON.parse(item.answer);
if(item.type == '单选题' || item.type == '多选题'){
const answer = answerArr.map( (item) => {
return String.fromCharCode(65+Number(item))
}).join('');
workAnswerHtml += `<div style='display:flex;'>${index+1}. ${answer}</div>`;
}
else if(item.type == '填空题' ){
const answer = answerArr.join('、');
workAnswerHtml += `<div style='display:flex;'>${index+1}. ${answer}</div>`;
}
else if(item.type == '判断题' ){
const answer = answerArr.map( (item) => {
return item === '1' ? '正确' : '错误'
}).join('、');
workAnswerHtml += `<div style='display:flex;'>${index+1}. ${answer}</div>`;
}
else if(item.type == '主观题' ){
// 复合题里面的主观题只有一个答案,或没填
const answer = answerArr.join('、');
if(answerArr[0]){
workAnswerHtml += `<div style='display:flex;'>${index+1}. ${answer}</div>`;
}else{
workAnswerHtml += `<div style='display:flex;'>${index+1}. ${answer}答案不唯一,请参考分析解答点评!</div>`;
}
}
})
row[i].workanswerFormat = workAnswerHtml;
}
}
else if(row[i].worktype == '主观题' || (row[i].worktype!=='单选题' && row[i].worktype!=='多选题' && row[i].worktype!=='填空题' && row[i].worktype!=='判断题')) {
// 处理[选项显示] - 主观题中无选项, 故置空
row[i].workdescFormat = '';
row[i].workanswerFormat = '';
// 答案处理- eg: "\"不唯一的答案,参考\""
if (row[i].workanswer && row[i].workanswer != '') {
row[i].workanswerFormat = JSON.parse(row[i].workanswer);
}
}
else {
// 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
// 通用选项结构 ['ABC123','ABC123'] | ['ABC123','ABC123'] | [](填空题无选项) | [](判断题无选项)
let workDescArr = [];
if (row[i].workdesc.charAt(0) === '[' && row[i].workdesc.charAt(row[i].workdesc.length - 1) === ']') {
//123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workDescArr = JSON.parse(row[i].workdesc);
}
else if(row[i].workdesc.indexOf('#&') !== -1) {
workDescArr = row[i].workdesc.split('#&');
}
else if(row[i].workdesc.indexOf(',') !== -1){
workDescArr = row[i].workdesc.split(',');
}
else {
// 单字符串直接添加至空数组(待考虑确认)
workDescArr.push(row[i].workdesc);
}
// 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
// 通用答案结构 ['0'] | ['0','1'] | ['填空1','填空2'] | ['0'/'1']
let workAnswerArr = [];
if (row[i].workanswer.charAt(0) === '[' && row[i].workanswer.charAt(row[i].workanswer.length - 1) === ']') {
// 123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
workAnswerArr = JSON.parse(row[i].workanswer);
}
else if(row[i].workanswer.indexOf('#&') !== -1) {
workAnswerArr = row[i].workanswer.split('#&');
}
else if(row[i].workanswer.indexOf(',') !== -1){
workAnswerArr = row[i].workanswer.split(',');
}
else {
// 单字符串直接添加至空数组(待考虑确认)
workAnswerArr.push(row[i].workanswer);
}
// 具体题型处理
if(row[i].worktype == '单选题' || row[i].worktype == '多选题' ){
// 处理[选项显示] - 拼接ABCD首序号
let tmp = '';
let j=0;
for(; j<workDescArr.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}.${workDescArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
}
}
if(j%2== 0){
tmp += '</div>';
}
row[i].workdescFormat = tmp;
// 处理[答案显示] - 转换ABCD
let arr2Char = workAnswerArr.map( (item) => {
return String.fromCharCode(65+Number(item))
}).join('');
row[i].workanswerFormat = arr2Char;
}
else if(row[i].worktype == '填空题'){
// 处理[选项显示] - 填空题中无选项, 故置空
row[i].workdescFormat = '';
// 处理[答案显示] - 逗号连接
row[i].workanswerFormat = workAnswerArr.join('、');
}
else if(row[i].worktype == '判断题'){
// 处理[选项显示] - 判断题中无选项, 故置空
row[i].workdescFormat = '';
// 处理[答案显示] - 1-正常 0-错误
const answer = workAnswerArr.map( (item) => {
return item === '1' ? '正确' : '错误'
}).join('、');
row[i].workanswerFormat = answer;
}
}
}
}
}

View File

@ -11,14 +11,14 @@ const path = isNode?require('path'):{}
const Remote = isNode?require('@electron/remote'):{}
const { ipcRenderer } = isNode?require('electron'):window.electron || {}
const API = isNode?window.api:{} // preload-api
import { useToolState } from '@/store/modules/tool' // 获取store状态
// import { useToolState } from '@/store/modules/tool' // 获取store状态
// const Store = isNode?require('electron-store'):null // 持久化存储
import store from './store'
// 常用变量
const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':''
const isDev = isNode?process.env.NODE_ENV !== 'production':''
const appPath = isNode?Remote.app.getAppPath():'' // 应用目录
// const toolState = useToolState() // 获取store状态
// 暴露Remote中的属性
export const ipcMain = Remote?.ipcMain || {}
@ -31,8 +31,7 @@ export const localStore = store.localStore
// 暴露Store存储对象
export const Store = store
// 延时
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
/**
* 获取静态资源开发和生产环境
* @param {*} url
@ -111,7 +110,6 @@ export const createWindow = async (type, data) => {
resizable: false, // 禁止窗口大小缩放
transparent: true, // 设置透明
alwaysOnTop: true, // 窗口是否总是显示在其他窗口之前
type: 'toolbar', // 创建的窗口类型为工具栏窗口
// parent: mainWin, // 父窗口
// autoClose: true, // 关闭窗口后自动关闭
}
@ -181,13 +179,13 @@ export function toolWindow({url, isConsole, isWeb=true, option={}}) {
return new Promise((resolve) => {
const config = {
width, height,
icon: path.join(appPath, '/resources/logo2.ico'),
type: 'toolbar', // 创建的窗口类型为工具栏窗口
// icon: path.join(__dirname, '../../resources/logo2.ico'),
webPreferences: {
preload: path.join(API.preloadPath, '/index.js'),
sandbox: false,
nodeIntegration: true, // nodeApi调用
contextIsolation: false, // 沙箱取消
devTools: true
},
...option
}
@ -223,7 +221,6 @@ export function toolWindow({url, isConsole, isWeb=true, option={}}) {
* @param {*} win 窗口对象
*/
const eventHandles = (type, win) => {
const toolState = useToolState() // 获取store状态
const winAll = Remote.BrowserWindow.getAllWindows()
const mainWin = winAll.find(o => o.type == 'main') // 主窗口对象
// 公共方法
@ -251,11 +248,8 @@ const eventHandles = (type, win) => {
const on = {
onClosed: () => {
Remote.ipcMain.removeHandler('tool-sphere:set:ignore', setIgnore)
// Remote.ipcMain.removeHandler('tool-sphere:reset')
Remote.ipcMain.removeHandler('tool-sphere:reset')
// Remote.ipcMain.removeAllListeners() // 移除所有监听事件
// 设置状态(再次设置-防止未设置到)
if(toolState.isToolWin) toolState.isToolWin = false
// sessionStore.set('isToolWin', false)
}
}
publicMethods(on) // 加载公共方法
@ -272,22 +266,9 @@ const eventHandles = (type, win) => {
winPdf=null
win&&win.destroy()
})
// 监听窗口的激活事件
win.on('focus', async () => {
console.log('激活窗口')
toolState.isPdfWin=true
await sleep(20) // 延时
toolState.showBoardAll=false //恢复默认值
// await sleep(50) // 延时
// 穿透开启
if (toolState.isToolWin) ipcRenderer.invoke('tool-sphere:set:ignore', true)
});
const on = {
onClosed: () => {
Remote.ipcMain.removeHandler('open-PDF:minimize')
// 设置状态(再次设置-防止未设置到)
if(toolState.isPdfWin) toolState.isPdfWin = false
}
}
publicMethods(on) // 加载公共方法

View File

@ -104,11 +104,7 @@ const switchPageMode = () => {
}
}
onMounted(async () => {
window.addEventListener('focus', () => {
console.log(11111111111111)
})
const isDev = process.env.NODE_ENV == 'development'
// toolState.showBoardAll = false //
toolState.isPdfWin=true //pdf
if (isDev)
pdfObj.pdfUrl = getStaticUrl('aaa.pdf', 'user', 'selfFile', true) //

View File

@ -8,7 +8,7 @@
<template v-for="(item,index) in classList" :key="index">
<el-sub-menu :index="`${index}`">
<template #title>
<span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;" @click="menuClick(item,index)">{{item.caption}}</span>
<span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;">{{item.caption}}</span>
</template>
<el-menu-item-group>
<el-menu-item v-for="(items,routerIndex) in menuItems" :key="`${routerIndex}`" :index="`${items.index}-${item.id}`">{{items.title}}</el-menu-item>
@ -33,7 +33,6 @@ const emits = defineEmits(['handleSelect'])
//
const handleSelect = (itemDom,pathKey) => {
console.log(itemDom,pathKey)
const parts = pathKey[1].split("-")
const result = parts.slice(0, 2).join("-")
const index = props.menuItems.findIndex(item=>item.index===result)
@ -44,18 +43,6 @@ const handleSelect = (itemDom,pathKey) => {
// })
emits('handleSelect',{index,id})
}
const menuClick = (item,index) => {
// el-sub-menu
const currentSubMenu = document.getElementsByClassName('el-sub-menu');
// is-open
if (!currentSubMenu[index].classList.contains('is-opened')) {
//
const str = `1-1-${item.id}`
const arr = [String(index),str]
handleSelect(str,arr)
}
}
</script>

View File

@ -31,7 +31,6 @@ import { getSelfReserv } from '@/api/classManage'
import ReservItem from '@/views/classManage/reserv-item.vue'
import Reserv from '@/views/prepare/container/reserv.vue'
import { useToolState } from '@/store/modules/tool'
import { sessionStore } from '@/utils/tool'
const reservDialog = ref(null)
const tabOptions = ref(['进行中', '已结束'])
const tabActive = ref('进行中')
@ -52,27 +51,21 @@ const doneDataList = computed(() => {
return item.status === '已结束'
})
})
//
const getData = () => {
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],
() => {
console.log('====',toolStore)
setTimeout(()=>{
getData() //
getSelfReserv().then((res) => {
dataList.value = [...res.data]
})
},300)
}
)
onMounted(() => {
getData() //
getSelfReserv().then((res) => {
dataList.value = res.data
})
})
</script>

View File

@ -12,11 +12,6 @@
<!-- <el-button @click="addClass" type="primary" :icon="Plus" >新增班级</el-button>-->
<!-- </div>-->
<!-- </template>-->
<template #footer>
<div>
<el-button @click="addClass" type="primary" :icon="Plus" >加入班级</el-button>
</div>
</template>
</el-card>
</el-aside>
<el-main :style="{'min-height': (viewportHeight - 160) + 'px'}">
@ -33,7 +28,7 @@
</el-main>
</el-container>
</div>
<el-dialog v-model="dialogVisible" title="新增班级" width="50%" append-to-body>
<el-dialog v-model="dialogVisible" title="新增班级" width="50%">
<el-form
style="width: 100%"
label-width="auto"
@ -41,45 +36,30 @@
:rules="rules"
ref="myForm"
>
<!-- <el-form-item label="班级名称" style="margin-right: 10px;width: 50%" prop="caption">-->
<!-- <el-input v-model="classForm.caption" placeholder="请输入班级名称"></el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="任选学科" style="margin-right: 10px;">-->
<!-- <el-radio-group v-model="classForm.edusubject" class="ml-4">-->
<!-- <template v-for="(item, index) in courseList" :key="index">-->
<!-- <el-radio v-if="item.edustage == userStore.edustage" :value="item.itemtitle">{{ item.itemtitle }}</el-radio>-->
<!-- </template>-->
<!-- </el-radio-group>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="年级" style="margin-right: 10px;" prop="agekey">-->
<!-- <el-radio-group v-model="classForm.edudegree">-->
<!-- <template v-for="(item,index) in gradeList" :key="index">-->
<!-- <el-radio v-if="item.edustage == userStore.edustage" :value="item.value">{{ item.label }}</el-radio>-->
<!-- </template>-->
<!-- </el-radio-group>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="老师" prop="teacherid">-->
<!-- {{ userStore.nickName }}-->
<!-- </el-form-item>-->
<!-- <el-form-item label="简要说明" style="margin-right: 10px;" prop="classdesc">-->
<!-- <el-input v-model="classForm.classdesc" placeholder="请输入简要说明"></el-input>-->
<!-- </el-form-item>-->
<el-form-item label="老师" style="margin-right: 10px;width: 50%">
<el-text>
{{userStore.nickName}}
</el-text>
<el-form-item label="班级名称" style="margin-right: 10px;width: 50%" prop="caption">
<el-input v-model="classForm.caption" placeholder="请输入班级名称"></el-input>
</el-form-item>
<el-form-item label="班级">
<el-tree-select
v-model="classids"
:data="gradeTree"
multiple
:render-after-expand="false"
style="width: 240px"
/>
<el-form-item label="任选学科" style="margin-right: 10px;">
<el-radio-group v-model="classForm.edusubject" class="ml-4">
<template v-for="(item, index) in courseList" :key="index">
<el-radio v-if="item.edustage == userStore.edustage" :value="item.itemtitle">{{ item.itemtitle }}</el-radio>
</template>
</el-radio-group>
</el-form-item>
<el-form-item label="年级" style="margin-right: 10px;" prop="agekey">
<el-radio-group v-model="classForm.edudegree">
<template v-for="(item,index) in gradeList" :key="index">
<el-radio v-if="item.edustage == userStore.edustage" :value="item.value">{{ item.label }}</el-radio>
</template>
</el-radio-group>
</el-form-item>
<el-form-item label="老师" prop="teacherid">
{{ userStore.nickName }}
</el-form-item>
<el-form-item label="简要说明" style="margin-right: 10px;" prop="classdesc">
<el-input v-model="classForm.classdesc" placeholder="请输入简要说明"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="btnSave"> </el-button>
@ -89,7 +69,7 @@
<script setup>
import {ref, onMounted, reactive,watch,nextTick} from 'vue'
import {listClassmain, addClassmain, listEvaluation,addClasses} from '@/api/classManage/index'
import {listClassmain, addClassmain, listEvaluation} from '@/api/classManage/index'
import { Plus } from '@element-plus/icons-vue'
import useUserStore from '@/store/modules/user'
import {ElMessage} from "element-plus";
@ -154,8 +134,6 @@
])
//
const dialogVisible = ref(false)
//
const gradeTree = ref([])
//
const myForm = ref(null)
const rules = reactive({
@ -164,147 +142,78 @@
{ min: 1, max: 10, message: '班级名称必须是 1-10 位 的字符', trigger: 'blur' }
],
})
const newGradeList = reactive([
[
{ label: '一年级', agekey: 1, checked: false, current: 1 },
{ label: '二年级', agekey: 2, checked: false, current: 1 },
{ label: '三年级', agekey: 3, checked: false, current: 1 },
{ label: '四年级', agekey: 4, checked: false, current: 1 },
{ label: '五年级', agekey: 5, checked: false, current: 1 },
{ label: '六年级', agekey: 6, checked: false, current: 1 },
],
[
{ label: '初一', agekey: 7, checked: false, current: 2 },
{ label: '初二', agekey: 8, checked: false, current: 2 },
{ label: '初三', agekey: 9, checked: false, current: 2 },
],
[
{ label: '高一', agekey: 10, checked: false, current: 3 },
{ label: '高二', agekey: 11, checked: false, current: 3 },
{ label: '高三', agekey: 12, checked: false, current: 3 },
],
])
//
const classids = ref('')
//
const getClassInfo = () => {
classList.value = []
listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' }).then(response => {
// response.rows.forEach(item => {
// if(item.teacherid && Number(item.teacherid) === userStore.userId){
// classList.value.push(item)
// }
// })
classList.value = [...response.rows]
//
gradeTree.value = groupByCondition(response.rows, item => item.agekey);
if(classList.value.length > 0){
classId.value = classList.value[0].id
currentIndex.value = 0
}
});
}
//
function groupByCondition(arr, condition) {
//
const groups = arr.reduce((groups, item) => {
const groupKey = condition(item);
item.label = item.caption
item.value = item.id
groups[groupKey] = groups[groupKey] || [];
groups[groupKey].push(item);
return groups;
}, {});
//
const formattedGroups = Object.keys(groups).map(key => ({
label: gradeName(key),
value:key,
children: groups[key]
}));
return formattedGroups;
}
//key
function gradeName(key){
//
const flatGradeDataList = newGradeList.flat();
const currentIndex = flatGradeDataList.findIndex(item => item.agekey === Number(key));
if(currentIndex !== -1){
return flatGradeDataList[currentIndex].label;
}else{
//
const defaultLabel = '社团';
flatGradeDataList[currentIndex] = { ...flatGradeDataList[currentIndex], label: defaultLabel };
return defaultLabel;
}
}
//
// const getCourseList = () => {
// //
// listEvaluation({ itemkey: "subject", pageSize: 500 }).then((res) => {
// courseList.value = [...res.rows];
// });
// }
const getCourseList = () => {
//
listEvaluation({ itemkey: "subject", pageSize: 500 }).then((res) => {
courseList.value = [...res.rows];
});
}
//
const addClass = () => {
dialogVisible.value = true
// getCourseList()
getCourseList()
}
// const btnSave = () => {
// myForm.value.validate((valid) => {
// if (valid) {
// //
// listClassmain({ entpid: userStore.deptId, status: 'open', pageSize: 500 }).then(response => {
// const data = [...response.rows]
// const existList = [];
// data.forEach(item => {
// if (parseInt(textSimilar(item.caption, classForm.caption, 2)) > 80) {
// existList.push(item);
// }
// })
//
// if (existList.length == 0) {
// const age = classForm.edudegree;
// const index = gradeList.value.findIndex(item => item.label === age);
// classForm.agekey = gradeList.value[index].agekey
// classForm.edudegree = `${gradeList.value[index].agekey}`
// classForm.entpid = userStore.deptId;
// classForm.status = 'open';
// classForm.teachername = userStore.nickName;
// classForm.teacherid = userStore.userId;
// classForm.teacherSubject = classForm.edusubject;
// addClassmain(classForm).then(response => {
// if (response.code === 200) {
// dialogVisible.value = false
// ElMessage({
// message: '',
// type: 'success',
// })
// getClassInfo()
// }
// });
// }else{
// ElMessage({
// message: '',
// type: 'warning',
// })
// }
// })
// }
// })
// }
//
const btnSave = () => {
addClasses({classIds:classids.value.join(','),regId:userStore.userId}).then(res => {
if (res.code === 200) {
myForm.value.validate((valid) => {
if (valid) {
//
listClassmain({ entpid: userStore.deptId, status: 'open', pageSize: 500 }).then(response => {
const data = [...response.rows]
const existList = [];
data.forEach(item => {
if (parseInt(textSimilar(item.caption, classForm.caption, 2)) > 80) {
existList.push(item);
}
})
if (existList.length == 0) {
const age = classForm.edudegree;
const index = gradeList.value.findIndex(item => item.label === age);
classForm.agekey = gradeList.value[index].agekey
classForm.edudegree = `${gradeList.value[index].agekey}年级`
classForm.entpid = userStore.deptId;
classForm.status = 'open';
classForm.teachername = userStore.nickName;
classForm.teacherid = userStore.userId;
classForm.teacherSubject = classForm.edusubject;
addClassmain(classForm).then(response => {
if (response.code === 200) {
dialogVisible.value = false
ElMessage({
message: res.msg,
message: '新增成功',
type: 'success',
})
getClassInfo()
}
});
}else{
ElMessage({
message: res.msg,
message: '班级名称重复',
type: 'warning',
})
}
})
}
})
}
//
const textSimilar = (s, t, f) => {
if (!s || !t) {

View File

@ -20,14 +20,14 @@
</div>
</div>
<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 !== '已结束'" type="primary" @click="startClassR(item)"
>上课</el-button
>
<el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>
<!-- <el-button v-if="item.status === '上课中'" type="info" @click="endClassR(item)"
>下课</el-button
>-->
<el-button v-if="item.status!='上课中'" type="danger" @click="deleteReserv">删除</el-button>
<el-button type="danger" @click="deleteReserv">删除</el-button>
</div>
</div>
</template>
@ -46,7 +46,6 @@ const props = defineProps({
}
})
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
const toolStore = useToolState() // -tool
const openEdit = () => {
emit('openEdit', props.item)
}
@ -62,20 +61,18 @@ const deleteReserv = () => {
})
}
const startClassR = (item) => {
// startClass(item.id).then((res) => {
// if (res.data === true) {
// item.status = ''
// openLesson()
// }
// })
startClass(item.id).then((res) => {
if (res.data === true) {
item.status = '上课中'
openLesson()
}
})
}
// const toolStore = useToolState()
let wins = null;
// -
const openLesson = () => {
// startClass(props.item.id)
startClass(props.item.id)
listEntpcourse({
evalid: props.item.ex2,
edituserid: useUserStore().user.userId,

View File

@ -1,15 +1,15 @@
<template>
<div style="height: 100%">
<el-card style="width: 100%;height: 100%;overflow-y: auto">
<!-- <template #header>-->
<!-- <div style="text-align: left;display: flex;justify-content: space-between">-->
<!--&lt;!&ndash; <div>&ndash;&gt;-->
<!--&lt;!&ndash; <el-button type="primary" @click="addStudent(0)">新增学生</el-button>&ndash;&gt;-->
<!--&lt;!&ndash; <el-button type="primary" @click="importStudent()">导入学生</el-button>&ndash;&gt;-->
<!--&lt;!&ndash; </div>&ndash;&gt;-->
<!--&lt;!&ndash; <el-text class="mx-1">点击学生头像查看学生信息</el-text>&ndash;&gt;-->
<template #header>
<div style="text-align: left;display: flex;justify-content: space-between">
<!-- <div>-->
<!-- <el-button type="primary" @click="addStudent(0)">新增学生</el-button>-->
<!-- <el-button type="primary" @click="importStudent()">导入学生</el-button>-->
<!-- </div>-->
<!-- </template>-->
<el-text class="mx-1">点击学生头像查看学生信息</el-text>
</div>
</template>
<div>
<div class="studentContent">
<template v-if="studentList.length > 0">

View File

@ -1,411 +0,0 @@
<!--
* @Author: 苦逼程序猿
* @Date: 2024-09-06 16:58:59
* @Warning: 千行代码Bug露锋芒
-->
<template>
<el-container class="class-reserv-wrap">
<div class="class-reserv-tabs">
<el-segmented v-model="tabActive" block :options="tabOptions" size="large" />
</div>
<div class="class-reserv-body">
<task-item
v-for="(item, index) in activeDataList"
v-show="tabActive === '进行中'"
:key="index"
:item="item"
:tabactive="tabActive"
@click="onClickItem(item)"
@delete-reserv="deleteReserv(item)"
></task-item>
<task-item
v-for="(item, index) in doneDataList"
v-show="tabActive === '已结束'"
:key="index"
:item="item"
:tabactive="tabActive"
@click="onClickItem(item)"
@delete-reserv="deleteReserv(item)"
></task-item>
</div>
<item-dialog ref="itemDialogRef" @cle-click="closeDialog"></item-dialog>
</el-container>
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, watch, reactive } from 'vue'
import { getSelfReserv } from '@/api/classManage'
import { listClassmain } from '@/api/classManage/index'
import { listClassworkdata } from '@/api/classTask'
import { homeworklist } from '@/api/teaching/classwork'
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 { sessionStore } from '@/utils/tool'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user
const itemDialogRef = ref(null)
const tabOptions = ref(['进行中', '已结束'])
const tabActive = ref('进行中')
const dataList = ref([])
//
const classList = ref([])
const classListIds = ref([])
//
const classWorkList = ref([])
const total = ref(0)
const loading = ref(false)
const activeDataList = computed(() => {
// return classWorkList.value
// []
return classWorkList.value && classWorkList.value.filter((item) => getDateTime > item.deaddate)
})
const deleteReserv = (item) => {
console.log('删除待开发', item)
// dataList.value = dataList.value.filter((is) => {
// return is.id !== item.id
// })
}
const doneDataList = computed(() => {
// return classWorkList.value
return classWorkList.value && classWorkList.value.filter((item) => getDateTime < item.deaddate)
})
//
const getData = () => {
//
listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' }).then((res) => {
var clslist = []
for (var i = 0; i < res.rows.length; i++) {
if (res.rows[i].classstudentlist != '') {
var array = JSON.parse('[' + res.rows[i].classstudentlist + ']')
res.rows[i].classstudents = array
}
classListIds.value.push(res.rows[i].id)
clslist.push(res.rows[i])
}
classList.value = clslist
//
homeworklist({
classidarray: classListIds.value.join(','),
//entpcourseid: '', // id
edustage: userStore.edustage,//
edusubject: userStore.edusubject,//
orderby: 'uniquekey DESC',
pageSize: 100
}).then((response) => {
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 (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
}
}
// (workdatacount)>0
if (response.rows && response.rows.length > 0) {
classWorkList.value = response.rows && response.rows.filter((item) => item.workdatacount > 0)
// classWorkList.value = response.rows && response.rows.filter((item) => item.workdatacount > 0 && item.uniquekey == '-0808-1')
//TODO total
total.value = response.total
}
loading.value = false
//
getStudentClassWorkData()
})
})
//--------------
// 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()
//
const escapeHtmlQuotes = (str) => {
// replace,
const regex1 = /\\+/g; //
let result = str.replace(regex1, '\\');
return result;
}
const pollingST = ref(null) //
onMounted(() => {
getData() //
//
getStudentClassWorkDataPolling()
})
//
const getStudentClassWorkDataPolling = () => {
//
getStudentVisible()
//
pollingST.value = setInterval(() => {
getStudentVisible()
}, 1000 * 10)
}
const closeDialog = () => {
console.log('关闭弹窗,开启作业进度轮询')
getStudentClassWorkDataPolling()
}
const onClickItem = (item) => {
console.log('开启弹窗,关闭作业进度轮询')
clearInterval(pollingST.value)
itemDialogRef.value.openDialog(item)
}
onUnmounted(() => {
clearInterval(pollingST.value)
})
const getDateTime = () => {
//
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hh = String(now.getHours()).padStart(2, '0')
const mm = String(now.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hh}:${mm}`
}
// [] -
const getStudentVisible = async () => {
if (classListIds.value.length <= 0) {
return
}
//
const response = await homeworklist({
classidarray: classListIds.value.join(','),
//entpcourseid: '', // id
edustage: userStore.edustage,//
edusubject: userStore.edusubject,//
orderby: 'uniquekey DESC',
pageSize: 100
})
const curWorkList = response.rows
/**
* 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 = (classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount).toFixed(0)
} else {
classWorkList.value[t].averagetime = 0
}
//
classWorkList.value[t].teacherrationgcount = curWork.teacherrationgcount
} else {
classWorkList.value[t].finishpercent = 0
}
}
return 1
}
//
const getStudentClassWorkData = () => {
//
listClassworkdata({
classids: classListIds.value.join(','),
//entpcourseid: '', // id
edustage: userStore.edustage,//
edusubject: userStore.edusubject,//
orderby: "deaddate DESC",
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].uniquekey == classWorkList.value[t].uniquekey) {
if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].resultcount > 0) {
console.log('==================')
// /
// resultcount0
classWorkList.value[t].workdatafeedbackcount++
//
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 (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])
}
}
// 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 = (classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount).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%'
}
//
//
}
})
}
watch(
() => [dataList, toolStore.isToolWin],
() => {
console.log('====', toolStore)
setTimeout(() => {
getData() //
}, 300)
}
)
</script>
<style scoped lang="scss">
.class-reserv-wrap {
height: 100%;
display: flex;
flex-direction: column;
padding: 15px 30px;
.class-reserv-tabs {
width: 30%;
text-align: left;
}
.class-reserv-body {
height: 100%;
flex: 1;
overflow: auto;
padding: 10px 0;
}
}
</style>

View File

@ -1,68 +0,0 @@
<template>
<div class="common-layout" style="width: 100%; height: 73vh;">
<el-container>
<el-container>
<el-header style="height: auto">
<!--学情分布-->
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
学情分布
</div>
</template>
<Distribution></Distribution>
</el-card>
</el-header>
<el-main>
<!-- 时长分析-->
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
时长分析
</div>
</template>
<TimeAnalyse></TimeAnalyse>
</el-card>
</el-main>
<el-footer style="height: auto;margin-bottom: 20px;">
<!--知识点概况-->
<el-card>
<template #header>
<div style="font-size: 20px;font-weight: bold">
知识点概览
</div>
</template>
<Konwledge></Konwledge>
</el-card>
</el-footer>
</el-container>
</el-container>
</div>
</template>
<script setup>
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: {
type: Array,
default: () => {
return []
}
},
// evalId:{
// type: Number,
// default: 0
// }
})
watch(() => props.tableList,() => {
useOverview.getTableList(props.tableList)
},{deep:true})
</script>

View File

@ -1,23 +0,0 @@
<template>
<div class="common-layout">
<el-container>
<el-aside width="400px">
<!-- 柱状图学情分布-->
<Echarts></Echarts>
</el-aside>
<el-main>
<!-- 列表分布的人员-->
<StuList></StuList>
</el-main>
</el-container>
</div>
</template>
<script setup>
import Echarts from '@/views/classTask/container/classOverview/distribution/echarts.vue'
import StuList from "@/views/classTask/container/classOverview/distribution/stuList.vue";
</script>
<style scoped>
</style>

View File

@ -1,109 +0,0 @@
<template>
<div className="chart-container">
<div ref="chartRef" className="chart"></div>
</div>
</template>
<script setup>
import {ref,nextTick,watch} from 'vue';
import * as echarts from 'echarts';
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
//
const chartRef = ref(null);
//
const dataList = ref([
{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},
]);
//
function getColor(index) {
//
const colors = ['#d14a61','#675bba', '#e89110','#008c8c','#5793f3'];
return colors[index];
}
//
function initChart() {
const myChart = echarts.init(chartRef.value);
const options = {
tooltip: {
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: 'bar',
barWidth: '30%',
data: dataList.value.map(item => item.value),
itemStyle: {
color: function (params) {
//
return getColor(params.dataIndex);
}
},
//
label: {
show: true,
position: 'top',
formatter: '{c}人',
color: '#333',
fontSize: 12
}
}]
};
myChart.setOption(options);
}
//
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,() => {
showEcharts()
nextTick(() => {
initChart();
})
})
</script>
<style scoped>
.chart-container {
width: 100%;
height: 400px;
}
.chart {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,115 +0,0 @@
<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">
<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}}</el-tag>
</template>
</template>
<template v-else>
<el-empty description="该分段暂时没有学生" />
</template>
</el-tab-pane>
</template>
</el-tabs>
</template>
<script setup>
import {nextTick, ref, watch} from 'vue'
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
const tabPosition = ref('left')
const leftList = ref([
{
label:'优',
stuList:[],
rating:1
},
{
label:'优-',
stuList:[],
rating:2
},
{
label:'良',
stuList:[],
rating:3
},
{
label:'良-',
stuList:[],
rating:4
},
{
label:'差',
stuList:[],
rating:5
},
])
//
const handelChange = (item) => {
showStudents(item)
}
//
const showStudents = (index) => {
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,() => {
showStudents(0)
})
</script>
<style scoped>
:deep(.el-tabs__item) {
position: relative; /* 使 ::before 相对于自身定位 */
padding-left: 24px; /* 增加左边距以留出圆圈的位置 */
}
/* 圆圈样式 */
:deep(.el-tabs__item::before) {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid #409eff; /* 创建空心圆圈 */
background-color: transparent; /* 设置背景颜色为透明 */
margin-left: 5px;
}
/* 根据索引设置不同的颜色 */
:deep(.el-tabs__item:nth-child(1)::before) {
border-color: #5793f3;
}
:deep(.el-tabs__item:nth-child(2)::before) {
border-color: #d14a61;
}
:deep(.el-tabs__item:nth-child(3)::before) {
border-color: #675bba;
}
:deep(.el-tabs__item:nth-child(4)::before) {
border-color: #e89110;
}
:deep(.el-tabs__item:nth-child(5)::before) {
border-color: #008c8c;
}
/* 选中状态下的样式 */
:deep(.el-tabs__item.is-active::before) {
background-color: transparent; /* 改变选中状态下的圆圈颜色 */
}
:deep(.el-tabs__item.is-active){
background-color: rgb(238, 241, 246);
}
:deep(.el-tabs--left .el-tabs__item.is-left){
text-align: left;
justify-content: flex-start;
}
</style>

View File

@ -1,104 +0,0 @@
<template>
<el-table
:data="tableData"
style="width: 100%; margin-bottom: 20px;min-height: 300px;"
row-key="id"
border
default-expand-all
>
<el-table-column prop="title" label="知识点"/>
<el-table-column prop="allPoint" label="分值" sortable/>
<el-table-column prop="point" label="平均分" sortable/>
<el-table-column prop="scoingRate" label="得分率" sortable>
<template #default="scope">
<div>{{scope.row.scoingRate + '%'}}</div>
</template>
</el-table-column>
</el-table>
</template>
<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 getKonwledge = () => {
useOverview.tableList.forEach(item => {
if(item.knowledgePoint){
konwledge.value.push({...JSON.parse(item.knowledgePoint),...{scoingRate:Number(item.scoingRate),point:item.point,allPoint:allScore.value}})
}
})
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,() => {
console.log(useOverview.tableList,'useOverview.tableList')
getScore()
})
</script>
<style scoped>
</style>

View File

@ -1,184 +0,0 @@
<template>
<div class="chart-container">
<div ref="chartRef" class="chart"></div>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import {ref, nextTick, watch} from 'vue'
import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore()
//
const chartRef = ref(null);
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);
const options = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: ['预估时长', '平均用时']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
name: '得分率',
nameTextStyle: {
color: '#999',
fontSize: 12,
padding: [0, 0, 10, 0]
},
data: xAxisData.value
},
yAxis: {
type: 'value',
name: '作业时长',
nameTextStyle: {
color: '#999',
fontSize: 12,
padding: [0, 0, 10, 0]
},
},
series: [
{
name: `预估时长`,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 10,
lineStyle: {
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(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;
}
watch(() => useOverview.tableList,() => {
getyAxisData()
nextTick(() => {
initChart();
})
})
</script>
<style scoped>
.chart-container {
width: 100%;
height: 400px;
}
.chart {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,976 +0,0 @@
<template>
<el-form ref="classWorkFormScoreRef" :model="classWorkFormScore">
<!-- <div class="teacher_content" :style="{ height: dialogProps.maxheight + 'px' }"> -->
<div class="teacher_content" :style="{ height: '75vh' }">
<div style="font-size: 18px; width: 100%; padding: 5px 10px" class="sticky">
{{ classWorkFormScore.name }} 答题详情
</div>
<div class="teacher_content_con">
<!-- 题目内容习题训练 -->
<div v-if="dialogProps.studentObj.worktype == '习题训练'">
<div v-for="(stuItem, sIndex) in dialogProps.studentQuizAllList" :key="stuItem.id">
<div v-for="quItem in dialogProps.quizlist" :key="quItem.id">
<div v-if="stuItem.entpcourseworkid == quItem.id">
<el-card style="max-width: 100%; margin-bottom: 10px">
<!-- 题型 分值 -->
<template #header>
<div class="card-header">
<span
>{{ sIndex + 1 }}{{ quItem.worktype }}
{{ stuItem.score ? stuItem.score : 0 }}</span
>
</div>
</template>
<!-- 习题训练 -->
<div v-if="dialogProps.studentObj.worktype == '习题训练'">
<el-row>
<el-col :span="24" style="padding: 10px">
<!-- 题源题目标题题目选项 -->
<span>{{ quItem.worktag }}</span>
<span style="margin-left: 4px" v-html="quItem.titleFormat"></span>
<div :span="24" style="padding: 10px" v-html="quItem.workdescFormat"></div>
<!-- 折叠 详情分析解答 -->
<div class="demo-collapse">
<el-collapse>
<el-collapse-item title="详情分析解答" name="1">
<el-row style="padding: 1% 4%; border: 2px dotted">
<template #default="scope">
<el-col :span="2" style="padding: 10px 0px"
><em>答案</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.workanswerFormat"
></el-col>
<el-col :span="2" style="padding: 10px 0px"
><em>分析</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.method"
></el-col>
<el-col :span="2" style="padding: 10px 0px"
><em>解答</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.analyse"
></el-col>
<el-col :span="2" style="padding: 10px 0px"
><em>点评</em></el-col
>
<el-col
:span="21"
style="padding: 10px 0px"
v-html="quItem.discuss"
></el-col>
</template>
</el-row>
</el-collapse-item>
</el-collapse>
</div>
</el-col>
</el-row>
</div>
<!-- 答案 -->
<template #footer>
<el-row>
<el-col :span="6" style="padding: 10px">
<span
>参考答案
<span v-if="quItem.workanswerFormat != ''">
<sapn
style="
background-color: #0ed116;
color: white;
padding: 0 5px;
border-radius: 5px;
"
>{{ quItem.workanswerFormat.replace(/<[^>]+>/g, '') }}</sapn
>
</span>
</span>
</el-col>
<el-col :span="6" style="padding: 10px">
<!-- <span>学生答案{{ stuItem.feedcontent }}</span> -->
<span
>学生答案
<span v-if="quItem.workdesc == ''|| quItem.workdesc == '[]' ">
<!-- quItem.workdesc 没值说明是非选择题 -->
<span
v-if="stuItem.feedcontent != ''"
style="
background-color: red;
color: white;
padding: 0 5px;
border-radius: 5px;
"
>
{{ stuItem.feedcontent.replace('#', '、') }}
</span>
</span>
<span v-else>
<!-- 选择题类型学生答题转换为 ABCD格式 -->
<span
v-if="stuItem.feedcontent != ''"
style="
background-color: red;
color: white;
padding: 0 5px;
border-radius: 5px;
"
>
{{
JSON.parse(quItem.workdesc)
.map((item, index) => {
if (item == stuItem.feedcontent) {
return String.fromCharCode(65 + Number(index))
}
})
.filter(Boolean)[0]
}}
</span>
</span>
</span>
</el-col>
<el-col :span="6" style="padding: 10px">
<div
v-for="(imageItem, index) in stuItem.imagefile"
v-if="stuItem.imagefile && stuItem.imagefile.length > 0"
:key="index"
>
<el-image
style="width: 30px; height: 30px"
:src="imageItem"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="stuItem.imagefile"
:initial-index="4"
/>
</div>
</el-col>
<el-col :span="6" style="padding: 10px">
<!-- 单选题 填空题 多选题 判断题 主观题 复合题 待完善
1目前只支持单选题多选题不用老师批改分数这里先禁止分值修改 -->
<el-input-number
v-model="classWorkFormScore.teacherRating[sIndex].score"
:min="0"
:max="classWorkFormScore.teacherRating[sIndex].maxScore"
size="small"
:disabled="
(quItem.worktype == '单选题' || quItem.worktype == '多选题') &&
stuItem.feedcontent == stuItem.rightanswer &&
stuItem.feedcontent != ''
? true
: false
"
></el-input-number>
</el-col>
</el-row>
</template>
</el-card>
</div>
</div>
</div>
</div>
<!-- 题目内容常规作业课堂展示 -->
<div
v-if="
dialogProps.studentObj.worktype == '常规作业' ||
dialogProps.studentObj.worktype == '课堂展示' ||
dialogProps.studentObj.worktype == '框架梳理'
"
>
<div v-for="(stuItem, sIndex) in dialogProps.studentQuizAllList" :key="stuItem.id">
<el-card style="max-width: 100%; margin-bottom: 10px">
<!-- 题型 分值 -->
<template #header>
<div class="card-header">
<span>{{ sIndex + 1 }}{{ stuItem.score ? stuItem.score : 0 }}</span>
</div>
</template>
<!-- 常规作业 -->
<div
v-if="
dialogProps.studentObj.worktype == '常规作业' ||
dialogProps.studentObj.worktype == '课堂展示' ||
dialogProps.studentObj.worktype == '框架梳理'
"
>
<!-- 文件内容格式mp3/mp4/doc/docx/excel/pdf/ppt/pptx/jpg/jpeg/gif/png/txt ->
<-- 老师附件展示 -->
<!-- 折叠 详情分析解答 -->
<div v-if="teacherFeedContentList.length > 0">
<div class="demo-collapse" style="border: 2px dotted">
<el-collapse>
<el-collapse-item title="老师布置详情" name="1">
<div class="image_list">
<div v-if="teachImageList.length > 0">
<div style="margin-bottom: 5px">
<span style="color: red">温馨提示点击图片可放大预览 </span>
</div>
<div v-for="(imageItem, index) in teachImageList" :key="index">
<el-image
style="width: 400px; height: 400px"
:src="imageItem.url"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="
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"
/>
</div>
</div>
<div v-if="teachFileList.length > 0">
<div style="margin: 10px 0">
<span style="color: red" @click="openFile"
>温馨提示点击此处 可预览其他类型附件
</span>
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
<!-- 学生答题展示 -->
<div v-if="feedContentList.length > 0">
<p>学生答题附件内容</p>
<div class="image_list">
<div v-if="imageList.length > 0">
<div style="margin-bottom: 5px">
<span style="color: red">温馨提示点击图片可放大预览 </span>
</div>
<div v-for="(imageItem, index) in imageList" :key="index">
<el-image
style="width: 500px; height: 500px"
:src="imageItem.url"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="
imageList
.filter(
(item) =>
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
)
.map((item) => item.url)
"
:initial-index="4"
/>
</div>
</div>
<div v-if="fileList.length > 0">
<div style="margin: 10px 0">
<span style="color: red" @click="openFile"
>温馨提示点击此处 可预览其他类型附件
</span>
</div>
</div>
</div>
</div>
<div v-else>
<el-empty
description="该学生还未作答"
style="width: 100%; height: 200px"
></el-empty>
</div>
</div>
<!-- 答案 -->
<!-- <template #footer>
<el-row>
<el-col :span="12" style="padding: 10px">
<el-input-number v-model="classWorkFormScore.teacherRating[sIndex].score"
:controls="false"
:min="0"
:max="classWorkFormScore.teacherRating[sIndex].maxScore"
size="small"
></el-input-number>
</el-col>
</el-row>
</template> -->
</el-card>
</div>
</div>
</div>
<!-- 批改评价与评语 -->
<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>
<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 style="margin: 0; color: red">{{
classWorkFormScore.teacherRating.reduce((a, b) => a + b.score, 0).toFixed(2)
}}</span
>
</span>
<span v-else>
得分
<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>
<div class="score-container">
<div
v-for="(score, index) in teacherRatingList"
:key="index"
:class="[
'score-circle',
{ active: classWorkFormScore.rating == score.ratingKey }
]"
@click="selectScore(score)"
>
{{ score.ratingValue }}
</div>
</div>
</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"
:label="item.label"
:value="item.value"
/>
<template #footer>
<el-button v-if="!isAdding" text bg size="small" @click="onAddOption">
新增常用语
</el-button>
<template v-else>
<el-input
v-model="optionName"
class="option-input"
placeholder="输入新的常用语"
size="small"
/>
<el-button type="primary" size="small" @click="onConfirm"> 确定 </el-button>
<el-button size="small" @click="clear">取消</el-button>
</template>
</template>
</el-select>
</el-col>
</el-row>
</el-col>
<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="3"
placeholder="请输入评语说明"
/>
</el-col>
</el-form-item>
</el-col>
</el-row>
<div style="margin: 10px">
<el-button type="primary" @click="onClassWorkFormScoreSave">批阅确认</el-button>
</div>
</div>
</div>
<!-- :style="{ height: dialogProps.maxheight + 'px' }" -->
<el-dialog
v-model="fileReadopen"
title="文件预览"
width="80%"
:style="{ height: '75vh' }"
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 v-if="fileList.length > 0">
<el-card style="max-width: 480px">
<template #header>
<div class="card-header">
<span>学生文件列表</span>
</div>
</template>
<div
v-for="item in fileList"
: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: dialogProps.maxheight + 'px' }">
<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>
</el-form>
</template>
<script setup name="classWorkAnalysisScoreDialogRef">
import { useRouter, useRoute } from 'vue-router'
// import useAppStore from '@/store/modules/app'
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, updateClassworkdata } from '@/api/classTask'
import { getTimeDate } from '@/utils/date'
import ReFilePreview from '@/components/refile-preview/index.vue'
const userStore = useUserStore()
const router = useRouter()
const route = useRoute()
const emits = defineEmits(['class_work_score_submit'])
const props = defineProps({})
const fileReadopen = ref(false)
const analysisScoreOpen = ref(false)
const dialogProps = ref({
maxheight: 300,
studentObj: {}, //
studentQuizAllList: [], //list
quizlist: [] // list
})
//list
const feedContentList = ref([])
const imageList = ref([])
const fileList = ref([])
// list
const teacherFeedContentList = ref([])
const teachImageList = ref([])
const teachFileList = ref([])
//
const classWorkFormScore = reactive({
name: '', //
score: 0, //
teacherRating: [], // list
rating: 0, // 1- 2- 3- 4- 5-
teacherremark: '' //
})
const teacherRatingList = ref([
{ ratingKey: '1', ratingValue: '优' },
{ ratingKey: '2', ratingValue: '优-' },
{ ratingKey: '3', ratingValue: '良' },
{ ratingKey: '4', ratingValue: '良-' },
{ ratingKey: '5', ratingValue: '差' }
])
// 线
//#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)
}
}
const openFile = () => {
console.log('打开文件弹窗!')
fileReadopen.value = true
}
// 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 = '文件读取失败,您可以点击上方链接跳到另外页面查看'
}
}
//#endregion
// e
// e valuenull
const handleChange = (value) => {
console.log(value, '=====')
console.log(typeof value, '===value==')
// if ((value+"").includes('e')) {
// classWorkFormScore.teacherRating[0].score = value.replace('e', '');
// }
if (typeof value === 'object') {
console.log('??????')
classWorkFormScore.teacherRating[0].score = 0
} else {
classWorkFormScore.teacherRating[0].score = value
}
}
//#region
const isAdding = ref(false)
const value = ref([])
const optionName = ref('')
const cities = ref([
{
value: '需要继续加油努力哟!棒棒哒!',
label: '需要继续加油努力哟!棒棒哒!'
},
{
value: '做得很好',
label: '做得很好'
},
{
value: '需要改进',
label: '需要改进'
},
{
value: '需要更多练习',
label: '需要更多练习'
},
{
value: '需要更仔细',
label: '需要更仔细'
},
{
value: '需要更清晰',
label: '需要更清晰'
},
{
value: '需要更准确',
label: '需要更准确'
},
{
value: '需要更详细',
label: '需要更详细'
},
{
value: '需要更简洁',
label: '需要更简洁'
},
{
value: '需要更有条理',
label: '需要更有条理'
},
{
value: '需要更有创意',
label: '需要更有创意'
}
])
const onSelectOption = (option) => {
classWorkFormScore.teacherremark = option
}
const onAddOption = () => {
isAdding.value = true
}
const onConfirm = () => {
if (optionName.value) {
cities.value.push({
label: optionName.value,
value: optionName.value
})
clear()
}
}
const clear = () => {
optionName.value = ''
isAdding.value = false
}
//#endregion
const selectScore = (score) => {
console.log(score, 'score----')
classWorkFormScore.rating = score.ratingKey
}
//
const acceptParams = (params) => {
console.log(params)
console.log(dialogProps, 'dialogProps')
//
//
classWorkFormScore.name = '' //
classWorkFormScore.score = 0 //
classWorkFormScore.teacherRating = [] //
classWorkFormScore.rating = 0 // 1- 2- 3- 4- 5-
classWorkFormScore.teacherremark = '' //
value.value = '' //
//
feedContentList.value = []
imageList.value = []
fileList.value = []
//
teacherFeedContentList.value = []
teachImageList.value = []
teachFileList.value = []
// -----------------
dialogProps.value = params
classWorkFormScore.name = params.studentObj.studentname
//
if (params.studentQuizAllList.length > 0) {
if (params.studentObj.worktype == '习题训练') {
params.studentQuizAllList.forEach((item) => {
params.quizlist.forEach((item2) => {
//
if (item.entpcourseworkid == item2.id) {
//
classWorkFormScore.teacherRating.push({
maxScore: item.score ? item.score : 100, // 100
score:
(item2.worktype == '单选题' || item2.worktype == '多选题') &&
item.feedcontent == item.rightanswer
? item.score
: item.teacherRating
? item.teacherRating
: 0, // :
id: item.id // id
})
classWorkFormScore.teacherremark = item.teacherremark //
// imagefile [null] "[\"https://wzyzoss.ee2f687.png\"]"
if (item.imagefile != '') {
// !
const filelist = JSON.parse(item.imagefile)
console.log(filelist, '学生习题附带图片fimagefile-------------')
if (filelist && filelist.length > 0 && filelist[0] != null) {
item.imagefile = filelist
} else {
item.imagefile = ''
}
}
}
})
})
} else {
//
if (params.studentObj.worktype == '常规作业') {
try {
// datacontent TODO
if (params.studentObj.datacontent != '') {
const teachWorkFileList = JSON.parse(params.studentObj.datacontent)
console.log(teachWorkFileList, '老师filelist-------------')
teachWorkFileList &&
teachWorkFileList.forEach((item) => {
if (
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
) {
teachImageList.value.push(item)
} else {
teachFileList.value.push(item)
}
})
teacherFeedContentList.value.push(teachWorkFileList)
}
dialogProps.value.studentObj.datacontent = dialogProps.value.studentObj.datacontent
} catch (error) {
console.error('Invalid JSON:', error)
}
params.studentQuizAllList.forEach((item) => {
classWorkFormScore.teacherRating.push({
maxScore: item.score ? item.score : 100, // 100
score: item.teacherRating, // :
id: item.id // id
})
classWorkFormScore.teacherremark = item.teacherremark //
if (item.feedcontent != '') {
const filelist = JSON.parse(item.feedcontent)
console.log(filelist, '学生filelist-------------')
// feedcontent
filelist &&
filelist.forEach((item) => {
if (
item.name.indexOf('jpg') > -1 ||
item.name.indexOf('jpeg') > -1 ||
item.name.indexOf('png') > -1
) {
imageList.value.push(item)
} else {
fileList.value.push(item)
}
})
feedContentList.value.push(filelist)
}
})
} else if (
params.studentObj.worktype == '课堂展示' ||
params.studentObj.worktype == '框架梳理'
) {
params.studentQuizAllList.forEach((item) => {
classWorkFormScore.teacherRating.push({
maxScore: item.score ? item.score : 100, // 100
score: item.teacherRating, // :
id: item.id // id
})
classWorkFormScore.teacherremark = item.teacherremark //
// rightanswer"https://wzyzoss.eos-chongqing-3.cmecloud.cn/2024/9/5/c5d8e00a93364dd3b975f669afa217f9.png"
//
console.log(item.rightanswer, '----------课堂展示学生答题------------------')
if (item.rightanswer != '' && item.rightanswer != null) {
if (
item.rightanswer.indexOf('jpg') > -1 ||
item.rightanswer.indexOf('jpeg') > -1 ||
item.rightanswer.indexOf('png') > -1
) {
const imgeobj = {
name: item.rightanswer,
url: item.rightanswer
}
// list
imageList.value.push(imgeobj)
//
//fileList.value.push(?);
feedContentList.value.push(imgeobj)
}
}
})
} else {
//
}
}
// 0
console.log(params.studentQuizAllList[0].rating, '----------------------------')
// null 0 0
classWorkFormScore.rating =
params.studentQuizAllList[0].rating == 0 ? 0 : params.studentQuizAllList[0].rating
}
analysisScoreOpen.value = true
}
//
const onClassWorkFormScoreSave = () => {
console.log(classWorkFormScore)
// rating 0
if (dialogProps.value.studentQuizAllList && dialogProps.value.studentQuizAllList[0].rating != 0) {
ElMessageBox.confirm(`该学生已批改,再次批改会覆盖之前的评分!`, '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true
})
.then(() => {
onSubmit()
})
.catch(() => {})
} else {
onSubmit()
}
}
const onSubmit = () => {
if (classWorkFormScore.rating == 0) {
ElMessage({
type: 'error',
message: '请您给学生一个评价吧!'
})
return
}
var formd = {
id: dialogProps.value.studentObj.id, // this.activeClassWork.id;
status: '1',//0 1
updatedate: getTimeDate(),// = year+'-'+month+'-'+day+' '+hh+':'+mm;
};
//
updateClassworkdata(formd).then(res => {
})
//
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)
updateClassworkeval(queryParams).then((res) => {
// if(res.code == 200){
//
// }
})
})
ElMessage({
type: 'success',
message: '提交成功!'
})
analysisScoreOpen.value = false
//
emits('class_work_score_submit')
}
//
// onMounted(() => {})
// ()
defineExpose({
acceptParams
})
</script>
<style scoped>
.teacher_content {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
overflow: hidden;
}
.teacher_content_con {
flex: 1;
width: 100%;
height: 100%;
overflow-y: auto;
}
.tacher_conten_foot {
width: 100%;
}
.image_list {
display: flex;
flex-wrap: nowrap;
flex-direction: column;
/* justify-content: space-evenly; */
}
/* .el-dialog__body{
padding: 0!important;
height: 100%;
} */
.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;
}
</style>

View File

@ -1,752 +0,0 @@
<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 #title>
<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:75vh; "
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.resultcount > 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, '')
}
}
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.resultcount > 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.resultcount == 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, '\\');
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 (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 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: space-around;
// align-items: center;
overflow: hidden;
}
.view_table {
flex: 1;
height: 100%;
overflow-y: auto;
}
.view_teachrting {
flex: 2;
height: 100%;
overflow-y: auto;
}
}
.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,279 +0,0 @@
<template>
<el-row class="c-warp" :gutter="10">
<el-col class="left" :span="16">
<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)">
<template #title>
<el-popover :width="500" placement="right">
<p>{{item.def?.titletext}}</p>
<template #reference>
<el-button type="primary" size="small" round>{{index+1}}</el-button>
</template>
</el-popover>
<span class="item-title-o">{{item.type}} {{getRatioTxt(item)}} </span>
<el-tag type="success" size="small">{{item.points}}%</el-tag>
</template>
<div class="respond">
<div class="c-label">
<b t1>作答情况</b>
<span>(已经完成 <el-text type="danger">{{item.accSum}}</el-text> )</span>
</div>
<div class="c-childen">
<template v-for="(it, ind) in item.children">
<el-collapse v-model="item.active">
<el-collapse-item class="collapse-item" :name="ind+1">
<template #title>
<div class="t-left">
<el-tooltip placement="right" :content="it.def">
<el-tag size="small" style="vertical-align: 2px;" v-html="it.code"></el-tag>
</el-tooltip>
<el-text t1>{{it.studentIds.length}} / {{ratio_1(it, item.accSum)}}%</el-text>
</div>
<div style="flex: 1;">
<el-progress :status="getStatus(it)" :stroke-width="10" :percentage="ratio_1(it, item.accSum)" />
</div>
</template>
<div class="c-respond">
<template v-for="(sid, indStu) in it.studentIds">
<el-tag>{{getStudentName(sid)}}</el-tag>
</template>
</div>
</el-collapse-item>
</el-collapse>
</template>
</div>
</div>
</el-collapse-item>
</template>
</el-collapse>
</el-col>
<el-col class="right" :span="8">
<div class="c-item">
<div class="title">答题情况</div>
<div class="respond">
<el-space wrap>
<!-- <template v-for="it in 11"> -->
<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)" />
<el-button type="primary" :plain="getActive(index+1)" size="small" round
@click="clickInfo(index+1)">{{index+1}}</el-button>
</div>
</el-card>
</template>
</el-space>
</div>
</div>
</el-col>
</el-row>
</template>
<script setup>
import { ref, defineExpose, onMounted, reactive, computed, watch, nextTick, watchEffect } from 'vue'
// -|(使-|-)
// === ===
// import { nextTick } from 'vue'
// import * as elementPlus from 'element-plus' // ElMessage ElMessageBox
let colorArr = [] // --
// const attrs = useAttrs() // props
const activeTopic = ref(0) //
let dataList = ref([]) //
let studentList = ref([]) //
const props = defineProps({ // defineProps
activeData: { //
type: Object,
// required: true, //
default: () => ({
quizlist: [], //
studentList: [], // -
workFeedList: [] // -
})
},
})
//
// dataList.value = [
// { id: 1, type: '', points: '47.5', accSum: 18, active: [], children: [
// { code: 'A', isOk: false, studentIds: [55056, 55057, 55058]},
// ]
// },
// ]
//
colorArr = [
{ color: '#f56c6c', percentage: 20 },
{ color: '#e6a23c', percentage: 50 },
{ color: '#1989fa', percentage: 80 },
{ color: '#5cb87a', percentage: 100 },
]
// === ===
onMounted(() => {})
// === (methods) ===
// -
const initData = () => {
// console.log('xxx', props)
// window.test = activeCourse
studentList.value = props.activeData.studentList || []
const activeWorkFeedList = props.activeData.workFeedList || []
const quizlist = props.activeData.quizlist || []
//
let data = quizlist.map(o => {
//
const workdesc = o.workdesc || ''
let accSum = 0 //
let activeIds = [] //
const quizFeedList = activeWorkFeedList.filter(f => f.entpcourseworkid == o.id) //
let children = []
if (['单选题','多选题'].includes(o.worktype)) { // '',''
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 => isOne ? f.feedcontent==v : f.feedcontent.includes(i)).map(f => f.studentid)||[]
accSum += studentIds.length
if(isOk) isOne ? activeIds.push(...studentIds) : activeIds=[...new Set(activeIds.concat(studentIds))] //
return { def: v, code, isOk, studentIds }
})
} else if (o.worktype == '填空题') { //
const regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA-->
children = (o.title||'').match(regex).map((v,i) => {
const def = `填空项 ${i+1}`
const code = '(&emsp;)', txt=v
// id
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'').split('#')[i]).map(f => f.studentid)||[]
activeIds=[...new Set(activeIds.concat(studentIds))] //
accSum = activeIds.length
return { def, code, txt, isOk:true, studentIds }
})
} else if (o.worktype == '论述题') { //
const code = '(&emsp;)', def = '论述内容'
const studentIds = quizFeedList.filter(f => !!(f.feedcontent||'').replace(/#$/,'')).map(f => f.studentid)||[]
activeIds=[...new Set(activeIds.concat(studentIds))] //
accSum = activeIds.length
children = [{ def, code, isOk:true, studentIds }]
}
const studentSum = studentList.value.length || 0 //
const points = percent((activeIds.length / (studentSum||1)).toFixed(2)) //
// def: type active: points: , accSum
return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, children }
})
console.log('获取数据: ', data)
dataList.value = data
}
// --
const ratio_1 = (row, sum = 1) => percent(((row.studentIds.length||0) / (sum||1)).toFixed(2))
// --
const ratio_2 = row => percent(((row.accSum||0) / (studentList.value.length||1)).toFixed(2))
// --txt
const getRatioTxt = row => row.type.includes('选题') ? '得分率' : '完成度'
// --
const getStatus = row => row.isOk ? 'success' : 'exception'
// -(id)
const getStudentName = id => studentList.value.length && (studentList.value.find(o => o.studentid == id)||{})?.name || id
// -
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 scrollToElement = id => {
const el = document.getElementById(id)
!!el && el.scrollIntoView({ behavior: 'smooth', block: 'center',inline:'center' })
}
// 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
}
// === ===
watchEffect(() => { initData() })
</script>
<style lang="scss" scoped>
//
.c-warp{
background: #F2F3F5;
height: 73vh;
margin: 0 !important;
.left{padding-left: 0 !important;}
.right{padding-right: 0 !important;}
.c-item{
padding: 10px;
background: #fff;
border: none;
overflow-y: auto;
height: 73vh;
}
.collapse-item{
.item-title-o{
margin: 0 10px;
font-size: 18px;
font-weight: bold;
}
&:last-child{
color: red;
:deep(.el-collapse-item__header){
border-bottom: none;
}
}
.respond{
.c-label{
b[t1]{margin-right: 10px;}
}
.c-childen{
padding: 15px;
border-radius: 4px;
border: 1px solid #CDD0D6;
.el-text[t1]{
margin-left: 10px;
}
.t-left{width: 160px;text-align: left;}
.c-respond{
.el-tag{margin: 0 5px;}
}
}
}
}
.right{
.title{
font-size: 18px;
font-weight: bold;
padding-bottom: 10px;
border-bottom: 1px solid #CDD0D6;
margin-bottom: 10px;
}
.respond{
height: calc(70vh - 65px);
overflow: auto;
.el-space{padding: 5px;}
.card-warp{
border: none;
:deep(.el-card__body){
padding: 10px !important;
}
}
.card-body{
display: flex;
flex-direction: column;
}
}
}
}
</style>

View File

@ -1,159 +0,0 @@
<template>
<div class="class-reserv-item">
<div class="class-reserv-item-body">
<div class="class-reserv-item-title1">
<el-tag style="margin-left: 5px" :type="item.workclass"> {{ item.worktype }}</el-tag>
<label style="margin-left: 10px">{{ item.uniquekey }}</label>
</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> -->
<span>{{ item.classcaption }}</span>
&nbsp;|&nbsp; 截止时间{{ item.deaddate }} &nbsp;|&nbsp;{{ tabactive }}
</div>
</div>
<!-- <el-switch v-model="value1" active-text="云同步"> </el-switch> -->
<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>
<span>平均用时</span>
</div>
<div class="class-reserv-item-tool">
<span style="color: #ff6ec7; font-weight: 900; font-size: 15px">{{ item.scoingRate }}</span>
<span>得分率</span>
</div>
</div>
</template>
<script setup>
import { useToolState } from '@/store/modules/tool'
import useUserStore from '@/store/modules/user'
import { createWindow } from '@/utils/tool'
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
import { ElMessage } from 'element-plus'
import { listEntpcourse } from '@/api/teaching/classwork'
const emit = defineEmits(['openEdit', 'deleteReserv'])
const props = defineProps({
item: {
type: Object,
default: () => {}
},
tabactive: {
type: String,
default: () => ''
}
})
import { ref, reactive } from 'vue'
const value1 = ref(true);
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
const toolStore = useToolState() // -tool
const openEdit = () => {
emit('openEdit', props.item)
}
const deleteReserv = () => {
deleteSmartReserv([props.item.id]).then((res) => {
if (res.data === true) {
ElMessage({
message: '删除成功',
type: 'success'
})
emit('deleteReserv', props.item)
}
})
}
const startClassR = (item) => {
// startClass(item.id).then((res) => {
// if (res.data === true) {
// item.status = ''
// openLesson()
// }
// })
item.status = '上课中'
openLesson()
}
// const toolStore = useToolState()
let wins = null;
// -
const openLesson = () => {
// startClass(props.item.id)
listEntpcourse({
evalid: props.item.ex2,
edituserid: useUserStore().user.userId,
pageSize: 500
}).then(async res=>{
if (res.rows[0].id) {
wins = await createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res.rows[0].id + "&reservId=" + props.item.id })
}
})
}
const endClassR = (item) => {
endClass(item.id).then((res) => {
if (res.data === true) {
ElMessage({
message: '下课成功',
type: 'success'
})
item.status = '已结束'
}
})
}
</script>
<style scoped lang="scss">
.class-reserv-item {
display: flex;
background-color: white;
border-radius: 10px;
padding: 10px 5px;
margin-bottom: 10px;
.class-reserv-item-body {
flex: 1;
display: flex;
flex-direction: column;
text-align: left;
padding-left: 10px;
font-size: 14px;
.class-reserv-item-title1 {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
label {
font-size: 20px;
font-weight: bold;
}
}
}
.class-reserv-item-tool {
margin-left: 10px;
display: flex;
align-items: center;
flex-direction: column;
font-size: 14px;
}
}
</style>

View File

@ -1,92 +0,0 @@
<template>
<div class="desktop-item">
<div class="item-title flex">
<span>工作动态</span>
<el-radio-group v-model="type">
<el-radio-button label="全部" :value="-1" />
<el-radio-button label="备课" :value="1" />
<el-radio-button label="上课" :value="2" />
<el-radio-button label="作业" :value="3" />
</el-radio-group>
</div>
<div class="item-content">
<ul>
<li class="flex class-item" v-for="item in classList" :key="item.id">
<div class="class-left flex">
<div class="class-name flex">
<span>{{ item.className }}</span>
</div>
<div class="class-time"> {{ item.classDay }}&nbsp;{{ item.startTime }} ~ {{ item.classDay }}&nbsp;{{ item.endTime }}</div>
<div class="class-grade">
<span v-for="(tag, index) in item.classItemList" :key="index" style="margin-left: 5px">
{{ index === 0 ? tag.name : '、' + tag.name }}
</span>
</div>
</div>
<div class="class-right">
<el-button type="primary" size="small">上课</el-button>
</div>
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getSelfReserv } from '@/api/classManage'
const type = ref(-1)
const classList = ref([])
//
const getClass = () =>{
getSelfReserv().then((res) => {
let list = res.data || []
list.sort((a,b) => { if(a.status=='上课中') return -1; else return 0 })
classList.value = list.filter(item => item.status !== '已结束')
})
}
onMounted(()=>{
getClass()
})
</script>
<style lang="scss" scoped>
.desktop-item{
margin-bottom: 20px;
align-items: center;
.item-title{
height: 32px;
text-align: left;
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
align-items: center;
justify-content: space-between;
}
.item-content{
background-color: #fff;
border-radius: 5px;
padding: 10px 15px;
font-size: 13px;
.class-item{
justify-content: space-between;
align-items: center;
background: #e2e4f4;
border-radius: 5px;
margin-bottom: 10px;
padding: 10px;
.class-left{
flex-direction: column;
align-items: flex-start;
.class-time{
font-size: 13px;
color: #bfbfbf;
}
}
}
}
}
</style>

View File

@ -1,287 +0,0 @@
<template>
<div class="page-desktop">
<el-row :gutter="20">
<el-col :span="17">
<el-row :gutter="20">
<el-col :span="12" v-for="item in menuList">
<div class="desktop-item">
<div class="item-title">{{ item.name }}</div>
<div class="item-content">
<ul class="flex con-ul">
<li v-for="menu in item.list" @click="clickMenu(menu)" class="flex item-menu" :class="menu.disabled ? 'menu-disabled' : ''">
<i class="iconfont" :class="menu.icon"></i>
<span>{{ menu.name }}</span>
</li>
</ul>
</div>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<div class="desktop-item">
<div class="item-title flex">
<span>教学进度</span>
</div>
<div class="item-content">
<div ref="chartDom" class="chart-box"></div>
</div>
</div>
</el-col>
</el-row>
</el-col>
<el-col :span="7">
<!--工作动态-->
<workTrend/>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import workTrend from './container/work-trend.vue'
import outLink from '@/utils/linkConfig'
import * as echarts from 'echarts'
const router = useRouter()
const { ipcRenderer } = window.electron || {}
const type = ref(-1)
const chartDom = ref(null);
let chartInstance = null
const menuList = [{
name: '教学分析',
list: [
{
name: '课标分析',
icon: 'icon-kecheng',
isOuter: true,
path: '/teaching/chatwithstandard'
},
{
name: '教材分析',
icon: 'icon-jiaocaixuanze',
isOuter: true,
path: '/teaching/chatwithtextbook'
},
{
name: '考试分析',
icon: 'icon-kaoshi',
path: '/examReport'
},
{
name: '学情分析',
icon: 'icon-xueqingfenxi',
disabled: true
},
{
name: '资源分析',
icon: 'icon-ziyuanfenxi',
disabled: true
}
]
},
{
name: '课程教学',
list: [
{
name: '教学设计',
icon: 'icon-jiaoxuesheji',
path: '/prepare'
},
{
name: '教学实施',
icon: 'icon-jiaoxuefenxi',
path: '/classReserv'
},
{
name: '教学反思',
icon: 'icon-jiaoxuefansi',
disabled: true
},
]
},
{
name: '作业管理',
list: [
{
name: '作业设计',
icon: 'icon-jiaoxuefansi',
disabled: true
},
{
name: '作业布置',
icon: 'icon-xiezuo1',
disabled: true
},
{
name: '作业批改',
icon: 'icon-pigai',
path: '/classTask'
},
{
name: '作业统计',
icon: 'icon-tongji',
disabled: true
},
]
},
{
name: '教学管控',
list: [
{
name: '教学计划',
icon: 'icon-jiaoxuejihua',
disabled: true
},
{
name: '教学组织',
icon: 'icon-organization-framework-line',
disabled: true
},
{
name: '教学质量',
icon: 'icon-jiaoxuezhiliangfenxi',
disabled: true
},
{
name: '教学反馈',
icon: 'icon-fankui',
disabled: true
},
]
}
]
const clickMenu = ({isOuter, path, disabled}) =>{
if(disabled) return
//
if(isOuter){
let configObj = outLink().getBaseData()
let fullPath = configObj.fullPath + path
fullPath = fullPath.replaceAll('//', '/')
//
ipcRenderer.send('openWindow', {
key: path,
fullPath: fullPath,
cookieData: { ...configObj.data }
})
} else{
router.push(path)
}
}
onMounted(async ()=>{
// DOM
await nextTick()
chartInstance = echarts.init(chartDom.value)
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {},
grid: {
left: '3%',
right: '2%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
// boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五','周六','周日']
},
series: [
{
name: '一班',
type: 'bar',
data: [18, 24, 10, 13, 23, 15, 12]
},
{
name: '二班',
type: 'bar',
data: [25, 21, 14, 13, 20, 17,13]
},
{
name: '三班',
type: 'bar',
data: [15, 16, 16, 18, 20, 16, 10]
}
]
}
chartInstance.setOption(option);
})
</script>
<style lang="scss" scoped>
.page-desktop{
height: 100%;
padding-top: 20px;
.desktop-item{
margin-bottom: 20px;
.item-title{
height: 32px;
text-align: left;
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
align-items: center;
justify-content: space-between;
}
.item-content{
background-color: #fff;
border-radius: 5px;
padding: 10px 0;
.con-ul{
justify-content: space-around;
.item-menu{
cursor: pointer;
flex-direction: column;
font-size: 14px;
.iconfont{
font-size: 28px;
color: #707070;
}
&:hover{
color: #409EFF;
.iconfont{
color: #409EFF;
}
}
}
.menu-disabled{
cursor: not-allowed;
color: #bfbfbf;
.iconfont{
color: #bfbfbf;
}
&:hover{
color: #bfbfbf;
.iconfont{
color: #bfbfbf;
}
}
}
}
.chart-box{
height: 220px;
}
}
}
}
</style>

View File

@ -1,3 +0,0 @@
<template>
<div>模拟命题</div>
</template>

View File

@ -1,101 +0,0 @@
<template>
<el-table ref="table" v-loading="loading" :data="listExamQuestion" class="table-main">
<el-table-column type="index" fixed="left" width="60" />
<el-table-column align="left" fixed="left" min-width="60%">
<template #default="scope">
<div @click="showExamAnalyseDrawer(scope.row)">
<div v-html="scope.row.titleFormat" class="main-title" ></div>
<div v-html="scope.row.workdescFormat" class="main-work-desc" ></div>
<el-col :span="24" style="display: flex">
<div class="main-user-info" style="">{{ scope.row.entpname }}&nbsp;{{ scope.row.editusername }}</div>
<div class="main-upl-time" style="">{{ scope.row.timestamp }}</div>
</el-col>
</div>
</template>
</el-table-column>
<el-table-column align="center" prop="worktype" width="100"></el-table-column>
<el-table-column align="center" prop="worktag" width="120"></el-table-column>
</el-table>
<!-- 试题详情 -->
<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} from 'vue'
const props = defineProps({
listExamQuestion: {type: Array},
loading: {type: Boolean}
})
const activeExamInfoDrawer = ref(false);
const activeExam = ref({});
const showExamAnalyseDrawer = (row) => {
activeExam.value = row;
activeExamInfoDrawer.value = true;
}
</script>
<style lang="scss" scoped>
.table-main {
width: 100%;
height: 100%;
text-align: left;
.main-title {
overflow: hidden;
text-overflow: ellipsis;
font-weight:700
}
.main-work-desc{
overflow: hidden;
text-overflow: ellipsis;
font-size: 0.9em;
margin-top: 6px;
}
.main-user-info{
font-size: 0.8em;
color: silver;
padding-top: 5px
}
.main-upl-time{
margin-left: 30px;
font-size: 0.8em;
color: silver;
padding-top: 7px
}
}
.drawer-main{
margin: 1%;
padding: 1% 2%;
border: 2px dotted;
display: flex;
text-align: left;
.drawer-main-col{
padding: 10px 0px;
}
}
</style>

View File

@ -1,3 +0,0 @@
<template>
<div>考点分析</div>
</template>

View File

@ -1,292 +0,0 @@
<template>
<div class="page-resource flex">
<!--左侧 教材 目录-->
<ChooseTextbook @change-book="getData" @node-click="getData" />
<!--右侧 习题 列表 -->
<div class="page-right">
<div class="page-right-top">
<el-popover placement="top-start" title="真题回顾" trigger="hover" content="勾画、圈点,添加标记等,整理出本课的重点与难点,用于老师讲解和学生自主预习">
<template #reference>
<el-button-group>
<el-button @click="changeTaskView('真题回顾', index)" :type="curTask.viewkey=='真题回顾'?'primary':'default'" round>真题回顾</el-button>
<el-button disabled :type="'default'" plain round ></el-button>
</el-button-group>
</template>
</el-popover>
<div style="margin-top: 7px">&nbsp;&nbsp;<el-icon><ArrowRight /></el-icon>&nbsp;&nbsp;</div>
<el-popover disabled placement="top-start" title="考点分析" trigger="hover" content="勾画、圈点,添加标记等,整理出本课的重点与难点,用于老师讲解和学生自主预习">
<template #reference>
<el-button-group>
<el-button disabled @click="changeTaskView('考点分析', index)" :type="curTask.viewkey=='考点分析'?'primary':'default'" round>考点分析</el-button>
<el-button disabled :type="'default'" plain round ></el-button>
</el-button-group>
</template>
</el-popover>
<div style="margin-top: 7px">&nbsp;&nbsp;<el-icon><ArrowRight /></el-icon>&nbsp;&nbsp;</div>
<el-popover disabled placement="top-start" title="模拟命题" trigger="hover" content="勾画、圈点,添加标记等,整理出本课的重点与难点,用于老师讲解和学生自主预习">
<template #reference>
<el-button-group>
<el-button disabled @click="changeTaskView('模拟命题', index)" :type="curTask.viewkey=='模拟命题'?'primary':'default'" round>模拟命题</el-button>
<el-button disabled :type="'default'" plain round ></el-button>
</el-button-group>
</template>
</el-popover>
<el-select
v-model="queryParams.workType"
placeholder="题型"
size="small"
class="work-type"
@change="queryExamQuestionByParams"
>
<template v-for="(item, index) in listWorkType" :key="index">
<el-option :label="item.label" :value="item" />
</template>
</el-select>
</div>
<div :style="{'width': (viewportWidth - 400) + 'px','height': (viewportHeight-38) + 'px','overflow-y': 'auto'}">
<examReview
:loading="loading"
:listExamQuestion="listExamQuestion"
v-if="curTask.viewkey=='真题回顾'"
/>
<pointAnalysis
v-else-if="curTask.viewkey=='考点分析'"
/>
<examMocks v-else
/>
</div>
</div>
</div>
</template>
<script setup>
import {ref, onMounted, reactive, watch, nextTick, getCurrentInstance} from 'vue'
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 {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'
const {proxy} = getCurrentInstance();
const sourceStore = useResoureStore();
const viewportHeight = ref(0);
const viewportWidth = ref(0);
//
const curNode = ref({});
//
const listExamQuestion = ref([]);
const loading = ref(false);
const curTask = reactive({
viewkey: '真题回顾',
});
const queryParams = reactive({
workType: {
label: '不限',
value: 0,
},
total: 0,
});
const listWorkType = ref([{
label: '不限',
value: 0,
}]);
/**
* @desc: 选中单元章节后的回调, 获取单元章节信息
* @return: {*}
* @param {*} data
*/
const getData = async (data) => {
if (curNode.value.id == data.node.id) {
return;
}
loading.value = true;
// 1.
getWorkType(data.node);
// 2.
curNode.value = data.node;
//
// const params = {
// pageNum: 1,
// pageSize: 100,
// workgroup: '1',
// edusubject: curNode.value.edusubject,
// evalid: curNode.value.id,
// orderby: 'concat(worktype,worktag) DESC',
// }
// const res = await listEntpcoursework(params);
// listExamQuestion.value = res.rows;
// +()
const params = {
eid: curNode.value.id,
workgroup: '1',
worktype: '不限',
workTypeId: '0',
edusubject: curNode.value.edusubject,
edustage: curNode.value.edustage,
sectionName: curNode.value.itemtitle,
}
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;
}
/**
* @desc: 切换节点
* @return: {*}
* @param {*} item
* @param {*} key
*/
const changeTaskView = (item, key) => {
curTask.viewkey = item;
}
const getWorkType = async (data) => {
const selName = `${data.edustage}${data.edusubject}`
const curName = `${curNode.value.edustage}${curNode.value.edusubject}`
if (selName === curName) {
return;
}
const jyCT = await JYApiListCT(proxy, selName);
if (jyCT.length == 0) {
ElMessage.error('获取题型失败!');
return;
}
listWorkType.value = jyCT;
}
const queryExamQuestionByParams = async () => {
loading.value = true;
//
// const params = {
// pageNum: 1,
// pageSize: 100,
// workgroup: '1',
// edusubject: curNode.value.edusubject,
// evalid: curNode.value.id,
// worktype: queryParams.workType.Value,
// orderby: 'concat(worktype,worktag) DESC',
// }
// if (queryParams.workType == '') {
// delete params.worktype;
// }
// const res = await listEntpcoursework(params);
// listExamQuestion.value = res.rows;
// +()
const params = {
eid: curNode.value.id,
workgroup: '1',
worktype: queryParams.workType.label,
workTypeId: queryParams.workType.value,
edusubject: curNode.value.edusubject,
edustage: curNode.value.edustage,
sectionName: curNode.value.itemtitle,
}
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;
}
//
const getViewportHeight = () => {
return Math.max(
document.documentElement.clientHeight,
window.innerHeight || 0
);
}
const getViewportWidth = () => {
return Math.max(
document.documentElement.clientWidth,
window.innerWidth || 0
);
}
onMounted(() => {
nextTick(() => {
viewportHeight.value = getViewportHeight();
viewportWidth.value = getViewportWidth();
})
window.addEventListener('resize', () => {
viewportHeight.value = getViewportHeight();
viewportWidth.value = getViewportWidth();
})
})
</script>
<style lang="scss" scoped>
.page-resource {
padding-top: 20px;
height: 100%;
//
.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);
.page-right-top {
display: flex;
margin-top: 6px;
margin-left: 6px;
.work-type {
width: 100px;
margin: 0 6px 0 auto;
}
}
}
}
</style>
<style src="@/assets/styles/JYStyle.css"></style>

View File

@ -1,5 +1,5 @@
<template>
<el-card style="overflow: auto; height: 100%;">
<el-card style="overflow: auto; height: 100%">
<div class="common-layout" style="overflow-y: auto">
<el-container>
<el-main style="--el-main-padding: 0">
@ -112,8 +112,8 @@ const title = reactive([
]
},
{
name: '考试分析',
url: '/examReport',
name: '高考研究',
url: '/education/colentrance',
img: 'iconfont icon-icon_kaoshifenxi',
child1: []
},

View File

@ -1,462 +0,0 @@
<template>
<el-dialog
v-model="dialogVisible"
title="注册账号"
:fullscreen="true"
center
:before-close="handleClose"
>
<el-steps style="max-width: 100%;-webkit-app-region: no-drag;height:35px" :active="activeIndex" finish-status="success" simple>
<el-step title="1.基本信息" @click="activeIndex=1" />
<el-step title="2.在校信息" />
</el-steps>
<el-form
style="width: 80%;margin: 0 auto;margin-top: 10px;-webkit-app-region: no-drag;"
:model="ruleForm"
:rules="rules"
label-width="auto"
status-icon
ref="ruleFormRef"
>
<el-form-item label="姓名" prop="name" v-if="activeIndex==1">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="身份证号" prop="idNumber" v-if="activeIndex==1">
<el-input v-model="ruleForm.idNumber" />
</el-form-item>
<el-form-item label="手机号" prop="phoneNumber" v-if="activeIndex==1">
<el-input v-model="ruleForm.phoneNumber" />
</el-form-item>
<el-form-item label="验证码" prop="Code" v-if="activeIndex==1">
<el-input style="width:60%" v-model="ruleForm.Code" :disabled="true" />
<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">
<el-input v-model="ruleForm.password" type="password" />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword" v-if="activeIndex==1">
<el-input v-model="ruleForm.confirmPassword" type="password" />
</el-form-item>
<el-form-item label="所属地区" prop="address" v-if="activeIndex==2">
<el-cascader
style="width:100%"
v-model="ruleForm.address"
:options="regionData"
@change="handleChange"
/>
</el-form-item>
<el-form-item label="学校" prop="school" v-if="activeIndex==2">
<el-cascader :options="optionsSchool" style="width:100%" v-model="ruleForm.school" @change="handleSchoolChange" />
</el-form-item>
<el-form-item label="班级" prop="class" v-if="activeIndex==2">
<el-tree-select
v-model="ruleForm.class"
:data="gradeTree"
multiple
:render-after-expand="false"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="学科" prop="discipline" v-if="activeIndex==2">
<div v-for="item in schoolSubject" style="display: flex;">
<span style="width:50px">{{ item.name }}</span>
<el-checkbox-group style="width:100%" v-model="ruleForm.discipline" >
<el-checkbox style="margin-right:10px" v-for="el in item.children" :key="el.id" :label="el.itemtitle" :value="el" />
</el-checkbox-group>
</div>
</el-form-item>
<el-form-item v-if="activeIndex==1">
<div class="centerDiv">
<el-button type="primary" @click="nextStep(ruleFormRef)">下一步</el-button>
</div>
</el-form-item>
<el-form-item v-if="activeIndex==2">
<div class="centerDiv">
<el-button type="primary" @click="submitForm(ruleFormRef)">立即注册</el-button>
</div>
</el-form-item>
</el-form>
<!-- <template #footer>
<div class="dialog-footer" >
<el-button @click="dialogVisible = false">下一步</el-button>
</div>
</template> -->
<el-dialog
v-model="isImg"
title="人机验证"
width="500"
>
<span>根据图片回答相关问题</span>
<div style="display: flex;align-items: center;;margin-top:30px">
<img :src="isPeopleImg" style="width:200px;height:60px;cursor: pointer;" alt="" srcset="" @click="refreshImg">
<el-input v-model="ruleForm.imgCode" style="width: 250px;height:40px;margin-left:20px" placeholder="请根据图片填入答案" />
</div>
<div style="display: flex;justify-content: center;margin-top:30px">
<el-button type="primary" @click="sbmitImg">确定</el-button>
</div>
<!-- <template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogVisible = false">
Confirm
</el-button>
</div>
</template> -->
</el-dialog>
</el-dialog>
</template>
<script setup>
import { ref, defineExpose, reactive ,onMounted} from 'vue'
import {captchaImg,sendCode,deptTree,getDept,listClassmain,listEvaluation,signIn} from '@/api/login'
import { ElMessage } from 'element-plus'
import {setToken, removeToken } from '@/utils/auth'
import { regionData, codeToText } from 'element-china-area-data'
const ruleFormRef = ref(null)
const activeIndex=ref(1)
const ruleForm = reactive({
name: '',
idNumber:'',
phoneNumber: '',
Code:'',
password:'',
confirmPassword:'',
class:[],
discipline:[],
school:[],
})
const isImg=ref(false)
const btnName=ref('发送验证码')
const resImg = reactive({ imgData: {} });
const optionsSchool=ref([])
const gradeTree=ref([])
const schoolSubject=ref([])
const allSubjectList = ref([])
const gradeDataList = [
[
{ label: '一年级', agekey: 1, checked: false, current: 1 },
{ label: '二年级', agekey: 2, checked: false, current: 1 },
{ label: '三年级', agekey: 3, checked: false, current: 1 },
{ label: '四年级', agekey: 4, checked: false, current: 1 },
{ label: '五年级', agekey: 5, checked: false, current: 1 },
{ label: '六年级', agekey: 6, checked: false, current: 1 },
],
[
{ label: '初一', agekey: 7, checked: false, current: 2 },
{ label: '初二', agekey: 8, checked: false, current: 2 },
{ label: '初三', agekey: 9, checked: false, current: 2 },
],
[
{ label: '高一', agekey: 10, checked: false, current: 3 },
{ label: '高二', agekey: 11, checked: false, current: 3 },
{ label: '高三', agekey: 12, checked: false, current: 3 },
],
]
//
const isPeopleImg=ref(null)
const rules = reactive({
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
],
Code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
],
phoneNumber: [
{ required: true, message: '手机号码是必填项', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' },
],
idNumber: [
{ required: true, message: '身份证号码是必填项', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!value) {
callback();
return;
}
// 1518
const pattern = /^(?:\d{15}|\d{17}[\dX])$/;
const valid = pattern.test(value);
if (!valid) {
callback(new Error('身份证号码格式不正确'));
} else {
callback();
}
},
trigger: 'blur'
}
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度必须大于6位数', trigger: 'blur' },
],
confirmPassword: [
{ required: true, message: '请再次输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度必须大于6位数', trigger: 'blur' },
{ validator: (rule, value, callback) => {
if (value !== ruleForm.password) {
callback(new Error('两次输入的密码不一致!'));
} else {
callback();
}
}, trigger: 'change' }
],
address:[ {
required: true,
message: '请选择地址',
trigger: 'change',
},],
school:[{
required: true,
message: '请选择地址',
trigger: 'change',
},],
class:[ {
type:'array',
required: true,
message: '请选择班级',
trigger: 'change',
},],
discipline:[ {
type: 'array',
required: true,
message: '请选择学科',
trigger: 'change',
},]
})
const dialogVisible = ref(false)
//
const OpenModel = () =>{
dialogVisible.value=true
}
//
const handleClose = () => {
if (ruleFormRef.value) ruleFormRef.value.resetFields()
removeToken();
activeIndex.value=1
dialogVisible.value=false
}
const nextStep = (formEl) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
activeIndex.value=2
//
deptTree({phone:ruleForm.phoneNumber}).then(res=>{
optionsSchool.value=res.data
optionsSchool.value.forEach(item=>{
item.value=item.id
if(!item.children){
item.disabled=true
}
if(item.children){
item.children.forEach(child=>{
child.value=child.id
if(!child.children){
child.disabled=true
}
if(child.children){
child.children.forEach(grandson=>{
grandson.value=grandson.id
})
}
})
}
})
getSubject()
console.log(res)
})
console.log('submit!')
} else {
console.log('error submit!')
}
})
}
//
const getSubject = async ()=>{
const rows= await listEvaluation({ itemkey: "subject", pageSize: 500 })
console.log(rows,'所有学科')
allSubjectList.value = rows.data
}
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
var araname = codeToText[ruleForm.address[0]] + '-' + codeToText[ruleForm.address[1]] + '-' + codeToText[ruleForm.address[2]];
var form={
mobile:ruleForm.phoneNumber,
code:ruleForm.Code,
name:ruleForm.name,
idCard:ruleForm.idNumber,
password:ruleForm.password,
confirmPassword:ruleForm.confirmPassword,
schoolId:ruleForm.school[2],
classIds:ruleForm.class.join(','),
subjectIds:ruleForm.discipline.map(o=>o.id).join(','),
subject:ruleForm.discipline.map(o=>o.itemtitle).join(','),
areaIds:ruleForm.address.join(','),
area:araname
}
signIn(form).then(res=>{
if(res.code==200){
ElMessage.success('您已注册成功,等待学校管理员审核')
if (ruleFormRef.value) ruleFormRef.value.resetFields()
var restValue={
name: '',
idNumber:'',
phoneNumber: '',
Code:'',
password:'',
confirmPassword:'',
class:[],
discipline:[],
school:[],
}
Object.assign(ruleForm, restValue);
activeIndex.value=1
dialogVisible.value=false
}else{
ElMessage.error(res.msg)
}
})
console.log('submit!')
} else {
console.log('error submit!', fields)
}
})
}
//
const sendcaptchaImg=()=>{
if(ruleForm.phoneNumber){
const pattern = /^1[3-9]\d{9}$/;
if( pattern.test(ruleForm.phoneNumber) ){
captchaImg({mobile:ruleForm.phoneNumber}).then(res=>{
ruleForm.imgCode=null
isImg.value=true
isPeopleImg.value='data:image/jpg;base64,'+res.img
if(res.token){
setToken(res.token)
}
resImg.imgData=res
})
}else{
ElMessage.error('请输入正确的手机号码')
}
// captchaImg({mobile:ruleForm.phoneNumber}).then(res=>{
// console.log('res->', res)
// })
}else{
ElMessage.error('请输入手机号码')
}
}
//
const refreshImg=()=>{
captchaImg({mobile:ruleForm.phoneNumber}).then(res=>{
isPeopleImg.value='data:image/jpg;base64,'+res.img
if(res.token){
setToken(res.token)
}
resImg.imgData=res
})
}
//
const sbmitImg=()=>{
if(ruleForm.imgCode){
sendCode({mobile:ruleForm.phoneNumber,code:ruleForm.imgCode,uuid:resImg.imgData.uuid}).then(res=>{
if(res.code==200){
ruleForm.Code=res.data
isImg.value=false
}
})
}else{
ElMessage.error('请根据图片输入验证码')
}
//
}
const handleChange = (value) => {
console.log('选中的地址值:', value);
};
const handleSchoolChange= async ()=>{
ruleForm.discipline=[];
ruleForm.class=[];
getDept({deptId:ruleForm.school[2]}).then(res =>{
if(res.data.studying && res.data.studying.length){
let studying = res.data.studying.split(',')
let ary = []
studying.forEach(item =>{
let obj = {
name: item,
children: []
}
allSubjectList.value.forEach(el =>{
if(item == el.edustage){
obj.children.push(el)
}
})
ary.push(obj)
})
schoolSubject.value = ary
}
else{
schoolSubject.value = []
}
})
listClassmain( {entpid: ruleForm.school[2], pageSize: 500, status: 'open'}).then(res=>{
gradeTree.value = groupByCondition(res.rows, item => item.agekey);
console.log(gradeTree.value,'班级')
})
}
//
const groupByCondition = (arr, condition)=>{
//
const groups = arr.reduce((groups, item) => {
const groupKey = condition(item);
item.label = item.caption
item.value = item.id
groups[groupKey] = groups[groupKey] || [];
groups[groupKey].push(item);
return groups;
}, {});
//
const formattedGroups = Object.keys(groups).map(key => ({
label: gradeName(key),
value: key,
children: groups[key]
}));
return formattedGroups;
}
//key
const gradeName = (key) =>{
//
const flatGradeDataList = gradeDataList.flat();
const currentIndex = flatGradeDataList.findIndex(item => item.agekey === Number(key));
if(currentIndex !== -1){
return flatGradeDataList[currentIndex].label;
}else{
//
const defaultLabel = '社团';
flatGradeDataList[currentIndex] = { ...flatGradeDataList[currentIndex], label: defaultLabel };
return defaultLabel;
}
}
onMounted(()=>{
})
defineExpose({
OpenModel,
})
</script>
<style scoped lang="scss">
.centerDiv{
-webkit-app-region: no-drag;
padding-bottom: 20px;
width: 100%;
display: flex;
justify-content: center;
}
</style>

View File

@ -21,21 +21,12 @@
placeholder="请输入密码"
/>
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe" >记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-checkbox v-model="loginForm.rememberMe" class="flex mb-5">记住密码</el-checkbox>
<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 style="margin-right: 10px;" @click="RegisterModel">注册账号</a>
<!-- | -->
<!-- <a style="margin-left: 10px;">忘记密码</a> -->
</div>
</el-form>
</div>
</div>
@ -57,8 +48,6 @@
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
@ -68,7 +57,6 @@ 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)
@ -78,7 +66,6 @@ const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
//
const loginForm = reactive({
username: '',
@ -97,10 +84,7 @@ ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
//
const RegisterModel = () =>{
RegModel.value.OpenModel()
}
//
const submitForm = async (formEl) => {
if (!formEl) return
@ -178,6 +162,7 @@ onMounted(() => {
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;

View File

@ -230,20 +230,6 @@ export default {
}
},
openFileWin(items) {
if (items.fileFlag === 'apt') {
console.log(items);
const path="/teaching/aptindex?id="+items.fileId
let configObj = outLink().getBaseData()
let fullPath = configObj.fullPath + path
fullPath = fullPath.replaceAll('//', '/')
//
ipcRenderer.send('openWindow', {
key: path,
fullPath: fullPath,
cookieData: { ...configObj.data }
})
return
}
if (!items||!items.fileSuffix) return;
getPrepareById(items.id).then((item) => {
Object.assign(items, item)
@ -260,12 +246,12 @@ export default {
fileType: item.fileType
})
ipcRenderer.on('listen-file-change-on' + item.fileNewName, () => {
items.async = 'on'
item.async = 'on'
})
ipcRenderer.on('listen-file-change-success' + item.fileNewName, (e, { data, md5 }) => {
items.fileSize = data.fileSize
items.md5 = md5
items.async = true
item.fileSize = data.fileSize
item.md5 = md5
item.async = true
})
}
})

View File

@ -20,14 +20,11 @@
</div>
<el-button
:type="!curClassReserv.id ? 'info' : 'primary'"
:disabled="!curClassReserv.id || toolStore.isToolWin"
:disabled="!curClassReserv.id"
class="to-class-btn"
@click="openLesson"
>
<label
><i class="iconfont icon-lingdang"></i
>{{ curClassReserv.status == '上课中' ? '上课中' : '上课' }}</label
>
<label><i class="iconfont icon-lingdang"></i>上课</label>
<label>{{ curClassReserv.classDay }} {{ getWeekday1(curClassReserv.classDay) }}</label>
<label>{{ curClassReserv.startTime }}-{{ curClassReserv.endTime }}</label>
</el-button>
@ -63,21 +60,9 @@
<el-button @click="handleOutLink('feedback')">作业反馈</el-button>
<el-button @click="handleOutLink('homeWork')">布置作业</el-button>
<el-button @click="isDialogOpen = true">上传资料</el-button>
<el-dropdown trigger="click" style="margin-left: 12px">
<el-button type="primary">
新建课件<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item style="padding: 0">
<el-button type="default" style="" @click="createFile">PPT课件</el-button>
</el-dropdown-item>
<el-dropdown-item style="padding: 0">
<el-button type="default" style="" @click="createAptFile">APT课件</el-button>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button type="primary" style="margin-left: 10px" @click="createFile"
>新建课件</el-button
>
</div>
</div>
<el-checkbox-group
@ -107,6 +92,7 @@
@on-delete="deleteTalk"
@on-set="openSet"
@on-delhomework="delhomework"
>
<el-checkbox v-if="!item.uniquekey" label="" :value="item" />
</file-list-item>
@ -124,7 +110,11 @@
</div>
<MoveFile v-model="isMoveDialogOpen" @on-submit="chooseMoveCata" />
<uploadDialog v-model="isDialogOpen" @submit-file="submitFile" />
<SetHomework v-model="setDialog" :entpcourseid="entpcourseid" :row="row" />
<SetHomework
v-model="setDialog"
:entpcourseid="entpcourseid"
:row="row"
/>
</div>
<reserv
ref="reservDialog"
@ -137,7 +127,6 @@
<script setup>
import { Check } from '@element-plus/icons-vue'
import Reserv from '@/views/prepare/container/reserv.vue'
import { ArrowDown } from '@element-plus/icons-vue'
</script>
<script>
const Remote = require('@electron/remote')
@ -149,20 +138,18 @@ import useUserStore from '@/store/modules/user'
import { useToolState } from '@/store/modules/tool'
import MoveFile from '@/components/move-file/index.vue'
import FileListItem from '@/views/prepare/container/file-list-item.vue'
import { getSmarttalkPage, moveSmarttalk, creatAPT } from '@/api/file'
import { getSmarttalkPage, moveSmarttalk } from '@/api/file'
import { toTimeText } from '@/utils/date'
import { ElMessage } from 'element-plus'
import { parseCataByNode, creatPPT, asyncLocalFile } from '@/utils/talkFile'
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
import SetHomework from '@/components/set-homework/index.vue'
import outLink from '@/utils/linkConfig'
import { createWindow, sessionStore } from '@/utils/tool'
import { createWindow, ipcMsgSend } from '@/utils/tool'
import { cloneDeep } from 'lodash'
import { delClasswork, listEntpcourse } from '@/api/teaching/classwork'
import { getSelfReserv } from '@/api/classManage'
import { delClasswork } from '@/api/teaching/classwork'
import { getSelfReserv, startClass } from '@/api/classManage'
import { useGetHomework } from '@/hooks/useGetHomework'
import { addEntpcoursefileReturnId } from '@/api/education/entpcoursefile'
const toolStore = useToolState()
const fs = require('fs')
@ -241,12 +228,14 @@ export default {
)
// electron
const curWin = Remote.getCurrentWindow()
curWin.on('focus', () => {
if (!this.isOpenHomework) return
curWin.on('focus', ()=>{
if(!this.isOpenHomework) return
this.initHomeWork()
this.asyncAllFile()
this.isOpenHomework = false
})
},
// activated() {
// if (this.uploadData.textbookId !== null) {
@ -310,90 +299,6 @@ export default {
this.currentFileList.unshift(res.resData)
})
},
createAptFile() {
listEntpcourse({
evalid: this.uploadData.levelSecondId,
edituserid: this.userStore.userId,
pageSize: 500
}).then((response) => {
if (response.rows.length <= 0) return
let resCourse = response.rows[0]
//
let form = {
parentid: 0,
entpid: this.userStore.deptId,
entpcourseid: resCourse.id,
ppttype: 'file',
title: resCourse.coursetitle,
fileurl: '',
filetype: 'ppt',
datacontent: '',
filekey: '',
filetag: '',
fileidx: 0,
dflag: 0,
status: '',
edituserid: this.userStore.userId
}
addEntpcoursefileReturnId(form).then((slideid) => {
let pagearray = []
//
pagearray.push({
key: '公屏',
title: '公屏页',
slidedata: {
attrs: { width: 1333, height: 749.8125 },
className: 'Stage',
children: [
{
attrs: {},
className: 'Layer',
children: [
{
attrs: {
width: 1333,
height: 749.8125,
fill: 'white',
name: 'fixedbackground',
listening: true
},
className: 'Rect'
}
]
}
]
}
})
//
var form = {
parentid: slideid,
entpid: resCourse.entpid,
entpcourseid: resCourse.id,
ppttype: 'file',
title: '第一页',
fileurl: '',
filetype: 'slide',
datacontent: JSON.stringify(pagearray),
filekey: '',
filetag: '',
fileidx: 0,
dflag: 0,
status: '',
edituserid: this.userStore.userId
}
addEntpcoursefileReturnId(form).then((res) => {
creatAPT({
...this.uploadData,
fileId: slideid,
fileShowName: this.currentNode.label + '.apt'
}).then((res) => {
this.currentFileList.unshift(res.resData)
})
})
})
})
},
onMoveSingleFile(item) {
this.moveFile = [item]
this.isMoveDialogOpen = true
@ -497,7 +402,6 @@ export default {
this.asyncAllFileVisiable = true
for (let i = 0; i < this.currentFileList.length; i++) {
let item = this.currentFileList[i]
if (item.fileFlag === 'apt') continue;
await asyncLocalFile(item)
}
this.asyncAllFileVisiable = false
@ -522,9 +426,7 @@ export default {
this.uploadData.textbookId = data.textBook.curBookId
toolStore.curSubjectNode.data = data
//
this.$nextTick(() => {
toolStore.curSubjectNode.querySearch = this.uploadData
})
this.$nextTick(() =>{ toolStore.curSubjectNode.querySearch = this.uploadData })
this.initHomeWork()
await this.asyncAllFile()
},
@ -539,20 +441,19 @@ export default {
},
//
handleOutLink(key) {
if (key == 'homeWork') {
if(key == 'homeWork'){
this.isOpenHomework = true
}
// key linkConfig.js
let configObj = outLink()[key]
let fullPath = configObj.fullPath
// urlunitId ID
if (key != 'standard' && key != 'aiModel') {
let unitId = this.uploadData.levelSecondId
? this.uploadData.levelSecondId
: this.uploadData.levelFirstId
if (key == 'gk') {
if(key != 'standard' && key != 'aiModel'){
let unitId = this.uploadData.levelSecondId ? this.uploadData.levelSecondId : this.uploadData.levelFirstId
if(key == 'gk'){
fullPath += `?unitId=${unitId}`
} else {
}
else{
fullPath += `&unitId=${unitId}`
}
}
@ -641,7 +542,7 @@ export default {
<style scoped lang="scss">
.page-resource {
user-select: none;
padding-top: 10px;
padding-top: 20px;
height: 100%;
.page-right {
@ -729,7 +630,6 @@ export default {
cursor: pointer !important;
}
& > :deep(span) {
pointer-events: none;
flex-direction: column !important;
font-size: 12px;
}

View File

@ -1,5 +1,5 @@
<template>
<div v-loading="loading" class="resource-list">
<div v-loading="sourceStore.loading" class="resource-list">
<el-scrollbar>
<el-empty v-if="!sourceStore.result.list.length" description="暂无数据" />
<ul>
@ -7,7 +7,7 @@
v-for="item in sourceStore.result.list"
:key="item.id"
class="list-item"
@click="handleRow(item)"
@click="handleRow"
>
<div class="item-left flex">
<FileImage :file-name="item.fileShowName" :size="50" />
@ -91,11 +91,10 @@
/>
</div>
</div>
<FilePreview v-model="isShow" :row="curRow"></FilePreview>
</template>
<script setup>
import { toRaw, ref, computed } from 'vue'
import { toRaw } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import FileImage from '@/components/file-image/index.vue'
import { deleteSmarttalk, updateSmarttalk } from '@/api/file'
@ -103,14 +102,13 @@ import { addFileToPrepare } from '@/api/subject'
import { getFileSuffix } from '@/utils/ruoyi'
import useUserStore from '@/store/modules/user'
import useResoureStore from '../store'
import FilePreview from '@/components/file-preview/index.vue'
const { ipcRenderer } = window.electron || {}
const userstore = useUserStore()
const sourceStore = useResoureStore()
const loading = computed(() => sourceStore.loading)
const userInfo = userstore.user
const isShow = ref(false)
const curRow = ref({})
// change
const handleSizeChange = (limit) => {
sourceStore.query.pageSize = limit
@ -184,12 +182,8 @@ const addLesson = ({ id }) => {
}
}
//
const handleRow = (item) => {
item.prevImgList.sort((a,b)=>{
return a.targetPrevIndex - b.targetPrevIndex
})
curRow.value = item
isShow.value = true
const handleRow = () => {
ElMessage.warning('请先加入备课,在备课里面进行预览!')
}
</script>

View File

@ -112,7 +112,7 @@ watch(() => sourceStore.query.fileSource,() => {
<style lang="scss" scoped>
.page-resource {
padding-top: 10px;
padding-top: 20px;
height: 100%;
.page-right {

View File

@ -94,7 +94,6 @@ export default defineStore('resource', {
getSmarttalkPage(data).then((res) => {
this.result.total = res.total
this.result.list = res.rows
this.loading = false
})
}else{
let data = JSON.parse(JSON.stringify(this.thirdQuery))
@ -108,13 +107,12 @@ export default defineStore('resource', {
if(res.data.code === 0){
this.thirdResult.total = res.data.page.totalCount
this.thirdResult.list = [...res.data.data]
this.loading = false
}
}
})
}
} finally {
// this.loading = false
this.loading = false
}
},
changeTab(val) {

View File

@ -81,8 +81,8 @@ import { ipcMsgSend, ipcMsgSend2, ipcMsgInvoke } from '@/utils/tool'
import { useToolState } from '@/store/modules/tool'
import Lesson from './lesson.vue';
import { parseCataByNode } from '@/utils/talkFile'
import outLink from '@/utils/linkConfig'
import MsgEnum from '@/plugins/imChat/msgEnum' //
import { createHomework } from '../createHomework'
const route = useRoute();
const usertore = useUserStore().user
@ -129,35 +129,16 @@ const changeChapter = async (data)=>{
getResource()
}
//
const openFileLink = async (item) =>{
/**
* 资源推送
* 1先生成作业
* 2再进行推送
*/
//
const today = new Date();
//
const month = String(today.getMonth() + 1).padStart(2, '0');
//
const day = String(today.getDate()).padStart(2, '0');
//
const uniquekey = `${usertore.edusubject}-${month}${day}-${dataList.value.length + 1}`
// ID
const querySearch = toolStore.curSubjectNode.querySearch
const evalid = querySearch.levelSecondId ? querySearch.levelSecondId : querySearch.levelFirstId
//
const data = [{name: item.fileShowName, ulr: item.fileFullPath}]
//
await createHomework({ uniquekey, evalid, data, entpcourseid: entpcourseid.value })
//
getHomework()
let row = dataList.value.at(-1)
sendHomework(row)
// web AIX
const openFileLink = (item) =>{
// key linkConfig.js
let key = 'filehomework'
let configObj = outLink()[key]
ipcMsgSend2('openWindow', {
key,
fullPath: configObj.fullPath + `&fileShowName=${item.fileShowName}&fileFullPath=${item.fileFullPath}`,
cookieData: { ...configObj.data }
})
}

View File

@ -1,37 +0,0 @@
import useUserStore from '@/store/modules/user'
import { addClassworkReturnId } from '@/api/teaching/classwork'
export const createHomework = ({ uniquekey, evalid, data, entpcourseid }) =>{
const usertore = useUserStore().user
var formObj = {};
formObj.id = 0;
formObj.deaddate = '';
formObj.entpid = usertore.deptId;
formObj.level = 1;
formObj.parentid = 0;
formObj.worktype = '常规作业';
formObj.workkey = '';
formObj.worktag = '';
formObj.uniquekey = uniquekey;
formObj.classid = 0;
formObj.classcourseid = 0;
formObj.entpcourseid = entpcourseid;
formObj.slideid = 0;
formObj.workcodes = JSON.stringify(data);
formObj.edusubject = usertore.edusubject;
formObj.evalid = evalid
formObj.edustage = usertore.edustage;
formObj.status = '';
formObj.edituserid = usertore.userId;
formObj.entpcourseworklist = JSON.stringify([{'id':-2, 'score': '10'}]);
return addClassworkReturnId(formObj)
}

View File

@ -39,7 +39,7 @@
<script setup>
// electron
import { onMounted, ref, reactive, watchEffect, nextTick } from 'vue'
import { onMounted, ref, reactive, watchEffect } from 'vue'
import { useRoute } from 'vue-router';
import { ElMessageBox, ElMessage, ElLoading } from 'element-plus'
import * as classManageApi from '@/api/classManage'
@ -83,8 +83,9 @@ const btnList = [ // 工具栏按钮列表
// === ===
onMounted(async() => {
if (!electron) return //
window.test = sessionStore
window.test1 = toolStore
// console.log(sessionStore)
// window.test = sessionStore
// window.test1 = toolStore
getClassInfo() // ex3
resetStatus() // -
})
@ -99,14 +100,12 @@ const getClassInfo = async () => {
console.log('获取群ID:', timGroupId)
const chat = await imChatRef.value?.initImChat(timGroupId) // im-chat
if (!timGroupId) timGroupId = chat?.timGroupId
if (!timGroupId) return ElMessage.error('房间创建-失败')
classManageApi.startClass(classObj.id, timGroupId) //
}
// tab-change
const tabChange = (val) => {
const bool = !toolStore.isPdfWin && !toolStore.showBoardAll
if(bool) toolStore.showBoardAll = true
console.log('tabChange:', val, bool)
toolStore.model = val // tab
}
// logo - |
@ -135,15 +134,8 @@ const logoHandle = (e,t) => {
// -穿
const mouseChange = (bool) => {
let resBool = false
if (bool == 0) return setIgnore(resBool) // 穿
if (tabActive.value == 'select') resBool = !!bool
else {
if (!isShow.value) resBool = !!bool
// pdf穿
const isPdf = !resBool && toolStore.isPdfWin
if (isPdf) resBool = true
}
// console.log('mouseChange:', bool, resBool)
setIgnore(resBool)
}
// im-chat: {type, data}
@ -177,15 +169,15 @@ const setIgnore = (bool) => {ipcMsgSend('tool-sphere:set:ignore', bool)}
// : |
const resetStatus = () => {
if (toolStore.isToolWin) return // -
// -
// ipcMain?.removeHandler('tool-sphere:reset') //
// ipcMain?.handle?.('tool-sphere:reset', () => {
// setTimeout(() => {
// boardVueRef.value.handleMode(tabActive.value)
// mouseChange(1)
// }, 500)
// })
ipcMain?.removeHandler('tool-sphere:reset') //
ipcMain?.handle?.('tool-sphere:reset', () => {
setTimeout(() => {
boardVueRef.value.handleMode(tabActive.value)
mouseChange(1)
}, 500)
})
toolStore.isToolWin = true //
}
// :
const sideMouse = e => {
@ -219,8 +211,7 @@ const sideChange = async o => {
// 2
setTimeout(async() => {
// elMsg.close()
// toolStore.isToolWin = false
toolStore.resetDef() //
toolStore.isToolWin = false
await imChatRef.value?.deleteGroup() //
await imChatRef.value?.logout() // 退im
await classManageApi.endClass(route.query.reservId)
@ -242,12 +233,6 @@ watchEffect(() => {
// , : -
const show = !toolStore.isPdfWin && toolStore.showBoardAll
if (show != isShow.value) isShow.value = show
if (isShow.value) {
nextTick(() => {
mouseChange(1) //
boardVueRef.value.handleMode(tabActive.value)
})
}
})
</script>