baigl #180
|
@ -34,10 +34,13 @@
|
||||||
"electron-updater": "^6.1.7",
|
"electron-updater": "^6.1.7",
|
||||||
"element-plus": "^2.7.6",
|
"element-plus": "^2.7.6",
|
||||||
"fabric": "^5.3.0",
|
"fabric": "^5.3.0",
|
||||||
|
"im_electron_sdk": "^8.0.5904",
|
||||||
|
"@vue-office/docx": "^1.6.2",
|
||||||
|
"@vue-office/excel": "^1.7.11",
|
||||||
|
"@vue-office/pdf": "^2.0.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"jsondiffpatch": "0.6.0",
|
"jsondiffpatch": "0.6.0",
|
||||||
"im_electron_sdk": "^8.0.5904",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"pdfjs-dist": "4.4.168",
|
"pdfjs-dist": "4.4.168",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
|
|
@ -39,6 +39,15 @@ export function updateClassworkeval(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改classworkdata
|
||||||
|
export function updateClassworkdata(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/classworkdata',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -92,7 +92,9 @@ const getData = () => {
|
||||||
// 班级作业数据,包含多个班级
|
// 班级作业数据,包含多个班级
|
||||||
homeworklist({
|
homeworklist({
|
||||||
classidarray: classListIds.value.join(','),
|
classidarray: classListIds.value.join(','),
|
||||||
entpcourseid: '', // 章节id? 这里要全课程的作业 不分章节?
|
//entpcourseid: '', // 章节id? 这里要全课程的作业 不分章节? 根据学段学科查询所有的作业
|
||||||
|
edustage: userStore.edustage,// 学段
|
||||||
|
edusubject: userStore.edusubject,//学科
|
||||||
orderby: 'uniquekey DESC',
|
orderby: 'uniquekey DESC',
|
||||||
pageSize: 100
|
pageSize: 100
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
|
@ -101,7 +103,7 @@ const getData = () => {
|
||||||
response.rows[i].workdatalist = []
|
response.rows[i].workdatalist = []
|
||||||
response.rows[i].workdatacount = 0 // 人数
|
response.rows[i].workdatacount = 0 // 人数
|
||||||
response.rows[i].workdatalistVisible = false
|
response.rows[i].workdatalistVisible = false
|
||||||
response.rows[i].workdatafeedbackcount = 0
|
response.rows[i].workdatafeedbackcount = 0 // 已交人数
|
||||||
response.rows[i].feedtimelength = 0
|
response.rows[i].feedtimelength = 0
|
||||||
response.rows[i].rightAnswerCount = 0
|
response.rows[i].rightAnswerCount = 0
|
||||||
response.rows[i].scoingRate = 0 + '%' // 得分率
|
response.rows[i].scoingRate = 0 + '%' // 得分率
|
||||||
|
@ -142,10 +144,10 @@ const getData = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示分配人数>0 的
|
// 显示分配人数(workdatacount)>0 的
|
||||||
if (response.rows && response.rows.length > 0) {
|
if (response.rows && response.rows.length > 0) {
|
||||||
classWorkList.value =
|
classWorkList.value = response.rows && response.rows.filter((item) => item.workdatacount > 0)
|
||||||
response.rows && response.rows.filter((item) => item.workdatacount > 0)
|
// classWorkList.value = response.rows && response.rows.filter((item) => item.workdatacount > 0 && item.uniquekey == '语文-0808-1')
|
||||||
//TODO: 这里没分页,貌似这个 total 不重要,后续看
|
//TODO: 这里没分页,貌似这个 total 不重要,后续看
|
||||||
total.value = response.total
|
total.value = response.total
|
||||||
}
|
}
|
||||||
|
@ -211,7 +213,9 @@ const getStudentVisible = async () => {
|
||||||
// 班级作业数据,多个班级
|
// 班级作业数据,多个班级
|
||||||
const response = await homeworklist({
|
const response = await homeworklist({
|
||||||
classidarray: classListIds.value.join(','),
|
classidarray: classListIds.value.join(','),
|
||||||
entpcourseid: '', // 章节???这里不需要,全课程的
|
//entpcourseid: '', // 章节id? 这里要全课程的作业 不分章节? 根据学段学科查询所有的作业
|
||||||
|
edustage: userStore.edustage,// 学段
|
||||||
|
edusubject: userStore.edusubject,//学科
|
||||||
orderby: 'uniquekey DESC',
|
orderby: 'uniquekey DESC',
|
||||||
pageSize: 100
|
pageSize: 100
|
||||||
})
|
})
|
||||||
|
@ -228,7 +232,7 @@ const getStudentVisible = async () => {
|
||||||
// }
|
// }
|
||||||
// 确保当前拿到的任务与页面中存在的任务能一对一(避免因删除其他操作而删除作业任务导致两个数组的index不统一而越界)
|
// 确保当前拿到的任务与页面中存在的任务能一对一(避免因删除其他操作而删除作业任务导致两个数组的index不统一而越界)
|
||||||
let curWork = curWorkList.find((work) => work.id === classWorkList.value[t].id)
|
let curWork = curWorkList.find((work) => work.id === classWorkList.value[t].id)
|
||||||
// workdataresultcount 完成人数 workdatacount人数要大于0
|
// workdataresultcount 完成人数 workdatacount人数要大于0
|
||||||
if (curWork && curWork.workdataresultcount > 0 && classWorkList.value[t].workdatacount > 0) {
|
if (curWork && curWork.workdataresultcount > 0 && classWorkList.value[t].workdatacount > 0) {
|
||||||
classWorkList.value[t].workdataresultcount = curWork.workdataresultcount
|
classWorkList.value[t].workdataresultcount = curWork.workdataresultcount
|
||||||
// 桌面端貌似不需要进度条了?
|
// 桌面端貌似不需要进度条了?
|
||||||
|
@ -236,12 +240,8 @@ const getStudentVisible = async () => {
|
||||||
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100
|
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100
|
||||||
)
|
)
|
||||||
// 计算参与学习任务的平均用时
|
// 计算参与学习任务的平均用时
|
||||||
console.log('平均用时??---', classWorkList.value[t].workdatafeedbackcount)
|
|
||||||
if (classWorkList.value[t].workdatafeedbackcount > 0) {
|
if (classWorkList.value[t].workdatafeedbackcount > 0) {
|
||||||
classWorkList.value[t].averagetime = (
|
classWorkList.value[t].averagetime = (classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount).toFixed(0)
|
||||||
classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount
|
|
||||||
).toFixed(0)
|
|
||||||
console.log('平均用时---', classWorkList.value[t].averagetime)
|
|
||||||
} else {
|
} else {
|
||||||
classWorkList.value[t].averagetime = 0
|
classWorkList.value[t].averagetime = 0
|
||||||
}
|
}
|
||||||
|
@ -257,15 +257,18 @@ const getStudentVisible = async () => {
|
||||||
const getStudentClassWorkData = () => {
|
const getStudentClassWorkData = () => {
|
||||||
// 再查找多个班级里,每个学生的作业数据
|
// 再查找多个班级里,每个学生的作业数据
|
||||||
console.log('======????????""""""""""')
|
console.log('======????????""""""""""')
|
||||||
|
//TODO 这里id变动,看后续用什么判断
|
||||||
listClassworkdata({
|
listClassworkdata({
|
||||||
classids: classListIds.value.join(','),
|
classids: classListIds.value.join(','),
|
||||||
entpcourseid: '', // 章节id? 这里要全课程的作业 不分章节?
|
//entpcourseid: '', // 章节id? 这里要全课程的作业 不分章节? 根据学段学科查询所有的作业
|
||||||
|
edustage: userStore.edustage,// 学段
|
||||||
|
edusubject: userStore.edusubject,//学科
|
||||||
|
orderby: "deaddate DESC",
|
||||||
pageSize: 1000
|
pageSize: 1000
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
console.log('多个班级里,每个学生的作业数据', res.rows)
|
|
||||||
for (var t = 0; t < classWorkList.value.length; t++) {
|
for (var t = 0; t < classWorkList.value.length; t++) {
|
||||||
for (var i = 0; i < res.rows.length; i++) {
|
for (var i = 0; i < res.rows.length; i++) {
|
||||||
if (res.rows[i].classworkid == classWorkList.value[t].id) {
|
if (res.rows[i].uniquekey == classWorkList.value[t].uniquekey) {
|
||||||
// if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].resultcount > 0) {
|
// if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].resultcount > 0) {
|
||||||
console.log('==================')
|
console.log('==================')
|
||||||
// 有几个学生完成/正在完成学习任务
|
// 有几个学生完成/正在完成学习任务
|
||||||
|
@ -332,10 +335,7 @@ const getStudentClassWorkData = () => {
|
||||||
|
|
||||||
// 计算参与学习任务的平均用时
|
// 计算参与学习任务的平均用时
|
||||||
if (classWorkList.value[t].workdatafeedbackcount > 0) {
|
if (classWorkList.value[t].workdatafeedbackcount > 0) {
|
||||||
classWorkList.value[t].averagetime =
|
classWorkList.value[t].averagetime = (classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount).toFixed(0)
|
||||||
(
|
|
||||||
classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount
|
|
||||||
).toFixed(0)
|
|
||||||
} else {
|
} else {
|
||||||
classWorkList.value[t].averagetime = 0
|
classWorkList.value[t].averagetime = 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<el-form ref="classWorkFormScoreRef" :model="classWorkFormScore">
|
<el-form ref="classWorkFormScoreRef" :model="classWorkFormScore">
|
||||||
<div class="teacher_content" :style="{ height: dialogProps.maxheight + 'px' }">
|
<!-- <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">
|
<div style="font-size: 18px; width: 100%; padding: 5px 10px" class="sticky">
|
||||||
{{ classWorkFormScore.name }} 答题详情
|
{{ classWorkFormScore.name }} 答题详情
|
||||||
</div>
|
</div>
|
||||||
|
@ -410,11 +411,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- :style="{ height: dialogProps.maxheight + 'px' }" -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="fileReadopen"
|
v-model="fileReadopen"
|
||||||
title="文件预览"
|
title="文件预览"
|
||||||
width="80%"
|
width="80%"
|
||||||
:style="{ height: dialogProps.maxheight + 'px' }"
|
:style="{ height: '75vh' }"
|
||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
<div class="file-read-dialog">
|
<div class="file-read-dialog">
|
||||||
|
@ -468,13 +470,13 @@
|
||||||
></span
|
></span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- <ReFilePreview
|
<ReFilePreview
|
||||||
:name="fileitem.name"
|
:name="fileitem.name"
|
||||||
:type="fileitem.type"
|
:type="fileitem.type"
|
||||||
:file-type="fileitem.type"
|
:file-type="fileitem.type"
|
||||||
:file-path="fileitem.url"
|
:file-path="fileitem.url"
|
||||||
:text-content="textContent"
|
:text-content="textContent"
|
||||||
/> -->
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
@ -488,12 +490,10 @@ import useUserStore from '@/store/modules/user'
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
// import { Plus } from '@element-plus/icons-vue'
|
// import { Plus } from '@element-plus/icons-vue'
|
||||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||||
import { updateClassworkeval } from '@/api/classTask'
|
import { updateClassworkeval, updateClassworkdata } from '@/api/classTask'
|
||||||
import { getTimeDate } from '@/utils/date'
|
import { getTimeDate } from '@/utils/date'
|
||||||
// import ReFilePreview from '@/components/ReFilePreview/index.vue'
|
import ReFilePreview from '@/components/refile-preview/index.vue'
|
||||||
|
|
||||||
// import mammoth from 'mammoth'
|
|
||||||
// import * as XLSX from 'xlsx'
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
@ -864,6 +864,16 @@ const onSubmit = () => {
|
||||||
return
|
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 &&
|
||||||
classWorkFormScore.teacherRating.map((item, index) => {
|
classWorkFormScore.teacherRating.map((item, index) => {
|
||||||
const queryParams = {
|
const queryParams = {
|
||||||
|
|
|
@ -54,12 +54,11 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 如果当前学习没有试题 -->
|
<!-- 如果当前学习没有试题 :height="mainHeight"-->
|
||||||
<div
|
<div
|
||||||
v-if="classWorkAnalysis.view == 'studentview'"
|
v-if="classWorkAnalysis.view == 'studentview'"
|
||||||
style="width: 100%"
|
style="width: 100%; height:75vh; "
|
||||||
class="clwk_dialog_view"
|
class="clwk_dialog_view"
|
||||||
:height="mainHeight"
|
|
||||||
>
|
>
|
||||||
<div class="view_table">
|
<div class="view_table">
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
|
@ -75,6 +74,7 @@
|
||||||
v-loading="loading_dt_table"
|
v-loading="loading_dt_table"
|
||||||
:data="tableRadio.list"
|
:data="tableRadio.list"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
|
style="height: 69vh;"
|
||||||
highlight-current-row
|
highlight-current-row
|
||||||
@row-click="getStudentClassWorkDataDetail"
|
@row-click="getStudentClassWorkDataDetail"
|
||||||
>
|
>
|
||||||
|
@ -491,6 +491,25 @@ const handleClassWorkAnalysissScoreOpen = (row) => {
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
/** 批阅:已交未交事件 */
|
||||||
|
const tableRadioChange = (e) => {
|
||||||
|
// 关闭右侧批阅ui
|
||||||
|
isopen_dtwk_table.value = false;
|
||||||
|
console.log(e,'??????')
|
||||||
|
console.log("学生列表:", classWorkAnalysis.classworkdata)
|
||||||
|
if(e=='1'){
|
||||||
|
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.resultcount > 0)
|
||||||
|
tableRadio.value = '1';
|
||||||
|
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
|
||||||
|
tableRadio.num1 = tableRadio.list.length;
|
||||||
|
}else if(e=='0'){
|
||||||
|
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.resultcount == 0)
|
||||||
|
tableRadio.value = '0';
|
||||||
|
tableRadio.num0 = tableRadio.list.length;
|
||||||
|
tableRadio.num1 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 将标签中的双引号增加转义
|
// 将标签中的双引号增加转义
|
||||||
const escapeHtmlQuotes = (str) => {
|
const escapeHtmlQuotes = (str) => {
|
||||||
// 后端已replace双引号, 故前端不用在处理
|
// 后端已replace双引号, 故前端不用在处理
|
||||||
|
|
|
@ -14,13 +14,16 @@
|
||||||
| 截止时间:{{ item.deaddate }} | {{ tabactive }}
|
| 截止时间:{{ item.deaddate }} | {{ tabactive }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-switch v-model="value1" active-text="云同步"> </el-switch>
|
<!-- <el-switch v-model="value1" active-text="云同步"> </el-switch> -->
|
||||||
<div class="class-reserv-item-tool">
|
<div class="class-reserv-item-tool">
|
||||||
<span><span style="color: #000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultcount }}</span>/{{ item.workdatacount }}</span>
|
<span>
|
||||||
|
<span v-if="item.workdataresultcount!=0" style="color:#000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultcount }}</span>
|
||||||
|
<span v-if="item.workdataresultcount==0">{{ item.workdataresultcount }}</span>
|
||||||
|
/{{ item.workdatacount }}</span>
|
||||||
<span>已交</span>
|
<span>已交</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="class-reserv-item-tool">
|
<div class="class-reserv-item-tool">
|
||||||
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">2</span>
|
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherRationgCount?item.teacherRationgCount:0 }}</span>
|
||||||
<span>待批阅</span>
|
<span>待批阅</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="class-reserv-item-tool">
|
<div class="class-reserv-item-tool">
|
||||||
|
|
Loading…
Reference in New Issue