解决冲突
|
@ -10,6 +10,7 @@ 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/'
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ module.exports = {
|
|||
],
|
||||
rules: {
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/multi-word-component-names': 'off'
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'prettier/prettier': 'off'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ 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/, '')
|
||||
|
|
11
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "aix-win",
|
||||
"version": "1.1.6",
|
||||
"version": "2.0.2",
|
||||
"description": "An Electron application with Vue",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "example.com",
|
||||
|
@ -25,24 +25,31 @@
|
|||
"@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",
|
||||
"circular-json": "^0.5.9",
|
||||
"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",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jsondiffpatch": "0.6.0",
|
||||
"im_electron_sdk": "^8.0.5904",
|
||||
"lodash": "^4.17.21",
|
||||
"pdfjs-dist": "4.4.168",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"spark-md5": "^3.0.2",
|
||||
"vue-qr": "^4.0.9",
|
||||
"vue-router": "^4.4.0",
|
||||
"xgplayer": "^3.0.19",
|
||||
"xlsx": "^0.18.5"
|
||||
|
|
|
@ -224,7 +224,7 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
|
|||
formData.append(key, uploadData[key])
|
||||
}
|
||||
}
|
||||
formData.append('fileFlag', '教案')
|
||||
formData.append('fileFlag', '课件')
|
||||
uploadFileByFS({
|
||||
url: uploadUrl,
|
||||
path,
|
||||
|
|
|
@ -53,7 +53,7 @@ function createLoginWindow() {
|
|||
updateInit(loginWindow)
|
||||
}
|
||||
|
||||
// loginWindow.webContents.openDevTools()
|
||||
loginWindow.webContents.openDevTools()
|
||||
loginWindow.once('ready-to-show', () => {
|
||||
loginWindow.show()
|
||||
})
|
||||
|
@ -68,7 +68,7 @@ function createLoginWindow() {
|
|||
function createMainWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
minWidth: 1200,
|
||||
minWidth: 1350,
|
||||
height: 700,
|
||||
show: false,
|
||||
frame: false, // 无边框
|
||||
|
@ -133,7 +133,7 @@ async function createLinkWin(data) {
|
|||
contextIsolation: true
|
||||
}
|
||||
})
|
||||
linkWin[data.key].type = 'link' // 唯一标识
|
||||
linkWin[data.key].type = 'link'+data.key // 唯一标识
|
||||
|
||||
let cookieDetails = { ...data.cookieData }
|
||||
await linkWin[data.key].webContents.session.cookies
|
||||
|
@ -142,9 +142,9 @@ async function createLinkWin(data) {
|
|||
.catch((error) => {})
|
||||
data.fullPath = data.fullPath.replaceAll('//', '/')
|
||||
if (data.fullPath.indexOf('?') !== -1) {
|
||||
data.fullPath += '&urlSource=smarttalk'
|
||||
data.fullPath += '&urlSource=smarttalk&t' + Date.now()
|
||||
}else {
|
||||
data.fullPath += '?urlSource=smarttalk'
|
||||
data.fullPath += '?urlSource=smarttalk&t' + Date.now()
|
||||
}
|
||||
linkWin[data.key].loadURL(data.fullPath)
|
||||
|
||||
|
|
After Width: | Height: | Size: 48 KiB |
|
@ -0,0 +1,20 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 创建对话
|
||||
export const createChart = ({ headers, data }) => {
|
||||
return request({
|
||||
url: '/qf/createChart',
|
||||
method: 'post',
|
||||
headers,
|
||||
data,
|
||||
})
|
||||
}
|
||||
// 大模型对话
|
||||
export const sendChart = ({ headers, data }) => {
|
||||
return request({
|
||||
url: '/qf/sendTalk',
|
||||
method: 'post',
|
||||
headers,
|
||||
data,
|
||||
})
|
||||
}
|
|
@ -187,3 +187,14 @@ 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'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// 查询evaluation列表
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询作业列表
|
||||
export function listByDeadDate(query) {
|
||||
return request({
|
||||
url: '/education/classwork/listByDeadDate',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
//多个班级学生作业数据
|
||||
export function listClassworkdataByDeadDate(query) {
|
||||
return request({
|
||||
url: '/education/classworkdata/listByDeadDate',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询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
|
||||
})
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
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,
|
||||
}
|
||||
})
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -9,6 +9,14 @@ 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,
|
||||
|
|
|
@ -57,3 +57,76 @@ 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 retrievePwd(data) {
|
||||
return request({
|
||||
url: '/smarttalk/register/authRetrievePwd',
|
||||
method: 'post',
|
||||
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
|
||||
})
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询classcourse列表
|
||||
export function listClasscourse(query) {
|
||||
return request({
|
||||
url: '/education/classcourse/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询classcourse详细
|
||||
export function getClasscourse(id) {
|
||||
return request({
|
||||
url: '/education/classcourse/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增classcourse
|
||||
export function addClasscourse(data) {
|
||||
return request({
|
||||
url: '/education/classcourse',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 新增classcourse
|
||||
export function addClasscourseReturnId(data) {
|
||||
return request({
|
||||
url: '/education/classcourse/saveReturnId',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改classcourse
|
||||
export function updateClasscourse(data) {
|
||||
return request({
|
||||
url: '/education/classcourse',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除classcourse
|
||||
export function delClasscourse(id) {
|
||||
return request({
|
||||
url: '/education/classcourse/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 删除classcourse
|
||||
export function delClasscourseWithData(id) {
|
||||
return request({
|
||||
url: '/education/classcourse/removeData/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// classcourse开始上课
|
||||
export function startCourseTeaching(id) {
|
||||
return request({
|
||||
url: '/education/classcourse/startCourseTeaching/'+id,
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
||||
// 老师学生发送新的消息
|
||||
export function sendCourseTeachingMsg(data) {
|
||||
return request({
|
||||
url: '/education/classcourse/sendCourseTeachingMsg',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 老师学生获取新的交互消息
|
||||
export function getCourseTeachingMsg(id) {
|
||||
return request({
|
||||
url: '/education/classcourse/getCourseTeachingMsg/'+id,
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2794390 */
|
||||
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');
|
||||
src: url('iconfont.woff2?t=1725847033097') format('woff2'),
|
||||
url('iconfont.woff?t=1725847033097') format('woff'),
|
||||
url('iconfont.ttf?t=1725847033097') format('truetype'),
|
||||
url('iconfont.svg?t=1725847033097#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
@ -14,6 +14,38 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-aijiqiren:before {
|
||||
content: "\e73c";
|
||||
}
|
||||
|
||||
.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";
|
||||
}
|
||||
|
|
|
@ -5,6 +5,62 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "34666608",
|
||||
"name": "ai机器人",
|
||||
"font_class": "aijiqiren",
|
||||
"unicode": "e73c",
|
||||
"unicode_decimal": 59196
|
||||
},
|
||||
{
|
||||
"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": "疑问",
|
||||
|
|
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 354 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 48 KiB |
|
@ -0,0 +1,99 @@
|
|||
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; }
|
|
@ -0,0 +1,47 @@
|
|||
<template>
|
||||
<div>
|
||||
{{ displayedText }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
speed: {
|
||||
type: Number,
|
||||
default: 50, // 默认每字符间隔100毫秒
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['loaded', 'onSuccess'])
|
||||
|
||||
const displayedText = ref('')
|
||||
|
||||
const startTyping = async () =>{
|
||||
if(!props.text || props.text.length == 0) return
|
||||
for (let i = 0; i < props.text.length; i++) {
|
||||
displayedText.value += props.text.charAt(i);
|
||||
if(displayedText.value.length == props.text.length -1){
|
||||
emit('onSuccess')
|
||||
}
|
||||
emit('loaded')
|
||||
await wait(props.speed);
|
||||
nextTick();
|
||||
}
|
||||
}
|
||||
|
||||
const wait = (ms) =>{
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
onMounted(() =>{
|
||||
startTyping()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,453 @@
|
|||
<template>
|
||||
<div class="page-ai-chart">
|
||||
<svg class="icon ai-icon" aria-hidden="true" @click="isOpen = true; isMax = false" v-if="!isOpen">
|
||||
<use xlink:href="#icon-aijiqiren"></use>
|
||||
</svg>
|
||||
<div v-else v-drag shadow="always" class="chart-card" :class="[isMax ? 'card-max' : '']">
|
||||
<div class="flex chart-header">
|
||||
<div class="header-name flex">
|
||||
<svg class="icon header-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-aijiqiren"></use>
|
||||
</svg>
|
||||
<span>教学助手</span>
|
||||
</div>
|
||||
<div class="header-tool">
|
||||
<i class="iconfont icon-window-max_line icon-tool" @click="isMax = !isMax"></i>
|
||||
<i class="iconfont icon-close icon-tool" @click="closeChart"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-body">
|
||||
<el-scrollbar ref="chatref">
|
||||
<div class="default-chart flex">
|
||||
<div>你好,{{ userStore.nickName }}</div>
|
||||
<div>我是AIx教学助手,我可以帮助你:</div>
|
||||
<div class="outer-ai flex">
|
||||
<ul class="ai-ul">
|
||||
<li class="flex ai-li" v-for="item in outerAi" :key="item.id" @click="onClick(item)"
|
||||
:class="item.disabled ? 'li-disabled' : ''">
|
||||
<el-image class="ai-img" :src="item.img" />
|
||||
<div class="ai-name flex">
|
||||
<span class="title">{{ item.title }}</span>
|
||||
<span>{{ item.secondTit }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!--对话-->
|
||||
<div class="chart-content" ref="innerRef">
|
||||
<div v-for="item in msgList" :key="item.timestamp">
|
||||
<div class="author-con" v-if="item.name == 'user'">
|
||||
<div class="author-msg">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="robot-msg">
|
||||
<Answer :text="item.content ? item.content : ''" @loaded="chartLoaded" @onSuccess="isFinally = true"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="loaded" class="chart-loading">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div class="chart-input">
|
||||
<el-input v-model="msgVal" size="large" class="chart-ipt" @keyup.enter="sendMsg" @focus="isFocus = true" @blur="isFocus = false" />
|
||||
<i class="iconfont icon-tujing" :class="[isFocus ? 'icon-focus' : '']" @click="sendMsg"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import outLink from '@/utils/linkConfig'
|
||||
import Answer from './container/text.vue'
|
||||
import { createChart, sendChart } from '@/api/ai/index'
|
||||
|
||||
import vDrag from '@/views/tool/directive/drag'
|
||||
|
||||
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
const userStore = useUserStore().user
|
||||
|
||||
|
||||
/**
|
||||
* 大模型相关
|
||||
* conversation_id : 会话ID 用于对话
|
||||
* headers : 请求头
|
||||
* app_id :大模型 应用id
|
||||
* loaded : 回答状态
|
||||
* msgList : 消息列表
|
||||
* curAnswer : 当前回答的文字答案
|
||||
*/
|
||||
|
||||
let conversation_id = null
|
||||
const app_id = '712ff0df-ed6b-470f-bf87-8cfbaf757be5'
|
||||
const headers = {
|
||||
isToken: true,
|
||||
Authorization: 'Bearer bce-v3/ALTAK-VpMGiUjWehcHSPZGjUKwB/c97384c2c71a0d3b1d1060d7f9e2a4eac3343732',
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
}
|
||||
const loaded = ref(false)
|
||||
const msgList = ref([])
|
||||
const chatref = ref(null)
|
||||
|
||||
const innerRef = ref(null)
|
||||
const isFinally = ref(true)
|
||||
// 是否最大化、打开对话框
|
||||
const isMax = ref(false)
|
||||
const isOpen = ref(false)
|
||||
// 对话框框输入的值
|
||||
const msgVal = ref('')
|
||||
// 对话框是否聚焦
|
||||
const isFocus = ref(false)
|
||||
|
||||
const outerAi = [
|
||||
{
|
||||
id: 1,
|
||||
title: '生成图片',
|
||||
secondTit: '文生图大模型',
|
||||
img: new URL('../../../src/assets/images/ai-01.png', import.meta.url).href,
|
||||
path: '/ais/aisd3'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '教学大模型',
|
||||
secondTit: '中小学基础教学大模型',
|
||||
img: new URL('../../../src/assets/images/ai-02.png', import.meta.url).href,
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '育人大模型',
|
||||
secondTit: '全场域育人大模型',
|
||||
img: new URL('../../../src/assets/images/ai-03.png', import.meta.url).href,
|
||||
path: '/ais/aimodel'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'ChatTTS',
|
||||
secondTit: '文字转语音大模型',
|
||||
img: new URL('../../../src/assets/images/ai-04.png', import.meta.url).href,
|
||||
path: '/ais/aiChatTTS'
|
||||
}
|
||||
]
|
||||
|
||||
// 打开web端相关内容
|
||||
const onClick = ({ path, disabled }) => {
|
||||
if (disabled) return
|
||||
let configObj = outLink().getBaseData()
|
||||
let fullPath = configObj.fullPath + path
|
||||
fullPath = fullPath.replaceAll('//', '/')
|
||||
// 通知主进程
|
||||
ipcRenderer.send('openWindow', {
|
||||
key: path,
|
||||
fullPath: fullPath,
|
||||
cookieData: { ...configObj.data }
|
||||
})
|
||||
}
|
||||
|
||||
// 创建对话
|
||||
const getChartId = () =>{
|
||||
const data = { app_id }
|
||||
createChart({ data, headers }).then( res =>{
|
||||
if( res.code == 200){
|
||||
localStorage.setItem("conversation_id", res.data.conversation_id);
|
||||
conversation_id = res.data.conversation_id;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 大模型对话
|
||||
const sendMsg = () =>{
|
||||
//发送的内容不能为空
|
||||
if(msgVal.value == '') return
|
||||
|
||||
if(!isFinally.value) return
|
||||
isFinally.value = false
|
||||
let msg = msgVal.value
|
||||
msgVal.value = ''
|
||||
msgList.value.push({ name: 'user', timestamp: + new Date(), content: msg})
|
||||
chatref.value.setScrollTop(innerRef.value.clientHeight)
|
||||
loaded.value = true
|
||||
|
||||
const data = {
|
||||
appId: app_id,
|
||||
content: msg,
|
||||
stream: false,
|
||||
conversationId: conversation_id,
|
||||
}
|
||||
sendChart({ data, headers }).then(res =>{
|
||||
loaded.value = false
|
||||
msgList.value.push({name: 'robot', timestamp: + new Date(), content: res.data.answer})
|
||||
})
|
||||
}
|
||||
|
||||
const chartLoaded = () =>{
|
||||
chatref.value.setScrollTop(innerRef.value.clientHeight)
|
||||
}
|
||||
|
||||
const closeChart = () =>{
|
||||
loaded.value = false
|
||||
msgList.value = []
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
watch(isOpen, (newVal)=>{
|
||||
if(newVal){
|
||||
if (!conversation_id) {
|
||||
getChartId();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
conversation_id = localStorage.getItem("conversation_id");
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@mixin flex-direction-c {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-ai-chart {
|
||||
|
||||
.ai-icon {
|
||||
position: fixed;
|
||||
right: 30px;
|
||||
bottom: 30px;
|
||||
font-size: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
background-color: #fff;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 300px;
|
||||
height: calc(100% - 110px);
|
||||
border-radius: 10px;
|
||||
z-index: 10;
|
||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, .12);
|
||||
|
||||
.chart-body {
|
||||
padding: 10px 15px;
|
||||
height: calc(100% - 110px);
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
}
|
||||
|
||||
.card-max {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
-webkit-app-region: no-drag;
|
||||
.header-name {
|
||||
align-items: center;
|
||||
.header-icon {
|
||||
font-size: 28px;
|
||||
align-items: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-tool {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: #8a8a8a;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.default-chart {
|
||||
background-color: #F2F2F2;
|
||||
font-size: 13px;
|
||||
border-radius: 5px;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.outer-ai {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
margin-top: 5px;
|
||||
|
||||
.ai-ul {
|
||||
@include flex-direction-c;
|
||||
|
||||
.ai-li {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
.ai-img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 10px
|
||||
}
|
||||
|
||||
.ai-name {
|
||||
@include flex-direction-c;
|
||||
align-items: flex-start;
|
||||
font-size: 13px;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.li-disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: .3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
font-size: 14px;
|
||||
margin-top: 20px;
|
||||
|
||||
.author-con {
|
||||
@include flex-direction-c;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.author-msg {
|
||||
display: inline-block;
|
||||
background-color: #E0DFFF;
|
||||
padding: 5px 8px;
|
||||
border-radius: 7px;
|
||||
|
||||
}
|
||||
|
||||
.robot-msg {
|
||||
background-color: #fff;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-input {
|
||||
position: absolute;
|
||||
width: 90%;
|
||||
left: 5%;
|
||||
bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 15px;
|
||||
|
||||
.icon-tujing {
|
||||
font-size: 26px;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
color: #c0c0c2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-focus {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.chart-ipt {
|
||||
:deep(.el-input__wrapper) {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.chart-loading,
|
||||
.chart-loading>div {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-loading {
|
||||
display: block;
|
||||
font-size: 0;
|
||||
color: #66b1ff;
|
||||
}
|
||||
|
||||
.chart-loading.la-dark {
|
||||
color: #66b1ff;
|
||||
}
|
||||
|
||||
.chart-loading>div {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
background-color: currentColor;
|
||||
border: 0 solid currentColor;
|
||||
}
|
||||
|
||||
.chart-loading {
|
||||
width: 54px;
|
||||
height: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chart-loading>div:nth-child(1) {
|
||||
animation-delay: -200ms;
|
||||
}
|
||||
|
||||
.chart-loading>div:nth-child(2) {
|
||||
animation-delay: -100ms;
|
||||
}
|
||||
|
||||
.chart-loading>div:nth-child(3) {
|
||||
animation-delay: 0ms;
|
||||
}
|
||||
|
||||
.chart-loading>div {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 100%;
|
||||
margin-right: 4px;
|
||||
animation: ball-pulse 1s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes ball-pulse {
|
||||
|
||||
0%,
|
||||
60%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 0.1;
|
||||
transform: scale(0.01);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,12 +2,12 @@
|
|||
<div class="book-wrap">
|
||||
<el-scrollbar height="100%">
|
||||
<div class="book-name flex" @click="dialogVisible = true">
|
||||
<span>{{ curBookName }}</span>
|
||||
<span>{{ curBook.data.itemtitle }}</span>
|
||||
<i class="iconfont icon-xiangyou"></i>
|
||||
</div>
|
||||
<div class="book-list" v-loading="treeLoading">
|
||||
<el-tree ref="refTree" :data="treeData" :props="defaultProps" node-key="id"
|
||||
:default-expanded-keys="defaultExpandedKeys" :current-node-key="currentNodeId" highlight-current
|
||||
<el-tree :data="treeData" accordion :props="defaultProps" node-key="id"
|
||||
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
|
||||
@node-click="handleNodeClick">
|
||||
<template #default="{ node }">
|
||||
<span :title="node.label" class="tree-label">{{ node.label }}</span>
|
||||
|
@ -28,9 +28,9 @@
|
|||
|
||||
<div class="textbook-container">
|
||||
<el-scrollbar height="450px">
|
||||
<div class="textbook-item flex" v-for="item in subjectList" :class="curBookId == item.id ? 'active-item' : ''"
|
||||
<div class="textbook-item flex" v-for="item in subjectList" :class="curBook.data.id == item.id ? 'active-item' : ''"
|
||||
:key="item.id" @click="changeBook(item)">
|
||||
<img v-if="item.avartar" :src="BaseUrl + item.avartar" class="textbook-img" alt="">
|
||||
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
|
||||
<div v-else class="textbook-img">
|
||||
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
|
||||
</div>
|
||||
|
@ -43,139 +43,71 @@
|
|||
|
||||
<script setup>
|
||||
import { onMounted, ref, nextTick, toRaw, reactive } from 'vue';
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { listEvaluation } from '@/api/subject'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { useGetSubject } from '@/hooks/useGetSubject'
|
||||
|
||||
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
|
||||
// 定义要发送的emit事件
|
||||
const emit = defineEmits(['nodeClick', 'changeBook'])
|
||||
// store
|
||||
const userStore = useUserStore()
|
||||
const { edustage, edusubject, userId } = userStore.user
|
||||
//
|
||||
let useSubject = null
|
||||
const subjectList = ref([])
|
||||
const evaluationList = ref([])
|
||||
const dialogVisible = ref(false)
|
||||
// 当前教材下面单元内容数据
|
||||
const treeData = ref([])
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
label: 'itemtitle',
|
||||
class: 'textbook-tree'
|
||||
}
|
||||
|
||||
const treeLoading = ref(false)
|
||||
//当前教材ID
|
||||
const curBookId = ref(-1)
|
||||
//当前教材名称
|
||||
const curBookName = ref('')
|
||||
//当前教材封面图
|
||||
const curBookImg = ref('')
|
||||
//当前教材文件路径
|
||||
const curBookPath = ref('')
|
||||
// 上册
|
||||
const volumeOne = ref([])
|
||||
//
|
||||
const volumeTwo = ref([])
|
||||
|
||||
// 当前选中的教材
|
||||
const curBook = reactive({
|
||||
data: {}
|
||||
})
|
||||
// 当前节点
|
||||
const currentNode = reactive({
|
||||
const curNode = reactive({
|
||||
data:{}
|
||||
})
|
||||
// 当前选中的节点ID
|
||||
const currentNodeId = ref(0)
|
||||
// 当前选中的节点名称
|
||||
const currentNodeName = ref('')
|
||||
const treeLoading = ref(false)
|
||||
// 默认展开的节点
|
||||
const defaultExpandedKeys = ref([])
|
||||
// tree
|
||||
const refTree = ref(null)
|
||||
|
||||
|
||||
//获取教材下面的单元 + 章节
|
||||
const getSubjectContent = async () => {
|
||||
treeLoading.value = true
|
||||
const params = {
|
||||
edusubject,
|
||||
edustage,
|
||||
// entpcourseedituserid: userId,
|
||||
itemgroup: 'textbook',
|
||||
pageSize: 500
|
||||
}
|
||||
|
||||
let data;
|
||||
const { rows } = await listEvaluation(params)
|
||||
localStorage.setItem('evaluationList', JSON.stringify(rows))
|
||||
evaluationList.value = rows
|
||||
data = rows
|
||||
treeLoading.value = false
|
||||
|
||||
//获取教材版本
|
||||
await getSubject()
|
||||
//上册
|
||||
/**
|
||||
* 不区分上下册
|
||||
* 2024/08/20调整
|
||||
*/
|
||||
// volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
|
||||
// volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
|
||||
getTreeData()
|
||||
}
|
||||
|
||||
//选择教材
|
||||
const changeBook = ({ id, itemtitle, avartar, fileurl }) => {
|
||||
curBookId.value = id
|
||||
curBookName.value = itemtitle
|
||||
curBookImg.value = BaseUrl + avartar
|
||||
curBookPath.value = fileurl
|
||||
getTreeData()
|
||||
const changeBook = (data) => {
|
||||
curBook.data = data
|
||||
|
||||
localStorage.setItem('curBook', JSON.stringify(data))
|
||||
treeData.value = useSubject.getTreeData(data.id)
|
||||
|
||||
//切换教材后默认展开第一个并选中
|
||||
nextTick(() =>{
|
||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||
curNode.data = getLastLevelData(treeData.value)[0]
|
||||
|
||||
localStorage.setItem('defaultExpandedKeys', JSON.stringify(defaultExpandedKeys.value))
|
||||
localStorage.setItem('curNode',JSON.stringify(curNode.data))
|
||||
emitChangeBook()
|
||||
})
|
||||
// 延迟关闭 视觉上选中
|
||||
setTimeout(() => {
|
||||
dialogVisible.value = false
|
||||
}, 100);
|
||||
}
|
||||
|
||||
const getTreeData = () => {
|
||||
//数据过滤
|
||||
let upData = transData(evaluationList.value)
|
||||
|
||||
|
||||
if(upData.length){
|
||||
treeData.value = [...upData]
|
||||
}
|
||||
else{
|
||||
treeData.value = []
|
||||
return
|
||||
}
|
||||
nextTick(() => {
|
||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||
currentNode.data = getLastLevelData(treeData.value)[0]
|
||||
currentNodeId.value = getLastLevelData(treeData.value)[0].id
|
||||
currentNodeName.value = getLastLevelData(treeData.value)[0].label
|
||||
emitChangeBook()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const emitChangeBook = () => {
|
||||
let curNode = {
|
||||
id: currentNodeId.value,
|
||||
label: currentNodeName.value,
|
||||
itemtitle: currentNode.data.itemtitle,
|
||||
edudegree: currentNode.data.edudegree,
|
||||
edustage: currentNode.data.edustage,
|
||||
edusubject: currentNode.data.edusubject,
|
||||
}
|
||||
let parentNode = findParentByChildId(treeData.value, currentNodeId.value)
|
||||
curNode.parentNode = toRaw(parentNode)
|
||||
|
||||
const emitChangeBook = async () => {
|
||||
let curData = cloneDeep(toRaw(curNode.data))
|
||||
let parentNode = findParentByChildId(treeData.value, curData.id)
|
||||
curData.parentNode = toRaw(parentNode)
|
||||
//怎加一个label 之前取的label
|
||||
curData.label = curData.itemtitle
|
||||
const data = {
|
||||
textBook: {
|
||||
curBookId: curBookId.value,
|
||||
curBookName: curBookName.value,
|
||||
curBookImg: curBookImg.value,
|
||||
curBookPath: curBookPath.value
|
||||
curBookId: curBook.data.id,
|
||||
curBookName: curBook.data.itemtitle,
|
||||
curBookImg: BaseUrl + curBook.data.avartar,
|
||||
curBookPath: curBook.data.fileurl
|
||||
},
|
||||
node: curNode
|
||||
node: curData
|
||||
}
|
||||
emit('changeBook', data)
|
||||
}
|
||||
|
@ -224,97 +156,79 @@ const findParentByChildId = (treeData, targetNodeId) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
ary2.push(obj2)
|
||||
}
|
||||
obj.children = ary2
|
||||
})
|
||||
ary.push(obj)
|
||||
}
|
||||
}
|
||||
})
|
||||
return ary
|
||||
}
|
||||
|
||||
//获取教材
|
||||
const getSubject = async () => {
|
||||
|
||||
const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 500 })
|
||||
|
||||
// subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject)
|
||||
subjectList.value = rows
|
||||
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
|
||||
|
||||
// 默认第一个
|
||||
if(!subjectList.value.length) return
|
||||
curBookName.value = subjectList.value[0].itemtitle
|
||||
curBookId.value = subjectList.value[0].id
|
||||
curBookImg.value = BaseUrl + subjectList.value[0].avartar
|
||||
curBookPath.value = subjectList.value[0].fileurl
|
||||
}
|
||||
|
||||
|
||||
const isHaveUnit = (id) => {
|
||||
return evaluationList.value.some(item => {
|
||||
return item.rootid == id
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const handleNodeClick = (data, node) => {
|
||||
/**
|
||||
* data : 当前节点数据
|
||||
* node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象
|
||||
*/
|
||||
|
||||
const nodeData = data;
|
||||
let nodeData = cloneDeep(toRaw(data));
|
||||
//怎加一个label 之前取的label
|
||||
nodeData.label = nodeData.itemtitle
|
||||
const parentNode = node.parent.data;
|
||||
|
||||
// parentNode 为数组 则点击的是一级节点
|
||||
if (Array.isArray(parentNode)) {
|
||||
//
|
||||
nodeData.parentNode = null
|
||||
}
|
||||
else {
|
||||
nodeData.parentNode = parentNode
|
||||
// 否则 点击的为二级节点 parentNode 为它的父级节点
|
||||
nodeData.parentNode = toRaw(parentNode)
|
||||
}
|
||||
|
||||
let curData = {
|
||||
textBook: {
|
||||
curBookId: curBookId.value,
|
||||
curBookName: curBookName.value,
|
||||
curBookImg: curBookImg.value,
|
||||
curBookPath: curBookPath.value
|
||||
curBookId: curBook.data.id,
|
||||
curBookName: curBook.data.itemtitle,
|
||||
curBookImg: BaseUrl + curBook.data.avartar,
|
||||
curBookPath: curBook.data.fileurl
|
||||
},
|
||||
node: toRaw(nodeData)
|
||||
}
|
||||
currentNode.data = curData
|
||||
localStorage.setItem('defaultExpandedKeys', parentNode ? JSON.stringify([parentNode.id]) : JSON.stringify([data.id]))
|
||||
localStorage.setItem('curNode', JSON.stringify(nodeData))
|
||||
emit('nodeClick', curData)
|
||||
|
||||
}
|
||||
onMounted(() => {
|
||||
getSubjectContent()
|
||||
onMounted( async () => {
|
||||
treeLoading.value = true
|
||||
|
||||
try{
|
||||
useSubject = await useGetSubject()
|
||||
subjectList.value = useSubject.subjectList
|
||||
|
||||
let book = localStorage.getItem('curBook')
|
||||
if(book){
|
||||
book = JSON.parse(book)
|
||||
curBook.data = book
|
||||
treeData.value = useSubject.getTreeData(book.id)
|
||||
}
|
||||
else{
|
||||
curBook.data = useSubject.subjectList[0]
|
||||
treeData.value = useSubject.treeData
|
||||
}
|
||||
// 设置展开并选中
|
||||
nextTick(() =>{
|
||||
// 取缓存
|
||||
let node = localStorage.getItem('curNode')
|
||||
if(node){
|
||||
curNode.data = JSON.parse(node)
|
||||
defaultExpandedKeys.value = JSON.parse(localStorage.getItem('defaultExpandedKeys'))
|
||||
}
|
||||
else{
|
||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||
curNode.data = getLastLevelData(treeData.value)[0]
|
||||
//缓存记录当前展开以及选中节点
|
||||
localStorage.setItem('defaultExpandedKeys', JSON.stringify(defaultExpandedKeys.value))
|
||||
localStorage.setItem('curNode', JSON.stringify(curNode.data))
|
||||
}
|
||||
emitChangeBook()
|
||||
})
|
||||
|
||||
} finally{
|
||||
treeLoading.value = false
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
|
|
@ -64,6 +64,8 @@ const getVertion = (data) => {
|
|||
childs: []
|
||||
}
|
||||
})
|
||||
//判断是否存在该教材,有则展示第一项
|
||||
if(treeData.value.length === 0) return
|
||||
nextTick(() => {
|
||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||
node.currentNode.data = treeData.value[0]
|
||||
|
|
|
@ -41,7 +41,9 @@ const getSubject = (value) => {
|
|||
gradeName:gradeList[currentIndex].label
|
||||
}
|
||||
})
|
||||
const nameIndex = subjectList.value.findIndex(item => item.subjectName === useStore.user.edusubject)
|
||||
//判断是否有名称或者包括名称就对应哪个学科
|
||||
const nameIndex = subjectList.value.findIndex(item => item.subjectName === useStore.user.edusubject || item.subjectName.includes(useStore.user.edusubject))
|
||||
if(nameIndex === -1) return;
|
||||
getTagId({subjectId:subjectList.value[nameIndex].subjectId,subjectName:subjectList.value[nameIndex].subjectName})
|
||||
if(textbookVersionId.value === 0){
|
||||
getTagId(subjectList.value[0])
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
<template>
|
||||
<!-- 表单-组件(自定义) -->
|
||||
<slot>
|
||||
<el-row v-bind="rows||{}">
|
||||
<!-- 其他内容-start -->
|
||||
<slot name="start"></slot>
|
||||
<el-form :model="form" ref="form" @submit.native.prevent
|
||||
v-bind="option||{}"
|
||||
:status-icon="option.isIcon" :inline-message="option.inlineMsg"
|
||||
:label-width="option.labelW" :label-position="option.labelP" :label-suffix="option.labelS">
|
||||
<!-- 内容区 -->
|
||||
<template v-for="(item, index) in itemFields">
|
||||
<slot :name="'cols_'+item.prop" :row="item" :index="index" :prop="item.prop">
|
||||
<el-col :span="colsFn(item,'span')" :offset="colsFn(item,'offset')" :push="colsFn(item,'push')" :pull="colsFn(item,'pull')"
|
||||
:xs="colsFn(item,'xs')" :sm="colsFn(item,'sm')" :md="colsFn(item,'md')" :lg="colsFn(item,'lg')" :xl="colsFn(item,'xl')"
|
||||
:tag="colsFn(item,'tag')" :key="index" :style="item.style">
|
||||
<!-- slot可自定义-元素 默认input -->
|
||||
<slot :name="item.prop" :row="item" :index="index" :prop="item.prop" :form="form">
|
||||
<el-form-item :label="item.label" :prop="item.prop" :label-width="item.labelW"
|
||||
:required="item.required" :rules="item.rules" :error="item.error"
|
||||
:show-message="item.showMsg" :inline-message="item.inlineMsg" :size="item.size">
|
||||
<!-- slot可自定义-item内部 单个 -->
|
||||
<slot :name="'item_'+item.prop" :row="item" :index="index" :value="form[item.prop]" :prop="item.prop" :form="form">
|
||||
<!-- slot可自定义-item内部 所有 -->
|
||||
<slot name="input" :row="item" :index="index" :value="form[item.prop]" :prop="item.prop" :form="form">
|
||||
<el-input v-model="form[item.prop]" v-bind="item.inputOpt||{}"
|
||||
@keyup.enter.native="handle(item, $event)" v-on="item.on||{}">
|
||||
<!-- slot自定义-输入框头部内容 单个 -->
|
||||
<template #prefix v-if="slotKeys.includes(`prefix_${item.prop}`)"><slot :name="'prefix_'+item.prop"></slot></template>
|
||||
<!-- slot自定义-输入框尾部内容 单个 -->
|
||||
<template #suffix v-if="slotKeys.includes(`suffix_${item.prop}`)"><slot :name="'suffix_'+item.prop"></slot></template>
|
||||
<!-- slot自定义-输入框前置内容 单个 -->
|
||||
<template #prepend v-if="slotKeys.includes(`prepend_${item.prop}`)"><slot :name="'prepend_'+item.prop"></slot></template>
|
||||
<!-- slot自定义-输入框后置内容 单个 -->
|
||||
<template #append v-if="slotKeys.includes(`append_${item.prop}`)"><slot :name="'append_'+item.prop"></slot></template>
|
||||
|
||||
<!-- slot自定义-输入框头部内容 所有 -->
|
||||
<template #prefix v-if="slotKeys.includes(`prefix`)"><slot :name="'prefix_'+item.prop"></slot></template>
|
||||
<!-- slot自定义-输入框尾部内容 所有 -->
|
||||
<template #suffix v-if="slotKeys.includes(`suffix`)"><slot :name="'suffix_'+item.prop"></slot></template>
|
||||
<!-- slot自定义-输入框前置内容 所有 -->
|
||||
<template #prepend v-if="slotKeys.includes(`prepend`)"><slot :name="'prepend_'+item.prop"></slot></template>
|
||||
<!-- slot自定义-输入框后置内容 所有 -->
|
||||
<template #append v-if="slotKeys.includes(`append`)"><slot :name="'append_'+item.prop"></slot></template>
|
||||
</el-input>
|
||||
</slot>
|
||||
</slot>
|
||||
<!-- slot自定义-标签文本的内容 单个 -->
|
||||
<template #label v-if="slotKeys.includes(`label_${item.prop}`)">
|
||||
<slot :name="'label_'+item.prop" :row="item" :index="index" :prop="item.prop"></slot>
|
||||
</template>
|
||||
<!-- slot自定义-标签文本的内容 所有 -->
|
||||
<template #label v-if="slotKeys.includes(`label`)">
|
||||
<slot name="label" :row="item" :index="index" :prop="item.prop"></slot>
|
||||
</template>
|
||||
|
||||
<!-- slot自定义-自定义表单校验信息的显示方式 单个 -->
|
||||
<template #error="{error}" v-if="slotKeys.includes(`error_${item.prop}`)">
|
||||
<slot :name="'error_'+item.prop" :row="item" :index="index" :prop="item.prop" :error="error"></slot>
|
||||
</template>
|
||||
<!-- slot自定义-自定义表单校验信息的显示方式 所有 -->
|
||||
<template #error="{error}" v-if="slotKeys.includes(`error`)">
|
||||
<slot name="error" :row="item" :index="index" :prop="item.prop" :error="error"></slot>
|
||||
</template>
|
||||
|
||||
</el-form-item>
|
||||
</slot>
|
||||
</el-col>
|
||||
</slot>
|
||||
</template>
|
||||
<!-- 其他内容 -->
|
||||
<slot name="add"></slot>
|
||||
<!-- 提交 -->
|
||||
<slot name="submit" :formRef="$refs.form">
|
||||
<div class="subBtn" v-if="btnDef" :style="btnStyle">
|
||||
<el-button type="primary" size="small" @click="confirm" :loading="btnLoad">{{btnText}}</el-button>
|
||||
</div>
|
||||
</slot>
|
||||
</el-form>
|
||||
<!-- 其他内容-end -->
|
||||
<slot name="end"></slot>
|
||||
</el-row>
|
||||
</slot>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'cForm',
|
||||
props: {
|
||||
form: { // 数据 form
|
||||
type: Object,
|
||||
default: _ => { return {} }
|
||||
},
|
||||
option: { // 配置
|
||||
type: Object,
|
||||
default: _ => {
|
||||
return { labelW: '80px' }
|
||||
}
|
||||
},
|
||||
itemOption: { // item配置
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
rows: { // 行 layout布局
|
||||
type: Object,
|
||||
default: _ => { return {} }
|
||||
},
|
||||
cols: { // 列 layout布局
|
||||
type: Object,
|
||||
default: _ => { return {} }
|
||||
},
|
||||
// onEnter: Function, // 默认回车
|
||||
onEnter: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
btnText: { // 文本
|
||||
type: String,
|
||||
default: '确 定'
|
||||
},
|
||||
btnDef: Boolean, // 默认提交-按钮
|
||||
btnStyle: String, // 按钮样式css
|
||||
btnLoad: Boolean, // 加载load
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
slotKeys: [] // 外部自定义插槽
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
itemFields() {
|
||||
return this.itemOption
|
||||
.filter(o => o.show !== false)
|
||||
.map(o => {
|
||||
for (const key in o) {
|
||||
const inputOpt = {placeholder:'请输入'}
|
||||
if (key.startsWith('i')) {
|
||||
const newKey = key.substring(1)
|
||||
inputOpt[newKey] = o[key]
|
||||
delete o[key]
|
||||
}
|
||||
o.inputOpt = inputOpt
|
||||
return o
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.test = this
|
||||
this.slotKeys = Object.keys(this.$slots)
|
||||
},
|
||||
methods: {
|
||||
// 字段--cols属性
|
||||
colsFn(row, attr) {
|
||||
const cols = row.cols || this.cols
|
||||
return cols[attr]
|
||||
},
|
||||
// input -- 回车事件
|
||||
handle(row, e) {
|
||||
const onEnter = row.onEnter || this.onEnter
|
||||
if (row.prop) onEnter(row, e)
|
||||
},
|
||||
// 确定--提交
|
||||
confirm() {
|
||||
const formRefs = this.$refs.form
|
||||
this.$emit('confirm', formRefs)
|
||||
},
|
||||
// 重置
|
||||
resetFields() {
|
||||
this.$refs.form.resetFields()
|
||||
},
|
||||
// 清除
|
||||
clearValidate(props) {
|
||||
this.$refs.form.clearValidate(props)
|
||||
},
|
||||
// 检验-字段
|
||||
validateField(props, callback) {
|
||||
this.$refs.form.validateField(props, callback)
|
||||
},
|
||||
// 整个表单验证
|
||||
validate(callback) {
|
||||
return this.$refs.form.validate(callback)
|
||||
}
|
||||
},
|
||||
// filters: {
|
||||
// // 是否清除input内容-按钮
|
||||
// clearable_f(val) {
|
||||
// return val == null ? true : val
|
||||
// }
|
||||
// }
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.subBtn{
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.el-form {
|
||||
width: 100%;
|
||||
}
|
||||
.el-form--inline > .el-col {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
float: unset;
|
||||
}
|
||||
.el-form-item .el-input{
|
||||
vertical-align: bottom;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
/**
|
||||
* 使用-批量导入--方式
|
||||
*/
|
||||
|
||||
// 导入 import.meta.glob 用于获取所有符合要求的 .vue 文件
|
||||
const files = import.meta.glob('./!(index).vue', { eager: true });
|
||||
|
||||
export default {
|
||||
install(Vue, options) {
|
||||
Object.entries(files).forEach(([path, file]) => {
|
||||
const fileName = path.split('/').pop().replace(/\.\w+$/, '')
|
||||
// fileName == 'cDialog' && initDialog(Vue, file) // 弹窗--组件化
|
||||
Vue.component(fileName, file.default)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 弹窗--函数化
|
||||
function initDialog(Vue, dialog) {
|
||||
// 全局绑定
|
||||
Vue.prototype.$cDialog = (props) => {
|
||||
// props.dialog = Object.assign({ isOpen: true }, props.dialog) // 默认配置 propsData: option
|
||||
props.dialog.isOpen == null && (props.dialog.isOpen = true) // 默认打开
|
||||
props.isRemove == null && (props.isRemove = true) // 默认关闭后移除
|
||||
const Constructor = Vue.extend(dialog)
|
||||
const Instance = new Constructor({ propsData: props })
|
||||
props.slots && (Instance.$slots = props.slots) // 插槽内容
|
||||
props.scopedSlots && (Instance.$scopedSlots = props.scopedSlots) // 作用域插槽内容
|
||||
props.content && (Instance.$slots.default = props.content) // 插槽内容
|
||||
document.body.appendChild(Instance.$mount().$el)
|
||||
return Instance.showBox().then(v => {
|
||||
props.callback && props.callback(v)
|
||||
return Promise.resolve(v)
|
||||
}).catch(v => {
|
||||
props.callback && props.callback(v)
|
||||
// 移除弹窗
|
||||
props.isRemove && document.body.removeChild(Instance.$mount().$el)
|
||||
return Promise.reject(v)
|
||||
})
|
||||
}
|
||||
// 全局绑定2
|
||||
Vue.prototype.$cDialog2 = (props) => {
|
||||
// props.dialog = Object.assign({ isOpen: true }, props.dialog) // 默认配置 propsData: option
|
||||
props.dialog.isOpen == null && (props.dialog.isOpen = true) // 默认打开
|
||||
props.isRemove == null && (props.isRemove = true) // 默认关闭后移除
|
||||
const Constructor = Vue.extend(dialog)
|
||||
const Instance = new Constructor({ propsData: props })
|
||||
props.slots && (Instance.$slots = props.slots) // 插槽内容
|
||||
props.scopedSlots && (Instance.$scopedSlots = props.scopedSlots) // 作用域插槽内容
|
||||
props.content && (Instance.$slots.default = props.content) // 插槽内容
|
||||
document.body.appendChild(Instance.$mount().$el)
|
||||
Instance.showBox().then(v => {
|
||||
props.callback && props.callback(v)
|
||||
return Promise.resolve(v)
|
||||
}).catch(v => {
|
||||
props.callback && props.callback(v)
|
||||
// 移除弹窗
|
||||
props.isRemove && document.body.removeChild(Instance.$mount().$el)
|
||||
return Promise.reject(v)
|
||||
})
|
||||
return Instance
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ const getFileTypeIcon = () => {
|
|||
gif: 'icon-gif',
|
||||
txt: 'icon-txt',
|
||||
rar: 'icon-rar',
|
||||
|
||||
apt: 'icon-lunwen'
|
||||
}
|
||||
if (iconObj[name]) {
|
||||
return '#' + iconObj[name]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-drawer v-model="model" class="preview-drawer" title="title" :modal="false" :with-header="false" append-to-body
|
||||
<el-drawer v-model="model" class="preview-drawer" title="title" :modal="true" :destroy-on-close="true" :with-header="false" :append-to-body="true"
|
||||
size="50%">
|
||||
<div class="flex drawer-header">
|
||||
<div>
|
||||
|
@ -16,19 +16,23 @@
|
|||
</div>
|
||||
<div class="drawer-content">
|
||||
<!-- <iframe src="./aaa.pdf" width="600px" height="600px"></iframe> -->
|
||||
<!-- <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 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>
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script setup>
|
||||
import { watch, ref, nextTick } from 'vue'
|
||||
import { watch, ref, nextTick, onMounted } 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: {
|
||||
|
@ -42,11 +46,26 @@ 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(() => {
|
||||
// 播放器 基础用法
|
||||
|
|
|
@ -115,7 +115,8 @@ const getSubjectContent = async () => {
|
|||
edustage,
|
||||
// entpcourseedituserid: userId,
|
||||
itemgroup: 'textbook',
|
||||
pageSize: 500
|
||||
orderby: 'orderidx asc',
|
||||
pageSize: 10000
|
||||
}
|
||||
let data;
|
||||
const { rows } = await listEvaluation(params)
|
||||
|
@ -136,13 +137,13 @@ const getSubjectContent = async () => {
|
|||
}
|
||||
|
||||
const getSubject = async () => {
|
||||
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
|
||||
|
||||
|
||||
if (localStorage.getItem('subjectList')) {
|
||||
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
|
||||
}
|
||||
else {
|
||||
const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 500 })
|
||||
const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 10000, orderby: 'orderidx asc', })
|
||||
subjectList.value = rows
|
||||
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
<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>
|
|
@ -0,0 +1,7 @@
|
|||
export interface FileProps {
|
||||
id?: number;
|
||||
type?: string;
|
||||
fileType?: string;
|
||||
raw?: File;
|
||||
filePath?: string;
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
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
|
||||
};
|
||||
}
|
||||
|
|
@ -10,13 +10,13 @@
|
|||
<div v-loading="setLoading">
|
||||
<el-form :model="form" label-width="80px" ref="ruleFormRef" :rules="rules">
|
||||
<el-form-item label="班级" prop="grade">
|
||||
<el-scrollbar max-height="200px" style="width: 100%">
|
||||
<el-scrollbar max-height="150px" style="width: 100%">
|
||||
<el-tree :props="defaultProps" :load="getLoad" node-key="id" highlight-current @check="handleCheckChange"
|
||||
lazy show-checkbox />
|
||||
</el-scrollbar>
|
||||
</el-form-item>
|
||||
<el-form-item label="选中学生" prop="student">
|
||||
<el-scrollbar max-height="200px">
|
||||
<el-scrollbar max-height="130px">
|
||||
<el-tag v-for="(tag, index) in studentList" :key="tag.studentid" closable type="primary"
|
||||
@close="delStudent(index)">
|
||||
{{ tag.name }}
|
||||
|
@ -53,6 +53,7 @@ import { ElMessage } from 'element-plus'
|
|||
import { listClassmain, listClassgroup } from '@/api/classManage/index'
|
||||
import { saveByClassWorkArray } from '@/api/teaching/classwork'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { getCurrentTime } from '@/utils/date'
|
||||
import { uniqBy, groupBy } from 'lodash'
|
||||
|
||||
const model = defineModel({ type: Boolean, default: false })
|
||||
|
@ -233,7 +234,7 @@ const onSubmit = (formEl) => {
|
|||
timelength: form.timelength,
|
||||
weights: 1,
|
||||
deaddate: form.deaddate,
|
||||
workdate: getCurrentDate(),
|
||||
workdate: getCurrentTime('YYYY-MM-DD'),
|
||||
uniquekey: props.row.uniquekey,
|
||||
entpcourseworklist: '[' + props.row.entpcourseworklist + ']',
|
||||
needMsgNotifine: 'false',
|
||||
|
@ -242,7 +243,8 @@ const onSubmit = (formEl) => {
|
|||
msgcontent: '',
|
||||
teachername: userInfo.nickName,
|
||||
unixstamp: new Date().getTime(),
|
||||
worktype: props.row.worktype
|
||||
worktype: props.row.worktype,
|
||||
status: '1'
|
||||
}
|
||||
ary.push(obj)
|
||||
}
|
||||
|
@ -275,24 +277,10 @@ const cloneDialog = (formEl) => {
|
|||
formEl.resetFields()
|
||||
model.value = false
|
||||
}
|
||||
// 获取当前年月日
|
||||
const getCurrentDate = () => {
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
let month = now.getMonth() + 1 // 月份是从0开始的,所以需要+1
|
||||
let day = now.getDate()
|
||||
if (month < 10) {
|
||||
month = '0' + month
|
||||
}
|
||||
if (day < 10) {
|
||||
day = '0' + day
|
||||
}
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 默认当前
|
||||
form.deaddate = getCurrentDate() + ' ' + '10:00:00'
|
||||
form.deaddate = getCurrentTime('YYYY-MM-DD HH:mm')
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
import { ref, watch } from 'vue'
|
||||
import FileImage from '@/components/file-image/index.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { resourceType } from '@/utils/resourceDict'
|
||||
import { getFileSuffix, getFileName } from '@/utils/ruoyi'
|
||||
|
||||
const props = defineProps({
|
||||
|
@ -58,6 +57,22 @@ const props = defineProps({
|
|||
default: false
|
||||
},
|
||||
})
|
||||
const resourceType = ref([
|
||||
{
|
||||
label: '素材',
|
||||
value: '素材'
|
||||
},
|
||||
|
||||
{
|
||||
label: '课件',
|
||||
value: '课件'
|
||||
},
|
||||
|
||||
{
|
||||
label: '教案',
|
||||
value: '教案'
|
||||
}
|
||||
])
|
||||
const dialogValue = ref(false)
|
||||
const limit = ref(5)
|
||||
// 定义要发送的emit事件
|
||||
|
|
|
@ -51,6 +51,8 @@ const closeWindow = () => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.header-tool {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
span {
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
import { nextTick, toRaw } from 'vue'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { listEvaluation } from '@/api/subject'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { edustage, edusubject } = userStore.user
|
||||
|
||||
let evaluationList = []; // 教材版本list
|
||||
let subjectList = []; // 教材list
|
||||
//当前教材ID
|
||||
let curBookId = -1;
|
||||
|
||||
/**
|
||||
* 外部链接初始化获取 跳转web端的 unitId 专用,
|
||||
* 暂时: 初始化作业设计专用,后期可能会取消
|
||||
*/
|
||||
export const useGetClassWork = async () => {
|
||||
|
||||
const params = {
|
||||
edusubject,
|
||||
edustage,
|
||||
// entpcourseedituserid: userId,
|
||||
itemgroup: 'textbook',
|
||||
orderby: 'orderidx asc',
|
||||
pageSize: 10000
|
||||
}
|
||||
|
||||
if(localStorage.getItem('evaluationList')){
|
||||
evaluationList = JSON.parse(localStorage.getItem('evaluationList'))
|
||||
}else{
|
||||
const { rows } = await listEvaluation(params)
|
||||
localStorage.setItem('evaluationList', JSON.stringify(rows))
|
||||
evaluationList = rows
|
||||
}
|
||||
|
||||
//获取教材版本
|
||||
await getSubject()
|
||||
//上册
|
||||
/**
|
||||
* 不区分上下册
|
||||
* 2024/08/20调整
|
||||
*/
|
||||
// volumeOne = data.filter(item => item.level == 1 && item.semester == '上册')
|
||||
// volumeTwo = data.filter(item => item.level == 1 && item.semester == '下册')
|
||||
getTreeData()
|
||||
}
|
||||
|
||||
//获取教材
|
||||
const getSubject = async () => {
|
||||
if(localStorage.getItem('subjectList')){
|
||||
subjectList = JSON.parse(localStorage.getItem('subjectList'))
|
||||
}else{
|
||||
const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 10000,orderby: 'orderidx asc', })
|
||||
|
||||
// subjectList = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject)
|
||||
subjectList = rows
|
||||
localStorage.setItem('subjectList', JSON.stringify(subjectList))
|
||||
}
|
||||
|
||||
// 默认第一个
|
||||
if(!subjectList.length) return
|
||||
// curBookName = subjectList[0].itemtitle
|
||||
curBookId = subjectList[0].id
|
||||
// curBookImg = BaseUrl + subjectList[0].avartar
|
||||
// curBookPath = subjectList[0].fileurl
|
||||
}
|
||||
|
||||
|
||||
const getTreeData = () => {
|
||||
//数据过滤
|
||||
let upData = transData(evaluationList)
|
||||
|
||||
if(upData.length){
|
||||
// treeData = [...upData]
|
||||
}else{
|
||||
// treeData = []
|
||||
return
|
||||
}
|
||||
nextTick(() => {
|
||||
// defaultExpandedKeys = [treeData[0].id]
|
||||
// let currentNodeObj = {...getLastLevelData(upData)[0]}
|
||||
let currentNodeId = getLastLevelData(upData)[0].id
|
||||
let currentNodeName = getLastLevelData(upData)[0].label
|
||||
|
||||
let curNode = {
|
||||
id: currentNodeId,
|
||||
label: currentNodeName,
|
||||
// itemtitle: currentNodeObj.itemtitle,
|
||||
// edudegree: currentNodeObj.edudegree,
|
||||
// edustage: currentNodeObj.edustage,
|
||||
// edusubject: currentNodeObj.edusubject,
|
||||
}
|
||||
let parentNode = findParentByChildId(upData, currentNodeId)
|
||||
curNode.parentNode = toRaw(parentNode)
|
||||
|
||||
let levelFirstId = '';
|
||||
let levelSecondId = '';
|
||||
|
||||
if (curNode.parentNode) {
|
||||
levelFirstId = curNode.parentNode.id
|
||||
} else {
|
||||
levelFirstId = curNode.id
|
||||
levelSecondId = ''
|
||||
}
|
||||
// 头部 教材分析、作业设计:打开外部链接需要当前章节ID
|
||||
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId}))
|
||||
|
||||
// const data = {
|
||||
// textBook: {
|
||||
// curBookId: curBookId,
|
||||
// curBookName: curBookName,
|
||||
// curBookImg: curBookImg,
|
||||
// curBookPath: curBookPath
|
||||
// },
|
||||
// node: curNode
|
||||
// }
|
||||
// emit('changeBook', data)
|
||||
})
|
||||
}
|
||||
|
||||
const getLastLevelData = (tree) => {
|
||||
let lastLevelData = [];
|
||||
// 递归函数遍历树形结构
|
||||
function traverseTree(nodes) {
|
||||
nodes.forEach((node) => {
|
||||
// 如果当前节点有子节点,继续遍历
|
||||
if (node.children && node.children.length > 0) {
|
||||
traverseTree(node.children);
|
||||
} else {
|
||||
// 如果没有子节点,说明是最后一层的节点
|
||||
lastLevelData.push(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 调用递归函数开始遍历
|
||||
traverseTree(tree);
|
||||
|
||||
// 返回最后一层的数据
|
||||
return lastLevelData;
|
||||
}
|
||||
|
||||
// 根据id 拿到父节点数据
|
||||
const findParentByChildId = (treeData, targetNodeId) => {
|
||||
// 递归查找函数
|
||||
// 遍历树中的每个节点
|
||||
for (let node of treeData) {
|
||||
// 检查当前节点的子节点是否包含目标子节点 ID
|
||||
if (node.children && node.children.some(child => child.id === targetNodeId)) {
|
||||
// 如果当前节点的某个子节点的 ID 匹配目标子节点 ID,则当前节点即为父节点
|
||||
return node;
|
||||
}
|
||||
// 如果当前节点没有匹配的子节点,则递归检查当前节点的子节点
|
||||
if (node.children) {
|
||||
let parentNode = findParentByChildId(node.children, targetNodeId);
|
||||
if (parentNode) {
|
||||
return parentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果未找到匹配的父节点,则返回 null 或者适当的默认值
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const transData = (data) => {
|
||||
let ary = []
|
||||
data.forEach(item => {
|
||||
let obj = {}
|
||||
// 根据当前教材ID 过滤出对应的单元、章节
|
||||
if (item.rootid == curBookId) {
|
||||
if(item.level == 1){
|
||||
obj.label = item.itemtitle
|
||||
obj.id = item.id
|
||||
obj.itemtitle = item.itemtitle
|
||||
obj.edudegree = item.edudegree
|
||||
obj.edustage = item.edustage
|
||||
obj.edusubject = item.edusubject
|
||||
let ary2 = []
|
||||
evaluationList.forEach(el => {
|
||||
let obj2 = {}
|
||||
if (item.id == el.parentid) {
|
||||
obj2 = {
|
||||
label: el.itemtitle,
|
||||
id: el.id,
|
||||
itemtitle : el.itemtitle,
|
||||
edudegree : el.edudegree,
|
||||
edustage : el.edustage,
|
||||
edusubject : el.edusubject,
|
||||
}
|
||||
ary2.push(obj2)
|
||||
}
|
||||
obj.children = ary2
|
||||
})
|
||||
ary.push(obj)
|
||||
}
|
||||
}
|
||||
})
|
||||
return ary
|
||||
}
|
||||
|
|
@ -71,7 +71,8 @@ const getHomeWorkList = async () => {
|
|||
return await homeworklist({
|
||||
entpcourseid: chapterId,
|
||||
edituserid: userStore.user.userId,
|
||||
pageSize: 100
|
||||
pageSize: 100,
|
||||
status: '10'
|
||||
}).then((res) => {
|
||||
//以下代码 参照AIx web端 作业布置
|
||||
let list = []
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import { ref } from 'vue'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { listEvaluation } from '@/api/subject'
|
||||
|
||||
export const useGetSubject = async () =>{
|
||||
|
||||
// user store
|
||||
const userStore = useUserStore()
|
||||
const { edustage, edusubject, userId } = userStore.user
|
||||
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
|
||||
// 章节List
|
||||
const unitList = ref([])
|
||||
// 教材List
|
||||
let subjectList = null
|
||||
// 单元章节树结构
|
||||
let treeData = null
|
||||
|
||||
|
||||
// 根据 学科 + 学段 获取所有单元章节
|
||||
const getSubjectUnit = async () =>{
|
||||
let strUnit = localStorage.getItem('unitList')
|
||||
if(strUnit){
|
||||
unitList.value = JSON.parse(strUnit)
|
||||
}
|
||||
else{
|
||||
const unitParams = {
|
||||
edusubject,
|
||||
edustage,
|
||||
itemgroup: 'textbook',
|
||||
orderby: 'orderidx asc',
|
||||
pageSize: 10000
|
||||
}
|
||||
const { rows } = await listEvaluation(unitParams)
|
||||
unitList.value = rows
|
||||
localStorage.setItem('unitList', JSON.stringify(rows))
|
||||
}
|
||||
|
||||
await getSubject()
|
||||
}
|
||||
|
||||
// 获取 学科 + 学段 获取教材
|
||||
const getSubject = async () =>{
|
||||
|
||||
let strSubject = localStorage.getItem('subjectList')
|
||||
if(strSubject){
|
||||
subjectList = JSON.parse(strSubject)
|
||||
}
|
||||
else{
|
||||
const subjectParams = {
|
||||
itemkey: "version",
|
||||
edusubject,
|
||||
edustage,
|
||||
pageSize: 10000,
|
||||
orderby: 'orderidx asc'
|
||||
}
|
||||
const { rows } = await listEvaluation(subjectParams)
|
||||
subjectList = rows
|
||||
localStorage.setItem('subjectList', JSON.stringify(rows))
|
||||
}
|
||||
|
||||
// 默认选中第一个教材
|
||||
if(subjectList && subjectList.length){
|
||||
treeData = getTreeData(subjectList[0].id)
|
||||
}
|
||||
}
|
||||
|
||||
// 单元章节数据转为“树”结构
|
||||
const getTreeData = (bookId) =>{
|
||||
// 根据当前教材的id 查找出对应的章节
|
||||
let data = unitList.value.filter(item => item.rootid == bookId && item.level == 1)
|
||||
data.forEach( item => {
|
||||
item.children = unitList.value.filter( item2 => item2.parentid == item.id && item2.level == 2)
|
||||
})
|
||||
return data
|
||||
|
||||
}
|
||||
|
||||
await getSubjectUnit()
|
||||
|
||||
return { subjectList, treeData, getTreeData }
|
||||
|
||||
}
|
|
@ -0,0 +1,514 @@
|
|||
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 = '见试题解答内容';
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
<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">
|
||||
<div v-show="$route != null" style="height: 100%; flex: 1;width: 100%">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<keep-alive>
|
||||
<component :is="Component" v-if="route.meta.keepAlive" :key="route.name" />
|
||||
|
@ -72,8 +72,9 @@ const title = reactive([
|
|||
child1: []
|
||||
},
|
||||
{
|
||||
name: '高考研究',
|
||||
url: '/education/colentrance',
|
||||
name: '考试分析',
|
||||
url: '/examReport',
|
||||
type: 'hash',
|
||||
img: 'iconfont icon-icon_kaoshifenxi',
|
||||
child1: []
|
||||
}
|
||||
|
@ -120,7 +121,7 @@ const active = (index) => {
|
|||
})
|
||||
}
|
||||
onMounted(()=>{
|
||||
active(0)
|
||||
// active(0)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
|
|
@ -4,20 +4,47 @@
|
|||
<Header />
|
||||
</el-header>
|
||||
<el-main>
|
||||
<AppMain />
|
||||
<template v-if="currentRoute.path != '/home'">
|
||||
<el-page-header @back="goBack">
|
||||
<template #content>
|
||||
<span class="text-large mr-3"> {{ currentRoute.meta.title }} </span>
|
||||
</template>
|
||||
</el-page-header>
|
||||
</template>
|
||||
<AppMain :style="{ height: currentRoute.path == '/home' ? '100%' : 'calc(100% - 45px)'}" />
|
||||
</el-main>
|
||||
<Uploader v-if="uploaderStore.uploadList && uploaderStore.uploadList.length > 0" />
|
||||
<AiChart/>
|
||||
</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 AiChart from '@/components/ai-chart/index.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>
|
||||
|
@ -32,6 +59,15 @@ let uploaderStore = ref(uploaderState())
|
|||
height: 80px;
|
||||
}
|
||||
.el-main {
|
||||
--el-main-padding: 0 20px 0 0;
|
||||
--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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -8,11 +8,13 @@ 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'
|
||||
import router from './router'
|
||||
import log from 'electron-log/renderer' // 渲染进程日志-文件记录
|
||||
import customComponent from '@/components/common' // 自定义组件
|
||||
|
||||
if(process.env.NODE_ENV != 'development') { // 非开发环境,将日志打印到日志文件
|
||||
Object.assign(console, log.functions) // 渲染进程日志-控制台替换
|
||||
|
@ -20,6 +22,22 @@ 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')
|
||||
.use(ElementPlus, { locale: zhLocale })
|
||||
.use(customComponent) // 自定义组件
|
||||
.mount('#app')
|
|
@ -173,6 +173,7 @@ export class ImChat {
|
|||
return this.timChat.TIMLogout().then(res => {
|
||||
this.setConsole('%cim-chat: logout', '登出成功')
|
||||
this.status.isLogin = false
|
||||
this.status.isConnect = false
|
||||
this.timChat.TIMUninit() // 反初始化
|
||||
return res
|
||||
}).catch(error => {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
const isNode = typeof require !== 'undefined' // 是否支持node函数
|
||||
const { ipcRenderer } = isNode?require('electron'):{} // app使用
|
||||
import { sessionStore } from '@/utils/store'
|
||||
import CircularJSON from 'circular-json'
|
||||
// import { diff } from 'jsondiffpatch'
|
||||
// const Remote = isNode?require('@electron/remote'):{} // 远程模块
|
||||
|
||||
|
@ -60,14 +61,15 @@ function stateSyncWatch(storeName, newState) {
|
|||
// console.log('state-change-diffData', diffData)
|
||||
try {
|
||||
for(const key in diffData) {
|
||||
const value = diffData[key]
|
||||
const value = diffData[key] || null
|
||||
const newValue = {} // 重新组装pinia需要的数据 {a:{b:1}} 这种
|
||||
const keyArr = key.split('.') || []
|
||||
keyArr.reduce((o,c,i)=>{o[c] = i === keyArr.length-1 ? value : {};return o[c]}, newValue)
|
||||
const jsonStr = JSON.stringify(newValue) // 从新组装-json数据
|
||||
// // 更新本地数据-session
|
||||
// 更新本地数据-session
|
||||
// console.log('state-change-update:', key, value)
|
||||
sessionStore.set(key, value)
|
||||
// // 通知主线程更新
|
||||
// 通知主线程更新
|
||||
ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
|
||||
// console.log('======',key, value, jsonStr )
|
||||
}
|
||||
|
@ -170,9 +172,12 @@ const getObjValue = (obj, key) => {
|
|||
const findDifferences = (obj1, obj2) => {
|
||||
const differences = {};
|
||||
function compareObjects(o1, o2, path = '') {
|
||||
if (o1 == null) return
|
||||
if (o2 == null) return
|
||||
for (const key in o1) {
|
||||
if (o1.hasOwnProperty(key)) {
|
||||
const newPath = path ? `${path}.${key}` : key;
|
||||
if(!o2) return
|
||||
if (o2.hasOwnProperty(key)) {
|
||||
const v1 = toJsonStr(o1[key])
|
||||
const v2 = toJsonStr(o2[key])
|
||||
|
@ -202,6 +207,6 @@ const findDifferences = (obj1, obj2) => {
|
|||
return differences;
|
||||
}
|
||||
// 对象克隆
|
||||
const objClone = (obj) => JSON.parse(JSON.stringify(obj))
|
||||
const objClone = (obj) => JSON.parse(CircularJSON.stringify(obj))
|
||||
// 转换为json
|
||||
const toJsonStr = (obj) => JSON.stringify(obj)
|
||||
const toJsonStr = (obj) => CircularJSON.stringify(obj)
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* @Author: 苦逼程序猿
|
||||
* @Date: 2024-09-06 16:15:32
|
||||
* @Warning: 千行代码,Bug露锋芒。
|
||||
*/
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
import Layout from '../layout/index.vue'
|
||||
|
@ -13,8 +18,14 @@ export const constantRoutes = [
|
|||
{
|
||||
path: '/',
|
||||
component: Layout,
|
||||
redirect: '/homepage',
|
||||
redirect: '/home',
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
component: () => import('@/views/desktop/index.vue'),
|
||||
name: 'desktop',
|
||||
meta: {title: '主页'}
|
||||
},
|
||||
{
|
||||
path: '/homepage',
|
||||
component: () => import('@/views/homePage/index.vue'),
|
||||
|
@ -25,13 +36,13 @@ export const constantRoutes = [
|
|||
path: '/resource',
|
||||
component: () => import('@/views/resource/index.vue'),
|
||||
name: 'resource',
|
||||
meta: {title: '资源'}
|
||||
meta: {title: '资源库'}
|
||||
},
|
||||
{
|
||||
path: '/prepare',
|
||||
component: () => import('@/views/prepare/index.vue'),
|
||||
name: 'prepare',
|
||||
meta: {title: '备课'}
|
||||
meta: {title: '教学实践'}
|
||||
},
|
||||
{
|
||||
path: '/teach',
|
||||
|
@ -69,6 +80,30 @@ export const constantRoutes = [
|
|||
name: 'class',
|
||||
meta: {title: '班级中心'},
|
||||
},
|
||||
{
|
||||
path: '/classTaskAssign',
|
||||
component: () => import('@/views/classTaskAssign/index.vue'),
|
||||
name: 'classTaskAssign',
|
||||
meta: {title: '作业设计'},
|
||||
},
|
||||
{
|
||||
path: '/classTask',
|
||||
component: () => import('@/views/classTask/classTask.vue'),
|
||||
name: 'classCorrect',
|
||||
meta: {title: '作业批改'},
|
||||
},
|
||||
{
|
||||
path: '/newClassTask',
|
||||
component: () => import('@/views/classTask/newClassTask.vue'),
|
||||
name: 'newClassCorrect',
|
||||
meta: {title: '作业设计'},
|
||||
},
|
||||
{
|
||||
path: '/examReport',
|
||||
component: () => import('@/views/examReport/index.vue'),
|
||||
name: 'examReport',
|
||||
meta: {title: '考试分析'}
|
||||
},
|
||||
]
|
||||
},
|
||||
...toolRouters
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { } from '@/api/classTask/index.js'
|
||||
import { listClassmain } from '@/api/classManage/index'
|
||||
|
||||
const useClassTaskStore = defineStore('classTask',{
|
||||
state: () => ({
|
||||
classListIds: [],
|
||||
}),
|
||||
actions: {
|
||||
listClassmain(params) {
|
||||
// 获取班级列表
|
||||
return new Promise((resolve, reject) => {
|
||||
listClassmain(params)
|
||||
.then((res) => {
|
||||
this.classListIds = res.rows&&res.rows.map((item) => item.id)
|
||||
resolve(res)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
persist: true
|
||||
})
|
||||
export default useClassTaskStore
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { defineStore } from 'pinia'
|
||||
const overviewStore = defineStore(
|
||||
'overview',
|
||||
{
|
||||
state: () => {
|
||||
return {
|
||||
tableList:[]
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
getTableList(data){
|
||||
this.tableList = [...data]
|
||||
}
|
||||
}
|
||||
})
|
||||
export default overviewStore
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* 工具类-窗口-状态管理
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { sessionStore } from '@/utils/tool'
|
||||
import { sessionStore } from '@/utils/store'
|
||||
|
||||
// 默认数据
|
||||
const defData = sessionStore.store || {}
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
|
||||
/**
|
||||
* @description 公共工具类
|
||||
* @author zdg
|
||||
* @date 2024-4-26
|
||||
*/
|
||||
|
||||
// ============= 文件工具--相关 ===================
|
||||
/**
|
||||
* 获取上传文件
|
||||
*/
|
||||
export function getFiles() {
|
||||
const cb = resolve => {
|
||||
const fileDom = document.createElement('input')
|
||||
fileDom.type = 'file'
|
||||
fileDom.onchange = e => {
|
||||
resolve(e.target.files)
|
||||
return fileDom.remove()
|
||||
}
|
||||
fileDom.click()
|
||||
}
|
||||
return new Promise(cb)
|
||||
}
|
||||
|
||||
// ============= 数学公式--相关 ===================
|
||||
/**
|
||||
* @description 计算两点直线距离 (获取直径)
|
||||
* (欧几里得距离公式): [ \text{distance} = \sqrt{(x2 - x1)^2 + (y2 - y1)^2} ]
|
||||
* @param {*} x1
|
||||
* @param {*} y1
|
||||
* @param {*} x2
|
||||
* @param {*} y2
|
||||
*/
|
||||
export function getDistance(x1,y1,x2,y2) {
|
||||
return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2))
|
||||
}
|
||||
// 获取半径
|
||||
export function getRadius(x1,y1,x2,y2) { return getDistance(x1,y1,x2,y2) / 2 }
|
||||
|
||||
/**
|
||||
* 计算某个值在总数中所占的百分比。
|
||||
*
|
||||
* 此函数用于根据给定的值和总数,计算该值占总数的百分比。它还支持指定百分比的小数位数。
|
||||
* 如果计算结果小于0,则返回0;如果大于100,则返回100,以确保百分比的合理范围。
|
||||
*
|
||||
* @param {number} v - 待计算的值。
|
||||
* @param {number} total - 总数。
|
||||
* @param {number} [step=2] - 百分比的小数位数,默认为2。
|
||||
* @returns {number} - 返回计算后的百分比,保证在0到100之间。
|
||||
*/
|
||||
export function getPercent(v, total, step=2) {
|
||||
!v && (v = 0)
|
||||
!total && (total = 1)
|
||||
// 计算百分比,保留指定的小数位,并转换为数字类型
|
||||
let res = (v / total * 100).toFixed(step)-0
|
||||
|
||||
// 确保百分比在0到100之间
|
||||
return res < 0 ? 0 : res > 100 ? 100 : res
|
||||
}
|
||||
|
||||
// ============= 格式化--相关 ===================
|
||||
|
||||
/**
|
||||
* @description 手机号隐藏中间几位
|
||||
* @param {*} phone 手机号
|
||||
* @param {*} start 前面保留几位 默认 3位
|
||||
* @param {*} end 后面保留几位 默认 3位
|
||||
* @param {*} rstr 替换字符 默认 ****
|
||||
* @returns
|
||||
*/
|
||||
export function phoneHideFormat(phone, start = 3, end = 4, rstr = '****') {
|
||||
// const reg = /^(\d{3})\d*(\d{4})$/
|
||||
if (!phone) return ''
|
||||
const reg = new RegExp(`(\\d\{${start}\})\\d*(\\d\{${end}\})`)
|
||||
return phone.replace(reg, `$1${rstr}$2`)
|
||||
}
|
||||
|
||||
// ============= 习题工具--相关 ===================
|
||||
/**
|
||||
* @description 将字符串转换为数组
|
||||
* @param {*} str
|
||||
* @returns
|
||||
*/
|
||||
export function quizStrToList(str = '') {
|
||||
if (!str) return []
|
||||
let resList = []
|
||||
if (isJson(str, true)) resList = JSON.parse(str) // 数组对象
|
||||
else if (str.includes('#&')) resList = str.split('#&') // 字符串数组 #&
|
||||
else if (str.includes(',')) resList = str.split(',') // 字符串数组 ,
|
||||
else resList = [str]
|
||||
return resList
|
||||
}
|
||||
|
||||
// ============= 常用工具--相关 ===================
|
||||
|
||||
export function isJson(str, isArray = false) {
|
||||
if(typeof str == 'string'){
|
||||
try {
|
||||
const res = JSON.parse(str)
|
||||
let isBool = typeof res == 'object' && res
|
||||
if(isBool && isArray) isBool = Array.isArray(res)
|
||||
return isBool
|
||||
} catch (error) {}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//获取临时唯一ID
|
||||
export const generateUniqueID = ()=> {
|
||||
const date = new Date();
|
||||
const timestamp = date.getTime().toString(36); // 使用36进制转换时间戳
|
||||
const random = Math.random().toString(36).substring(2, 9); // 获取随机数的一部分并转换为36进制
|
||||
return timestamp + random;
|
||||
}
|
||||
|
||||
// 清理参数中的undefined、null
|
||||
export const removePropertyOf = function(obj){
|
||||
Object.keys(obj).forEach(item=>{
|
||||
if(obj[item] === undefined || obj[item] === null) delete obj[item]
|
||||
})
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除children 为空 -- tree
|
||||
*/
|
||||
export function removeTree(list) {
|
||||
var this_ = this
|
||||
for (var i in list) {
|
||||
if (list[i].children.length == 0) {
|
||||
list[i].children = undefined
|
||||
} else {
|
||||
this_.removeTree(list[i].children)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// ============= 校验工具--相关 ===================
|
||||
|
||||
// url校验
|
||||
export const validateUrl = (url) => {
|
||||
const regex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
|
||||
if (!regex.test(url.trim())) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ============= 时间工具--相关 ===================
|
||||
|
||||
/**
|
||||
* @description 时间 秒 转化 时分秒
|
||||
* @param {*} seconds
|
||||
* @returns
|
||||
*/
|
||||
export function formatTime(seconds) {
|
||||
seconds = parseInt(seconds) // 转换整数
|
||||
const h = (Math.floor(seconds / 3600)+'').padStart(2,'0')
|
||||
const m = (Math.floor((seconds % 3600) / 60)+'').padStart(2,'0')
|
||||
const s = ((seconds % 60)+'').padStart(2,'0')
|
||||
return `${h}:${m}:${s}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 时间格式化
|
||||
* @param {*} time
|
||||
* @param {*} fmt
|
||||
* @returns
|
||||
*/
|
||||
export function formatDate(time, fmt = 'yyyy-MM-dd') {
|
||||
let date
|
||||
if (time) {
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
} else {
|
||||
date = new Date()
|
||||
}
|
||||
if (/(y+)/.test(fmt)) {
|
||||
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
|
||||
}
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'h+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds(),
|
||||
'a': date.getDay()
|
||||
}
|
||||
for (const k in o) {
|
||||
if (new RegExp(`(${k})`).test(fmt)) {
|
||||
if (k === 'a') {
|
||||
const str = ['日', '一', '二', '三', '四', '五', '六'][o[k]]
|
||||
fmt = fmt.replace(RegExp.$1, str)
|
||||
continue
|
||||
}
|
||||
const str = o[k] + ''
|
||||
// fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str))
|
||||
fmt = fmt.replace(RegExp.$1, str.padStart(2, '0'))
|
||||
}
|
||||
}
|
||||
return fmt
|
||||
}
|
||||
/**
|
||||
* 快速获取日期
|
||||
* num: 1 今日 2 昨日 3 本周 4 上周 5 本月
|
||||
* bool: true 有时分秒 false | null 不要时分秒
|
||||
*/
|
||||
export function getDateStr(num, bool) {
|
||||
let now = new Date() // 当前日期
|
||||
let nowDayOfWeek = now.getDay() // 今天本周的第几天
|
||||
let nowDay = now.getDate() // 当前日
|
||||
let nowMonth = now.getMonth() // 当前月
|
||||
let nowYear = now.getYear() // 当前年
|
||||
nowYear += (nowYear < 2000) ? 1900 : 0 //
|
||||
let monthStartDate = new Date(nowYear, nowMonth, 1)
|
||||
let monthEndDate = new Date(nowYear, nowMonth + 1, 1)
|
||||
let days = (monthEndDate - monthStartDate) / (1000 * 60 * 60 * 24)
|
||||
|
||||
let fmt = 'yyyy-MM-dd'
|
||||
let startTime = ''
|
||||
let endTime = ''
|
||||
|
||||
if (num == 1) { // 今日
|
||||
startTime = formatDate(now, fmt)
|
||||
endTime = startTime
|
||||
}
|
||||
if (num == 2) { // 昨日
|
||||
now.setDate(nowDay - 1)
|
||||
startTime = formatDate(now, fmt)
|
||||
endTime = startTime
|
||||
}
|
||||
if (num == 3) { // 本周
|
||||
startTime = formatDate(new Date(nowYear, nowMonth, nowDay - nowDayOfWeek), fmt)
|
||||
endTime = formatDate(new Date(nowYear, nowMonth, nowDay + (6 - nowDayOfWeek)), fmt)
|
||||
}
|
||||
if (num == 4) { // 上周
|
||||
startTime = formatDate(new Date(nowYear, nowMonth, nowDay - nowDayOfWeek - 7), fmt)
|
||||
endTime = formatDate(new Date(nowYear, nowMonth, nowDay - nowDayOfWeek - 1), fmt)
|
||||
}
|
||||
if (num == 5) { // 本月
|
||||
startTime = formatDate(new Date(nowYear, nowMonth, 1), fmt)
|
||||
endTime = formatDate(new Date(nowYear, nowMonth, days), fmt)
|
||||
}
|
||||
if (num == 6) { // 本年
|
||||
startTime = formatDate(new Date(nowYear, 0, 1), fmt)
|
||||
endTime = formatDate(new Date(nowYear, 11, 31), fmt)
|
||||
}
|
||||
if (bool) {
|
||||
startTime += ' 00:00:00'
|
||||
endTime += ' 23:59:59'
|
||||
}
|
||||
return [startTime, endTime]
|
||||
}
|
||||
/**
|
||||
* 获取 昨天 今天 明天 一周前 一月前 1-5
|
||||
*/
|
||||
export function getDateStr1(num, fmt = 'yyyy-MM-dd hh:mm:ss') {
|
||||
const now = new Date() // 当前日期
|
||||
const curr = Date.now() // 当前时间戳
|
||||
if (num == 1) return formatDate(new Date(curr-(24*60*60*1000)),fmt) // 昨天
|
||||
else if (num == 2) return formatDate(now,fmt) // 今天
|
||||
else if (num == 3) return formatDate(new Date(curr+(24*60*60*1000)),fmt) // 明天
|
||||
else if (num == 4) return formatDate(new Date(curr-(7*24*60*60*1000)),fmt) // 一周前
|
||||
else if (num == 5) return formatDate(new Date(curr-(30*24*60*60*1000)),fmt) // 一月前
|
||||
return ''
|
||||
}
|
||||
/**
|
||||
* 获取当前 0:0:0:0 时间
|
||||
* @param {*} [fmt] 格式 'yyyy-MM-dd hh:mm:ss'
|
||||
*/
|
||||
export function getDateNow(fmt) {
|
||||
const date = new Date()
|
||||
date.setHours(0, 0, 0, 0)
|
||||
if (fmt) return formatDate(date, fmt)
|
||||
return date
|
||||
}
|
||||
/** 默认加上-时分秒 */
|
||||
export function toTimeStr(arr = ['','']) {
|
||||
return [`${arr[0]} 00:00:00`, `${arr[1]} 23:59:59`]
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒转时分
|
||||
*/
|
||||
export function timeToStr(time,str = '时分秒', isPad = false) {
|
||||
let s = parseInt(time)
|
||||
let h = 0, m = 0 // 初始化时|分
|
||||
if (s >= 60) {
|
||||
// 如果秒数大于60,将秒数转换成整数
|
||||
m = parseInt(s / 60) // 获取分钟,除以60取整数,得到整数分钟
|
||||
s = parseInt(s % 60) // 获取秒数,秒数取佘,得到整数秒数
|
||||
if (m >= 60) { // 如果分钟大于60,将分钟转换成小时
|
||||
h = parseInt(m / 60) // 获取小时,获取分钟除以60,得到整数小时
|
||||
m = parseInt(m % 60) // 获取小时后取佘的分,获取分钟除以60取佘的分
|
||||
}
|
||||
}
|
||||
const toStr = v => v.toString().padStart(2, '0') // 转换字符
|
||||
const arr = str.split('')
|
||||
if (isPad) return `${h?toStr(h)+arr[0]:''}${m?toStr(m)+arr[1]:''}${toStr(s)}${arr[2]||''}`
|
||||
return `${h?h+arr[0]:''}${m?m+arr[1]:''}${s?s+arr[2]||'':''}`
|
||||
}
|
|
@ -88,12 +88,18 @@ export const getCurrentTime = (format)=> {
|
|||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
if(format == 'YYYY-MM-DD HH:mm'){
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
if(format == 'YYYY-MM-DD'){
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
if(format == 'HH:mm'){
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
if(format == 'MMDD'){
|
||||
return `${month}${day}`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
|
@ -109,3 +115,44 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取明天日期
|
||||
* @returns
|
||||
*/
|
||||
export function getTomorrow() {
|
||||
let date = new Date();
|
||||
var year = date.getFullYear()
|
||||
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
|
||||
var day = date.getDate()+1 < 10 ? '0' + date.getDate()+1 : date.getDate()+1
|
||||
|
||||
// 获取明天的日期 .getDate() + 1
|
||||
let tomorrow = `${year}-${month}-${day}`;
|
||||
|
||||
return tomorrow;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import useUserStore from '@/store/modules/user'
|
||||
const baseConfig = () => {
|
||||
const userStore = useUserStore()
|
||||
export const baseConfig = (token) => {
|
||||
const userStore = token ? {} : useUserStore()
|
||||
return {
|
||||
// Electron 设置cookie
|
||||
url: import.meta.env.VITE_APP_BUILD_BASE_PATH,
|
||||
|
@ -8,7 +8,7 @@ const baseConfig = () => {
|
|||
//cookie 名称 这里为 token
|
||||
name: 'Admin-Token',
|
||||
//cookie 值
|
||||
value: userStore.token,
|
||||
value: token ? '' : userStore.token,
|
||||
// 域名
|
||||
domain: import.meta.env.VITE_APP_DOMAIN
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
export const tabs = [
|
||||
{
|
||||
label: '平台资源',
|
||||
label: '平台',
|
||||
value: '平台'
|
||||
},
|
||||
{
|
||||
label: '校本资源',
|
||||
label: '校本',
|
||||
value: '校本'
|
||||
},
|
||||
{
|
||||
label: '第三方资源',
|
||||
label: '第三方',
|
||||
value: '第三方'
|
||||
},
|
||||
]
|
||||
|
@ -57,18 +57,23 @@ export const resourceFormat = [
|
|||
// 资源类型
|
||||
export const resourceType = [
|
||||
{
|
||||
label: '素材',
|
||||
value: '素材'
|
||||
label: '课例库',
|
||||
value: "'apt','课件','教案'"
|
||||
},
|
||||
{
|
||||
label: '作业库',
|
||||
value: '作业',
|
||||
disabled: true
|
||||
},
|
||||
|
||||
{
|
||||
label: '课件',
|
||||
value: '课件'
|
||||
label: '素材库',
|
||||
value: "'素材'"
|
||||
},
|
||||
|
||||
{
|
||||
label: '教案',
|
||||
value: '教案'
|
||||
label: '习题库',
|
||||
value: '习题',
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
// 年级划分
|
||||
|
|
|
@ -257,3 +257,17 @@ export const getFileName = (filename) => {
|
|||
if(!filename) return
|
||||
return filename.replace(/\.[^/.]+$/, "");
|
||||
}
|
||||
|
||||
// 清除当前选中的教材 章节 相关信息
|
||||
export const clearBookInfo = () =>{
|
||||
//当前选中的教材
|
||||
localStorage.removeItem('curBook')
|
||||
// 当前选中的节点
|
||||
localStorage.removeItem('curNode')
|
||||
// 所有章节单元数据
|
||||
localStorage.removeItem('unitList')
|
||||
// 所有教材数据
|
||||
localStorage.removeItem('subjectList')
|
||||
// 展开的节点
|
||||
localStorage.removeItem('defaultExpandedKeys')
|
||||
}
|
|
@ -12,8 +12,8 @@ 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状态
|
||||
// const Store = isNode?require('electron-store'):null // 持久化存储
|
||||
import store from './store'
|
||||
import { baseConfig } from './linkConfig' // 外部连接-配置
|
||||
// 常用变量
|
||||
const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':''
|
||||
const isDev = isNode?process.env.NODE_ENV !== 'production':''
|
||||
|
@ -297,6 +297,23 @@ const eventHandles = (type, win) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 外部跳转-web网页
|
||||
* @param {*} path
|
||||
* @param {*} params
|
||||
*/
|
||||
export const toLinkWeb = (path) => {
|
||||
const config = baseConfig()
|
||||
// console.log(config)
|
||||
const fullPath = config.url + path
|
||||
// 通知主进程
|
||||
ipcRenderer.send('openWindow', {
|
||||
key: `win-${Date.now()}`,
|
||||
fullPath: fullPath,
|
||||
cookieData: { ...config }
|
||||
})
|
||||
}
|
||||
|
||||
// const taskHandles = () => {
|
||||
// // 设置任务栏上下文菜单
|
||||
// const contextMenu = new Remote.Menu()
|
||||
|
|
|
@ -104,6 +104,9 @@ const switchPageMode = () => {
|
|||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
window.addEventListener('focus', () => {
|
||||
console.log(11111111111111)
|
||||
})
|
||||
const isDev = process.env.NODE_ENV == 'development'
|
||||
// toolState.showBoardAll = false // 隐藏画板
|
||||
toolState.isPdfWin=true //设置打开pdf窗口
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
class="el-menu-vertical-demo"
|
||||
:default-active="activeIndex"
|
||||
@select="handleSelect"
|
||||
unique-opened
|
||||
>
|
||||
<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;">{{item.caption}}</span>
|
||||
<span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;" @click="menuClick(item,index)">{{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>
|
||||
|
@ -43,6 +44,18 @@ 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>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-card style="width: 100%;height: 100%">
|
||||
<el-card style="width: 100%;height: 100%;overflow: auto">
|
||||
<!-- <template #header>-->
|
||||
<!-- <div class="card-header" style="text-align: left">-->
|
||||
<!-- <el-button type="primary" @click="addGroup">新建分组</el-button>-->
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-aside style="width: 200px;margin-top: 20px">
|
||||
<el-card style="width: 200px" class="el-card-demo" :style="{'min-height': (viewportHeight - 120) + 'px'}">
|
||||
<div :style="{'max-height': (viewportHeight - 120) + 'px','overflow-y': 'auto'}">
|
||||
<el-card style="width: 200px" class="el-card-demo" :style="{'min-height': (viewportHeight - 164) + 'px','max-height': (viewportHeight - 164) + 'px'}">
|
||||
<div >
|
||||
<Aside :menuItems="menuItems" :classList="classList" @handleSelect="handleSelect"></Aside>
|
||||
</div>
|
||||
<!-- 隐藏操作按钮-->
|
||||
|
@ -12,11 +12,16 @@
|
|||
<!-- <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'}">
|
||||
<el-main :style="{'min-height': (viewportHeight - 204) + 'px'}">
|
||||
<!-- <router-view :style="{'height': (viewportHeight - 120) + 'px','overflow-y': 'auto'}" :key="route.path"></router-view>-->
|
||||
<div :style="{'height': (viewportHeight - 120) + 'px','overflow-y': 'auto'}">
|
||||
<div :style="{'height': (viewportHeight - 164) + 'px','overflow-y': 'auto'}">
|
||||
<!-- 班级概况-->
|
||||
<ClassInfo v-if="currentIndex==0" :classId="classId"></ClassInfo>
|
||||
<!-- 学生列表-->
|
||||
|
@ -28,7 +33,7 @@
|
|||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible" title="新增班级" width="50%">
|
||||
<el-dialog v-model="dialogVisible" title="新增班级" width="50%" append-to-body>
|
||||
<el-form
|
||||
style="width: 100%"
|
||||
label-width="auto"
|
||||
|
@ -36,30 +41,45 @@
|
|||
: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 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>
|
||||
<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 label="班级">
|
||||
<el-tree-select
|
||||
v-model="classids"
|
||||
:data="gradeTree"
|
||||
multiple
|
||||
:render-after-expand="false"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="btnSave">确 定</el-button>
|
||||
|
@ -69,7 +89,7 @@
|
|||
|
||||
<script setup>
|
||||
import {ref, onMounted, reactive,watch,nextTick} from 'vue'
|
||||
import {listClassmain, addClassmain, listEvaluation} from '@/api/classManage/index'
|
||||
import {listClassmain, addClassmain, listEvaluation,addClasses} from '@/api/classManage/index'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import {ElMessage} from "element-plus";
|
||||
|
@ -134,6 +154,8 @@
|
|||
])
|
||||
//打开班级
|
||||
const dialogVisible = ref(false)
|
||||
//班级列表
|
||||
const gradeTree = ref([])
|
||||
//校验表单
|
||||
const myForm = ref(null)
|
||||
const rules = reactive({
|
||||
|
@ -142,78 +164,157 @@
|
|||
{ 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]
|
||||
if(classList.value.length > 0){
|
||||
classId.value = classList.value[0].id
|
||||
currentIndex.value = 0
|
||||
}
|
||||
});
|
||||
listClassmain({entpid: userStore.deptId, status: 'open', pageSize: 100}).then(response => {
|
||||
//清除已有的班级
|
||||
let arr = [...response.rows]
|
||||
classList.value.forEach(item => {
|
||||
const currentIndex = arr.findIndex(items => items.id === item.id)
|
||||
if(currentIndex) arr.splice(currentIndex, 1)
|
||||
})
|
||||
//这里获取组装所有班级
|
||||
gradeTree.value = groupByCondition(arr, item => item.agekey);
|
||||
})
|
||||
}
|
||||
//将所有班级筛选成二级的数组
|
||||
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 = () => {
|
||||
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) {
|
||||
addClasses({classIds:classids.value.join(','),userId:userStore.userId}).then(res => {
|
||||
if (res.code === 200) {
|
||||
dialogVisible.value = false
|
||||
ElMessage({
|
||||
message: '新增成功',
|
||||
message: res.msg,
|
||||
type: 'success',
|
||||
})
|
||||
getClassInfo()
|
||||
}
|
||||
});
|
||||
//清空所选中的值
|
||||
classids.value = []
|
||||
}else{
|
||||
ElMessage({
|
||||
message: '班级名称重复',
|
||||
message: res.msg,
|
||||
type: 'warning',
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
//文本计算
|
||||
const textSimilar = (s, t, f) => {
|
||||
if (!s || !t) {
|
||||
|
|
|
@ -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">
|
||||
<!-- <div>-->
|
||||
<!-- <el-button type="primary" @click="addStudent(0)">新增学生</el-button>-->
|
||||
<!-- <el-button type="primary" @click="importStudent()">导入学生</el-button>-->
|
||||
<!-- <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>–>-->
|
||||
<!--<!– <el-text class="mx-1">点击学生头像查看学生信息</el-text>–>-->
|
||||
<!-- </div>-->
|
||||
<el-text class="mx-1">点击学生头像查看学生信息</el-text>
|
||||
</div>
|
||||
</template>
|
||||
<!-- </template>-->
|
||||
<div>
|
||||
<div class="studentContent">
|
||||
<template v-if="studentList.length > 0">
|
||||
|
|
|
@ -0,0 +1,446 @@
|
|||
<!--
|
||||
* @Author: 苦逼程序猿
|
||||
* @Date: 2024-09-06 16:58:59
|
||||
* @Warning: 千行代码,Bug露锋芒。
|
||||
-->
|
||||
<template>
|
||||
<el-container class="class-reserv-wrap">
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<div class="class-reserv-tabs">
|
||||
<el-segmented v-model="tabActive" block :options="tabOptions" size="large" />
|
||||
</div>
|
||||
<div v-if="tabActive === '已结束'">
|
||||
<div class="demo-date-picker">
|
||||
<div class="block">
|
||||
<el-date-picker
|
||||
v-model="EndDate"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择截止日期"
|
||||
size="large"
|
||||
:disabled-date="disabledDate"
|
||||
@change="changeEndDate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="class-reserv-body">
|
||||
<!-- loading <el-skeleton :rows="5" animated />-->
|
||||
<el-skeleton :rows="5" :loading="loading" animated />
|
||||
<div v-if="classWorkList.length > 0">
|
||||
<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>
|
||||
<div v-else>
|
||||
<el-empty
|
||||
v-show="classWorkList.length == 0 && !loading"
|
||||
:description="'暂无'+tabActive+'作业数据'"
|
||||
style="width: 100%; height: 400px"
|
||||
></el-empty>
|
||||
</div>
|
||||
</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 { listByDeadDate, listClassworkdataByDeadDate } from '@/api/classTask'
|
||||
|
||||
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 { getCurrentTime, getTomorrow } from '@/utils/date'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import useClassTaskStore from "@/store/modules/classTask";
|
||||
|
||||
const classTaskStore = useClassTaskStore()
|
||||
const userStore = useUserStore().user
|
||||
const itemDialogRef = ref(null)
|
||||
const tabOptions = ref(['进行中', '已结束'])
|
||||
const tabActive = ref('进行中')
|
||||
const dataList = ref([])
|
||||
const EndDate = ref(getCurrentTime('YYYY-MM-DD'))
|
||||
|
||||
// 所有班级作业列表
|
||||
const classWorkList = ref([])
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
const activeDataList = computed(() => {
|
||||
return classWorkList.value
|
||||
})
|
||||
const deleteReserv = (item) => {
|
||||
console.log('删除待开发', item)
|
||||
// dataList.value = dataList.value.filter((is) => {
|
||||
// return is.id !== item.id
|
||||
// })
|
||||
}
|
||||
const doneDataList = computed(() => {
|
||||
return classWorkList.value
|
||||
})
|
||||
|
||||
// 当日之后的日期禁用
|
||||
const disabledDate = (time) => {
|
||||
return time.getTime() > Date.now()
|
||||
}
|
||||
// 截止日期改变
|
||||
const changeEndDate = (val) => {
|
||||
console.log('截止日期改变', val)
|
||||
getData() // 加载数据
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
const getData = () => {
|
||||
classWorkList.value = []
|
||||
loading.value = true
|
||||
// 1、班级列表
|
||||
getClassList()
|
||||
// 2、班级作业
|
||||
getClassWorkList()
|
||||
// 3、班级学生作业 包含多个班级
|
||||
getStudentClassWorkData()
|
||||
loading.value = false
|
||||
}
|
||||
/**
|
||||
* 1、获取班级列表数据
|
||||
* TODO 这里暂时取班级id的list,后续需要在修改
|
||||
*/
|
||||
const getClassList = () => {
|
||||
if(classTaskStore.classListIds.length==0){
|
||||
// 获取 班级列表ids 这里暂时取班级id的list,后续需要在修改
|
||||
classTaskStore.listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' })
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 2、获取班级作业
|
||||
*/
|
||||
const getClassWorkList = () => {
|
||||
if(classTaskStore.classListIds.length>0){
|
||||
// 班级作业数据,包含多个班级 homeworklist
|
||||
listByDeadDate({
|
||||
classidarray: classTaskStore.classListIds.join(','),
|
||||
edustage: userStore.edustage,// 学段
|
||||
edusubject: userStore.edusubject,//学科
|
||||
deaddate: tabActive.value === '进行中'? getTomorrow() : EndDate.value,// 进行中:明天,已结束:选择的日期
|
||||
status: '1', // 作业状态:1-已发布
|
||||
orderby: 'concat(deaddate,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)
|
||||
//TODO: 这里没分页,貌似这个 total 不重要,后续看
|
||||
total.value = response.total
|
||||
}else{
|
||||
classWorkList.value = []
|
||||
total.value = 0
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 3、获取多个班级学生作业数据
|
||||
*/
|
||||
const getStudentClassWorkData = () => {
|
||||
if(classTaskStore.classListIds.length>0){
|
||||
listClassworkdataByDeadDate({
|
||||
classids: classTaskStore.classListIds.join(','),
|
||||
edustage: userStore.edustage,// 学段
|
||||
edusubject: userStore.edusubject,//学科
|
||||
deaddate: tabActive.value === '进行中'? getTomorrow() : EndDate.value,// 进行中:明天,已结束:选择的日期
|
||||
status: '1', // 作业状态:1-已发布
|
||||
orderby: "uniquekey DESC",// TODO: 这里是否加 deaddate 的排序,后续看
|
||||
pageSize: 1000
|
||||
}).then((res) => {
|
||||
for (var t = 0; t < classWorkList.value.length; t++) {
|
||||
for (var i = 0; i < res.rows.length; i++) {
|
||||
if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].finishtimelength != '0') {
|
||||
console.log('==================')
|
||||
// 有几个学生完成/正在完成学习任务
|
||||
// 至少resultcount不是0
|
||||
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])
|
||||
}
|
||||
}
|
||||
// 计算完成进度 workdatacount人数要大于0
|
||||
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-12,酉阳,by 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%'
|
||||
}
|
||||
// 设定典型作答,需要获取每个题目的类型,找出主观题
|
||||
// 暂缓
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 getStudentVisible = async () => {
|
||||
if (classTaskStore.classListIds.length <= 0) {
|
||||
return
|
||||
}
|
||||
// 班级作业数据,多个班级
|
||||
const response = await listByDeadDate({
|
||||
classidarray: classTaskStore.classListIds.join(','),
|
||||
edustage: userStore.edustage,// 学段
|
||||
edusubject: userStore.edusubject,//学科
|
||||
deaddate: tabActive.value === '进行中'? getTomorrow() : EndDate.value,// 进行中:明天,已结束:选择的日期
|
||||
status: '1', // 作业状态:1-已发布
|
||||
orderby: 'concat(deaddate,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 完成人数 workdatacount人数要大于0
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => [dataList, toolStore.isToolWin],
|
||||
() => {
|
||||
console.log('====', toolStore)
|
||||
}
|
||||
)
|
||||
watch(tabActive, (newVal,oldVal)=>{
|
||||
console.log('newVal',newVal);
|
||||
getData() // 加载数据
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.class-reserv-wrap {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px 30px;
|
||||
.class-reserv-tabs {
|
||||
width: 30%;
|
||||
text-align: left;
|
||||
}
|
||||
.class-reserv-body {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,68 @@
|
|||
<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>
|
|
@ -0,0 +1,23 @@
|
|||
<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>
|
|
@ -0,0 +1,109 @@
|
|||
<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>
|
|
@ -0,0 +1,115 @@
|
|||
<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>
|
|
@ -0,0 +1,104 @@
|
|||
<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) {
|
||||
// 累加point和scoingRate
|
||||
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>
|
|
@ -0,0 +1,184 @@
|
|||
<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() {
|
||||
// 超过8条开始计算x轴
|
||||
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>
|
|
@ -0,0 +1,985 @@
|
|||
<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="(quItem, qIndex) in dialogProps.quizlist" :key="quItem.id">
|
||||
<div v-for="(stuItem, sIndex) in dialogProps.studentQuizAllList" :key="stuItem.id">
|
||||
<div v-if="stuItem.entpcourseworkid == quItem.id">
|
||||
<el-card style="max-width: 100%; margin-bottom: 10px">
|
||||
<!-- 题型 分值 -->
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span
|
||||
>{{ qIndex + 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;">{{ formatWorkAnswer(quItem) }}</sapn>
|
||||
</span>
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="6" style="padding: 10px">
|
||||
<!-- <span>学生答案:{{ stuItem.feedcontent }}</span> -->
|
||||
<span>学生答案:
|
||||
<span v-if="stuItem.feedcontent !=''" style="background-color: red; color: white; padding: 0 5px; border-radius: 5px;">
|
||||
{{ formatFeedContent(stuItem, quItem) }}
|
||||
</span>
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="6" style="padding: 10px">
|
||||
<div v-if="stuItem.imagefile && stuItem.imagefile.length > 0">
|
||||
<div v-for="(imageItem, index) in stuItem.imagefile" :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"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
</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"
|
||||
fit="contain"
|
||||
/>
|
||||
</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">
|
||||
<div v-if="dialogProps.studentObj.worktype == '常规作业' && stuItem.rightanswer != ''&& stuItem.rightanswer != null">
|
||||
<!-- 常规作业:学生有的会答复 -->
|
||||
<p style="padding: 10px 0;">学生答复内容</p>
|
||||
<div style="padding: 0 20px">{{stuItem.rightanswer}}</div>
|
||||
</div>
|
||||
<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"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="fileList.length > 0">
|
||||
<div style="margin: 10px 0">
|
||||
<span style="color: red" @click="openFile"
|
||||
>温馨提示:点击此处 可预览其他类型附件!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无附件内容 -->
|
||||
<div v-if="imageList.length == 0 && fileList.length == 0" style="padding: 0 20px">未提交附件内容</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 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>得分: </span>
|
||||
<span style="margin: 0; color: red">{{
|
||||
classWorkFormScore.teacherRating.reduce((a, b) => a + b.score, 0).toFixed(2)
|
||||
}}</span>
|
||||
<span>分</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
<span>得分: </span>
|
||||
<span v-if="classWorkFormScore.teacherRating.length > 0">
|
||||
<el-input-number
|
||||
v-model="classWorkFormScore.teacherRating[0].score"
|
||||
:controls="false"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="classWorkFormScore.teacherRating[0].maxScore"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
@change="handleChange"
|
||||
></el-input-number>
|
||||
</span>
|
||||
<span>分</span>
|
||||
</span>
|
||||
</span>
|
||||
<div class="score-container">
|
||||
<div
|
||||
v-for="(score, index) in teacherRatingList"
|
||||
:key="index"
|
||||
: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="1"
|
||||
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>
|
||||
</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'
|
||||
import { quizStrToList } from '@/utils/comm';
|
||||
|
||||
|
||||
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 value为null
|
||||
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 formatWorkAnswer = (quItem) => {
|
||||
let format = '';
|
||||
if (!Array.isArray(quItem.workanswerFormat)) {
|
||||
format = quItem.workanswerFormat.replace(/<[^>]+>/g, '')
|
||||
} else {
|
||||
format = quItem.workanswerFormat.map(item => item.replace(/<[^>]+>/g, '')).join('、');
|
||||
}
|
||||
return format;
|
||||
}
|
||||
// 根据题型处理学生答案
|
||||
const formatFeedContent = (stuItem, quItem) => {
|
||||
let format = '';
|
||||
|
||||
if (quItem.workdesc == '' || quItem.workdesc == '[]') {
|
||||
// 非单选题和多选题
|
||||
const arr = stuItem.feedcontent.split('#');
|
||||
return arr.map(item => {
|
||||
if (item == '') {
|
||||
item = '空';
|
||||
}
|
||||
return item;
|
||||
}).join('、');
|
||||
}else {
|
||||
const list = quizStrToList(quItem.workdesc);
|
||||
format = list.map((item,index) =>{
|
||||
if (quItem.worktype == '单选题') {
|
||||
const workdesc = item.replace(/<[^>]*>/g,'');
|
||||
const feedcontent = stuItem.feedcontent.replace(/<[^>]*>/g,'');
|
||||
if(workdesc == feedcontent){
|
||||
return (String.fromCharCode(65+Number(index)))
|
||||
}
|
||||
} else if (quItem.worktype == '多选题') {
|
||||
const arr = stuItem.feedcontent.split(',');
|
||||
return arr.map(item => String.fromCharCode(65+Number(item))).join('');
|
||||
}
|
||||
}).filter(Boolean)[0];
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
// 提交批改评分
|
||||
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>
|
|
@ -0,0 +1,752 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="classWorkAnalysis.open"
|
||||
:modal-append-to-body="false"
|
||||
class="clwk_dialog"
|
||||
style="width: 90%; height: 85vh"
|
||||
:show-close="false"
|
||||
top="8vh"
|
||||
append-to-body
|
||||
destory-on-close
|
||||
:before-close="onBeforeClose"
|
||||
>
|
||||
<template #header>
|
||||
<div style="font-size: 18px; display: flex; flex-wrap: nowrap">
|
||||
<div style="flex: 1">
|
||||
{{ classWorkAnalysis.title }}答题情况
|
||||
<el-tag :type="classWorkAnalysis.workclass" size="large" style="height: 25px">{{
|
||||
classWorkAnalysis.worktype
|
||||
}}</el-tag>
|
||||
</div>
|
||||
<!-- classWorkAnalysis.entpcourseworklistarray 当前学习任务所包含的试题ID -->
|
||||
<el-row
|
||||
v-if="classWorkAnalysis.entpcourseworklistarray.length > 0"
|
||||
style="margin: 0 auto; flex: 1"
|
||||
>
|
||||
<el-button-group style="margin-bottom: 10px">
|
||||
<el-button
|
||||
:type="classWorkAnalysis.view == 'studentview' ? 'success' : ''"
|
||||
@click="classWorkAnalysis.view = 'studentview'"
|
||||
>作业批阅</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="classWorkAnalysis.row.worktype == '习题训练'"
|
||||
:type="classWorkAnalysis.view == 'quizStats' ? 'success' : ''"
|
||||
@click="workHandle('quizStats')"
|
||||
>作业概况</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="classWorkAnalysis.row.worktype == '习题训练'"
|
||||
:type="classWorkAnalysis.view == 'report' ? 'success' : ''"
|
||||
@click="handleClassOverviewOpen('report')"
|
||||
>作业报告</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
<div style="flex: 1">
|
||||
<div
|
||||
style="float: right; padding: 0 10px; cursor: pointer"
|
||||
icon="el-icon-close"
|
||||
@click="closeDialog"
|
||||
>
|
||||
x
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 如果当前学习没有试题 :height="mainHeight"-->
|
||||
<div
|
||||
v-if="classWorkAnalysis.view == 'studentview'"
|
||||
style="width: 100%; height: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.finishtimelength != '0')
|
||||
tableRadio.value = '1'
|
||||
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length
|
||||
tableRadio.num1 = tableRadio.list.length
|
||||
})
|
||||
.catch(() => {
|
||||
loading_dt_table.value = false
|
||||
})
|
||||
}
|
||||
/** 2、查看某一个学生的学习任务完成详情*/
|
||||
const getStudentClassWorkDataDetail = (row) => {
|
||||
// 这里取出的是学生完成情况
|
||||
// 具体的题目数据在this.classWorkAnalysis.quizlist里
|
||||
console.log(row, '点击了左侧学生')
|
||||
//更新到待批改的学生信息中
|
||||
classWorkAnalysisScore.studentObj = row
|
||||
listClassworkeval({ workdataid: row.id, pageSize: 100 })
|
||||
.then((wevalres) => {
|
||||
for (var i = 0; i < classWorkAnalysis.quizlist.length; i++) {
|
||||
// 分析每一道题目
|
||||
for (var w = 0; w < wevalres.rows.length; w++) {
|
||||
if (wevalres.rows[w].entpcourseworkid == classWorkAnalysis.quizlist[i].id) {
|
||||
wevalres.rows[w].quiztitle = classWorkAnalysis.quizlist[i].title
|
||||
wevalres.rows[w].quiztitletext = classWorkAnalysis.quizlist[i].title.replace(
|
||||
/<[^>]*>/g,
|
||||
''
|
||||
)
|
||||
wevalres.rows[w].score = wevalres.rows[w].score ? wevalres.rows[w].score : 0
|
||||
|
||||
// 参考答案 去除下html标签
|
||||
wevalres.rows[w].rightanswer =
|
||||
wevalres.rows[w].rightanswer != '' && wevalres.rows[w].rightanswer != null
|
||||
? wevalres.rows[w].rightanswer.replace(/<[^>]+>/g, '')
|
||||
: wevalres.rows[w].rightanswer
|
||||
// 学生回答 去除下html标签
|
||||
wevalres.rows[w].feedcontent =
|
||||
wevalres.rows[w].feedcontent != '' && wevalres.rows[w].feedcontent != null
|
||||
? wevalres.rows[w].feedcontent.replace(/<[^>]+>/g, '')
|
||||
: wevalres.rows[w].feedcontent
|
||||
|
||||
if (classWorkAnalysis.row.worktype == '常规作业') {
|
||||
wevalres.rows[w].feedcontent = JSON.parse(wevalres.rows[w].feedcontent)
|
||||
}
|
||||
if (wevalres.rows[w].feedcontent != '') {
|
||||
if (wevalres.rows[w].feedcontent == wevalres.rows[w].rightanswer) {
|
||||
wevalres.rows[w].scoingStatus = true
|
||||
// 学生回答与参考答案一样,则:得分=分值
|
||||
wevalres.rows[w].teacherRating = wevalres.rows[w].score
|
||||
} else {
|
||||
wevalres.rows[w].scoingStatus = false
|
||||
}
|
||||
} else {
|
||||
wevalres.rows[w].scoingStatus = ''
|
||||
}
|
||||
// 计算得分
|
||||
}
|
||||
// "回答" prop="feedcontent" width="200" align="center"></el-table-column>
|
||||
// <el-table-column label="参考答案" prop="rightanswer"
|
||||
//新增了 复合题、主观题(背景+小题目) 题目标题优化一下
|
||||
wevalres.rows[w].worktitle = wevalres.rows[w].worktitle.replace(/!@#\$%/g, '')
|
||||
}
|
||||
}
|
||||
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
|
||||
// 1、清空答题情况的两个table数据,- 左侧学生列表:classWorkAnalysis.classworkdata;- 右侧学生回答题目列表:classWorkAnalysis.activeStudentQuizlist
|
||||
// - 左侧学生列表
|
||||
classWorkAnalysis.classworkdata = []
|
||||
classWorkAnalysis.activeStudentQuizlist = []
|
||||
// 2、刷新左侧学生列表数据,更新批改状态
|
||||
const rowid = localStorage.getItem('activeClassWorkRowId')
|
||||
getClassWorkStudentList(rowid)
|
||||
}
|
||||
// 查看并批改一个学生的题目作答(答题题目分析与评价)
|
||||
const handleClassWorkAnalysissScoreOpen = (row) => {
|
||||
console.log(row, '所选点击的信息')
|
||||
|
||||
// 当前学生的回答list
|
||||
classWorkAnalysisScore.studentQuizAllList = classWorkAnalysis.activeStudentQuizlist
|
||||
// 当前学生的所有题目list
|
||||
classWorkAnalysisScore.quizlist = classWorkAnalysis.quizlist
|
||||
// 格式化试题格式信息
|
||||
processList(classWorkAnalysisScore.quizlist)
|
||||
// 屏幕高度
|
||||
classWorkAnalysisScore.maxheight = mainHeight.value - 100
|
||||
|
||||
// 防止组件未渲染完成 调用里面方法报错
|
||||
nextTick(() => {
|
||||
proxy.$refs.classWorkAnalysisScoreDialogRef.acceptParams(classWorkAnalysisScore)
|
||||
})
|
||||
}
|
||||
//#endregion
|
||||
|
||||
/** 批阅:已交未交事件 */
|
||||
const tableRadioChange = (e) => {
|
||||
// 关闭右侧批阅ui
|
||||
isopen_dtwk_table.value = false;
|
||||
console.log(e,'??????')
|
||||
console.log("学生列表:", classWorkAnalysis.classworkdata)
|
||||
if(e=='1'){
|
||||
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength != '0')
|
||||
tableRadio.value = '1';
|
||||
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
|
||||
tableRadio.num1 = tableRadio.list.length;
|
||||
}else if(e=='0'){
|
||||
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength == '0')
|
||||
tableRadio.value = '0';
|
||||
tableRadio.num0 = tableRadio.list.length;
|
||||
tableRadio.num1 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
|
||||
}
|
||||
}
|
||||
|
||||
// 将标签中的双引号增加转义
|
||||
const escapeHtmlQuotes = (str) => {
|
||||
// 后端已replace双引号, 故前端不用在处理
|
||||
const regex1 = /\\+/g; // 匹配多个反斜杠
|
||||
let result = str.replace(regex1, '\\');
|
||||
|
||||
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>
|
|
@ -0,0 +1,452 @@
|
|||
<template>
|
||||
<div class="page-typeview flex">
|
||||
<el-form ref="classWorkForm" :model="classWorkForm" label-width="90" style="height: calc(100% - 55px);display: flex;flex-direction: column;">
|
||||
<!-- 标题 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="作业类型:">
|
||||
<el-radio-group v-model="formType" @change="changeFormType">
|
||||
<template v-for="(item) in listWorkType" :key="item">
|
||||
<el-radio :value="item" >{{ item }}</el-radio>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="作业资源:" class="el-form-work-list">
|
||||
<el-col :span="15" class="work-left">
|
||||
<div v-if="classWorkForm.worktype=='习题训练'" style="height: 100%; display: flex; flex-direction: column;">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="题型" label-width="70">
|
||||
<el-select v-model="entpCourseWorkQueryParams.worktype" placeholder="请选择" >
|
||||
<el-option v-for="(item, index) in entpCourseWorkTypeList" :key="index" :label="item.label" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="题源" label-width="70">
|
||||
<el-select v-model="entpCourseWorkQueryParams.workgroup" placeholder="请选择" >
|
||||
<el-option v-for="(item, index) in entpCourseWorkGroupList" :key="index" :label="item.Value" :value="item.Key" ></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<el-form-item label="知识点" label-width="70">
|
||||
<el-cascader
|
||||
v-model="entpCourseWorkQueryParams.point"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
:options="entpCourseWorkPointList"
|
||||
:props="knowledgePointProps"
|
||||
popper-class="my-popper"
|
||||
:show-all-levels="false"
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" style="margin-top: 4px">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="年份" label-width="70">
|
||||
<el-select v-model="entpCourseWorkQueryParams.yearStr" placeholder="请选择" >
|
||||
<el-option v-for="(item, index) in entpCourseWorkYearList" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="15">
|
||||
<el-form-item label="关键词" label-width="70">
|
||||
<el-input
|
||||
v-model="entpCourseWorkQueryParams.keyWord"
|
||||
style="width: 70%" type="text"
|
||||
placeholder="请输入关键词"
|
||||
/>
|
||||
<el-button @click="handleQueryParamFromEntpCourseWork(1)"><el-icon><Search /></el-icon> 查找</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table :data="workResource.entpCourseWorkList" style="width: 100%;">
|
||||
<el-table-column type="index" width="60" />
|
||||
<el-table-column align="left" >
|
||||
<template #header>
|
||||
<div style="display: flex">
|
||||
<div style="align-items: center;">题目内容</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<div style="overflow: hidden; text-overflow: ellipsis" v-html="scope.row.titleFormat"></div>
|
||||
<div style="overflow: hidden; text-overflow: ellipsis; font-size: 0.9em; margin-top: 6px;" v-html="scope.row.workdescFormat"></div>
|
||||
<el-col :span="24" style="display: flex">
|
||||
<div style="font-size: 1em; color: silver; padding-top: 5px">{{ scope.row.entpname }} {{ scope.row.editusername }}</div>
|
||||
<div style="margin-left: 30px; font-size: 1em; color: silver; padding-top: 5px">{{ scope.row.worktag }}</div>
|
||||
</el-col>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" width="100">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" @click="handleClassWorkQuizAdd('entpcourseworklist', scope.row.id)">添加</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="height: 55px;">
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-show="entpCourseWorkTotal > 0"
|
||||
v-model:page="paginationParams.pageNum"
|
||||
v-model:limit="paginationParams.pageSize"
|
||||
:total="entpCourseWorkTotal"
|
||||
:style="{ position: 'relative', 'margin-top': '5px' }"
|
||||
@pagination="getPaginationList" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div v-if="classWorkForm.worktype!='习题训练'">
|
||||
<div :style="{ 'overflow': 'auto'}">
|
||||
<template v-if="classWorkForm.worktype!='常规作业'">
|
||||
<template v-for="(item, index) in workResource.teachResourceList" :key="item">
|
||||
<div v-if="item.worktype==classWorkForm.worktype" style="border-bottom: 1px dotted;display: flex;justify-content: space-between;">
|
||||
<div style="margin-bottom: 5px; padding-left: 15px;display: flex;flex-direction: row;align-items: center;">
|
||||
<div style="font-size: 1.2em; font-weight: bold;margin-right: 5px">{{ item.worktype }}</div>
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<div style="color: silver; display: flex;align-items: center;">
|
||||
<el-image :src="item.userheadimgurl" style="height: 30px; width: 30px; border-radius: 50%;"></el-image>
|
||||
<div style="line-height: 18px">
|
||||
<div style="margin-top: 5px; margin-left: 5px">{{ item.username }} 来自{{ item.parententpname }} {{ item.userentpname }}</div>
|
||||
<div style="margin-top: 5px; margin-left: 5px">{{ item.timestamp }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;align-items: center; justify-content: space-around; margin-left: 15px; margin-right: 15px;">
|
||||
<el-button @click="prevRead(item)" icon="Search">预览</el-button>
|
||||
<el-button @click="handleClassWorkAddOfResource(item)" icon="FolderAdd">添加到作业</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="classWorkForm.worktype =='常规作业'">
|
||||
<div class="upload-homework" v-loading="fileLoading">
|
||||
<FileUpload v-model="fileHomeworkList" :fileSize="800" :fileType="['mp3','mp4','doc','docx','xlsx','xls','pdf','ppt','pptx','jpg','jpeg','gif','png','txt']"/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- <div v-if="classWorkForm.activeIndex==3">
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="查找筛选条件">
|
||||
本节课、本单元,其他单元
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4" style="text-align: right;">
|
||||
<el-button @click="handleQueryFromEntpCourseWork" icon="Search">更多</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div :style="{ 'overflow': 'auto'}">
|
||||
<template v-for="(item, index) in workResource.classWorkList" :key="item.id">
|
||||
<template v-if="item.classid > 0">
|
||||
<div style="margin-bottom: 30px; padding-left: 15px">
|
||||
<div style="font-size: 1.2em; font-weight: bold">{{ item.worktype }}</div>
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<div style="color: silver; display: flex">
|
||||
<el-image :src="item.edituserheadimgurl" style="height: 30px; width: 30px; border-radius: 50%;"></el-image>
|
||||
<div style="margin-top: 5px; margin-left: 10px">{{ item.editusername }} 来自{{ item.parententpname }} {{ item.entpname }}</div>
|
||||
</div>
|
||||
<div style="color: silver; margin-right: 15px; padding-top: 5px">{{item.workdate}}</div>
|
||||
</div>
|
||||
<div style="font-size: 1em; margin-top: 10px; margin-bottom: 10px; display: flex">
|
||||
<div style="max-width: 100%;word-wrap: break-word;">{{ item.workcontent }}</div>
|
||||
<div v-if="item.entpcourseworklistarray.length>0" style="margin-top: -5px; margin-left: 15px">
|
||||
<el-button v-if="item.expanded==false" style="margin-right: 4px" @click="handleClassWorkResNodeClick(index)">展开</el-button>
|
||||
<el-button v-if="item.expanded==true" @click="handleClassWorkResNodeClick(index)">缩回</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="item.expanded == true">
|
||||
<template v-for="(qitem, qindex) in item.quizlist" :key="qitem.id">
|
||||
<el-row style="margin-bottom: 20px; border: 1px dotted; padding: 20px" >
|
||||
<el-col :span="22">
|
||||
<div v-html="qitem.titleFormat" style="overflow: hidden; text-overflow: ellipsis"></div>
|
||||
<div v-html="qitem.workdescFormat" style="overflow: hidden; text-overflow: ellipsis; margin-top: 6px;"></div>
|
||||
</el-col>
|
||||
<el-col :span="2"><el-button type="primary" @click="handleClassWorkQuizAdd('classworklist', qitem.id)"><el-icon><CirclePlus /></el-icon> 添加</el-button></el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</template>
|
||||
<div style="display: flex; justify-content: end; margin-left: 15px; margin-right: 15px">
|
||||
<el-button @click="handleClassWorkPackAdd(index)" icon="FolderAdd">添加到作业</el-button>
|
||||
</div>
|
||||
<el-divider />
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div> -->
|
||||
</el-col>
|
||||
<el-col :span="8" style="padding: 0 0 0 5px;height: 60vh; overflow: auto; line-height: 26px">
|
||||
<div v-if="classWorkForm.worktype=='习题训练'" :style="{ 'overflow': 'auto', 'border':'1px dotted blue','border-radius':'5px', 'background-color': '#f7f7f7'}">
|
||||
<template v-for="(item,index) in classWorkForm.quizlist" :key="item.id">
|
||||
<div style="margin: 5px; background-color: white">
|
||||
<div v-html="item.titleFormat" style="padding: 15px 20px 5px 20px"></div>
|
||||
<div style="display: flex;">
|
||||
<el-form-item label="分值">
|
||||
<el-input-number v-model="item.score" :min="1" :max="100" size="small"></el-input-number >
|
||||
</el-form-item>
|
||||
<div style="margin-left: auto; padding: 0px 20px"><el-button size="small" type="danger" @click="handleClassWorkFormQuizRemove(index)">删除</el-button></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="classWorkForm.worktype!='习题训练'" :style="{'overflow': 'auto', 'border':'1px dotted blue','border-radius':'5px', 'background-color': '#f7f7f7'}">
|
||||
<div style="margin: 5px; background-color: white">
|
||||
<template v-for="(item,index) in chooseWorkLists" :key="item.id">
|
||||
<div v-if="item.worktype==classWorkForm.worktype">
|
||||
<div style="margin-bottom: 5px; padding-left: 15px;display: flex;flex-direction: row;align-items: center;">
|
||||
<div style="font-size: 1.2em; font-weight: bold;margin-right: 5px">{{ item.worktype }}</div>
|
||||
<div style="display: flex;align-items: center; justify-content: space-around; margin-left: 15px; margin-right: 15px;flex: 1;">
|
||||
<div style="color: silver; display: flex;align-items: center;flex: 1;">
|
||||
<el-form-item label="分值">
|
||||
<el-input-number v-model="item.score" :min="1" :max="100" size="small"></el-input-number >
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-button @click="prevRead(item)" icon="Search">预览</el-button>
|
||||
<el-button @click="deleteClassWorkAddOfResource(item)" type="danger" icon="Delete">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, toRaw,watch, reactive } from 'vue'
|
||||
import { useToolState } from '@/store/modules/tool'
|
||||
import { getCurrentTime } from '@/utils/date'
|
||||
import { processList } from '@/hooks/useProcessList'
|
||||
import {listEntpcoursework, listEntpcourseworkNew} from '@/api/education/entpCourseWork'
|
||||
|
||||
import useUserStore from '@/store/modules/user'
|
||||
const userStore = useUserStore().user
|
||||
|
||||
const props = defineProps({
|
||||
bookobj: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
const isDialogOpen = ref(false)
|
||||
const toolStore = useToolState()
|
||||
const openDialog = () => {
|
||||
isDialogOpen.value = true
|
||||
}
|
||||
const formType = ref('习题训练')
|
||||
// ---------------------------------------------------
|
||||
const listWorkType = ref(['习题训练', '框架梳理', '课堂展示', '常规作业']); //作业类型
|
||||
|
||||
const entpCourseWorkTypeList = ref([
|
||||
{value: 0, label: "不限"},
|
||||
{value: 1, label: "单选题"},
|
||||
{value: 2, label: "填空题"},
|
||||
{value: 3, label: "多选题"},
|
||||
{value: 4, label: "判断题"},
|
||||
{value: 5, label: "主观题"},
|
||||
{value: 6, label: "复合题"},
|
||||
]); // 习题查询条件 - 题型
|
||||
|
||||
const entpCourseWorkGroupList = ref([{
|
||||
Key: -1,
|
||||
Value: '不限',
|
||||
}, {
|
||||
Key: 1,
|
||||
Value: '真题',
|
||||
}, {
|
||||
Key: 0,
|
||||
Value: '非真题',
|
||||
}]); // 习题查询条件 - 题源
|
||||
|
||||
const entpCourseWorkPointList = ref([
|
||||
{label: '不限', value: []},
|
||||
]); // 习题查询条件 - 知识点
|
||||
|
||||
const entpCourseWorkYearList =ref([
|
||||
{label: '不限', value: '-1'},
|
||||
{label: '2024', value: '2024'},
|
||||
{label: '2023', value: '2023'},
|
||||
{label: '2022', value: '2022'},
|
||||
{label: '2021', value: '2021'},
|
||||
{label: '2020', value: '2020'},
|
||||
]); // 习题查询条件 - 年份
|
||||
|
||||
|
||||
const paginationParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
}); // 分页传参
|
||||
|
||||
// 习题查询参数条件
|
||||
const entpCourseWorkQueryParams = reactive({
|
||||
worktype: {
|
||||
label: '不限',
|
||||
value: 0,
|
||||
},
|
||||
workgroup: 0,
|
||||
yearStr: '-1',
|
||||
point: [],
|
||||
keyWord: '',
|
||||
});
|
||||
|
||||
const workResource = reactive({
|
||||
options: ['学习任务', '云题库'],
|
||||
worktype: '全部',
|
||||
activeIndex: "3",
|
||||
dialogOfTaskOpen: false,
|
||||
dislogOfAssignOpen: false,
|
||||
quiztype: '',
|
||||
queryForm: {},
|
||||
classWorkList: [], // 教学分析里产生的学习任务
|
||||
entpCourseWorkList: [],
|
||||
}); // 作业资源
|
||||
const classWorkForm = reactive({
|
||||
worktype: '习题训练', //作业类型
|
||||
// uniquekey: userStore.edusubject+'-' + getCurrentTime('MMDD')+'-'+(this.taskList.length+1),
|
||||
|
||||
})
|
||||
const entpCourseWorkList = ref([]); // 习题列表
|
||||
const entpCourseWorkTotal = ref(0); // 习题总数
|
||||
|
||||
|
||||
|
||||
/***
|
||||
* 作业类型切换
|
||||
*/
|
||||
const changeFormType = (val) => {
|
||||
console.log(val)
|
||||
classWorkForm.value.worktype = val;
|
||||
}
|
||||
|
||||
const queryForm = reactive({
|
||||
// 课程相关参数
|
||||
eid: props.bookobj.levelSecondId,
|
||||
sectionName: props.bookobj.coursetitle,
|
||||
edusubject: userStore.edusubject,
|
||||
edustage: userStore.edustage,
|
||||
//
|
||||
// 题类
|
||||
worktype: entpCourseWorkQueryParams.worktype.label,
|
||||
workTypeId: entpCourseWorkQueryParams.worktype.value,
|
||||
// 题源
|
||||
workgroup: entpCourseWorkQueryParams.workgroup,
|
||||
// 年份
|
||||
yearStr: entpCourseWorkQueryParams.yearStr !== '-1' ? entpCourseWorkQueryParams.yearStr:'',
|
||||
// 知识点
|
||||
thirdId: entpCourseWorkQueryParams.point.length > 0 ? entpCourseWorkQueryParams.point[0]:'',
|
||||
// 关键字
|
||||
keyword: entpCourseWorkQueryParams.keyWord && entpCourseWorkQueryParams.keyWord !== '' ? entpCourseWorkQueryParams.keyWord:'',
|
||||
|
||||
// 分页参数
|
||||
// pageNum: paginationParams.pageNum,
|
||||
// pageSize: paginationParams.pageSize,
|
||||
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* @desc: 新查询试题
|
||||
* @return: {*}
|
||||
* @param {*} queryType
|
||||
* 0 - 标准查询
|
||||
* 1 - 按条件查询
|
||||
* 2 - 按关键词查询
|
||||
*/
|
||||
const handleQueryFromEntpCourseWork= (queryType) => {
|
||||
//queryForm.pageNum = this.paginationParams.pageNum;
|
||||
//queryForm.pageSize = this.paginationParams.pageSize;
|
||||
|
||||
// 初中政治特殊处理( warn: 需确认是否修改 )
|
||||
// if (this.courseObj.edusubject=='政治' && this.courseObj.edustage=='初中') {
|
||||
// // [初中+政治]需改为[初中+道德与法治]
|
||||
// queryForm.edusubject = '道德与法治';
|
||||
// }
|
||||
|
||||
|
||||
console.log(queryForm)
|
||||
listEntpcourseworkNew(queryForm).then(entpcourseworkres => {
|
||||
// if (queryType == 1 && this.entpCourseWorkQueryParams.worktype == '主观题') {
|
||||
// // 因菁优网题型因学科而不固定, 故非常规题重定义定为【主观题】
|
||||
// const allowedWorkTypes = ['单选题', '填空题', '多选题', '判断题', '复合题'];
|
||||
// workResource.entpCourseWorkList = entpcourseworkres.rows.filter(item => {
|
||||
// return !allowedWorkTypes.includes(item.worktype);
|
||||
// });
|
||||
// } else {
|
||||
// workResource.entpCourseWorkList = entpcourseworkres.rows;
|
||||
// }
|
||||
if(entpcourseworkres.data == null) {
|
||||
workResource.entpCourseWorkList = [];
|
||||
entpCourseWorkTotal.value = 0
|
||||
return;
|
||||
}
|
||||
entpCourseWorkList.value = entpcourseworkres.data;
|
||||
workResource.entpCourseWorkList.forEach(item=> {
|
||||
if (item.worktype == '选择题') {
|
||||
item.worktype = '单选题'
|
||||
}
|
||||
})
|
||||
// 同步数量
|
||||
entpCourseWorkTotal.value = 0;
|
||||
if (entpcourseworkres.data.length > 0) {
|
||||
entpCourseWorkTotal.value = entpcourseworkres.data.length;
|
||||
}
|
||||
|
||||
//格式化试题信息
|
||||
processList(workResource.entpCourseWorkList);
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQueryFromEntpCourseWork(0);
|
||||
})
|
||||
|
||||
|
||||
// watch(() => sourceStore.query.fileSource,() => {
|
||||
// sourceStore.query.fileSource === '第三方'?isThird.value = true:isThird.value = false
|
||||
// })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-typeview{
|
||||
padding-top: 10px;
|
||||
height: 100%;
|
||||
|
||||
.el-form-work-list{
|
||||
:deep(.el-form-item__content){
|
||||
align-items: normal;
|
||||
}
|
||||
|
||||
.work-left{
|
||||
height: 50vh;
|
||||
background-color: white;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
border:1px dotted blue;
|
||||
border-radius:5px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,279 @@
|
|||
<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 = '( )', 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 = '( )', 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>
|
|
@ -0,0 +1,159 @@
|
|||
<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.workclass:'info'"> {{ 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>
|
||||
| 截止时间:{{ item.deaddate }} | {{ 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>
|
|
@ -0,0 +1,164 @@
|
|||
<template>
|
||||
<div class="page-resource flex">
|
||||
<el-menu
|
||||
default-active="1"
|
||||
class="el-menu-vertical-demo"
|
||||
:collapse="isCollapse"
|
||||
>
|
||||
<!--左侧 教材 目录-->
|
||||
<div v-if="!isCollapse">
|
||||
<ChooseTextbook @change-book="getData" @node-click="getData" />
|
||||
</div>
|
||||
</el-menu>
|
||||
|
||||
<div class="page-right" :style="{'margin-left': isCollapse ? '0' : '20px'}">
|
||||
<!-- 标题 -->
|
||||
<el-row style="align-items: center; margin-bottom: 0px">
|
||||
<el-col :span="12" style="padding-left: 20px; text-align: left;">
|
||||
<div class="unit-top-left" @click="isCollapse = !isCollapse">
|
||||
<i v-if="!isCollapse" class="iconfont icon-xiangzuo"></i>
|
||||
<span>作业范围</span>
|
||||
<i v-if="isCollapse" class="iconfont icon-xiangyou"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="classtype-right">
|
||||
<el-form-item label="作业名称">
|
||||
<el-input v-model="classWorkForm.uniquekey" type="text" placeholder="请输入作业名称"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 作业类型:内容 -->
|
||||
<task-type-view :bookobj="courseObj" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, toRaw,watch, reactive } from 'vue'
|
||||
// import useResoureStore from './store'
|
||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||
import Third from '@/components/choose-textbook/third.vue'
|
||||
// import ResoureSearch from './container/resoure-search.vue'
|
||||
// import ResoureList from './container/resoure-list.vue'
|
||||
// import ThirdList from './container/third-list.vue'
|
||||
import TaskTypeView from '@/views/classTask/container/newTask/taskTypeView.vue'
|
||||
import uploadDialog from '@/components/upload-dialog/index.vue'
|
||||
// import { createWindow } from '@/utils/tool'
|
||||
import { useToolState } from '@/store/modules/tool'
|
||||
import { getCurrentTime } from '@/utils/date'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
const userStore = useUserStore().user
|
||||
|
||||
// const sourceStore = useResoureStore()
|
||||
const isDialogOpen = ref(false)
|
||||
const toolStore = useToolState()
|
||||
const openDialog = () => {
|
||||
isDialogOpen.value = true
|
||||
}
|
||||
// ---------------------------------------------------
|
||||
const classWorkForm = reactive({
|
||||
// uniquekey: userStore.edusubject+'-' + getCurrentTime('MMDD')+'-'+(this.taskList.length+1),
|
||||
uniquekey: userStore.edusubject+'-' + getCurrentTime('MMDD')+'-'+(1),
|
||||
|
||||
})
|
||||
const isCollapse = ref(false)
|
||||
|
||||
|
||||
const courseObj = reactive({
|
||||
// 课程相关参数: 教材id,单元id,章节id,课程名称
|
||||
textbookId: '',
|
||||
levelFirstId: '',
|
||||
levelSecondId: '',
|
||||
coursetitle:'',
|
||||
//
|
||||
})
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
|
||||
// 查询
|
||||
const getData = (data) => {
|
||||
const { textBook, node } = data
|
||||
let textbookId = textBook.curBookId
|
||||
let levelSecondId = node.id
|
||||
let levelFirstId
|
||||
if (node.parentNode) {
|
||||
levelFirstId = node.parentNode.id
|
||||
} else {
|
||||
levelFirstId = node.id
|
||||
levelSecondId = ''
|
||||
}
|
||||
|
||||
courseObj.textbookId = textbookId // 版本
|
||||
courseObj.levelFirstId = levelFirstId // 单元
|
||||
courseObj.levelSecondId = levelSecondId // 章节
|
||||
courseObj.coursetitle = node.itemtitle // (单元/章节) 名称
|
||||
|
||||
// 头部 教材分析打开外部链接需要当前章节ID
|
||||
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId}))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
// sourceStore.getCreate()
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
classWorkForm.uniquekey = userStore.edusubject+'-' + getCurrentTime('MMDD')+'-'+(1);
|
||||
|
||||
}
|
||||
|
||||
// watch(() => sourceStore.query.fileSource,() => {
|
||||
// sourceStore.query.fileSource === '第三方'?isThird.value = true:isThird.value = false
|
||||
// })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-resource {
|
||||
padding-top: 10px;
|
||||
height: 100%;
|
||||
|
||||
.el-menu--collapse {
|
||||
width: 0px;
|
||||
min-height: 100%;
|
||||
}
|
||||
.el-menu-vertical-demo:not(.el-menu--collapse) {
|
||||
width: 300px;
|
||||
min-height: 100%;
|
||||
}
|
||||
.unit-top-left {
|
||||
cursor: pointer;
|
||||
|
||||
.icon-xiangzuo {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.classtype-right{
|
||||
padding: 5px;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.el-form-item--default{
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.page-right {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
||||
}
|
||||
.icon-jiahao {
|
||||
font-size: 12px;
|
||||
margin-right: 3px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,242 @@
|
|||
<template>
|
||||
<div v-loading="isLoading" class="page-resource flex">
|
||||
<!--左侧 教材 目录-->
|
||||
<ChooseTextbook @change-book="getData" @node-click="getData" />
|
||||
<!--右侧 作业设计/布置 列表 -->
|
||||
<div class="page-right">
|
||||
<div class="prepare-body-header">
|
||||
<el-button @click="handleOutLink('design')">作业设计</el-button>
|
||||
<el-button @click="handleOutLink('assign')">作业布置</el-button>
|
||||
<label style="font-size: 15px; margin-left: 20px">共{{ listClassWork.length }}个作业</label>
|
||||
<el-select
|
||||
v-model="queryParams.workType"
|
||||
placeholder="作业类型"
|
||||
size="small"
|
||||
@change="queryClassWorkByParams"
|
||||
style="width: 100px; margin-left: auto;"
|
||||
>
|
||||
<template v-for="(item, index) in listWorkType" :key="index">
|
||||
<el-option :label="item.label" :value="item" />
|
||||
</template>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="prepare-work-wrap">
|
||||
<FileListItem
|
||||
v-for="(item, index) in desingDataList"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:index="index"
|
||||
@on-set="openSet"
|
||||
@on-delhomework="delhomework"
|
||||
>
|
||||
</FileListItem>
|
||||
</div>
|
||||
</div>
|
||||
<SetHomework v-model="setAssingDialog" :entpcourseid="entpcourseid" :row="curClassWork" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import {ref, onMounted, reactive, watch, nextTick, getCurrentInstance, computed} from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { homeworklist, delClasswork } from '@/api/teaching/classwork'
|
||||
import useResoureStore from '@/views/resource/store'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import useClassTaskStore from "@/store/modules/classTask";
|
||||
import outLink from '@/utils/linkConfig'
|
||||
|
||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||
import FileListItem from '@/views/prepare/container/file-list-item.vue'
|
||||
import SetHomework from '@/components/set-homework/index.vue'
|
||||
|
||||
|
||||
const { ipcRenderer } = require('electron')
|
||||
const userStore = useUserStore().user
|
||||
const classTaskStore = useClassTaskStore()
|
||||
const {proxy} = getCurrentInstance();
|
||||
const sourceStore = useResoureStore();
|
||||
// 当前选中的章节或单元
|
||||
const curNode = ref({});
|
||||
const isLoading = ref(false);
|
||||
const listClassWork = ref([]);
|
||||
const listWorkType = ref(['不限', '习题训练', '框架梳理', '课堂展示', '常规作业']);
|
||||
const isOpenHomework = ref(false);
|
||||
const curClassWork = ref({});
|
||||
const setAssingDialog = ref(false);
|
||||
const entpcourseid = ref(0);
|
||||
|
||||
const queryParams = reactive({
|
||||
workType: '不限',
|
||||
total: 0,
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @desc: 更新作业任务
|
||||
* @return: {*}
|
||||
* @param {*} computed
|
||||
*/
|
||||
const desingDataList = computed(() => {
|
||||
return listClassWork.value;
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* @desc: 选中单元章节后的回调, 获取单元章节信息
|
||||
* @return: {*}
|
||||
* @param {*} data
|
||||
*/
|
||||
const getData = async (data) => {
|
||||
if (curNode.value.id == data.node.id) {
|
||||
return;
|
||||
}
|
||||
// 1. 情况原作业集合并切换章节
|
||||
curNode.value = data.node;
|
||||
listClassWork.value = [];
|
||||
isLoading.value = true;
|
||||
console.log(curNode.value);
|
||||
|
||||
// 2. 作业设计模板
|
||||
await getClassWorkList();
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 根据作业类型查询作业模板
|
||||
* @return: {*}
|
||||
*/
|
||||
const queryClassWorkByParams = async () => {
|
||||
// 1.先清空原作业集合
|
||||
listClassWork.value = [];
|
||||
isLoading.value = true;
|
||||
|
||||
// 2.根据[作业类型]查询
|
||||
const params = {
|
||||
worktype: queryParams.workType, // 此处多了[作业类型]参数
|
||||
}
|
||||
if (queryParams.workType == '不限') {
|
||||
delete params.worktype;
|
||||
}
|
||||
await getClassWorkList(params);
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获取作业设计模板
|
||||
* @return: {*}
|
||||
*/
|
||||
const getClassWorkList = async(params) => {
|
||||
// 班级作业数据,包含多个班级 homeworklist
|
||||
let query = {
|
||||
evalid: curNode.value.id,
|
||||
edituserid: userStore.userId,
|
||||
edustage: userStore.edustage,
|
||||
edusubject: userStore.edusubject,
|
||||
status: '10',
|
||||
orderby: 'concat(worktype,uniquekey) DESC',
|
||||
pageSize: 100,
|
||||
}
|
||||
// 将形参更新至query
|
||||
if (params !== null && params !== undefined) {
|
||||
query = {...query, ...params};
|
||||
}
|
||||
const res = await homeworklist(query);
|
||||
if (res.rows && res.rows.length > 0) {
|
||||
for (const item of res.rows) {
|
||||
item.fileShowName = item.uniquekey;
|
||||
}
|
||||
listClassWork.value = res.rows;
|
||||
//TODO: 这里没分页,貌似这个 total 不重要,后续看
|
||||
queryParams.total = res.total
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 作业设计/布置
|
||||
* @return: {*}
|
||||
* @param {*} key design-设计 assign-布置. 当key为设计时, url需再增加openDialog字段以便自动打开[设计新作业]
|
||||
*/
|
||||
const handleOutLink = (key) => {
|
||||
isOpenHomework.value = true;
|
||||
// key 对应的 linkConfig.js 外部链接配置
|
||||
let configObj = outLink()['homeWork']
|
||||
let fullPath = configObj.fullPath;
|
||||
|
||||
//打开作业url增加unitId 章节ID
|
||||
let unitId = curNode.value.id;
|
||||
fullPath += `&unitId=${unitId}`;
|
||||
|
||||
// 作业设计时, 再增加参数openDialog以自动打开教师端的[设计新作业]
|
||||
if (key == 'design') {
|
||||
fullPath += `&openDialog=newClassTask`;
|
||||
}
|
||||
// 通知主进程
|
||||
ipcRenderer.send('openWindow', {
|
||||
key,
|
||||
fullPath: fullPath,
|
||||
cookieData: { ...configObj.data }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const openSet = (item) => {
|
||||
// 打开布置作业窗口
|
||||
curClassWork.value = item;
|
||||
setAssingDialog.value = true;
|
||||
}
|
||||
|
||||
const delhomework = (item) => {
|
||||
isLoading.value = true
|
||||
delClasswork(item.id)
|
||||
.then(async(res) => {
|
||||
ElMessage.success('删除成功');
|
||||
isLoading.value = false;
|
||||
await getClassWorkList();
|
||||
})
|
||||
.catch(() => {
|
||||
isLoading.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-resource {
|
||||
padding-top: 10px;
|
||||
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);
|
||||
|
||||
.prepare-body-header {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.prepare-work-wrap{
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,194 @@
|
|||
<template>
|
||||
<div class="desktop-work-item">
|
||||
<div class="item-title flex">
|
||||
<span class="title">工作动态</span>
|
||||
<!-- <el-radio-group v-model="type">
|
||||
<el-radio-button label="全部" :value="-1" />
|
||||
</el-radio-group> -->
|
||||
</div>
|
||||
<div class="item-content" v-loading="loading">
|
||||
<el-scrollbar height="500px">
|
||||
<ul>
|
||||
<!--作业-->
|
||||
<li class="flex class-item home-list" v-for="item in homeworkList" :key="item.id" @click="onClickItem(item)">
|
||||
<div class="class-left flex">
|
||||
<div class="class-name flex">
|
||||
<span class="name">{{ item.uniquekey }}</span>
|
||||
<el-tag class="tag" round :type="tagType(item.deaddate)" effect="dark" size="small">{{
|
||||
getCurrentTime('YYYY-MM-DD HH:mm') > item.deaddate ? '已结束' : '进行中' }}</el-tag>
|
||||
</div>
|
||||
<div class="class-time">{{ item.classcaption }} | 截止时间:{{ item.deaddate }} </div>
|
||||
</div>
|
||||
<div class="class-right">
|
||||
<div><span class="num">{{ item.workdataresultcount }}</span> / {{ item.workdatacount }}</div>
|
||||
<div>已交</div>
|
||||
</div>
|
||||
</li>
|
||||
<el-empty v-if="!homeworkList.length" description="暂无数据" />
|
||||
</ul>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<item-dialog ref="itemDialogRef" @cle-click="closeDialog"></item-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { getSelfReserv } from '@/api/classManage'
|
||||
import { homeworklist } from '@/api/teaching/classwork'
|
||||
import { getCurrentTime, getTomorrow } from '@/utils/date'
|
||||
import ItemDialog from '@/views/classTask/container/item-dialog.vue'
|
||||
|
||||
const type = ref(-1)
|
||||
const user = useUserStore().user
|
||||
const loading = ref(false)
|
||||
const classList = ref([])
|
||||
const homeworkList = ref([])
|
||||
const itemDialogRef = ref(null)
|
||||
|
||||
// 获取上课
|
||||
const getClass = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getSelfReserv()
|
||||
let list = res.data || []
|
||||
list.sort((a, b) => { if (a.status == '上课中') return -1; else return 0 })
|
||||
classList.value = list.filter(item => item.status !== '已结束')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
// 获取作业
|
||||
const getHomework = async () => {
|
||||
loading.value = true
|
||||
const { edustage, edusubject } = user
|
||||
try {
|
||||
const { rows } = await homeworklist({ edituserid: user.userId, edustage, edusubject, deaddate: getTomorrow(), status: '1', orderby: 'uniquekey DESC', pageSize: 500 })
|
||||
// 只展示进行中的
|
||||
homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getCurrentTime('YYYY-MM-DD HH:mm') < item.deaddate)
|
||||
homeworkList.value.forEach((item) => {
|
||||
item.workdatacount = JSON.parse('[' + item.classworkdatastudentids + ']').length
|
||||
// 如果是习题训练任务,则检查一共有多少道
|
||||
if (item.entpcourseworklist != '') {
|
||||
item.entpcourseworklistarray = JSON.parse(
|
||||
'[' + item.entpcourseworklist + ']'
|
||||
)
|
||||
} else {
|
||||
item.entpcourseworklistarray = []
|
||||
}
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
// 批改作业
|
||||
const onClickItem = (item) => {
|
||||
console.log('开启弹窗')
|
||||
itemDialogRef.value.openDialog(item)
|
||||
}
|
||||
// 批阅关闭
|
||||
const closeDialog = () => {
|
||||
console.log('关闭弹窗,查询一下作业数据,更新界面')
|
||||
getHomework()
|
||||
}
|
||||
|
||||
const tagType = (time) => {
|
||||
return getCurrentTime('YYYY-MM-DD HH:mm') > time ? 'info' : 'warning'
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
// getClass()
|
||||
getHomework()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.desktop-work-item {
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
.item-title {
|
||||
height: 32px;
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
padding: 10px 15px;
|
||||
font-size: 13px;
|
||||
height: calc(100% - 60px);
|
||||
|
||||
.class-item {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #eff5fa;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.class-left {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 90%;
|
||||
|
||||
.class-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tag {
|
||||
margin-left: 5px;
|
||||
border: none;
|
||||
|
||||
:deep(.el-tag__content) {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.class-time {
|
||||
font-size: 13px;
|
||||
color: #a5a4a4;
|
||||
padding-top: 3px;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.class-right {
|
||||
font-size: 12px;
|
||||
color: #a5a4a4;
|
||||
flex-shrink: 0;
|
||||
font-size: 13px;
|
||||
|
||||
.num {
|
||||
font-size: 14px;
|
||||
color: #409EFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.class-grade {
|
||||
padding-top: 3px }
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,314 @@
|
|||
<template>
|
||||
<div class="page-desktop">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="17">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="item.span" v-for="item in menuList" :key="item.id">
|
||||
<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'
|
||||
import { useGetClassWork } from '@/hooks/useGetClassWork'
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
const type = ref(-1)
|
||||
const chartDom = ref(null);
|
||||
let chartInstance = null
|
||||
|
||||
const menuList = [{
|
||||
name: '课程教学',
|
||||
span: 24,
|
||||
id: 1,
|
||||
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-jiaoxuesheji',
|
||||
// path: '/prepare'
|
||||
// },
|
||||
{
|
||||
name: '教学实践',
|
||||
icon: 'icon-jiaoxuefenxi',
|
||||
path: '/prepare'
|
||||
},
|
||||
{
|
||||
name: '教学反思',
|
||||
icon: 'icon-jiaoxuefansi',
|
||||
disabled: true
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '作业管理',
|
||||
span: 12,
|
||||
id: 2,
|
||||
list: [
|
||||
{
|
||||
name: '作业设计',
|
||||
icon: 'icon-jiaoxuefansi',
|
||||
isOuter: true,
|
||||
path: '/teaching/classtaskassign?titleName=作业布置&openDialog=newClassTask'
|
||||
//path: '/newClassTask'
|
||||
//path: '/classTaskAssign'
|
||||
//isOuter: true,
|
||||
//path: '/teaching/classtaskassign?titleName=作业布置&&openDialog=newClassTask'
|
||||
},
|
||||
{
|
||||
name: '作业布置',
|
||||
icon: 'icon-xiezuo1',
|
||||
isOuter: true,
|
||||
path: '/teaching/classtaskassign?titleName=作业布置'
|
||||
},
|
||||
{
|
||||
name: '作业批改',
|
||||
icon: 'icon-pigai',
|
||||
path: '/classTask'
|
||||
},
|
||||
{
|
||||
name: '作业统计',
|
||||
icon: 'icon-tongji',
|
||||
disabled: true
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '教学管控',
|
||||
span: 12,
|
||||
id: 3,
|
||||
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
|
||||
if(path == '/teaching/classtaskassign?titleName=作业布置&openDialog=newClassTask' || path == '/teaching/classtaskassign?titleName=作业布置'){
|
||||
// 头部 教材分析打开外部链接需要当前章节ID
|
||||
const { levelFirstId, levelSecondId } = JSON.parse(localStorage.getItem('unitId'))
|
||||
|
||||
let unitId = levelSecondId ? levelSecondId : levelFirstId
|
||||
fullPath = fullPath + `&unitId=${unitId}`
|
||||
|
||||
console.log(fullPath)
|
||||
}
|
||||
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);
|
||||
// 初始化 获取教材下面的单元 + 章节
|
||||
getSubjectInit();
|
||||
|
||||
})
|
||||
|
||||
const getSubjectInit = async () => {
|
||||
//查看本地是否有缓存
|
||||
let unitId = localStorage.getItem('unitId')
|
||||
if(unitId){
|
||||
// 有缓存不用管
|
||||
} else{
|
||||
// 没有 就获取 获取教材下面的单元 + 章节
|
||||
useGetClassWork();
|
||||
}
|
||||
}
|
||||
|
||||
</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;
|
||||
font-weight: bold;
|
||||
}
|
||||
&: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>
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<div>模拟命题</div>
|
||||
</template>
|
|
@ -0,0 +1,101 @@
|
|||
<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 }} {{ 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>
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<div>考点分析</div>
|
||||
</template>
|
|
@ -0,0 +1,292 @@
|
|||
<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"> <el-icon><ArrowRight /></el-icon> </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"> <el-icon><ArrowRight /></el-icon> </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: 10px;
|
||||
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>
|
|
@ -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: '/education/colentrance',
|
||||
name: '考试分析',
|
||||
url: '/examReport',
|
||||
img: 'iconfont icon-icon_kaoshifenxi',
|
||||
child1: []
|
||||
},
|
||||
|
|
|
@ -0,0 +1,507 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="type==1?'注册账号':'找回密码'"
|
||||
:fullscreen="true"
|
||||
center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<el-steps v-if="type==1" 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="type==1 && activeIndex==1">
|
||||
<el-input v-model="ruleForm.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="idNumber" v-if="type==1 && activeIndex==1">
|
||||
<el-input v-model="ruleForm.idNumber" placeholder="请输入身份证号码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="phoneNumber" v-if="activeIndex==1">
|
||||
<el-input v-model="ruleForm.phoneNumber" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" prop="Code" v-if="activeIndex==1">
|
||||
<el-input style="width:60%" v-model="ruleForm.Code" :disabled="true" placeholder="请输入验证码" />
|
||||
<el-button type="primary" style="margin-left:10px" @click="sendcaptchaImg">发送验证码</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="设置密码" prop="password" v-if="activeIndex==1">
|
||||
<el-input v-model="ruleForm.password" type="password" placeholder="6-30位,至少包含数字和字母,允许特殊符号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword" v-if="activeIndex==1">
|
||||
<el-input v-model="ruleForm.confirmPassword" type="password" placeholder="请确认密码" />
|
||||
</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%;-webkit-app-region: no-drag;"
|
||||
/>
|
||||
</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)">{{ ['下一步','重置密码'][type-1]||'未知异常' }}</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, retrievePwd} 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;
|
||||
}
|
||||
// 正则表达式同时支持15位和18位身份证号码
|
||||
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 type = ref(1) // 1注册 2找回密码
|
||||
// 打开弹窗
|
||||
const OpenModel = v =>{
|
||||
if (!v) return ElMessage.success('操作失败')
|
||||
type.value = v
|
||||
dialogVisible.value=true
|
||||
}
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
var restValue={
|
||||
name: '',
|
||||
idNumber:'',
|
||||
phoneNumber: '',
|
||||
Code:'',
|
||||
password:'',
|
||||
confirmPassword:'',
|
||||
class:[],
|
||||
discipline:[],
|
||||
school:[],
|
||||
}
|
||||
Object.assign(ruleForm, restValue);
|
||||
schoolSubject.value=[]
|
||||
gradeTree.value=[]
|
||||
if (ruleFormRef.value) ruleFormRef.value.resetFields()
|
||||
removeToken();
|
||||
activeIndex.value=1
|
||||
dialogVisible.value=false
|
||||
}
|
||||
|
||||
const nextStep = (formEl) => {
|
||||
if (!formEl) return
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
// 如果 type 1 注册,2 找回密码
|
||||
if (type.value == 2) {
|
||||
let params = {
|
||||
mobile:ruleForm.phoneNumber,
|
||||
code:ruleForm.Code,
|
||||
password:ruleForm.password,
|
||||
confirmPassword:ruleForm.confirmPassword,
|
||||
}
|
||||
retrievePwd(params).then(res=>{
|
||||
if(res.code==200){
|
||||
ElMessage.success(res.msg)
|
||||
handleClose()
|
||||
}else{
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
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.edustage}-${o.edusubject}`).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:[],
|
||||
}
|
||||
schoolSubject.value=[]
|
||||
gradeTree.value=[]
|
||||
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, type: type.value}).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, type: type.value}).then(res=>{
|
||||
isPeopleImg.value='data:image/jpg;base64,'+res.img
|
||||
if(res.token){
|
||||
setToken(res.token)
|
||||
}
|
||||
resImg.imgData=res
|
||||
})
|
||||
}
|
||||
// 提交人机验证
|
||||
const sbmitImg=()=>{
|
||||
if(ruleForm.imgCode){
|
||||
// {mobile:ruleForm.phoneNumber,code:ruleForm.imgCode,uuid:resImg.imgData.uuid}
|
||||
const { phoneNumber:mobile,imgCode:code } = ruleForm
|
||||
const params = {
|
||||
mobile, code,
|
||||
uuid: resImg.imgData.uuid,
|
||||
type: type.value
|
||||
}
|
||||
sendCode(params).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>
|
|
@ -21,12 +21,21 @@
|
|||
placeholder="请输入密码"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-checkbox v-model="loginForm.rememberMe" class="flex mb-5">记住密码</el-checkbox>
|
||||
<div class="flex mb-5">
|
||||
<el-checkbox v-model="loginForm.rememberMe" >记住密码</el-checkbox>
|
||||
<!-- <el-checkbox >阅读并同意《xxx》</el-checkbox> -->
|
||||
</div>
|
||||
|
||||
<el-form-item>
|
||||
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)"
|
||||
>登录</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
|
||||
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="RegisterModel(1)">注册账号</a>
|
||||
|
|
||||
<a class="hover:text-sky-500" style="margin-left: 10px;" @click="RegisterModel(2)">忘记密码</a>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,6 +57,8 @@
|
|||
</el-dialog>
|
||||
<!--选择学科-->
|
||||
<SelectSubject v-model="isSubject" :login-data="loginForm" />
|
||||
<!--注册弹框-->
|
||||
<Register ref="RegModel"></Register>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
|
@ -57,6 +68,7 @@ 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)
|
||||
|
@ -66,6 +78,7 @@ const formRef = ref()
|
|||
const userStore = useUserStore()
|
||||
const btnLoading = ref(false)
|
||||
const isSubject = ref(false)
|
||||
const RegModel = ref(false)
|
||||
//表单
|
||||
const loginForm = reactive({
|
||||
username: '',
|
||||
|
@ -84,7 +97,10 @@ ipcRenderer.on('update-app-progress', (e, prop) => {
|
|||
downloadProp.value = prop
|
||||
showDownLoading.value = prop !== 100
|
||||
})
|
||||
|
||||
// 打开弹窗
|
||||
const RegisterModel = type =>{
|
||||
RegModel.value.OpenModel(type)
|
||||
}
|
||||
//登录
|
||||
const submitForm = async (formEl) => {
|
||||
if (!formEl) return
|
||||
|
@ -162,7 +178,6 @@ onMounted(() => {
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-app-region: drag;
|
||||
|
||||
.box-item {
|
||||
width: 444px;
|
||||
height: 520px;
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
<template>
|
||||
<el-dialog v-model="visible" width="75%" :close-on-click-modal="false"
|
||||
@close="handleClose">
|
||||
<template #header><b>准备开始上课</b></template>
|
||||
<div class="class-all">
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<c-form v-bind="classForm">
|
||||
<template #item_classid="{prop, form}">
|
||||
<el-select v-model="form[prop]" placeholder="请选择班级">
|
||||
<el-option v-for="item in listData.classList" :value="item.id"
|
||||
:label="`${item.caption} (${item.classstudentcount}人)`" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template #item_student>
|
||||
<el-scrollbar max-height="30em">
|
||||
<template v-for="(item, index) in listData.activeStudentList">
|
||||
<el-tag style="margin:0 7px 7px 0;cursor:default;">{{ item.name }}</el-tag>
|
||||
</template>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
</c-form>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<c-form v-bind="teacherForm">
|
||||
<!-- 上课 -->
|
||||
<template #item_classcourseid="{prop,form}">
|
||||
<el-radio-group v-model="form[prop]">
|
||||
<el-radio :value="0">开始新课</el-radio>
|
||||
<template v-for="(item,index) in listData.classcourseList">
|
||||
<template v-if="dt.isCreate&&!index">
|
||||
<el-radio :value="item.id">{{ item.opendate }} <span style="color: silver">({{ item.teachername }})</span></el-radio>
|
||||
</template>
|
||||
<template v-else-if="dt.isHistory&&(dt.isCreate&&index||!dt.isCreate)">
|
||||
<el-radio :value="item.id">{{ item.opendate }} <span style="color: silver">({{ item.teachername }})</span></el-radio>
|
||||
</template>
|
||||
</template>
|
||||
<el-button class="absolute top-1 right-0" size="small" type="primary" round
|
||||
:plain="!dt.isHistory" @click="getClasscourseList('toggle')">历史记录</el-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<!-- 老师扫码 -->
|
||||
<template #item_qrUrl="{value}">
|
||||
<div :title="value" v-if="!!value">
|
||||
<vue-qr :text="value" :size="200" :margin="10" colorDark="green" colorLight="white" :logoSrc="getStaticUrl('/img/logo.png')" :logoScale="0.2" :dotScale="0.7"></vue-qr>
|
||||
</div>
|
||||
<el-button type="warning" :loading="dt.loadingDel" @click="removeClasscourse()">删除记录</el-button>
|
||||
</template>
|
||||
<!-- 手机登录 -->
|
||||
<template #item_mobile>
|
||||
<div>
|
||||
<div>开始新的课堂,需要点击先创建课堂,才能显示手机二维码</div>
|
||||
<el-button type="warning" :loading="dt.loading" @click="createClasscourse">创建课堂</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 故障备用 -->
|
||||
<template #item_backup>
|
||||
<div>
|
||||
<div>如果手机扫码后进入课堂,但本页面没自动跳转,请点击下面按钮</div>
|
||||
<el-button type="primary" plain @click="classTeachingStart">开始上课</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</c-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- im-chat 聊天组件 -->
|
||||
<im-chat ref="imChatRef" v-if="visible" @change="chatChange" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 功能说明:课程开始
|
||||
import { onMounted, reactive, ref, watchEffect, watch, nextTick } from 'vue' // vue
|
||||
import { ElMessage, ElMessageBox } from 'element-plus' // ui框架: 消息提示
|
||||
import vueQr from 'vue-qr/src/packages/vue-qr.vue' // 插件: 二维码
|
||||
import imChat from '@/views/tool/components/imChat.vue' // im-chat-子组件
|
||||
import MsgEnum from '@/plugins/imChat/msgEnum' // 消息头-相关定义(nuem)
|
||||
import * as commUtil from '@/utils/comm' // 工具类-通用
|
||||
import { toLinkWeb, getStaticUrl } from '@/utils/tool'
|
||||
|
||||
import * as Http_ClassManage from '@/api/classManage' // api接口
|
||||
import * as Http_Classcourse from '@/api/teaching/classcourse' // api接口
|
||||
import * as Http_Entpcoursefile from '@/api/education/entpcoursefile' // api接口
|
||||
import useUserStore from "@/store/modules/user" // 状态管理:user
|
||||
|
||||
const baseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
|
||||
const userStore = useUserStore()
|
||||
const visible = ref(false) // 是否打开窗口
|
||||
const myClassActive = ref({}) // 我的课件:准备上课的APT课件
|
||||
const imChatRef = ref(null) // im-chat ref
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const classForm = reactive({ // 班级(左侧):表单数据 表单配置
|
||||
form: {}, itemOption: [], option: {}
|
||||
})
|
||||
const teacherForm = reactive({ // 老师(右侧):表单数据 表单配置
|
||||
form: {}, itemOption: []
|
||||
})
|
||||
const listData = reactive({ // 列表数据
|
||||
classList: [], // 班级列表
|
||||
activeStudentList: [], // 学生列表-选择
|
||||
classcourseList: [], // 课程列表-历史记录
|
||||
})
|
||||
const dt = reactive({ // 其他数据
|
||||
isCreate: false, // 是否创建
|
||||
isHistory: false, // 是否显示-历史记录
|
||||
loading: false, // 加载-loading
|
||||
loadingDel: false, // 删除-loading
|
||||
atCourse: {}, // 当前课程
|
||||
})
|
||||
let chat = null // im-chat 对象
|
||||
|
||||
// 加载完,钩子
|
||||
onMounted(() => {
|
||||
initData() // 初始化-数据
|
||||
})
|
||||
/**
|
||||
* @description 暴露方法-打开对话框
|
||||
* @param row 课件对象
|
||||
*/
|
||||
const open = async (id) => {
|
||||
visible.value = true
|
||||
if (id) {
|
||||
// 重置数据
|
||||
reset()
|
||||
// 获取apt详情
|
||||
await getAptInfo(id)
|
||||
// 获取班级列表
|
||||
getClassList()
|
||||
// 初始化im-chat
|
||||
nextTick(async() => {
|
||||
chat = await imChatRef.value?.initImChat()
|
||||
})
|
||||
}
|
||||
}
|
||||
// 关闭弹窗
|
||||
const handleClose = async () => {
|
||||
reset() // 重置数据
|
||||
await chat?.logout()
|
||||
chat = null
|
||||
emit('close')
|
||||
}
|
||||
// 初始化-数据
|
||||
const initData = () => {
|
||||
// 班级-表单配置
|
||||
classForm.option = { labelW: 40 }
|
||||
classForm.itemOption = [
|
||||
{ label: '班级', prop: 'classid' },
|
||||
{ label: '学生', prop: 'student' },
|
||||
]
|
||||
// 老师-表单提交
|
||||
teacherForm.form = { classcourseid: 0 }
|
||||
teacherForm.itemOption = [
|
||||
{ label: '上课', prop: 'classcourseid' },
|
||||
{ label: '老师扫码', prop: 'qrUrl', show: false },
|
||||
{ label: '手机登录', prop: 'mobile', show: false },
|
||||
{ label: '故障备用', prop: 'backup', show: false },
|
||||
]
|
||||
}
|
||||
// 重置数据
|
||||
const reset = () => {
|
||||
// 初始化-表单数据
|
||||
classForm.form = {}
|
||||
teacherForm.form = { classcourseid: 0 }
|
||||
dt.isCreate = false
|
||||
dt.isHistory = false
|
||||
dt.atCourse = {}
|
||||
}
|
||||
// 获取课件APT
|
||||
const getAptInfo = async (id) => {
|
||||
const res = await Http_Entpcoursefile.getEntpcoursefile(id)
|
||||
if (res.code == 200) {
|
||||
myClassActive.value = res.data
|
||||
}
|
||||
}
|
||||
// 获取当前账号-班级
|
||||
const getClassList = async () => {
|
||||
const res = await Http_ClassManage.listClassmain({
|
||||
classuserid: userStore.id, pageSize: 100, status: 'open'
|
||||
})
|
||||
if (res.code == 200) {
|
||||
listData.classList = (res.rows || []).map(o => {
|
||||
if(!!o.classstudentlist) { // 学生列表转为数组
|
||||
o.classstudentlist = JSON.parse('[' + o.classstudentlist + ']')
|
||||
}
|
||||
return o
|
||||
});
|
||||
// 默认选中第一项
|
||||
if (listData.classList.length > 0) {
|
||||
classForm.form.classid = listData.classList[0].id
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// 获取开课列表 - 历史记录
|
||||
const getClasscourseList = async type => {
|
||||
if (type == 'toggle') { // 切换是否显示数据
|
||||
dt.isHistory = !dt.isHistory
|
||||
if (!dt.isHistory || listData.classcourseList.length) return // 不显示或者有数据时-不再请求
|
||||
}
|
||||
const {classid} = classForm.form
|
||||
const {entpcourseid} = myClassActive.value
|
||||
if (!classid || !entpcourseid) return
|
||||
const params = {classid, entpcourseid, entpcoursefileid: 0}
|
||||
const res = await Http_Classcourse.listClasscourse(params)
|
||||
if (res.code == 200) {
|
||||
// zdg: 排序id大的在前(最新的)
|
||||
const list = (res.rows || []).sort((a, b) => b.id - a.id)
|
||||
listData.classcourseList = list
|
||||
// 默认选中第一个
|
||||
if (type == 'update' && list.length) {
|
||||
dt.isCreate = true // 显示-新创建记录
|
||||
teacherForm.form.classcourseid = list[0].id
|
||||
}
|
||||
}
|
||||
}
|
||||
// 创建课程
|
||||
const createClasscourse = async () => {
|
||||
dt.loading = true
|
||||
const { classid } = classForm.form
|
||||
const { entpcourseid, evalid, id, coursetitle } = myClassActive.value // 课件对象
|
||||
const curDate = commUtil.getDateNow('yyyy-MM-dd')
|
||||
const params = {
|
||||
id: 0, coursetype: '', courseverid: 0, coursedesc: '', status: '',
|
||||
teacherid: userStore.id, entpcoursefileid: id, classid,
|
||||
entpcourseid, evalid, coursetitle,
|
||||
plandate: curDate, opendate: curDate
|
||||
}
|
||||
await Http_Classcourse.addClasscourseReturnId(params)
|
||||
dt.loading = false
|
||||
getClasscourseList('update') // 更新列表
|
||||
ElMessage.success('创建课程-成功')
|
||||
|
||||
}
|
||||
// 删除课程
|
||||
const removeClasscourse = async () => {
|
||||
const { classcourseid:id } = teacherForm.form
|
||||
const ind = listData.classcourseList.findIndex(o => o.id == id)
|
||||
if(ind < 0) return ElMessage.warning('删除课堂记录-失败')
|
||||
await ElMessageBox.confirm('请确认是否删除课堂记录?')
|
||||
dt.loadingDel = true
|
||||
const res = await Http_Classcourse.delClasscourse(id)
|
||||
dt.loadingDel = false
|
||||
if (res.code == 200) {
|
||||
const list = listData.classcourseList
|
||||
list.splice(ind, 1) // 删除-id的课堂记录
|
||||
// 重新赋值选中的课堂记录,默认是下一条记录
|
||||
if (list.length==0) teacherForm.classcourseid = 0
|
||||
else {
|
||||
const newInd = ind < list.length ? ind : ind-1
|
||||
teacherForm.form.classcourseid = list[newInd].id
|
||||
}
|
||||
ElMessage.success('删除成功')
|
||||
}
|
||||
}
|
||||
// 开始上课
|
||||
const classTeachingStart = async () => {
|
||||
const { classcourseid:id } = teacherForm.form
|
||||
if (id) { // 开始上课
|
||||
const url = `/teaching/classteaching?classcourseid=${id}&actor=classTeachingOnPublicScreen`
|
||||
toLinkWeb(url) // 跳转web-公屏
|
||||
visible.value = false // 关闭弹窗
|
||||
handleClose() // 关闭im-chat
|
||||
}
|
||||
}
|
||||
// 获取二维码地址
|
||||
const getQrUrl = () => {
|
||||
const { classcourseid:id } = teacherForm.form
|
||||
const { userName } = userStore.user
|
||||
if (!id||!userName) return
|
||||
const qrCodeUrl = `wxlogin?username=${userName}&nextaction=classteaching&id=${id}`
|
||||
teacherForm.form.qrUrl = baseUrl + qrCodeUrl
|
||||
}
|
||||
|
||||
// ================== 监听 =======================
|
||||
// im-chat: 聊天事件 {type, data}
|
||||
const chatChange = (type, data, ...args) => {
|
||||
if (type == 'msg') { // im-chat 消息监听
|
||||
console.log('msg:===== ',data, args)
|
||||
// const msgId = (args||[])[0].message_msg_id
|
||||
const { msgKey:head, msgcontent:msg, senduserid:sendId, msgType } = data
|
||||
switch(head) {
|
||||
case MsgEnum.HEADS.MSG_classcourseopen:{ // 开课, 跳转公屏
|
||||
const { classcourseid:id } = teacherForm.form
|
||||
const { classcourseid: imId, status } = data
|
||||
if (imId == id && status == 'open') {
|
||||
classTeachingStart() // 开始跳转
|
||||
}
|
||||
break}
|
||||
default:
|
||||
console.log('未知消息:', data)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听-班级id
|
||||
watch(() => classForm.form.classid, (val)=> {
|
||||
// 获取选中课程
|
||||
dt.atCourse = listData.classList.find(o => o.id === val) || {}
|
||||
// 获取选中课程-学生列表
|
||||
listData.activeStudentList = dt.atCourse?.classstudentlist || []
|
||||
// 清空课程列表
|
||||
listData.classcourseList = []
|
||||
// 如果当前显示历史, 就从新获取
|
||||
if (dt.isHistory) getClasscourseList()
|
||||
})
|
||||
// 监听-课程id
|
||||
watch(() => teacherForm.form.classcourseid, (val) => {
|
||||
const bool = !!val
|
||||
// 获取选中课程-生成二维码地址
|
||||
bool && getQrUrl()
|
||||
// 监听课程id,是否显示功能
|
||||
teacherForm.itemOption.forEach(o => {
|
||||
// 有课程id,触发
|
||||
if (['qrUrl','backup'].includes(o.prop)) o.show = bool
|
||||
// 课程id为空,触发
|
||||
if (['mobile'].includes(o.prop)) o.show = !bool
|
||||
})
|
||||
})
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.class-all{
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
|
@ -230,6 +230,20 @@ 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)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="file-oper-batch-wrap">
|
||||
<div style="margin: 0 20px; line-height: 40px">
|
||||
<div style="line-height: 40px">
|
||||
<el-checkbox
|
||||
v-model="isCheckAll"
|
||||
:indeterminate="indeterminate"
|
||||
|
@ -15,7 +15,7 @@
|
|||
</template>
|
||||
<script>
|
||||
import { deleteSmarttalkBatch } from '@/api/file'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { exportFile } from '@/utils/talkFile'
|
||||
export default {
|
||||
name: 'FileOperBatch',
|
||||
|
|
|
@ -0,0 +1,331 @@
|
|||
<template>
|
||||
<div class="prepare-body-main-item">
|
||||
<div class="prepare-body-main-item-icon">
|
||||
<slot name="default"></slot>
|
||||
<FileImage :size="50" :file-name="item.fileShowName" @click="openFileWin(item)" />
|
||||
</div>
|
||||
<div class="prepare-body-main-item-info" @click="openFileWin(item)">
|
||||
<div class="prepare-item-info-title" :title="item.fileShowName">
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 200px;
|
||||
"
|
||||
>
|
||||
{{ item.fileShowName.substring(0, item.fileShowName.lastIndexOf('.')) }}
|
||||
<el-tag type="danger" effect="dark">{{item.fileShowName.substring(item.fileShowName.lastIndexOf('.')+1)}}</el-tag>
|
||||
<template v-if="item.fileTag">
|
||||
<el-tag v-for="(item1, index1) in item.fileTag.split(',')" :key="index1" type="success" effect="dark" style="margin-left: 5px">{{item1}}</el-tag>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prepare-item-info-message">
|
||||
<div v-if="item.fileFlag ==='课件'" style="width: 60px">
|
||||
<el-icon
|
||||
v-loading="item.async === 'on'"
|
||||
style="background-color: green; border-radius: 20px; color: white; top: 2px"
|
||||
>
|
||||
<Check v-if="item.async === true" />
|
||||
<UploadFilled v-if="!item.async" />
|
||||
</el-icon>
|
||||
{{ item.async === true ? '已同步' : '' }}
|
||||
{{ !item.async ? '待同步' : '' }}
|
||||
{{ item.async === 'on' ? '同步中' : '' }}
|
||||
</div>
|
||||
<template v-if="item.fileFlag ==='课件'">|</template>
|
||||
<div style="width: 70px">访问 100</div>
|
||||
|
|
||||
<div style="width: 70px">引用 50</div>
|
||||
|
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 200px;
|
||||
">点赞 20</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prepare-body-main-item-btn">
|
||||
<!-- <el-button v-if="activeClassId==item.id" type="success" @click="clickStartClass(item)">上课中</el-button> -->
|
||||
<el-button type="primary" @click="clickStartClass(item)">上课</el-button>
|
||||
</div>
|
||||
<div class="prepare-body-main-item-tool">
|
||||
<el-popover
|
||||
:ref="'popover_' + index"
|
||||
placement="left-start"
|
||||
:hide-after="100"
|
||||
popper-class="prepare-popper"
|
||||
trigger="click"
|
||||
>
|
||||
<template #default>
|
||||
<div style="width: 100%">
|
||||
<div class="item-popover" @click="closePopver(index)">
|
||||
<template v-if="userInfo.userId === Number(item.createUserId)">
|
||||
<div class="item-popover-item">
|
||||
<el-button text @click="editTalk(item, index)">
|
||||
<i class="iconfont icon-bianji"></i>
|
||||
<span>标签</span>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="item-popover-item">
|
||||
<el-button text @click="deleteTalk(item)">
|
||||
<i class="iconfont icon-shanchu"></i>
|
||||
<span>删除</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #reference>
|
||||
<span class="iconfont icon-shenglvehao" style="cursor: pointer" @click.stop></span>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Check, UploadFilled, Switch } from '@element-plus/icons-vue'
|
||||
</script>
|
||||
<script>
|
||||
import FileImage from '@/components/file-image/index.vue'
|
||||
import { asyncLocalFile } from '@/utils/talkFile'
|
||||
import { toTimeText } from '@/utils/date'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { deleteSmarttalk, updateSmarttalk, getPrepareById } from '@/api/file'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import outLink from '@/utils/linkConfig'
|
||||
import { sessionStore } from '@/utils/store'
|
||||
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
export default {
|
||||
name: 'FileListItem',
|
||||
components: { FileImage },
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: function () {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
activeClassId: { // 当前上课id
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: { 'on-start-class': null, 'on-delete': null, 'on-set': null, 'on-delhomework': null,'on-filearg': null },
|
||||
data() {
|
||||
return {
|
||||
listenList: [],
|
||||
userInfo:{}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.userInfo = useUserStore().user
|
||||
},
|
||||
methods: {
|
||||
clickStartClass(item) {
|
||||
this.$emit('on-start-class', item)
|
||||
},
|
||||
editTalk(item) {
|
||||
ElMessageBox.prompt('请输入新的标签', '添加标签', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
inputValue: "",
|
||||
inputPattern: /^[a-zA-Z0-9\u4e00-\u9fa5]{1,5}$/,
|
||||
inputErrorMessage: '请输入最多五个字的标签,不能携带标点符号'
|
||||
})
|
||||
.then(({ value }) => {
|
||||
let fileTagList = []
|
||||
if (!item.fileTag) {
|
||||
item.fileTag="";
|
||||
fileTagList = [];
|
||||
}else {
|
||||
fileTagList = item.fileTag.split(',');
|
||||
}
|
||||
fileTagList.push(value);
|
||||
item.fileTagList = fileTagList;
|
||||
item.fileTag = fileTagList.join(",")
|
||||
updateSmarttalk({ id: item.id, fileTag: item.fileTag, fileShowName: item.fileShowName }).then((res) => {
|
||||
if (res.data === true) {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: `添加成功!`
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
downloadFile(item) {
|
||||
ipcRenderer.send('save-as', item.fileFullPath, item.fileShowName)
|
||||
},
|
||||
deleteTalk(item) {
|
||||
deleteSmarttalk(item.id).then((res) => {
|
||||
if (res.data === true) {
|
||||
this.$emit('on-delete', item)
|
||||
}
|
||||
})
|
||||
},
|
||||
closePopver(index) {
|
||||
this.$refs['popover_' + index].hide()
|
||||
},
|
||||
formatFileSize(fileSize) {
|
||||
if (fileSize < 1024) {
|
||||
return fileSize + 'B'
|
||||
} else if (fileSize < 1024 * 1024) {
|
||||
let temp = fileSize / 1024
|
||||
temp = temp.toFixed(2)
|
||||
return temp + 'KB'
|
||||
} else if (fileSize < 1024 * 1024 * 1024) {
|
||||
let temp = fileSize / (1024 * 1024)
|
||||
temp = temp.toFixed(2)
|
||||
return temp + 'MB'
|
||||
} else {
|
||||
let temp = fileSize / (1024 * 1024 * 1024)
|
||||
temp = temp.toFixed(2)
|
||||
return temp + 'GB'
|
||||
}
|
||||
},
|
||||
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)
|
||||
asyncLocalFile(items).then(() => {
|
||||
ipcRenderer.send('open-path-app', item.fileNewName)
|
||||
if (this.listenList.indexOf(item.fileNewName) === -1) {
|
||||
this.listenList.push(item.fileNewName)
|
||||
let cookie = localStorage.getItem('Admin-Token')
|
||||
ipcRenderer.send('listen-file-change', {
|
||||
id: item.id,
|
||||
fileNewName: item.fileNewName,
|
||||
md5: item.fileMd5,
|
||||
cookie,
|
||||
fileType: item.fileType
|
||||
})
|
||||
ipcRenderer.on('listen-file-change-on' + item.fileNewName, () => {
|
||||
items.async = 'on'
|
||||
})
|
||||
ipcRenderer.on('listen-file-change-success' + item.fileNewName, (e, { data, md5 }) => {
|
||||
items.fileSize = data.fileSize
|
||||
items.md5 = md5
|
||||
items.async = true
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
//布置
|
||||
setHomeWork(item) {
|
||||
// this.$emit('on-set', item)
|
||||
},
|
||||
// 删除作业
|
||||
deleteHomework(item){
|
||||
this.$emit('on-delhomework', item)
|
||||
},
|
||||
// 文件资源打开web AIX 布置作业
|
||||
openFileLink(item){
|
||||
let unitId = item.levelSecondId ? item.levelSecondId : item.levelFirstId
|
||||
// key 对应的 linkConfig.js 外部链接配置
|
||||
let key = 'filehomework'
|
||||
let configObj = outLink()[key]
|
||||
// 通知主进程
|
||||
ipcRenderer.send('openWindow', {
|
||||
key,
|
||||
fullPath: configObj.fullPath + `&fileShowName=${item.fileShowName}&fileFullPath=${item.fileFullPath}&unitId=${unitId}`,
|
||||
cookieData: { ...configObj.data }
|
||||
})
|
||||
|
||||
this.$emit('on-filearg', item)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.prepare-item-info-message {
|
||||
.circular {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss">
|
||||
.prepare-body-main-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid rgba(131, 131, 127, 0.17);
|
||||
padding: 10px 0;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(144, 147, 153, 0.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prepare-body-main-item-icon {
|
||||
width: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.icon-zuoye {
|
||||
font-size: 40px;
|
||||
color: #707070;
|
||||
}
|
||||
}
|
||||
|
||||
.prepare-body-main-item-tool {
|
||||
font-size: 18px !important;
|
||||
font-weight: bold;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.prepare-body-main-item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
|
||||
.prepare-item-info-title {
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.prepare-item-info-message {
|
||||
font-size: 12px;
|
||||
line-height: 23px;
|
||||
color: #909399;
|
||||
display: flex;
|
||||
|
||||
.circular {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,7 +2,7 @@
|
|||
<el-dialog
|
||||
v-model="centerDialogVisible"
|
||||
class="reserv-dialog"
|
||||
title="预约课程"
|
||||
title="上课"
|
||||
destroy-on-close
|
||||
:before-close="closeDialog"
|
||||
width="600"
|
||||
|
@ -74,7 +74,7 @@
|
|||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="centerDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm"> 提交 </el-button>
|
||||
<el-button type="primary" @click="submitForm"> 上课 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
@ -86,7 +86,7 @@ import useUserStore from '@/store/modules/user'
|
|||
import { ElMessage } from 'element-plus'
|
||||
import { getCurrentTime, getAfterMinutes } from '@/utils/date'
|
||||
|
||||
const emit = defineEmits(['addSuccess'])
|
||||
const emit = defineEmits(['addSuccess','close'])
|
||||
const props = defineProps({
|
||||
bookId: {
|
||||
type: Number,
|
||||
|
@ -105,7 +105,7 @@ const userStore = useUserStore().user
|
|||
const centerDialogVisible = ref(false)
|
||||
const form = reactive({
|
||||
name: '',
|
||||
type: '常规课',
|
||||
type: '互动课',
|
||||
day: '',
|
||||
time: [],
|
||||
resource: [],
|
||||
|
@ -115,7 +115,7 @@ const updateForm = ref({})
|
|||
watch(
|
||||
() => props.currentNode,
|
||||
(newValue, oldValue) => {
|
||||
form.name = newValue.label
|
||||
form.name = newValue.itemtitle
|
||||
}
|
||||
)
|
||||
const ruleForm = reactive({
|
||||
|
@ -126,12 +126,6 @@ const ruleForm = reactive({
|
|||
resource: [{ required: true, message: '请选择授课对象', trigger: 'change' }]
|
||||
})
|
||||
const locationOptions = [
|
||||
{
|
||||
label: '常规课',
|
||||
value: '常规课',
|
||||
disabled: false,
|
||||
message: '现场公屏授课,学生无需长时间打开平板上。'
|
||||
},
|
||||
{
|
||||
label: '互动课',
|
||||
value: '互动课',
|
||||
|
@ -178,16 +172,23 @@ const disabledHours = ()=>{
|
|||
}
|
||||
// 限制分-返回被禁选的
|
||||
const disabledMinute = (hour,role) => {
|
||||
if(form.time == null) return
|
||||
if(getCurrentTime('YYYY-MM-DD') == form.day){
|
||||
const arrs = []
|
||||
if(role == 'start'){
|
||||
let nowHour = new Date().getHours().toString().padStart(2, '0');
|
||||
if(nowHour < hour){
|
||||
return []
|
||||
}
|
||||
else{
|
||||
for (let i = 0; i < 60; i++) {
|
||||
if (new Date().getMinutes() <= i) continue;
|
||||
arrs.push(i)
|
||||
}
|
||||
return arrs;
|
||||
}
|
||||
else{
|
||||
}
|
||||
if(role == 'end'){
|
||||
if(form.time[0]) return []
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +216,9 @@ const openDialog = (data) => {
|
|||
const closeDialog = () => {
|
||||
ruleFormDialog.value.resetFields()
|
||||
centerDialogVisible.value = false
|
||||
form.name = props.currentNode.label
|
||||
form.name = props.currentNode.itemtitle
|
||||
emit('close')
|
||||
|
||||
}
|
||||
const classList = ref([])
|
||||
onMounted(() => {
|
||||
|
@ -270,6 +273,11 @@ const updateClassReserv = (formData) => {
|
|||
}
|
||||
const addClassReserv = (formData) => {
|
||||
let ids = formData.resource.join(',')
|
||||
// 判断是否添加教材
|
||||
if(!props.bookId){
|
||||
ElMessage.warning('请选择教材')
|
||||
return
|
||||
}
|
||||
let param = {
|
||||
className: formData.name,
|
||||
classType: formData.type,
|
||||
|
@ -283,13 +291,13 @@ const addClassReserv = (formData) => {
|
|||
ex2: props.currentNode.id
|
||||
}
|
||||
addSmartClassReserv(param).then((res) => {
|
||||
if (res.data === true) {
|
||||
if (res.msg) {
|
||||
closeDialog()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '预约成功!'
|
||||
})
|
||||
emit('addSuccess')
|
||||
emit('addSuccess',res.msg)
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
|
|
|
@ -1,8 +1,38 @@
|
|||
<template>
|
||||
<div v-loading="isLoading" class="page-resource flex">
|
||||
<ChooseTextbook @change-book="nodeClick" @node-click="nodeClick" />
|
||||
<div class="page-center-wrap">
|
||||
<el-tabs v-model="activeAptTab" style="height: 100%;">
|
||||
<el-tab-pane label="教学课件" name="教学课件" class="prepare-center-jxkj">
|
||||
<div class="prepare-center-header">
|
||||
<div class="center-create-btn" style="background-color: rgb(64,158,255)" @click="createAptFile">
|
||||
<div class="create-btn-title"><el-icon><Plus /></el-icon><label>创建APT</label></div>
|
||||
<div class="create-btn-info">智能交互课件</div>
|
||||
</div>
|
||||
<div class="center-create-btn" style="background-color: rgb(112,182,3)" @click="createFile">
|
||||
<div class="create-btn-title"><el-icon><Plus /></el-icon><label>创建PPT</label></div>
|
||||
<div class="create-btn-info">传统课件</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prepare-center-body">
|
||||
<kj-list-item
|
||||
v-for="(item, index) in currentKJFileList"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:index="index"
|
||||
@on-delete="deleteTalk"
|
||||
@on-start-class="startClass"
|
||||
>
|
||||
</kj-list-item>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="教学实录" name="教学实录" class="prepare-center-jxsl">
|
||||
<class-reserv></class-reserv>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<div class="page-right">
|
||||
<div class="header-top flex">
|
||||
<!-- <div class="header-top flex">
|
||||
<div class="textbook-img" @click="navtoPdf">
|
||||
<el-image style="width: 80px; height: 110px" :src="curBookImg" />
|
||||
<el-progress
|
||||
|
@ -20,19 +50,25 @@
|
|||
</div>
|
||||
<el-button
|
||||
:type="!curClassReserv.id ? 'info' : 'primary'"
|
||||
:disabled="!curClassReserv.id||toolStore.isToolWin"
|
||||
:disabled="!curClassReserv.id || toolStore.isToolWin"
|
||||
class="to-class-btn"
|
||||
@click="openLesson"
|
||||
>
|
||||
<label><i class="iconfont icon-lingdang"></i>{{curClassReserv.status=='上课中'?'上课中':'上课'}}</label>
|
||||
<label
|
||||
><i class="iconfont icon-lingdang"></i
|
||||
>{{ curClassReserv.status == '上课中' ? '上课中' : '上课' }}</label
|
||||
>
|
||||
<label>{{ curClassReserv.classDay }} {{ getWeekday1(curClassReserv.classDay) }}</label>
|
||||
<label>{{ curClassReserv.startTime }}-{{ curClassReserv.endTime }}</label>
|
||||
</el-button>
|
||||
<div class="top-zoom-style"></div>
|
||||
</div>
|
||||
</div>-->
|
||||
<div style="padding: 0 20px;height: 100%;">
|
||||
<el-tabs v-model="activeTab" class="prepare-tabs" >
|
||||
<el-tab-pane label="素材" name="素材">
|
||||
<div class="prepare-body-header">
|
||||
<div>
|
||||
<label style="font-size: 15px">共{{ currentFileList.length }}个文件</label>
|
||||
<label style="font-size: 15px">共{{ currentFileList.filter(ite=>ite.fileFlag!=='apt'&&ite.fileFlag!=='课件').length }}个文件</label>
|
||||
<el-popover placement="top-start" :width="250" trigger="hover">
|
||||
<template #default>
|
||||
<div>
|
||||
|
@ -55,14 +91,7 @@
|
|||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<el-button @click="handleOutLink('feedback')">作业反馈</el-button>
|
||||
<el-button @click="handleOutLink('homeWork')">布置作业</el-button>
|
||||
<el-button @click="isDialogOpen = true">上传资料</el-button>
|
||||
<el-button type="primary" style="margin-left: 10px" @click="createFile"
|
||||
>新建课件</el-button
|
||||
>
|
||||
<el-button size="small" @click="isDialogOpen = true">上传资料</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-checkbox-group
|
||||
|
@ -71,7 +100,7 @@
|
|||
:style="{ 'margin-bottom': checkFileList.length > 0 ? '40px' : '0' }"
|
||||
>
|
||||
<file-list-item
|
||||
v-for="(item, index) in currentFileList"
|
||||
v-for="(item, index) in currentSCFileList"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:index="index"
|
||||
|
@ -83,6 +112,17 @@
|
|||
>
|
||||
<el-checkbox v-if="!item.uniquekey" label="" :value="item" />
|
||||
</file-list-item>
|
||||
</el-checkbox-group>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="作业" name="作业">
|
||||
<div class="prepare-body-header">
|
||||
<div>
|
||||
<label style="font-size: 15px">共{{ currentWorkList.length }}个作业</label>
|
||||
<el-button size="small" @click="handleOutLink('feedback')">作业反馈</el-button>
|
||||
<el-button size="small" @click="handleOutLink('homeWork')">布置作业</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prepare-work-wrap">
|
||||
<file-list-item
|
||||
v-for="(item, index) in currentWorkList"
|
||||
:key="index"
|
||||
|
@ -92,14 +132,15 @@
|
|||
@on-delete="deleteTalk"
|
||||
@on-set="openSet"
|
||||
@on-delhomework="delhomework"
|
||||
|
||||
>
|
||||
<el-checkbox v-if="!item.uniquekey" label="" :value="item" />
|
||||
</file-list-item>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<file-oper-batch
|
||||
v-show="checkFileList.length > 0"
|
||||
:indeterminate="checkFileList.length > 0 && checkFileList.length < currentFileList.length"
|
||||
:indeterminate="checkFileList.length > 0 && checkFileList.length < currentSCFileList.length"
|
||||
:choose="checkFileList"
|
||||
:check-all="isCheckAll"
|
||||
@click-delete="clickDelete"
|
||||
|
@ -110,23 +151,22 @@
|
|||
</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"
|
||||
:current-node="currentNode"
|
||||
:book-id="uploadData.textbookId"
|
||||
@add-success="initReserv"
|
||||
@close="closeChange"
|
||||
></reserv>
|
||||
<!-- <button @click="ipcMsgSend('tool-sphere:close')">测试</button> -->
|
||||
<!-- 上课配置 -->
|
||||
<class-start ref="calssRef" @close="closeChange"/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Check } from '@element-plus/icons-vue'
|
||||
import { Check,Plus } 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')
|
||||
|
@ -138,18 +178,22 @@ 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 } from '@/api/file'
|
||||
import KjListItem from '@/views/prepare/container/kj-list-item.vue'
|
||||
import { getSmarttalkPage, moveSmarttalk, creatAPT } 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, ipcMsgSend, sessionStore } from '@/utils/tool'
|
||||
import { createWindow, sessionStore } from '@/utils/tool'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { delClasswork } from '@/api/teaching/classwork'
|
||||
import { getSelfReserv, startClass } from '@/api/classManage'
|
||||
import { delClasswork, listEntpcourse } from '@/api/teaching/classwork'
|
||||
import { getClassInfo, getSelfReserv } from '@/api/classManage'
|
||||
import { useGetHomework } from '@/hooks/useGetHomework'
|
||||
import { addEntpcoursefileReturnId } from '@/api/education/entpcoursefile'
|
||||
import ClassReserv from '@/views/classManage/classReserv.vue'
|
||||
import classStart from './container/class-start.vue' // 预备上课
|
||||
const toolStore = useToolState()
|
||||
|
||||
const fs = require('fs')
|
||||
|
@ -162,9 +206,12 @@ export default {
|
|||
Refresh,
|
||||
uploadDialog,
|
||||
FileListItem,
|
||||
KjListItem,
|
||||
FileOperBatch,
|
||||
MoveFile,
|
||||
SetHomework
|
||||
SetHomework,
|
||||
ClassReserv,
|
||||
classStart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -183,6 +230,8 @@ export default {
|
|||
curClassReserv: {},
|
||||
downloadNum: 0,
|
||||
lastAsyncAllTime: '',
|
||||
activeTab: "素材",
|
||||
activeAptTab: "教学课件",
|
||||
uploadData: {
|
||||
textbookId: null,
|
||||
levelFirstId: 39103,
|
||||
|
@ -198,14 +247,22 @@ export default {
|
|||
// 布置作业弹窗
|
||||
setDialog: false,
|
||||
row: '',
|
||||
isOpenHomework: false
|
||||
isOpenHomework: false,
|
||||
// 当前上课课程
|
||||
activeClass: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isCheckAll() {
|
||||
return (
|
||||
this.checkFileList.length > 0 && this.checkFileList.length === this.currentFileList.length
|
||||
this.checkFileList.length > 0 && this.checkFileList.length === this.currentSCFileList.length
|
||||
)
|
||||
},
|
||||
currentKJFileList() {
|
||||
return this.currentFileList.filter((item) => item.fileFlag === 'apt' || item.fileFlag === '课件')
|
||||
},
|
||||
currentSCFileList() {
|
||||
return this.currentFileList.filter((item) => item.fileFlag !== 'apt' && item.fileFlag !== '课件')
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -217,27 +274,26 @@ export default {
|
|||
this.callback(param)
|
||||
})
|
||||
this.lastAsyncAllTime = localStorage.getItem('lastAsyncAllTime')
|
||||
this.initReserv()
|
||||
// this.initReserv()
|
||||
|
||||
// zdg: 获取缓存中的当前课程
|
||||
this.activeClass = sessionStore.get('activeClass') || null
|
||||
},
|
||||
mounted() {
|
||||
window.test = sessionStore
|
||||
window.test1 = toolStore
|
||||
this.$watch(
|
||||
() => toolStore.isToolWin,
|
||||
(newD, oldD) => {
|
||||
setTimeout(this.initReserv, 500)
|
||||
// setTimeout(this.initReserv, 500)
|
||||
}
|
||||
)
|
||||
// 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) {
|
||||
|
@ -247,16 +303,31 @@ export default {
|
|||
// }
|
||||
// },
|
||||
methods: {
|
||||
initReserv() {
|
||||
getSelfReserv().then((res) => {
|
||||
let list = res.data.filter((item) => {
|
||||
return item.status !== '已结束'
|
||||
})
|
||||
if (list.length > 0) {
|
||||
this.curClassReserv = list[list.length - 1]
|
||||
} else {
|
||||
this.curClassReserv = {}
|
||||
startClass(item) {
|
||||
// console.log(item, sessionStore)
|
||||
// 关闭状态,打开上课相关功能(已打开,忽略)
|
||||
const id = sessionStore.has('activeClass.id') ? sessionStore.get('activeClass.id') : null
|
||||
if (id && id == item.id) return ElMessage.warning('当前正在上课,请勿重复操作')
|
||||
if(item.fileFlag === '课件') {
|
||||
this.openReserv()
|
||||
}
|
||||
if(item.fileFlag === 'apt') {
|
||||
//TODO 打开apt - fileId: aptId
|
||||
this.$refs.calssRef.open(item.fileId)
|
||||
}
|
||||
// 当前上课-store
|
||||
sessionStore.set('activeClass', item)
|
||||
this.activeClass = item
|
||||
},
|
||||
closeChange() { // 上课弹窗被关闭-触发
|
||||
console.log('关闭上课弹窗')
|
||||
this.activeClass = null
|
||||
sessionStore.delete('activeClass')
|
||||
},
|
||||
initReserv(id) {
|
||||
getClassInfo(id).then((res) => {
|
||||
this.curClassReserv = res.data
|
||||
this.openLesson(res.data.id);
|
||||
})
|
||||
},
|
||||
getBookPathFromServer() {
|
||||
|
@ -297,10 +368,94 @@ export default {
|
|||
this.downloadNum = num
|
||||
},
|
||||
createFile() {
|
||||
creatPPT(this.currentNode.label + '.pptx', this.uploadData).then((res) => {
|
||||
creatPPT(this.currentNode.itemtitle + '.pptx', this.uploadData).then((res) => {
|
||||
this.currentFileList.unshift(res.resData)
|
||||
})
|
||||
},
|
||||
createAptFile() {
|
||||
listEntpcourse({
|
||||
evalid: this.currentNode.id,
|
||||
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.itemtitle + '.apt'
|
||||
}).then((res) => {
|
||||
this.currentFileList.unshift(res.resData)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
onMoveSingleFile(item) {
|
||||
this.moveFile = [item]
|
||||
this.isMoveDialogOpen = true
|
||||
|
@ -404,6 +559,7 @@ 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
|
||||
|
@ -428,7 +584,9 @@ 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()
|
||||
},
|
||||
|
@ -443,19 +601,20 @@ export default {
|
|||
},
|
||||
// 打开外部链接
|
||||
handleOutLink(key) {
|
||||
if(key == 'homeWork'){
|
||||
if (key == 'homeWork') {
|
||||
this.isOpenHomework = true
|
||||
}
|
||||
// key 对应的 linkConfig.js 外部链接配置
|
||||
let configObj = outLink()[key]
|
||||
let fullPath = configObj.fullPath
|
||||
//打开作业 高考 url增加unitId 章节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}`
|
||||
}
|
||||
}
|
||||
|
@ -499,16 +658,16 @@ export default {
|
|||
})
|
||||
},
|
||||
// 上课-工具类悬浮
|
||||
async openLesson() {
|
||||
async openLesson(id) {
|
||||
// await startClass(this.curClassReserv.id)
|
||||
createWindow('tool-sphere', {
|
||||
url:
|
||||
'/tool/sphere?entpcourseid=' +
|
||||
this.entpcourseid +
|
||||
'&label=' +
|
||||
this.currentNode.label +
|
||||
this.currentNode.itemtitle +
|
||||
'&reservId=' +
|
||||
this.curClassReserv.id
|
||||
id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -544,22 +703,80 @@ export default {
|
|||
<style scoped lang="scss">
|
||||
.page-resource {
|
||||
user-select: none;
|
||||
padding-top: 20px;
|
||||
padding-top: 10px;
|
||||
height: 100%;
|
||||
|
||||
:deep(.el-tabs__nav) {
|
||||
.el-tabs__item{
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
.page-center-wrap{
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
padding: 0 10px;
|
||||
.prepare-center-jxkj{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.prepare-center-header{
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.center-create-btn{
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
flex: 1;
|
||||
height: 60px;
|
||||
border-radius: 5px;
|
||||
.create-btn-title{
|
||||
font-weight: 900;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
height: 15px;
|
||||
margin-top: 15px;
|
||||
label{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.create-btn-info{
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: #ffffff;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.prepare-center-body{
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
.prepare-center-jxsl{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.page-right {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
min-width: 375px;
|
||||
width: 375px;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.prepare-tabs{
|
||||
height: 100%;
|
||||
:deep(.el-tab-pane) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
.header-top {
|
||||
height: 150px;
|
||||
align-items: center;
|
||||
|
@ -645,20 +862,24 @@ export default {
|
|||
}
|
||||
|
||||
.prepare-body-header {
|
||||
height: 60px;
|
||||
//height: 60px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
//padding: 0 20px;
|
||||
}
|
||||
.prepare-work-wrap{
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.prepare-body-main {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
padding: 0 30px;
|
||||
//padding: 0 30px;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,11 +40,12 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, getCurrentInstance } from 'vue'
|
||||
import { ref, getCurrentInstance,onMounted } from 'vue'
|
||||
import {updateUserInfo } from '@/api/system/user'
|
||||
import { listEvaluation } from '@/api/subject/index'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import { clearBookInfo } from '@/utils/ruoyi'
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
|
@ -90,9 +91,14 @@ const rules = ref({
|
|||
const subjectList = ref([])
|
||||
|
||||
// 获取基础的学科
|
||||
listEvaluation({ itemkey: 'subject', pageSize: 500 }).then((res) => {
|
||||
subjectList.value = res.rows
|
||||
})
|
||||
const getSubject = () => {
|
||||
//没有学科则不进行下面的步骤
|
||||
if(!userStore.user.subject) return
|
||||
listEvaluation({ itemkey: 'subject', pageSize: 500 }).then((res) => {
|
||||
const arr = userStore.user.subject.split(',')
|
||||
subjectList.value = res.rows.filter(item => arr.includes(String(item.id))).map(items => items)
|
||||
})
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (props.user.edudegree != '') {
|
||||
|
@ -132,6 +138,7 @@ function submit() {
|
|||
userStore.login({username:props.user.userName,password:props.user.plainpwd}).then(() => {
|
||||
userStore.getInfo().then(res => {
|
||||
if(res.code === 200){
|
||||
clearBookInfo()
|
||||
ElMessage.success('修改成功')
|
||||
}else{
|
||||
ElMessage.error(response.msg)
|
||||
|
@ -150,4 +157,7 @@ const semeterChange = (item) => {
|
|||
props.user.edusubject = ''
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
getSubject()
|
||||
})
|
||||
</script>
|
||||
|
|