diff --git a/src/renderer/src/api/classTask/index.js b/src/renderer/src/api/classTask/index.js new file mode 100644 index 0000000..f441957 --- /dev/null +++ b/src/renderer/src/api/classTask/index.js @@ -0,0 +1,237 @@ +// 查询evaluation列表 +import request from '@/utils/request' + +// 查询反馈列表 + +// 查询classworkdata列表 班级作业列表 +export function listClassworkdata(query) { + return request({ + url: '/education/classworkdata/list', + method: 'get', + params: query + }) +} + +// 查询entpcoursework列表 课程作业列表 +export function listEntpcoursework(query) { + return request({ + url: '/education/entpcoursework/list', + method: 'get', + params: query + }) +} + +// 查询classworkeval列表 课堂作业列表 +export function listClassworkeval(query) { + return request({ + url: '/education/classworkeval/list', + method: 'get', + params: query + }) +} + +// 修改classworkeval +export function updateClassworkeval(data) { + return request({ + url: '/education/classworkeval', + method: 'put', + data: data + }) +} + + + + + + + + + +// 0------------------------ +// 查询班级列表 +export function listClassmain(query) { + return request({ + url: '/education/classmain/list', + method: 'get', + params: query + }) +} +// 查询学生列表 +export function listClassuser(query) { + return request({ + url: '/education/classuser/list', + method: 'get', + params: query + }) +} +// 新增班级 +export function addClassmain(data) { + return request({ + url: '/education/classmain', + method: 'post', + data: data + }) +} +// 查询所有学科的列表 +export function listEvaluation(query) { + return request({ + url: '/education/evaluation/list', + method: 'get', + params: query + }) +} +// 新增小组 +export function addClassgroup(data) { + return request({ + url: '/education/classgroup', + method: 'post', + data: data + }) +} +//班级详情 +export function getClassmain(id) { + return request({ + url: '/education/classmain/' + id, + method: 'get' + }) +} +// 获取小组列表 +export function listClassgroup(query) { + return request({ + url: '/education/classgroup/new/list', + method: 'get', + params: query + }) +} +//删除小组 +export function delClassgroup(id) { + return request({ + url: '/education/classgroup/' + id, + method: 'delete' + }) +} +//查询小组信息 +export function getClassgroup(id) { + return request({ + url: '/education/classgroup/' + id, + method: 'get' + }) +} +//修改小组信息 +export function updateClassgroup(data) { + return request({ + url: '/education/classgroup', + method: 'put', + data: data + }) +} +//新增学生 +export function addStudentmain(data) { + return request({ + url: '/education/studentmain', + method: 'post', + data: data + }) +} +//修改学生信息 +export function updateStudentmain(data) { + return request({ + url: '/education/studentmain', + method: 'put', + data: data + }) +} +//获取学生信息 +export function getStudentmain(id) { + return request({ + url: '/education/studentmain/' + id, + method: 'get' + }) +} +//删除学生 +export function leaveClass(data) { + return request({ + url: '/education/classuser/leaveClass', + method: 'post', + data: data + }) +} +//删除学生所有数据 +export function removeStudentDataAll(id) { + return request({ + url: '/education/studentmain/removeStudent/' + id, + method: 'post' + }) +} +//删除教室 +export function delClassroom(id) { + return request({ + url: '/education/classroom/' + id, + method: 'delete' + }) +} +//导入学生 +export function addStudentmainByNameArray(data) { + return request({ + url: '/education/studentmain/addByNameArray', + method: 'post', + data: data + }) +} +//新增课程预约 +export function addSmartClassReserv(data) { + return request({ + url: '/smarttalk/classReserv/addSmartClassReserv', + method: 'post', + data: data + }) +} +//修改课程预约 +export function updateSmartClassReserv(data) { + return request({ + url: '/smarttalk/classReserv/updateSmartClassReserv', + method: 'post', + data: data + }) +} +//查询课程预约 +export function getSelfReserv() { + return request({ + url: '/smarttalk/classReserv/getSelfReserv', + method: 'get' + }) +} +export function deleteSmartReserv(id) { + return request({ + url: '/smarttalk/classReserv/' + id, + method: 'delete' + }) +} +export function startClass(id, ex3) { + const params = {id} + !!ex3 && (params.ex3 = ex3) + return request({ + url: '/smarttalk/classReserv/startClass', + method: 'get', + params + }) +} +export function endClass(id) { + return request({ + url: '/smarttalk/classReserv/endClass', + method: 'get', + params: {id} + }) +} +/** + * @description 获取课堂信息 + * @param {*} id + * @returns + */ +export function getClassInfo(id) { + return request({ + url: '/smarttalk/classReserv/selectById', + method: 'get', + params: {id} + }) +} diff --git a/src/renderer/src/hooks/useProcessList.js b/src/renderer/src/hooks/useProcessList.js new file mode 100644 index 0000000..d53f73e --- /dev/null +++ b/src/renderer/src/hooks/useProcessList.js @@ -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 += `
` + } + const char = String.fromCharCode(65 + j) + tmp += `
${char}.${workDescArr[j]}
` + if (j % 2 == 1) { + tmp += '
' + } + } + // j此刻已自增1, 故当选项为单数时, 需要补充结束标签 + if (j % 2 == 1) { + tmp += '' + } + + // 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('
') + + 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 = `
${index + 1}. ${item.title}
` + let tmp = '' + let j = 0 + let optionsArr = item.options + for (; j < optionsArr.length; j++) { + if (j % 2 == 0) { + tmp += `
` + } + const char = String.fromCharCode(65 + j) + tmp += `
${char}.${optionsArr[j]}
` + if (j % 2 == 1) { + tmp += '
' + } + } + // j此刻已自增1, 故当选项为单数时, 需要补充结束标签 + if (j % 2 == 1) { + tmp += '' + } + + workDescHtml += tmp + } else if (item.type == '填空题' || item.type == '判断题' || item.type == '主观题') { + workDescHtml += `
${index + 1}. ${item.title}
` + } + }) + workDescHtml += '' + 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 += `
${index + 1}. ${answer}
` + } else if (item.type == '填空题') { + const answer = answerArr.join('、') + workAnswerHtml += `
${index + 1}. ${answer}
` + } else if (item.type == '判断题') { + const answer = answerArr + .map((item) => { + return item === '1' ? '正确' : '错误' + }) + .join('、') + workAnswerHtml += `
${index + 1}. ${answer}
` + } else if (item.type == '主观题') { + // 复合题里面的主观题只有一个答案,或没填 + const answer = answerArr.join('、') + if (answerArr[0]) { + workAnswerHtml += `
${index + 1}. ${answer}
` + } else { + workAnswerHtml += `
${index + 1}. ${answer}答案不唯一,请参考分析解答点评!
` + } + } + }) + 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 += `
` + } + const char = String.fromCharCode(65 + j) + tmp += `
${char}.${workDescArr[j]}
` + if (j % 2 == 1) { + tmp += '
' + } + } + if (j % 2 == 0) { + tmp += '' + } + 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: "①②#&①③#&②④#&③④" || "
为了活着
#&
为了填报肚子
#&
为了吃饭而吃饭
" + 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]*>/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`; + } + const char = String.fromCharCode(65+j); + tmp += `
${char}.${jsonArr[j]}
`; + if(j%2 == 1){ + tmp += ''; + } + } + + if(j%2== 0){ + tmp += ''; + } + 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'; + } + 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 { + 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('
'); + + 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 += '
'+(index+1)+'.'+item.replace(/#&/g, ',')+'
'; + }) + row[i].workanswerFormat = tmp; + + } + else { + //处理答案 + row[i].workanswerFormat = '见试题解答内容'; + } + */ + } + } +} diff --git a/src/renderer/src/layout/components/Header.vue b/src/renderer/src/layout/components/Header.vue index e7a4e04..e151832 100644 --- a/src/renderer/src/layout/components/Header.vue +++ b/src/renderer/src/layout/components/Header.vue @@ -37,6 +37,7 @@ 个人中心 课程预约 班级中心 + 作业批改 退出登录 diff --git a/src/renderer/src/router/index.js b/src/renderer/src/router/index.js index 14b0cd0..000314e 100644 --- a/src/renderer/src/router/index.js +++ b/src/renderer/src/router/index.js @@ -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' @@ -57,6 +62,12 @@ export const constantRoutes = [ name: 'class', meta: {title: '班级中心'}, }, + { + path: '/classTask', + component: () => import('@/views/classTask/classTask.vue'), + name: 'class', + meta: {title: '作业批改'}, + }, ] }, ...toolRouters diff --git a/src/renderer/src/utils/date.js b/src/renderer/src/utils/date.js index f70414b..2a178ca 100644 --- a/src/renderer/src/utils/date.js +++ b/src/renderer/src/utils/date.js @@ -108,4 +108,29 @@ export const getAfterMinutes = (m) => { let minutes = afterMinutes.getMinutes(); 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 } \ No newline at end of file diff --git a/src/renderer/src/views/classTask/classTask.vue b/src/renderer/src/views/classTask/classTask.vue new file mode 100644 index 0000000..98a5d7b --- /dev/null +++ b/src/renderer/src/views/classTask/classTask.vue @@ -0,0 +1,394 @@ + + + + + + diff --git a/src/renderer/src/views/classTask/container/item-dialog-score.vue b/src/renderer/src/views/classTask/container/item-dialog-score.vue new file mode 100644 index 0000000..fcd5ee4 --- /dev/null +++ b/src/renderer/src/views/classTask/container/item-dialog-score.vue @@ -0,0 +1,964 @@ + + + + + diff --git a/src/renderer/src/views/classTask/container/item-dialog.vue b/src/renderer/src/views/classTask/container/item-dialog.vue new file mode 100644 index 0000000..4519b62 --- /dev/null +++ b/src/renderer/src/views/classTask/container/item-dialog.vue @@ -0,0 +1,616 @@ + + + + + + diff --git a/src/renderer/src/views/classTask/container/task-item.vue b/src/renderer/src/views/classTask/container/task-item.vue new file mode 100644 index 0000000..9e7142e --- /dev/null +++ b/src/renderer/src/views/classTask/container/task-item.vue @@ -0,0 +1,143 @@ + + +