Compare commits

..

2 Commits

Author SHA1 Message Date
M了个J
12c6f0759b Merge branch 'main' of https://gitlab.lingxixue.com/limeng/lingxixue-web 2026-05-11 15:15:37 +08:00
M了个J
87b0b49e14 feat:调整主题色 2026-05-11 15:15:28 +08:00
27 changed files with 21 additions and 5096 deletions

View File

@ -36,7 +36,7 @@ const props = withDefaults(
const emits = defineEmits(['loaded'])
const OSS_URL = import.meta.env.VITE_OSS_URL
const bg = `url(${OSS_URL}/urm/urm_load_bg.png) 100%/cover no-repeat`
// const bg = `url(${OSS_URL}/urm/urm_load_bg.png) 100%/cover no-repeat`
const { size, loadProgress, loadSize, loaded, loadText, init } = useLoad()
const simulationLoadProgress = ref('0%') //
@ -113,7 +113,7 @@ const progress = computed(() =>
.load {
width: 100%;
height: 100%;
background: v-bind(bg);
background-color: $theme-color-bg;
padding: 0 160px;
position: relative;
z-index: 9999;

View File

@ -4,11 +4,7 @@
"/subject/book_shelf_top.svg",
"/subject/book_shelf_bottom.svg",
"/urm/bg_home.svg",
"/urm/bg_login.svg",
"/urm/book_bg.svg",
"/urm/mail_bg.svg",
"/urm/main_bg.svg",
"/urm/urm_load_bg.png",
"/urm/subject_target_dia_bg.svg",
@ -110,10 +106,6 @@
"/urm/task-yilingqu.png",
"/urm/task-kelingqu.png",
"/urm/task-normal.png",
"/urm/task_achievement.svg",
"/urm/task_day.svg",
"/urm/task_parent.svg",
"/urm/task_lead.svg"
"/urm/task-normal.png"
]
}

View File

@ -10,7 +10,6 @@ import hud from '@/utils/hud'
import sass from '@/utils/sass'
const OSS_URL = import.meta.env.VITE_OSS_URL
const bg = `url(${OSS_URL}/urm/bg_login.svg) 100%/cover no-repeat`
const logo = sass.getLogo()
const qr = sass.getQrCode()
const url = window.location.href
@ -110,12 +109,7 @@ defineOptions({
})
</script>
<template>
<div
class="page"
:style="{
background: bg,
}"
>
<div class="page">
<BackBar v-if="showBack" class="backBar" />
<div class="box">
<img class="logo" :src="logo" @click="onEnter" />
@ -166,6 +160,7 @@ defineOptions({
display: flex;
align-items: center;
justify-content: center;
background-color: $theme-color-bg;
.backBar {
position: fixed;

View File

@ -12,7 +12,6 @@ import globalApi from '@/api/global'
import userApi from '@/api/system/user'
const OSS_URL = import.meta.env.VITE_OSS_URL
const bg = `url(${OSS_URL}/urm/bg_login.svg) 100%/cover no-repeat`
const logo = sass.getLogo()
const ipGirl = `${OSS_URL}/urm/ip_girl.png`
@ -138,12 +137,7 @@ onMounted(() => {
</script>
<template>
<div
class="page"
:style="{
background: bg,
}"
>
<div class="page">
<div v-if="step === 0" class="step0" @click="step += 1">
<img class="logo" :src="logo" />
<img class="ip" :src="ipGirl" />
@ -257,6 +251,7 @@ onMounted(() => {
width: 100%;
height: 100%;
padding: $space-2_h $space-6;
background-color: $theme-color-bg;
.step0 {
height: 100%;

View File

@ -19,7 +19,6 @@ const handleClick = (buttonId: any) => {
const OSS_URL = import.meta.env.VITE_OSS_URL
const xxlIcon = `${OSS_URL}/icon/module_xxl.png`
const xzyIcon = `${OSS_URL}/icon/module_bjxzy.jpg`
const bg = `url(${OSS_URL}/urm/bg_login.svg) 100%/cover no-repeat`
const title_size = 68
const title_height = 112
</script>
@ -51,7 +50,7 @@ const title_height = 112
display: flex;
justify-content: center;
align-items: center;
background: v-bind(bg);
background-color: $theme-color-bg;
gap: 250px;
.box {

View File

@ -96,12 +96,7 @@ const startDoWork = () => {
}
</script>
<template>
<div
class="uncomplete"
:style="{
'background-image': `url(${OSS_URL}/urm/mail_bg.svg)`,
}"
>
<div class="uncomplete">
<div
class="book"
:style="{
@ -238,6 +233,7 @@ const startDoWork = () => {
position: relative;
background-repeat: no-repeat;
background-size: cover;
background-color: $theme-color-bg;
.book {
box-sizing: border-box;

View File

@ -1,645 +0,0 @@
<script setup lang="ts">
import SingleChoice from '@c/question/answer/SingleChoice.vue'
import Judgment from '@c/question/answer/Judgment.vue'
import JudgmentCloze from '@c/question/answer/JudgmentCloze.vue'
import MultipleChoice from '@c/question/answer/MultipleChoice.vue'
import ClozeTest from '@c/question/answer/ClozeTest.vue'
import Other from '@c/question/answer/Other.vue'
import HandDrawn from '@c/question/answer/HandDrawn.vue'
import Input from '@c/question/answer/Input.vue'
import { useAudio, TypeSet } from '@/hooks'
import homeWorkApi from '@/api/system/homeWork'
import EchartsBar from '@/page/system/homework/reports/components/EchartsBar.vue'
import { useRoute } from 'vue-router'
const props = defineProps<{
data: any
idx: number
reportFlag: number
uploadFile: any
beforeUpload: any
isDeadline: boolean
}>()
const emit = defineEmits(['submit', 'handleTime', 'showPic', 'beforeUpload', 'uploadError'])
const route = useRoute()
const component = computed(() => {
console.log(props.data)
if (props.data?.autoMarkFlag === 0) {
//
if (props.data?.titleChannelTypeName === '填空题') {
//
return HandDrawn
}
if (props.data?.subjectId === 3 && props.data?.titleChannelTypeName === '解答题') {
//
return HandDrawn
}
return Other
}
if (props.data?.fillBlankNum) {
//
return Input
}
//
if (props.data?.optionList?.length > 1) {
if (['判断', '判断题'].includes(props.data?.titleChannelTypeName)) {
return JudgmentCloze
}
return ClozeTest
}
if (props.data?.titleChannelTypeName === '多选题') {
return MultipleChoice
}
if (['判断', '判断题'].includes(props.data?.titleChannelTypeName)) {
return Judgment
}
// optionList
return props.data?.optionList?.[0]?.length ? SingleChoice : Other
})
//
const imgDia = reactive({
show: false,
url: '',
})
function handleShow(url: string) {
if (url) {
imgDia.show = true
imgDia.url = url
}
}
const resultClass = computed(() => {
return (data: any) => {
if (!data)
return {
class: 'info',
text: 0 + '分',
// icon: 'emoji_info',
}
if (data.titleStatus === 2) {
return {
class: 'wait',
text: '待批改',
// icon: 'emoji_info',
}
} else if (!data.markFlag && !data.submitAnswer && !data.submitAnswerPic) {
return {
class: 'empty',
text: '未作答',
icon: 'emoji_empty',
}
} else if (data.markFlag) {
return {
class: 'success',
text: '答对啦',
icon: 'emoji_success',
}
} else if (!data.markFlag && data.answerScore === 0) {
return {
class: 'error',
text: '答错了',
icon: 'emoji_fail',
}
} else {
return {
class: 'info',
text: data.answerScore + '分',
// icon: 'emoji_info',
}
}
}
})
//
const filterTitle = computed(() => {
return (title: string | undefined, cidx?: number) => {
if (title) {
const t = title.replace(/http:\/\//gi, 'https://')
// .replace(/contenteditable/g, 'no-contenteditable');
if (cidx !== undefined) {
return `(${cidx + 1})` + t
} else {
return t
}
}
return undefined
}
})
/**
* @description: 音频
*/
//
const isAudioPlay = ref(false)
//
let audioPlayer: anyObj = {}
//
function playAudio() {
if (isAudioPlay.value) {
audioPlayer.pause()
} else {
audioPlayer.src = props.data?.voiceUrl
audioPlayer.play()
}
}
//
const quizList = ref<anyObj[]>([])
function inputChange(val: string, idx: number) {
quizList.value[idx].innerText = val
}
const jobStuStatistics = ref<anyObj>([])
const rightRef = ref()
const echartsRef = ref()
const echartsData = reactive<{ xData: string[]; seriesVal: anyObj[] }>({
xData: [],
seriesVal: [],
})
watch(
() => props.data?.jobStuTittleId,
async nv => {
if (nv) {
//
audioPlayer?.pause?.()
rightRef.value && (rightRef.value.scrollTop = 0)
await nextTick()
TypeSet([document.querySelector('.question-item')])
//
document.querySelectorAll('.dowork .left-title img').forEach((i: anyObj) => {
i?.addEventListener('click', () => {
handleShow(i?.src)
})
})
document.querySelectorAll('.dowork .choice-box img').forEach((i: anyObj) => {
i?.addEventListener('click', (e: any) => {
handleShow(i?.src)
e?.stopPropagation?.()
})
})
document.querySelectorAll('.dowork .answer-box-main img').forEach((i: anyObj) => {
i?.addEventListener('click', () => {
handleShow(i?.src)
})
})
document.querySelectorAll('.dowork .analyze-title img').forEach((i: anyObj) => {
i?.addEventListener('click', () => {
handleShow(i?.src)
})
})
if (props.reportFlag) {
const { data: res } = await homeWorkApi.getJobStatistics({
titleId: props.data.id,
jobId: props.data.jobId,
stuId: route.query.stuId || '',
})
jobStuStatistics.value = res.data?.map((i: anyObj) => i.scStuList) || []
echartsData.xData = []
echartsData.seriesVal = []
res.data?.forEach((i: anyObj) => {
echartsData.xData.push(i.intervalValue + '分')
echartsData.seriesVal.push({
value: i.num,
itemStyle: {
color: i.curFlag ? '#FE8200' : '#7FADFF',
},
})
})
nextTick(() => {
echartsRef.value?.drawGraph()
})
}
//
if (props.data?.fillBlankNum) {
quizList.value = []
document
.querySelectorAll('.quizPutTag[contenteditable]')
.forEach((i: anyObj, idx: number) => {
i.setAttribute('contenteditable', false)
i.innerText = props.data.submitAnswer?.split(',')?.[idx] || ''
quizList.value.push(i)
})
}
}
},
{ immediate: true },
)
const xlDia = reactive<{ show: boolean; data: anyObj[] }>({
show: false,
data: [],
})
function handleBarClick(params: anyObj) {
const arr = jobStuStatistics.value[params.dataIndex]
if (arr?.length) {
xlDia.show = true
xlDia.data = arr.map((i: anyObj) => ({
id: i.id,
name: i.stuName,
}))
}
}
;(() => {
nextTick(() => {
audioPlayer = useAudio({
autoplay: false,
})
audioPlayer.on('play', () => {
isAudioPlay.value = true
})
audioPlayer.on('pause', () => {
isAudioPlay.value = false
})
audioPlayer.on('ended', () => {
isAudioPlay.value = false
})
})
})()
onUnmounted(() => {
audioPlayer?.destroy?.()
isAudioPlay.value = false
})
</script>
<template>
<div class="question-item">
<div class="left hidden_scroll">
<div class="left-subType">{{ idx }}. ({{ data?.score }}) {{ data?.channelTypeName }}</div>
<div v-if="data?.title" class="left-title" v-html="filterTitle(data?.title)"></div>
<div v-if="data?.voiceUrl" class="left-voice">
<Horn :active="isAudioPlay" @click="playAudio"></Horn>
</div>
<div v-if="data?.children?.length" class="left-children">
<template v-for="(c, idx) in data?.children" :key="c.id">
<div class="left-title" v-html="filterTitle(c?.title, idx)"></div>
</template>
</div>
</div>
<div ref="rightRef" class="right hidden_scroll">
<!-- 批改结构 -->
<div class="result-box">
<div v-if="isDeadline && reportFlag" :class="['result', resultClass(data).class]">
<div>我的答案</div>
<div class="emoji-box">
<div class="emoji">
<Icon
v-if="resultClass(data).icon"
:name="resultClass(data).icon || ''"
width="48"
></Icon>
</div>
{{ resultClass(data).text }}
</div>
</div>
</div>
<!-- 答题区域 -->
<component
:is="component"
:reportFlag="Number(reportFlag)"
class="answer-com"
:titleId="data?.id"
:optionList="data?.optionList || undefined"
:answer="data?.answer"
:submitAnswer="data?.submitAnswer"
:submitAnswerPic="data?.submitAnswerPic"
:submitAnswerPicOriginal="data?.submitAnswerPicOriginal"
:isAndroid="data?.isAndroid"
:markAnswerPic="data?.markAnswerPic"
:markAnswer="data?.markAnswer"
:fillBlankNum="data?.fillBlankNum"
:isDeadline="isDeadline"
:uploadLoading="data?.uploadLoading"
:uploadFile="uploadFile"
:beforeUpload="beforeUpload"
@submit="
emit('submit', {
...$event,
id: data?.jobStuTittleId,
})
"
@handleTime="emit('handleTime', $event)"
@showPic="handleShow"
@inputChange="inputChange"
>
<template #markAnswer>
<div v-if="isDeadline && reportFlag && data?.markAnswer" class="markAnswerPic">
<div class="answer-box">
<div class="answer-box-title">批改说明</div>
</div>
<div class="text-box">
{{ data?.markAnswer }}
</div>
</div>
</template>
</component>
<!-- 答案与解析 -->
<template v-if="isDeadline && reportFlag">
<div v-if="echartsData.xData.length" class="answer-box">
<div class="answer-box-title">答题统计</div>
<div class="answer-box-echarts">
<EchartsBar
ref="echartsRef"
:data="echartsData"
yName="(人)"
@handleBarClick="handleBarClick"
></EchartsBar>
</div>
</div>
<div v-if="data?.answer" class="answer-box">
<div class="answer-box-title">参考答案</div>
<div class="answer-box-main" v-html="data?.answer"></div>
</div>
<!-- 暂时隐藏 data.titleAnalyzeJson -->
<div
v-if="data?.titleAnalyze || data?.titleAnalyzePicture"
class="answer-box"
style="margin-top: 60px"
>
<div class="answer-box-title">解析</div>
<div v-if="data?.titleAnalyze" class="answer-box-main analyze analyze-title">
<div class="titleAnalyze" v-html="data?.titleAnalyze"></div>
</div>
<div v-if="data?.titleAnalyzePicture" class="answer-box-main analyze analyze-pic">
<div
v-for="i in data.titleAnalyzePicture.split(',')"
:key="i"
class="titleAnalyzePicture"
@click="handleShow(i)"
>
<img :src="i" />
</div>
</div>
</div>
</template>
</div>
<ImgView v-model="imgDia.show" :url="imgDia.url"></ImgView>
<XLDia v-model="xlDia.show" title="对应学员">
<div class="stu_list">
<div v-for="i in xlDia.data" :key="i.id" class="stu_list-item">
{{ i.name }}
</div>
</div>
</XLDia>
</div>
</template>
<style scoped lang="scss">
.question-item {
box-sizing: border-box;
width: 100%;
height: 100%;
padding: 2.67vh;
display: flex;
align-items: center;
&::before {
position: absolute;
left: 50%;
bottom: 2.67vh;
transform: translate(-50%);
content: '';
display: block;
width: 6px;
height: calc(100% - 64px);
border-radius: 0.5vh;
background: repeating-linear-gradient(
to bottom,
#cdd5ff,
#cdd5ff 8px,
transparent 10px,
transparent 20px /* 线条间隔 */
);
}
.left {
width: calc(50% - 3px);
height: 100%;
padding-right: 48px;
// word-break: break-all;
word-break: normal;
word-wrap: break-word;
overflow-y: auto;
&-subType {
color: #666;
font-family: 'OPPO Sans';
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
margin-bottom: 2vh;
}
&-title {
padding-left: 40px;
color: $font-color;
font-family: 'OPPO Sans';
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
// line-height: 48px; /* 150% */
line-height: 1.8em;
// overflow-x: hidden;
}
&-voice {
padding-left: 40px;
margin-top: 2vh;
}
&-children {
padding-left: 40px;
color: $font-color;
font-family: 'OPPO Sans';
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
// &-item {
// display: flex;
// flex-wrap: wrap;
// }
margin-top: 1.33vh;
.left-title {
padding-left: 0;
}
}
}
.right {
width: calc(50% - 3px);
height: 100%;
padding-left: 48px;
padding-right: 40px;
padding-top: 1.33vh;
.result-box {
width: 100%;
height: 4vh;
margin-bottom: 2vh;
}
.result {
padding: 0 20px 0 32px;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
color: $font-color;
font-family: $font-special;
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
border-radius: 0.33vh;
&::before {
content: '';
width: 12px;
height: 100%;
flex-shrink: 0;
position: absolute;
top: 0;
left: 0;
background-color: #669cfe;
border-radius: 0 1vh 1vh 0;
}
&.error {
background: linear-gradient(90deg, rgba(255, 191, 191, 0) 0%, #ffbfbf 100%);
}
&.success {
background: linear-gradient(90deg, rgba(221, 255, 140, 0) 0%, #ddff8c 100%);
}
&.empty {
background: linear-gradient(90deg, rgba(255, 191, 191, 0) 0%, #ffbfbf 100%);
}
&.wait {
background: linear-gradient(90deg, rgba(190, 213, 254, 0) 0%, #bed5fe 100%);
}
&.info {
background: linear-gradient(90deg, rgba(190, 213, 254, 0) 0%, #bed5fe 100%);
}
.emoji-box {
display: flex;
align-items: center;
.emoji {
width: 48px;
height: 4vh;
margin-right: 16px;
}
}
}
.answer-com {
// max-height: calc(100% - 100px);
}
.answer-box {
margin-top: 4vh;
&-title {
padding: 0 20px 0 32px;
position: relative;
color: $font-color;
// font-family: $font-special;
font-family: 'OPPO Sans';
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
border-radius: 0.33vh;
margin-bottom: 2vh;
&::before {
content: '';
width: 12px;
height: 100%;
flex-shrink: 0;
position: absolute;
top: 0;
left: 0;
background-color: #669cfe;
border-radius: 0 1vh 1vh 0;
}
}
&-echarts {
height: 28.33vh;
}
&-main {
color: $font-color;
font-family: 'OPPO Sans';
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
padding-left: 32px;
box-sizing: border-box;
width: 100%;
&.analyze {
// padding-left: 0;
width: 100%;
&.analyze-pic {
margin-top: 2vh;
img {
max-width: 100%;
max-height: 66.67vh;
object-fit: contain;
}
.titleAnalyzePicture {
margin-bottom: 1vh;
&:last-child {
margin-bottom: 0;
}
}
}
&.analyze-title {
margin-top: 2vh;
.titleAnalyze {
color: $font-color;
font-family: 'OPPO Sans';
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
}
}
}
}
}
.text-box {
// color: $font-color;
// font-family: $font-special;
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
padding-left: 48px;
}
}
}
.xl-dia {
:deep(.xl-dia-main) {
padding: 2.33vh;
}
.stu_list {
display: flex;
align-items: start;
flex-wrap: wrap;
height: 100%;
@include hidden_scroll;
&-item {
border-radius: 3.5vh;
border: 0.17vh solid $font-color;
padding: 1.33vh 32px;
color: $font-color;
text-align: center;
font-family: OPPOSans;
font-size: 2.67vh;
margin: 1vh;
}
}
}
</style>

View File

@ -1,584 +0,0 @@
<script setup lang="ts">
import homeWorkApi from '@/api/system/homeWork'
import globalApi from '@/api/global'
import { debounce, XMessage } from '@/hooks'
// import Question from './components/Question.vue'
import dayjs from 'dayjs'
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'
import { $XXL, getDurationText } from '@/utils'
import { loading } from '@/components/mj/mj-loading'
import { userStore } from '@/stores'
import type { IQuestion } from '@/types/question'
import { globalBack } from '@/router'
import { dialog } from '@/components/mj/mj-dialog'
defineOptions({
name: 'HomeWorkDoWork',
})
type PaperInfo = {
name: string
costTime: number
currentTitleId: string
}
const { token } = userStore()
const router = useRouter()
const route = useRoute()
const pageLoading = ref(true)
// 0 1
const reportFlag = ref(0)
//
interface WorkQuestion extends IQuestion {
/** 还未做过这道题 */
unLook: boolean
/** 上传中 */
uploadLoading: boolean
}
const paperList = ref<WorkQuestion[]>([])
//
const paperInfo = ref<PaperInfo>({} as PaperInfo)
const isDeadline = ref(false)
//
const activeIdx = ref(-1)
const indicatorRef = ref()
//
async function switchQuestion(idx?: number) {
if (idx === activeIdx.value) return
//
const lastIdx = paperList.value.length - 1
//
let nextQuestion = false
if (idx !== undefined) {
activeIdx.value = idx
} else if (activeIdx.value < lastIdx) {
//
activeIdx.value++
nextQuestion = true
} else if (activeIdx.value === lastIdx) {
if (!reportFlag.value) {
//
if (paperList.value.some(i => i.uploadLoading)) {
return XMessage.error('上传中,请稍后~')
}
let tips = '请确认是否提交本次作业'
if (paperList.value.some(i => !i.submitAnswer && !i.submitAnswerPic)) {
tips = '还有题目未作答,确定要提交答案吗?'
}
dialog.show({
content: tips,
ok: handleSubmitPaper,
})
}
}
if (!reportFlag.value) {
paperList.value[activeIdx.value].unLook = false
//
//
// const { id, jobStuId } = paperList.value[activeIdx.value]
// submitCurrentTitleId({
// jobStuId,
// currentTitleId: id,
// costTime: paperInfo.value.costTime * 1000
// })
}
//
await nextTick()
if (nextQuestion && indicatorRef.value) {
indicatorRef.value.scrollLeft += 104
}
}
const debounceChange = debounce(switchQuestion, 200)
function getResourceType() {
return route.query.resource
}
//
function beforeUpload(rawFile: any) {
const fileType = ['image/jpeg', 'image/png']
if (!fileType.includes(rawFile.type)) {
XMessage.error('上传格式不正确只支持JPG/PNG')
return false
}
if (rawFile.size / 1024 / 1024 > 5) {
XMessage.error('图片过大')
return false
}
return true
}
//
async function uploadFile(options: anyObj) {
const currentTitleId = paperList.value[activeIdx.value].jobStuTittleId
paperList.value[activeIdx.value].uploadLoading = true
try {
let path = ''
if (options.isAndroid) {
if (options.submitAnswerPicOriginal) {
const res = await $XXL.uploadFile({
url: `${import.meta.env.VITE_SERVER_URL}/sysFileInfo/tenUploadAll`,
header: {
Authorization: token,
},
filePath: options.file,
fileKey: 'file',
})
path = res.url
}
} else {
const formData = new FormData()
formData.append('file', options.file)
const { data: res } = await globalApi.uploadFile(formData)
path = res.data.filePath
}
if (options.submitAnswerPicOriginal) {
handleSubmitSingle({
submitAnswerPic: path,
submitAnswerPicOriginal: options.submitAnswerPicOriginal,
id: currentTitleId,
isAndroid: options.isAndroid || 0,
})
} else {
if (options.isAndroid) {
handleSubmitSingle({
submitAnswerPic: '',
submitAnswerPicOriginal: '',
id: currentTitleId,
isAndroid: 1,
})
} else {
handleSubmitSingle({ filePath: path, id: currentTitleId })
}
}
} catch (error) {
XMessage.error('网络异常,请检查网络~')
}
const currentIdx = paperList.value.findIndex(i => i.jobStuTittleId === currentTitleId)
if (currentIdx !== -1) {
paperList.value[currentIdx].uploadLoading = false
paperList.value[currentIdx].isAndroid = options.isAndroid
}
}
//
async function handleSubmitSingle(answer: any) {
if (reportFlag.value) return
const params: anyObj = {
id: answer.id,
isAndroid: answer.isAndroid,
}
if (answer.filePath) {
//
const idx = paperList.value.findIndex(i => i.jobStuTittleId === answer.id)
if (idx !== -1) {
const safeUrl = String(answer.filePath || '')
.replace(/[`]/g, '')
.replace(/http:\/\//gi, 'https://')
.trim()
const q = paperList.value[idx]
const isHandDrawn =
q?.autoMarkFlag === 0 &&
(q?.titleChannelTypeName === '填空题' ||
(q?.subjectId === 3 && q?.titleChannelTypeName === '解答题'))
const submitPic = isHandDrawn
? safeUrl
: q.submitAnswerPic || ''
? `${q.submitAnswerPic},${safeUrl}`
: safeUrl
paperList.value[idx].submitAnswerPic = submitPic
params.submitAnswerPic = submitPic
}
} else if (answer.submitAnswer) {
paperList.value[activeIdx.value].submitAnswer = answer.submitAnswer
params.submitAnswer = answer.submitAnswer
} else if (answer.delUrl) {
//
const picList = paperList.value[activeIdx.value].submitAnswerPic.split(',') || []
const idx = picList.indexOf(answer.delUrl)
picList.splice(idx, 1)
params.submitAnswerPic = picList.join(',')
paperList.value[activeIdx.value].submitAnswerPic = params.submitAnswerPic
} else if (answer.submitAnswerPic) {
//
const idx = paperList.value.findIndex(i => i.jobStuTittleId === answer.id)
paperList.value[idx].submitAnswerPic = answer.submitAnswerPic
paperList.value[idx].submitAnswerPicOriginal = answer.submitAnswerPicOriginal
params.submitAnswerPic = answer.submitAnswerPic
params.submitAnswerPicOriginal = answer.submitAnswerPicOriginal
} else if (answer.submitAnswerPic === '' && answer.isAndroid) {
//
const idx = paperList.value.findIndex(i => i.jobStuTittleId === answer.id)
paperList.value[idx].submitAnswerPic = ''
paperList.value[idx].submitAnswerPicOriginal = ''
params.submitAnswerPic = ''
params.submitAnswerPicOriginal = ''
}
//
if (answer.toNext && activeIdx.value < paperList.value.length - 1) setTimeout(switchQuestion, 50)
await homeWorkApi.submitAnswer(params)
}
let timeout: any
function startTime() {
clearInterval(timeout)
timeout = setInterval(() => {
paperInfo.value.costTime++
}, 1000)
}
const timeFilter = computed(() => getDurationText(paperInfo.value.costTime))
function handleTime(type: 0 | 1) {
if (type) {
startTime()
} else {
clearInterval(timeout)
}
}
async function handleSubmitPaper() {
clearInterval(timeout)
const { jobId, jobStuId } = paperList.value[activeIdx.value]
loading.show('正在提交中...')
try {
await homeWorkApi.submitPaper({
id: jobStuId,
jobId,
costTime: paperInfo.value.costTime * 1000,
})
//
leave.value = true
//
router.replace('/system/homeWork/reports/' + jobId)
} finally {
loading.hide()
}
}
//
function hasSubmitAnswer(question: WorkQuestion) {
return question.submitAnswer || question.submitAnswerPic || question.submitAnswerPicOriginal
}
//
async function getPaper(options: any) {
//
const { data } = await homeWorkApi.getJobTittle(options)
paperInfo.value = {
name: data.paperName,
costTime: data.costTime / 1000,
currentTitleId: data.currentTitleId,
}
const oldQuestions = data.tjobStuVoList
//
isDeadline.value = dayjs().isAfter(data.submitEndTime)
if (!reportFlag.value) {
//
const answeredQuestions: WorkQuestion[] = []
const unansweredQuestions: WorkQuestion[] = []
let idx = 0
for (const question of oldQuestions) {
if (hasSubmitAnswer(question)) {
answeredQuestions.push(question)
idx += 1
} else {
question.unLook = true
unansweredQuestions.push(question)
}
}
paperList.value = [...answeredQuestions, ...unansweredQuestions]
return Math.min(idx, paperList.value.length - 1)
} else {
paperList.value = oldQuestions
return 0
}
}
const leave = ref(true)
onBeforeRouteLeave((to, _from, next) => {
if (leave.value || to.path === '/login' || reportFlag.value) {
return next()
}
next(false)
dialog.show({
content: '还没有提交作业,是否确认退出?',
ok: () => {
leave.value = true
globalBack()
},
})
})
onMounted(async () => {
const options: anyObj = route.query || {}
if (!options?.id) {
// id
globalBack()
return
}
//
reportFlag.value = Number(options.reportFlag)
//
pageLoading.value = true
const currentIdx = await getPaper(options)
pageLoading.value = false
//
leave.value = false
//
switchQuestion(options?.idx ? Number(options?.idx) : currentIdx)
if (!reportFlag.value) {
startTime()
}
await nextTick()
if (indicatorRef.value) {
indicatorRef.value.scrollLeft = ((options?.idx ? Number(options?.idx) : currentIdx) - 1) * 104
}
//
$XXL.setScreenOn(1)
})
onUnmounted(() => {
paperInfo.value.costTime = 0
activeIdx.value = 0
paperList.value = []
paperInfo.value = {} as PaperInfo
// showQuesImgDia.value = false;
// quesImg.value = '';
// showDia.value = false
// leaveFlag = false;
clearInterval(timeout)
})
</script>
<template>
<div class="dowork g-main-bg">
<div class="title-box">
<div class="title" :data-content="paperInfo.name">
{{ paperInfo.name }}
</div>
</div>
<div class="top">
<BackBar />
<div v-if="!pageLoading" class="top-timeout">
<Icon name="clock" width="48"></Icon>
{{ timeFilter }}
</div>
</div>
<div v-if="!pageLoading" class="main">
<div class="main-box">
<div class="book-box">
<div class="book">
<Question
:data="paperList[activeIdx]"
:idx="activeIdx + 1"
:is-homework="true"
:total="paperList.length"
:reportFlag="reportFlag"
:isDeadline="isDeadline"
:resource-type="getResourceType()"
:uploadFile="uploadFile"
:beforeUpload="beforeUpload"
@submit="handleSubmitSingle"
@handleTime="handleTime"
></Question>
</div>
</div>
<div class="bottom">
<div ref="indicatorRef" :class="{ indicator: true, hidden_scroll: true }">
<div
v-for="(i, idx) in paperList"
v-show="!i.unLook"
:key="i.id"
:class="{
'indicator-item': true,
pointer: true,
active: activeIdx === idx,
success: isDeadline && reportFlag && i.markFlag,
error: isDeadline && reportFlag && !i.markFlag && i.titleStatus === 3,
empty: !i.submitAnswer && !i.submitAnswerPic,
}"
@click="switchQuestion(idx)"
>
{{ idx + 1 }}
</div>
</div>
<Btn
v-if="!reportFlag"
class="btn"
type="success"
width="192"
height="80"
size="32"
@click="debounceChange()"
>
{{ activeIdx < paperList.length - 1 ? '下一题' : '提交' }}
</Btn>
</div>
</div>
</div>
<mj-loading v-else v-model="pageLoading" :fullscreen="true" text="正在加载试卷中..." />
</div>
</template>
<style scoped lang="scss">
$filter: brightness(0) saturate(0) invert(0) sepia(0) hue-rotate(30deg);
.dowork {
position: relative;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
.title-box {
position: absolute;
left: 0;
right: 0;
top: 2.92vh;
.title {
color: #fff;
text-shadow: 0 0.17vh 0.17vh rgba(0, 0, 0, 0.25);
font-family: $font-special;
font-size: 4vh;
font-style: normal;
font-weight: 400;
line-height: 6vh; /* 150% */
text-align: center;
letter-spacing: 10px;
@include font-stroke($font-color, 6px);
}
}
.top {
padding: 2.67vh 64px 0;
position: relative;
z-index: 1;
display: flex;
justify-content: space-between;
align-items: center;
&-timeout {
color: $font-color;
text-align: center;
text-shadow: 0 0.17vh 0.17vh rgba(0, 0, 0, 0.25);
font-family: $font-special;
font-size: 2.67vh;
height: 4vh;
line-height: 4vh;
display: flex;
.x-icon {
margin-right: 10px;
filter: $filter;
}
}
}
.main {
box-sizing: border-box;
padding: 0 32px;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
&-box {
position: relative;
top: 10.5vh;
}
.book-box {
position: relative;
height: 80vh;
padding-top: 1.25vh;
}
.book {
box-sizing: border-box;
position: relative;
z-index: auto;
height: 80vh;
flex-shrink: 0;
border-radius: 3.33vh;
padding-bottom: 5.83vh;
}
}
.bottom {
display: flex;
align-items: center;
width: 100%;
padding: 0 32px;
.indicator {
flex: 1;
display: flex;
overflow-x: auto;
padding: 0.83vh 0;
&-item {
box-sizing: border-box;
width: 80px;
height: 6.67vh;
background: #bed5fe;
// border: 4px solid #fff;
border-radius: 50%;
margin-right: 24px;
color: $font-color;
text-align: center;
font-family: $font-special;
font-size: 3.33vh;
font-style: normal;
font-weight: 400;
line-height: 6.67vh; /* 150% */
text-align: center;
flex-shrink: 0;
box-shadow: 0 0.33vh 0.33vh 0 rgba(0, 0, 0, 0.25);
&:last-child {
margin-right: 0;
}
&.error {
background: #febebf;
border-color: #f00809;
color: #f00809;
}
&.success {
background: #ddff8c;
border-color: #8ecc00;
color: #8ecc00;
}
&.empty {
background: #cccccc;
color: #ffffff;
border-color: #aaa;
}
&.active {
background: #ebf1fd;
border: 0.33vh solid #7eacfe;
line-height: 6vh;
&.empty {
color: #7eacfe;
}
}
}
}
.btn {
margin-left: 62px;
}
}
}
</style>

View File

@ -1,473 +0,0 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { rateType } from '@/hooks'
import homeWorkApi from '@/api/system/homeWork'
import { useRouter } from 'vue-router'
import { userStore } from '@/stores'
import { storeToRefs } from 'pinia'
const router = useRouter()
const OSS_URL = import.meta.env.VITE_OSS_URL
const { userInfo } = storeToRefs(userStore())
const colorConfig = {
// progerss_border progress_bg progress_active
primary: ['#2D39BC', '#2D39BC', '#6B73E5', '#90FCF7'],
success: ['#2F8A5B', '#27A363', '#25BF6E', '#1FFF8A'],
error: ['#BC2D37', '#B22731', '#CB3741', '#FF3542'],
warning: ['#a13c00', '#C08129', '#CC8A33', '#FFFE36'],
}
const rateConfig = computed(() => {
return (rate: number) => {
const r = Number(rate) * 100
return {
color: colorConfig[rateType.value(r) || 'primary'][0],
border: colorConfig[rateType.value(r) || 'primary'][1],
bg: colorConfig[rateType.value(r) || 'primary'][2],
activeBg: colorConfig[rateType.value(r) || 'primary'][3],
}
}
})
//
const activeStatus = ref('')
function handleChangeStatus() {
refreshJobList()
}
//
const classList = ref<any[]>([])
async function getClassList() {
const { data: res } = await homeWorkApi.getSubjectList({ statusList: [1, 2, 3], historyFlag: 1 })
if (res.code === 200) {
classList.value = res.data
}
}
//
const activeClass = ref<number>(0)
function handleSelectClass(_item: any) {
refreshJobList()
}
//
const jobList = ref<any[]>([])
const page = reactive({
pageSize: 4,
pageNum: 1,
total: -1,
})
const listLoading = ref(false)
async function getJobList() {
const { data: res } = await homeWorkApi.getJobList({
statusList: activeStatus.value ? (activeStatus.value === '3' ? [3] : [1, 2]) : [1, 2, 3],
...page,
subjectId: activeClass.value,
historyFlag: 1,
})
if (res.code === 200) {
jobList.value = res.rows
page.total = res.total
}
}
const pageError = ref(false)
async function changePage(type: 0 | 1) {
pageError.value = false
const { total, pageNum, pageSize } = page
if (type) {
if (pageNum * pageSize >= total) return
page.pageNum++
} else {
if (pageNum <= 1) return
page.pageNum--
}
try {
listLoading.value = true
jobList.value = []
await getJobList()
} catch {
pageError.value = true
}
listLoading.value = false
}
async function refreshJobList() {
pageError.value = false
listLoading.value = true
jobList.value = []
page.pageNum = 1
try {
await getJobList()
} catch {
pageError.value = true
}
listLoading.value = false
}
const pageLoading = ref(false)
onActivated(async () => {
pageLoading.value = true
listLoading.value = true
jobList.value = []
page.pageNum = 1
try {
await getClassList()
if (classList.value.length) {
if (classList.value.every(i => i.id !== activeClass.value)) {
activeClass.value = classList.value[0]?.id
}
await getJobList()
}
} catch {}
listLoading.value = false
pageLoading.value = false
})
function toReports(item: any) {
router.push(`/system/homeWork/reports/${item.id}`)
}
//
const isDeadline = computed(() => (time: string) => dayjs().isAfter(time))
</script>
<template>
<div class="uncomplete">
<div class="top">
<BackBar leftText="作业记录" />
<Select
v-model="activeStatus"
:selectList="[
{ label: '全部', value: '' },
{ label: '已批改', value: '3' },
{ label: '未批改', value: '2' },
]"
@selectChange="handleChangeStatus"
></Select>
</div>
<div v-if="classList.length" class="main">
<div class="main-sidebar">
<Sidebar
v-model="activeClass"
:config="classList"
:prop="{ name: 'subject' }"
@handle-change="handleSelectClass"
></Sidebar>
</div>
<div class="list-box">
<template v-if="jobList.length">
<div class="list hidden_scroll">
<div ref="scrollRef">
<div v-for="i in jobList" :key="i.id" class="list-item pointer" @click="toReports(i)">
<div class="list-item-info">
<div class="list-item-title">{{ i.name }}</div>
<div class="list-item-name">{{ i.paperName }}</div>
<div class="list-item-time">
{{
i.createTime && `发布时间:${dayjs(i.createTime).format('YYYY/MM/DD HH:mm')}`
}}
&nbsp;&nbsp;
{{
i.submitEndTime &&
`截止时间:${dayjs(i.submitEndTime).format('YYYY/MM/DD HH:mm')}`
}}
&nbsp;&nbsp;
{{ i.markTime && `批改时间:${dayjs(i.markTime).format('YYYY/MM/DD HH:mm')}` }}
</div>
<div
v-if="i.status === 3 && isDeadline(i.submitEndTime)"
class="list-item-rate"
:style="{ color: rateConfig(i.scoreRate).color }"
>
得分率{{ i.scoreRate }}%
<ProgressBar
:rate="i.scoreRate / 100"
width="196"
v-bind="rateConfig(i.scoreRate)"
></ProgressBar>
<div class="reward-list">
<div v-if="i.addDiamond" class="reward-list-item">
<ItemPopover
:data="{
iconName: 'reward_diamond',
rewardName: '钻石',
personalCount: userInfo.diamond,
}"
>
<Icon name="diamond_nobg" width="48" @click.stop="false"></Icon>
</ItemPopover>
<span class="text">+{{ i.addDiamond }}</span>
</div>
<div v-if="i.addExp" class="reward-list-item">
<ItemPopover
:data="{
iconName: 'reward_exp',
rewardName: '经验值',
personalCount: userInfo.experience,
}"
>
<Icon name="exp_nobg" width="48" @click.stop="false"></Icon>
</ItemPopover>
<span class="text">+{{ i.addExp }}</span>
</div>
</div>
</div>
</div>
<div
:class="{ 'list-item-status': true, active: i.status === 3 }"
:style="{
'background-image':
i.status === 3
? `url(${OSS_URL}/urm/homeWork/corrected.svg)`
: `url(${OSS_URL}/urm/homeWork/uncorrected.svg)`,
}"
>
{{ i.status === 3 ? '已批改' : '未批改' }}
</div>
</div>
</div>
</div>
<div class="page">
<Btn v-if="page.pageNum > 1" width="162" height="80" tilt @click="changePage(0)">
上一页
</Btn>
<div>{{ page.pageNum }}/{{ Math.ceil(page.total / page.pageSize) }}</div>
<Btn
v-if="page.pageNum * page.pageSize < page.total"
type="success"
width="162"
height="80"
tilt
@click="changePage(1)"
>
下一页
</Btn>
</div>
</template>
<Empty
v-else
class="empty"
:type="listLoading ? 'loading' : pageError ? 'network' : 'learn'"
:content="pageError ? '网络出问题了啦' : '暂无作业记录,快去做作业吧!'"
></Empty>
</div>
</div>
<Empty
v-else
class="empty"
:type="pageLoading ? 'loading' : 'learn'"
content="暂无作业记录,快去做作业吧!"
></Empty>
</div>
</template>
<style scoped lang="scss">
.uncomplete {
@include main_bg;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
.top {
padding: 3vh 64px 0;
// margin-bottom: 24px;
display: flex;
justify-content: space-between;
align-items: flex-end;
// &-menu {
// display: flex;
// justify-content: space-between;
// align-items: center;
// // flex: 1;
// padding-left: 80px;
// }
}
.main {
// display: flex;
height: calc(100% - 130px);
padding-left: 64px;
padding-right: 64px;
padding-top: 2.67vh;
position: relative;
&-sidebar {
position: absolute;
left: 64px;
top: 2.67vh;
height: calc(100% - 66px);
width: 272px;
}
.list-box {
height: 100%;
margin-left: 310px;
overflow: hidden;
.page {
margin-top: 0.83vh;
display: flex;
align-items: center;
color: #666;
font-family: $font-special;
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh;
justify-content: flex-end;
:deep(.x-button) {
box-shadow: none;
margin: 0 40px;
&.primary {
margin-right: 80px;
}
}
}
}
.list {
height: calc(100% - 90px);
overflow-y: auto;
&-item {
display: flex;
align-items: center;
border-radius: 1.67vh;
background: #fff;
overflow: hidden;
margin-bottom: 1.33vh;
&:last-child {
margin-bottom: 0;
}
&-info {
width: calc(100% - 230px);
position: relative;
}
&-title {
// display: inline-block;
padding: 0 8px;
height: 6.67vh;
line-height: 6.67vh;
background: #7effff;
margin-bottom: 2vh;
width: 500px;
box-sizing: border-box;
text-align: center;
// max-width: 100%;
color: $font-color;
font-family: $font-special;
font-size: 2.5vh;
font-style: normal;
font-weight: 400;
border-top-left-radius: 20px;
@include single_ellipsis;
}
&-name {
color: #666;
font-family: 'OPPO Sans';
font-size: 2.33vh;
font-style: normal;
font-weight: 400;
line-height: 3.5vh; /* 150% */
padding-left: 32px;
width: 100%;
margin-bottom: 1.33vh;
@include single_ellipsis;
}
&-time {
color: #999;
font-family: 'OPPO Sans';
font-size: 2vh;
font-style: normal;
font-weight: 400;
line-height: 3vh; /* 150% */
padding-left: 32px;
padding-bottom: 2vh;
}
&-status {
width: 216px;
height: 8vh;
// background: url('~@/static/unity/schoolJob/uncorrected.svg') no-repeat;
background-repeat: no-repeat;
background-size: 100%;
color: $font-color;
font-family: $font-special;
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 8vh; /* 150% */
text-align: center;
// &.active {
// background: url('~@/static/unity/schoolJob/corrected.svg') no-repeat;
// }
}
&-rate {
display: flex;
align-items: center;
position: absolute;
left: 516px;
top: 1.83vh;
font-family: $font-special;
font-size: 2.33vh;
line-height: 3vh;
.progress-bar {
margin-left: 12px;
}
}
.reward-list {
display: flex;
align-items: center;
justify-content: center;
margin-left: 12px;
&-item {
display: flex;
align-items: center;
margin: 0 8px;
// color: $font-color;
font-family: $font-special;
font-size: 2.33vh;
.x-icon {
margin-right: 8px;
}
}
}
}
}
}
.empty {
height: calc(100% - 156px);
position: relative;
.box {
position: absolute;
top: 22.08vh;
left: 50%;
transform: translateX(-50%);
text-align: center;
}
img {
width: 320px;
height: 26.67vh;
}
.text {
color: #afbfcf;
text-align: center;
font-family: $font-special;
font-size: 2.33vh;
font-style: normal;
font-weight: 400;
line-height: 3.5vh; /* 150% */
}
}
}
.info-time {
color: #999;
font-family: 'PingFang SC';
font-size: 1.67vh;
font-style: normal;
font-weight: 400;
line-height: 2.5vh; /* 150% */
margin-top: 0.5vh;
span {
margin-right: 24px;
}
}
</style>

View File

@ -1,506 +0,0 @@
<script setup lang="ts">
import LoadIcon from '@/page/system/mailbox/components/LoadIcon.vue'
import homeWorkApi from '@/api/system/homeWork'
import { debounce } from '@/hooks'
import dayjs from 'dayjs'
import { useRouter } from 'vue-router'
import { $XXL } from '@/utils'
const router = useRouter()
const OSS_URL = import.meta.env.VITE_OSS_URL
//
const classList = ref<any[]>([])
async function getClassList() {
const { data: res } = await homeWorkApi.getSubjectList({ statusList: [0] })
if (res.code === 200) {
classList.value = res.data.map((i: anyObj) => ({ ...i, warning: true }))
}
}
//
const activeClass = ref('')
async function handleSelectClass(_item: any) {
jobList.value = []
await getJobList()
handleSelectJob(jobList.value[0])
}
//
const jobList = ref<any[]>([])
const listLoading = ref(false)
const page = reactive({
pageSize: 10,
pageNum: 1,
total: -1,
})
async function getJobList() {
try {
listLoading.value = true
const res = await homeWorkApi.getJobList({
statusList: [0],
...page,
subjectId: activeClass.value,
})
jobList.value.push(...res.rows)
page.total = res.total
} finally {
listLoading.value = false
}
}
const bookSidebarRef = ref()
async function handleScroll(event: any) {
if (jobList.value.length >= page.total) return
if (listLoading.value) return
if (
event.target.scrollTop + event.target.offsetHeight >=
bookSidebarRef.value?.offsetHeight - 50
) {
page.pageNum++
try {
await getJobList()
} catch {
page.pageNum--
}
}
}
const debounceScroll = debounce(handleScroll, 200, false)
//
const jobData = ref<any>({})
function handleSelectJob(item: any) {
jobData.value = item
}
const pageLoading = ref(false)
const pageError = ref(false)
onActivated(async () => {
pageLoading.value = true
listLoading.value = true
jobList.value = []
page.pageNum = 1
$XXL.setScreenOn(1)
try {
await getClassList()
if (classList.value.length) {
if (classList.value.every(i => i.id !== activeClass.value)) {
activeClass.value = classList.value[0]?.id
}
await getJobList()
handleSelectJob(jobList.value[0])
}
} catch {
pageError.value = true
} finally {
pageLoading.value = false
listLoading.value = false
}
})
function to(url: string) {
router.push(url)
}
</script>
<template>
<div
class="uncomplete"
:style="{
'background-image': `url(${OSS_URL}/urm/mail_bg.svg)`,
}"
>
<div
class="book"
:style="{
'background-image': `url(${OSS_URL}/urm/book_bg.svg)`,
}"
>
<template v-if="jobList.length">
<div class="book-sidebar-box">
<div class="book-sidebar hidden_scroll" @scroll="debounceScroll">
<div ref="bookSidebarRef" class="scroll-box">
<div
v-for="i in jobList"
:key="i.id"
:class="{
'book-sidebar-item': true,
pointer: true,
active: jobData.id === i.id,
}"
:style="{
'background-image':
jobData.id === i.id
? `url(${OSS_URL}/urm/mailbox_sidebar_active_bg.svg)`
: i.readStatus
? `url(${OSS_URL}/urm/mailbox_sidebar_ready_bg.svg)`
: `url(${OSS_URL}/urm/mailbox_sidebar_bg.svg)`,
}"
@click="handleSelectJob(i)"
>
<!-- <div class="icon"></div> -->
<Icon
:name="jobData.id === i.id ? 'homeWork_job-item-active' : 'homeWork_job-item'"
class="icon"
width="84"
></Icon>
<div class="info">
<div class="info-title">{{ i.name }}</div>
<div class="info-time">
{{ dayjs(i.createTime).format('YYYY/MM/DD&nbsp;&nbsp;&nbsp;HH:mm') }}
</div>
</div>
</div>
</div>
</div>
<LoadIcon v-if="listLoading" style="margin-top: 0"></LoadIcon>
</div>
<div class="book-main">
<div class="info flex-end">
<div class="info-title">{{ jobData.name }}</div>
<div class="info-time">
发布日期
{{ dayjs(jobData.createTime).format('YYYY/MM/DD&nbsp;&nbsp;&nbsp;HH:mm') }}
</div>
</div>
<div class="info center">
<div class="info-name">
<span style="color: #999">来自</span>{{ jobData.teacherName }}
</div>
<div class="info-time">
截止日期
{{ dayjs(jobData.submitEndTime).format('YYYY/MM/DD&nbsp;&nbsp;&nbsp;HH:mm') }}
</div>
</div>
<div class="content">{{ jobData.content }}</div>
<div class="tab">
<div
class="tab-icon"
:style="{
'background-image': `url(${OSS_URL}/urm/homeWork/tab.svg)`,
}"
>
<Icon class="icon" name="homeWork_paper" width="32"></Icon>
组卷
</div>
</div>
<div class="content paper">{{ jobData.paperName }}</div>
<div class="btn-box">
<mj-button
width="386"
height="108"
size="48"
:type="jobData.currentTitleId ? 'success' : 'primary'"
@click="to(`/system/homeWork/doWork?id=${jobData.id}&reportFlag=${0}`)"
>
{{ jobData.currentTitleId ? '继续答题' : '开始答题' }}
</mj-button>
</div>
</div>
</template>
<Empty
v-else
class="empty"
:type="listLoading ? 'loading' : 'task'"
content="还没有作业哦~"
></Empty>
</div>
<div class="top">
<BackBar leftText="作业" />
<div class="top-history">
<mj-button type="success" tilt @click="to(`/system/homeWork/statistics`)"
>统计分析</mj-button
>
<mj-button
type="success"
tilt
@click="to(`/system/homeWork/wrong?subjectId=${activeClass}`)"
>错题本</mj-button
>
<!-- <Icon
name="homeWork_temporary"
width="120"
class="pointer"
@click="to(`/system/homeWork/temporary`)"
></Icon> -->
<Icon
name="homeWork_hisJob"
width="120"
class="pointer"
@click="to(`/system/homeWork/history`)"
></Icon>
</div>
</div>
<div v-if="classList.length" class="main">
<div class="main-sidebar">
<Sidebar
v-model="activeClass"
:config="classList"
:prop="{ name: 'subject' }"
@handle-change="handleSelectClass"
></Sidebar>
</div>
</div>
<Empty
v-else
class="empty"
:type="pageLoading ? 'loading' : 'task'"
content="还没有作业哦~"
></Empty>
</div>
</template>
<style scoped lang="scss">
.uncomplete {
height: 100vh;
overflow: hidden;
box-sizing: border-box;
position: relative;
background-repeat: no-repeat;
background-size: cover;
.book {
box-sizing: border-box;
position: fixed;
width: 133.5vh;
height: 94vh;
left: 300px;
top: 6.67vh;
background-repeat: no-repeat;
background-position: top left;
background-size: 100% 100%;
padding: 10vh 11.67vh 6.33vh 9vh;
display: flex;
z-index: 2;
&-sidebar {
width: 36.67vh;
height: 100%;
flex-shrink: 0;
&-item {
padding: 0.83vh;
display: flex;
height: 9.67vh;
flex-shrink: 0;
margin-bottom: 1.33vh;
background-color: #fff;
box-shadow: 0 0.33vh 0.17vh 0 rgba(0, 0, 0, 0.15);
background-repeat: no-repeat;
background-position: 26.17vh center;
background-size: 11.17vh, 13vh;
border: 0.5vh solid transparent;
&.unready {
background-color: #dcdff5;
}
&.active {
background-color: #fff;
border-color: rgb(255, 240, 31);
// background-position:
// center,
// 314px center;
// background-size: 100%, 134px, 156px;
}
&:last-child {
margin-bottom: 0;
}
.icon {
width: 7vh;
height: 7vh;
flex-shrink: 0;
background: #1c2a3a;
margin-right: 1.33vh;
border-radius: 50%;
}
.info {
width: calc(100% - 8.33vh);
&-title {
color: $font-color;
font-family: $font-text;
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
@include single_ellipsis;
}
&-time {
font-size: 2vh;
}
}
}
}
&-main {
margin-left: 5.83vh;
padding-top: 1.33vh;
width: calc(100% - 3.33vh);
.info {
padding: 1.33vh 0;
display: flex;
justify-content: space-between;
&.flex-end {
align-items: end;
border-bottom: 0.17vh solid rgba(0, 0, 0, 0.45);
}
&.center {
align-items: center;
border-bottom: 0.08vh solid rgba(0, 0, 0, 0.45);
}
&-title {
color: $font-color;
font-family: $font-special;
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 1.5;
width: 41.67vh;
margin-right: 1vh;
@include single_ellipsis;
}
&-name {
color: #999;
font-family: 'PingFang SC';
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 1.2;
// width: calc(100% - 400px);
width: 41.67vh;
margin-right: 1vh;
@include single_ellipsis;
}
&-time {
font-size: 2vh;
line-height: 3vh; /* 150% */
@include single_ellipsis;
}
}
.content {
color: #333;
font-family: $font-text;
font-size: 2.67vh;
font-style: normal;
font-weight: 400;
line-height: 4vh; /* 150% */
margin: 2.67vh 0;
text-indent: 2em;
height: 20vh;
// @include multiple_ellipsis(5);
@include hidden_scroll;
&.paper {
height: 8vh;
@include multiple_ellipsis(2);
}
}
.tab {
// padding-top: 32px;
&-icon {
box-sizing: border-box;
width: 19vh;
height: 4.33vh;
background-position: top left;
background-repeat: no-repeat;
background-size: 100%;
padding: 0.17vh 0 0.17vh 4.67vh;
color: $font-color;
font-family: 'PingFang SC';
font-size: 2.67vh;
font-style: normal;
font-weight: 500;
line-height: 4vh; /* 150% */
display: flex;
align-items: center;
.icon {
// width: 32px;
// height: 32px;
margin-right: 1.33vh;
}
}
}
.btn-box {
box-sizing: border-box;
padding-top: 1.33vh;
display: flex;
justify-content: flex-end;
.x-button {
font-family: $font-text;
}
}
.btn {
box-sizing: border-box;
padding-top: 1.5vh;
width: 32.17vh;
height: 9vh;
flex-shrink: 0;
border-radius: 5vh;
background-size: 100%;
box-shadow: 0 0.5vh 0.17vh 0 rgba(0, 0, 0, 0.25);
text-align: center;
color: $font-color;
text-align: center;
font-family: $font-text;
font-size: 4vh;
font-style: normal;
font-weight: 400;
line-height: 6vh; /* 150% */
}
}
.empty {
height: 100%;
}
}
.top {
padding: 3vh 5.33vh 0;
}
.top-history {
z-index: 2;
position: absolute;
top: 1.67vh;
right: 64px;
display: flex;
align-items: center;
.mj-button {
margin: 0 16px 0.67vh;
font-family: $font-text;
}
.x-icon {
margin-left: 16px;
}
}
.main {
height: calc(100% - 13vh);
padding-left: 64px;
padding-right: 20px;
position: relative;
&-sidebar {
position: absolute;
left: 64px;
top: 1vh;
height: calc(100% - 2.5vh);
width: 22.92vh;
}
}
.empty {
height: calc(100% - 25vh);
}
}
.info-time {
color: #999;
font-family: 'PingFang SC';
font-size: 1.67vh;
font-style: normal;
font-weight: 400;
line-height: 2.5vh; /* 150% */
margin-top: 0.5vh;
span {
margin-right: 24px;
}
}
</style>

View File

@ -1,261 +0,0 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { rateType } from '@/hooks'
import { colorConfig } from './hooks'
import EchartsPie from './EchartsPie.vue'
import EchartsBar from './EchartsBar.vue'
import { dialog } from '@/components/mj/mj-dialog'
const props = withDefaults(
defineProps<{
params: anyObj
jobAnalyse: anyObj
}>(),
{
jobAnalyse: () => ({}),
pageLoading: false,
},
)
const difficultyColor = [
{ name: '容易', color: '#8FCC00' },
{ name: '较易', color: '#48A3FE' },
{ name: '普通', color: '#FFD04D' },
{ name: '较难', color: '#FF8200' },
{ name: '困难', color: '#FF5151' },
]
const report = computed(() => props.jobAnalyse.jobReportVo)
const analyze = computed(() =>
props.jobAnalyse.paperChanelAnalyzeList.map((i: anyObj) => {
return {
value: i.titleCount,
name: i.chanelTypeName,
}
}),
)
const difficultyPie = computed(() =>
props.jobAnalyse.paperDifficultyAnalyzeList.map((i: anyObj) => {
return {
value: i.titleCount,
name: i.difficultyName,
itemStyle: {
color: difficultyColor.find(item => item.name === i.difficultyName)?.color || '#8FCC00',
},
}
}),
)
const difficultyBar = computed(() => {
const xData: string[] = []
const seriesVal: anyObj[] = []
props.jobAnalyse.paperDifficultyAnalyzeList.forEach((i: anyObj) => {
xData.push(`${i?.difficultyName}\n(${i?.score}分)`)
seriesVal.push({
value: i.score,
itemStyle: {
color: difficultyColor.find(item => item.name === i.difficultyName)?.color || '#8FCC00',
},
})
})
return {
xData,
seriesVal,
}
})
function showWorkPoints() {
dialog.show({
title: '作业内容',
content: report.value?.jobContent,
hideCancel: true,
hideClose: true,
})
}
</script>
<template>
<div class="analyse">
<div v-if="jobAnalyse.jobReportVo" class="analyse-main">
<div class="analyse-info analyse-item">
<div class="analyse-item-title">作业信息</div>
<div class="analyse-info-main">
<p>
<span>作业名称</span>
<span class="job-title">{{ report?.jobName }}</span>
<Icon name="report_detail" class="pointer" width="42" @click="showWorkPoints"></Icon>
</p>
<p>
<span>发布日期</span>
{{ dayjs(report?.releaseTime).format('YYYY/MM/DD HH:mm') }}
</p>
<p>
<span>截止日期</span>
{{ dayjs(report?.submitEndTime).format('YYYY/MM/DD HH:mm') }}
</p>
<p>
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
{{ report?.totalScore }}
</p>
<p>
<span>作答时长</span>
{{ report?.costTimeInfo }}
</p>
</div>
</div>
<div class="analyse-structure analyse-item">
<div class="analyse-item-title">试卷结构</div>
<div class="echarts-pie">
<div class="echarts-pie-item">
<EchartsPie :data="analyze" title="题型分布"></EchartsPie>
</div>
<div class="echarts-pie-item">
<EchartsPie :data="difficultyPie" title="难度分布"></EchartsPie>
</div>
</div>
<div class="echarts-bar">
<EchartsBar :data="difficultyBar" title="试卷难易度显示"></EchartsBar>
</div>
</div>
<div class="analyse-rank analyse-item">
<el-table
v-if="jobAnalyse.stuRankVoList?.length"
:data="jobAnalyse.stuRankVoList"
:row-class-name="({ row }) => (row.selfFlag ? 'self' : '')"
>
<el-table-column label="序号" align="center" minWidth="30%">
<template #default="{ row }">
{{ row.rankNum ? row.rankNum : '' }}
</template>
</el-table-column>
<el-table-column prop="stuName" label="姓名" align="center" minWidth="30%" />
<el-table-column label="得分" align="center" minWidth="40%">
<template #default="{ row }">
<span
:style="{
color: colorConfig[rateType((Number(row.scoreRate) || 0) * 100)],
}"
>{{ row.markScore }}</span
>/{{ row.scoreRate }}%
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- <Empty v-else class="empty" :type="pageLoading ? 'loading' : 'network'"></Empty> -->
</div>
</template>
<style scoped lang="scss">
.analyse {
width: 100%;
height: 100%;
line-height: 1.5;
&-main {
display: grid;
grid-template-areas:
'a b'
'c b';
grid-template-columns: 563px 895px;
grid-template-rows: 430px 585px;
gap: 24px;
}
&-item {
border-radius: 3.33vh;
border: 0.67vh solid #fff;
background: linear-gradient(179deg, #fff 0.69%, #dfebf7 99.53%);
box-shadow: 0 0.33vh 0.67vh 0 rgba(0, 0, 0, 0.25);
&-title {
color: $font-color;
font-family: $font-special;
font-size: 3.33vh;
text-align: center;
margin-bottom: 2vh;
}
}
&-info {
grid-area: a;
padding: 1.33vh;
&-main {
color: $font-color;
font-family: OPPOSans;
font-size: 2.33vh;
p {
display: flex;
align-items: center;
margin-bottom: 1.67vh;
line-height: 3.5vh;
.job-title {
width: calc(100% - 185px);
@include single_ellipsis;
}
}
}
}
&-structure {
grid-area: b;
padding: 1.33vh 44px;
.echarts-pie {
display: flex;
align-items: center;
justify-content: space-between;
height: 35vh;
margin-bottom: 4.17vh;
&-item {
height: 100%;
width: 50%;
}
}
.echarts-bar {
height: 37.25vh;
}
}
&-rank {
grid-area: c;
overflow-y: hidden;
:deep(.el-table) {
// border-radius: 3.33vh;
height: 100%;
background: transparent;
.cell {
line-height: 1.5;
color: $font-color;
font-size: 2.33vh;
padding: 0 4px;
}
th.el-table__cell {
background: #ccdeff;
font-family: $font-special;
padding: 1.5vh 0;
}
.el-table__cell {
&::after {
position: absolute;
left: 0;
bottom: 0;
content: '';
display: block;
width: 100%;
height: 0.17vh;
background: repeating-linear-gradient(
to right,
#deedfc,
#deedfc 8px,
transparent 8px,
transparent 16px /* 线条间隔 */
);
}
}
tr {
background: transparent;
td {
font-family: OPPOSans;
padding: 1.5vh 0;
}
}
.el-table__row.self {
background: #fff7e0;
}
}
}
}
</style>

View File

@ -1,116 +0,0 @@
<script setup lang="ts">
import homeWorkApi from '@/api/system/homeWork'
import { useRouter } from 'vue-router'
const props = defineProps<{
params: anyObj
isDeadline: boolean
}>()
const router = useRouter()
const list = ref<anyObj>([])
async function getData() {
const { data } = await homeWorkApi.getJobTittle({ ...props.params, reportFlag: 1 })
list.value = data.tjobStuVoList
}
function toWorkDetail(idx: string) {
const query: anyObj = {
reportFlag: 1,
idx,
...props.params,
}
router.push({
path: `/system/homeWork/doWork`,
query,
})
}
const pageLoading = ref(false)
onMounted(async () => {
pageLoading.value = true
try {
await getData()
} catch {}
pageLoading.value = false
})
</script>
<template>
<div class="detail">
<div v-if="list.length" class="detail-main">
<div class="detail-main-title">答题情况</div>
<div class="detail-main-list">
<div
v-for="(i, idx) in list"
:key="i.jobId"
:class="{
'detail-main-list-item': true,
pointer: true,
success: isDeadline && i.titleStatus === 3 && i.markFlag,
error: isDeadline && i.titleStatus === 3 && !i.markFlag,
}"
@click="toWorkDetail(idx)"
>
{{ idx + 1 }}
</div>
</div>
</div>
<Empty v-else class="empty" :type="pageLoading ? 'loading' : 'network'"></Empty>
</div>
</template>
<style scoped lang="scss">
.detail {
width: 100%;
height: 100%;
line-height: 1.5;
&-main {
border-radius: 3.33vh;
border: 0.67vh solid #fff;
background: linear-gradient(179deg, #fdfdf9 0.69%, #dfebf7 99.53%);
box-shadow: 0 0.33vh 0.67vh 0 rgba(0, 0, 0, 0.25);
height: 100%;
&-title {
color: $font-color;
font-family: $font-special;
font-size: 4vh;
margin: 3.33vh 0;
text-align: center;
}
&-list {
overflow-y: auto;
display: grid;
grid-template-columns: repeat(7, 160px);
grid-template-rows: 160px;
grid-auto-rows: 160px;
gap: 40px;
justify-content: center;
height: calc(100% - 160px);
&-item {
border-radius: 1vh;
border: 0.33vh solid #9fa5aa;
background: #e4e9ec;
text-align: center;
line-height: 13.33vh;
color: #666;
font-family: $font-special;
font-size: 4vh;
font-style: normal;
font-weight: 400;
&.success {
color: #8fcc00;
background-color: #ddff8c;
border-color: #8fcc00;
}
&.error {
color: #f00a0a;
background-color: #febebf;
border-color: #f00a0a;
}
}
}
}
}
</style>

View File

@ -1,129 +0,0 @@
<script setup lang="ts">
import { $font_color } from '@/styles/variate'
import * as echarts from 'echarts'
const props = withDefaults(
defineProps<{
data: {
xData: string[]
seriesVal: anyObj[]
}
title?: string
yName?: string
}>(),
{},
)
const emit = defineEmits(['handleBarClick'])
const widthRate = window.screen.width / 1920
const chartContainer = ref<HTMLDivElement | null>(null)
let myChart = reactive<anyObj>({})
const option = reactive<anyObj>({
title: {
text: '',
textStyle: {
color: $font_color,
fontSize: widthRate * 28,
fontWeight: 'normal',
},
top: 0,
left: 'center',
},
xAxis: {
data: [],
axisLabel: {
show: true,
textStyle: {
fontSize: widthRate * 22, //
color: $font_color,
},
},
axisLine: {
show: true,
lineStyle: {
color: '#8A94FD70',
width: widthRate * 4,
},
},
},
yAxis: {
type: 'value',
minInterval: 1,
// name: '',
nameLocation: 'end',
// nameGap: widthRate * -35,
nameTextStyle: {
color: $font_color,
fontSize: widthRate * 22,
},
axisLabel: {
show: true,
textStyle: {
fontSize: widthRate * 22, //
color: $font_color,
},
},
axisLine: {
show: true,
lineStyle: {
color: 'rgba(138, 148, 253, 0.25)',
width: widthRate * 4,
},
},
splitLine: {
lineStyle: {
type: [5, 10],
color: 'rgba(138, 148, 253, 0.25)',
width: 2,
},
},
},
series: {
data: [],
type: 'bar',
barMaxWidth: widthRate * 80,
label: {
show: true,
position: 'top',
fontSize: widthRate * 20,
color: $font_color,
},
},
})
async function drawGraph() {
await nextTick()
if (myChart) {
myChart.dispose?.()
myChart = echarts.init(chartContainer.value)
}
option.xAxis.data = props.data?.xData
option.series.data = props.data?.seriesVal
option.title.text = props.title
option.yAxis.name = props.yName || ''
myChart.setOption(option)
myChart.off('click').on('click', async (params: anyObj) => {
emit('handleBarClick', params)
})
myChart.resize()
}
onMounted(() => {
drawGraph()
})
defineExpose({ drawGraph })
</script>
<template>
<div ref="chartContainer" class="echarts-bar"></div>
</template>
<style scoped lang="scss">
.echarts-bar {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,101 +0,0 @@
<script setup lang="ts">
import { $font_color } from '@/styles/variate'
import * as echarts from 'echarts'
const props = withDefaults(
defineProps<{
data: anyObj[]
title: string
}>(),
{},
)
const widthRate = window.screen.width / 1920
const chartContainer = ref<HTMLDivElement | null>(null)
let myChart = reactive<anyObj>({})
const option = reactive<anyObj>({
title: {
text: '',
textStyle: {
color: $font_color,
fontSize: widthRate * 28,
fontWeight: 'normal',
},
top: 0,
left: 'center',
},
legend: {
bottom: 0,
left: 'center',
icon: 'circle',
itemWidth: widthRate * 8,
textStyle: {
fontSize: widthRate * 20,
color: '#666',
},
data: [],
},
series: {
type: 'pie',
radius: ['30%', '70%'],
avoidLabelOverlap: false,
legendHoverLink: false,
emphasis: {
label: {
show: false,
},
},
label: {
position: 'outer',
alignTo: 'labelLine',
formatter: (params: anyObj) => Math.round(params.percent) + '%',
color: 'inherit',
fontSize: widthRate * 22,
distanceToLabelLine: widthRate * -50,
},
labelLine: {
length: widthRate * 22,
length2: widthRate * 75,
maxSurfaceAngle: 80,
},
labelLayout: {
verticalAlign: 'bottom',
},
data: [],
// width: '85%',
// height: '85%',
left: 'center',
top: 'center',
},
})
async function drawGraph() {
await nextTick()
if (myChart) {
myChart.dispose?.() //
myChart = echarts.init(chartContainer.value)
}
option.series.data = props.data
option.legend.data = props.data
option.title.text = props.title
myChart.setOption(option)
myChart.resize()
}
onMounted(() => {
drawGraph()
})
</script>
<template>
<div ref="chartContainer" class="echarts-pie"></div>
</template>
<style scoped lang="scss">
.echarts-pie {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,180 +0,0 @@
<script setup lang="ts">
import homeWorkApi from '@/api/system/homeWork'
import { colorConfig } from './hooks'
import { rateType } from '@/hooks'
const props = defineProps<{
params: anyObj
}>()
// const width_rate = window.screen.width / 1920;
const data = ref<anyObj[]>([])
async function getData() {
const { data: res } = await homeWorkApi.getKnowledgeAnalyse(props.params)
data.value = res.data
currentType.value = res.data[0].type
}
const typeConfig = ['薄弱', '合格', '良好', '优秀']
const dataList = computed(() =>
data.value.map(({ type }) => ({
value: type,
label: typeConfig[type],
})),
)
const tableData = computed(
() => data.value.find(i => i.type === currentType.value)?.knowledgeAnalyseVos,
)
const currentType = ref(0)
const pageLoading = ref(false)
onMounted(async () => {
pageLoading.value = true
try {
await getData()
} catch {}
pageLoading.value = false
})
</script>
<template>
<div class="knowledge">
<div v-if="data.length" class="knowledge-main">
<div class="knowledge-main-select">
<SelectTab v-model="currentType" :dataList="dataList"></SelectTab>
</div>
<div class="knowledge-main-table">
<el-table :data="tableData">
<el-table-column label="知识点名称" prop="name" align="center" minWidth="53%">
</el-table-column>
<el-table-column
label="满分"
prop="fullScore"
align="center"
minWidth="23.5%"
></el-table-column>
<el-table-column label="得分" align="center" minWidth="23.5%">
<template #default="{ row }">
<span
:style="{
color: colorConfig[rateType((Number(row.scoreRate.replace('%', '')) || 0) * 100)],
}"
>{{ row?.score }}/{{ row?.scoreRate }}</span
>
</template>
</el-table-column>
</el-table>
</div>
</div>
<Empty
v-else
class="empty"
:type="pageLoading ? 'loading' : 'knowledge'"
content="暂无知识点数据~"
></Empty>
</div>
</template>
<style scoped lang="scss">
.knowledge {
width: 100%;
height: 100%;
&-main {
border-radius: 3.33vh;
border: 0.67vh solid #fff;
background: linear-gradient(179deg, #fdfdf9 0.69%, #dfebf7 99.53%);
box-shadow: 0 0.33vh 0.67vh 0 rgba(0, 0, 0, 0.25);
padding: 4.33vh 48px;
height: 100%;
&-select {
text-align: center;
}
:deep(.select-list) {
display: inline-flex;
margin-bottom: 5vh;
.data-item {
width: 298px;
}
}
&-table {
height: calc(100% - 100px);
:deep(.el-table) {
box-sizing: border-box;
border: 0.33vh #659cfe solid;
border-radius: 3.33vh;
background: transparent;
max-height: 100%;
@include hidden_scroll;
.cell {
line-height: 1.5;
color: $font-color;
font-size: 2.33vh;
padding: 0 4px;
@include single_ellipsis;
}
th.el-table__cell {
background: #ccdeff;
font-family: $font-special;
padding: 1.5vh 0;
}
.el-table__cell {
height: 7.5vh;
color: $font-color;
font-family: OPPOSans;
font-size: 2.33vh;
position: relative;
border: none;
.cell {
line-height: 1.5;
}
&::after {
position: absolute;
left: 0;
bottom: 0;
content: '';
display: block;
width: 100%;
height: 0.17vh;
background: repeating-linear-gradient(
to right,
#deedfc,
#deedfc 8px,
transparent 8px,
transparent 16px /* 线条间隔 */
);
}
&::before {
position: absolute;
right: 0;
top: 0;
content: '';
display: block;
width: 2px;
height: 100%;
background: repeating-linear-gradient(
to bottom,
#deedfc,
#deedfc 8px,
transparent 8px,
transparent 16px /* 线条间隔 */
);
}
&:last-child {
&::before {
display: none;
}
}
}
tr {
background: transparent;
td {
font-family: OPPOSans;
padding: 1.5vh 0;
}
}
}
}
}
}
</style>

View File

@ -1,6 +0,0 @@
export const colorConfig = {
primary: '#2D39BC',
success: '#2F8A5B',
error: '#B22731',
warning: '#CC8A33',
}

View File

@ -1,117 +0,0 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import Analyse from './components/Analyse.vue'
import Detail from './components/Detail.vue'
import Knowledge from './components/Knowledge.vue'
import { useRoute } from 'vue-router'
import homeWorkApi from '@/api/system/homeWork'
const route = useRoute()
const params = {
id: route.params.id,
stuId: route.query?.stuId || '',
}
//
const activeType = ref('1')
const sidebarConfig = ref<anyObj[]>([])
const isDeadline = ref(false)
const jobAnalyse = ref<anyObj>({})
async function getJobAnalyse() {
const { data: res } = await homeWorkApi.getJobAnalyse(params)
jobAnalyse.value = res.data
isDeadline.value = dayjs().isAfter(jobAnalyse.value.jobReportVo.submitEndTime)
const detailCmp = { id: '1', name: '答题详情', c: markRaw(Detail) }
if (isDeadline.value) {
sidebarConfig.value = [
{ id: '0', name: '整体分析', c: markRaw(Analyse) },
detailCmp,
{ id: '2', name: '知识点分析', c: markRaw(Knowledge) },
]
activeType.value = '0'
} else {
sidebarConfig.value = [detailCmp]
activeType.value = '1'
}
}
const COMPONENT = computed(() => {
return sidebarConfig.value.find(i => i.id === activeType.value)?.c
})
const pageLoading = ref(false)
onMounted(async () => {
pageLoading.value = true
try {
await getJobAnalyse()
} catch {}
pageLoading.value = false
})
defineOptions({
name: 'HomeWorkReports',
})
</script>
<template>
<div class="reports">
<div class="top">
<BackBar :leftText="(route.query?.name as string) || ' '" />
</div>
<div v-if="!pageLoading" class="main">
<div class="main-sidebar">
<Sidebar v-model="activeType" :config="sidebarConfig"></Sidebar>
</div>
<div class="main-box">
<KeepAlive>
<component
:is="COMPONENT"
:params="params"
:jobAnalyse="jobAnalyse"
:isDeadline="isDeadline"
></component>
</KeepAlive>
</div>
</div>
<Empty v-else class="empty" :type="pageLoading ? 'loading' : 'network'"></Empty>
</div>
</template>
<style scoped lang="scss">
.reports {
@include main_bg;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
.top {
padding: 3vh 64px 0;
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.main {
height: calc(100% - 130px);
padding-left: 64px;
padding-right: 64px;
position: relative;
&-sidebar {
position: absolute;
left: 64px;
top: 3.67vh;
height: calc(100% - 66px);
width: 272px;
}
&-box {
width: 1482px;
height: 86.67vh;
margin-left: 310px;
overflow: hidden;
:deep(.empty) {
height: calc(100% - 150px);
}
}
}
}
</style>

View File

@ -1,463 +0,0 @@
<script setup lang="ts">
import * as echarts from 'echarts'
import dayjs from '@/plugins/dayjs'
import Card from '@/page/subject/components/Card.vue'
import { $font_color } from '@/styles/variate'
const props = defineProps<{
data: {
level?: number
stability?: number
jobScoringAbilityDataVoList?: {
level: number
scoreRate: number
submitDate: string
}[]
}
table?: anyObj[]
tableLoading: boolean
type: number
}>()
const emit = defineEmits(['update:type'])
/*
level 级别0-优秀 1- 2- 3-
stability 稳定性 0-稳定 1-相对波动
jobScoringAbilityDataVoList {
level 级别0-优秀 1- 2- 3-
scoreRate 得分率
submitDate 日期
}
*/
const levelConfig: anyObj = {
0: { label: '优秀' },
1: { label: '良好' },
2: { label: '合格' },
3: { label: '薄弱' },
}
const stabilityConfig: anyObj = {
0: { label: '稳定' },
1: { label: '相对波动' },
}
const configColor = {
success: '#2F8A5B',
primary: '#2D39BC',
warning: '#A13C00',
error: '#B22731',
}
//
function getColor(rate: number) {
return rate === 100 ? 'success' : rate >= 80 ? 'primary' : rate >= 60 ? 'warning' : 'error'
}
// 0- 1- 2-
const recentScoreType = [
{ label: '零分', color: $font_color },
{ label: '失分', color: '#B22731' },
{ label: '满分', color: '#2F8A5B' },
]
const lossFrequency = computed(() => {
return (item: anyObj) => {
if (item) {
const { lostPointsNum, jobNum } = item
if (lostPointsNum / jobNum >= 1) {
return '每次'
} else if (lostPointsNum / jobNum >= 0.5) {
return '高频'
} else if (lostPointsNum / jobNum < 0.5) {
return '低频'
}
}
}
})
// type 0- 1- 2- 3-
const typeConfig = [
{
label: '薄弱',
value: 0,
},
{
label: '合格',
value: 1,
},
{
label: '良好',
value: 2,
},
{
label: '优秀',
value: 3,
},
]
const widthRate = window.screen.width / 1920
const echartRef = ref<HTMLDivElement | null>(null)
let myChart = reactive<anyObj>({})
const option = reactive<anyObj>({
legend: {
show: true,
data: ['我的得分能力'],
textStyle: {
color: '#666',
fontSize: widthRate * 24,
fontFamily: 'OPPOSans',
padding: [0, 8],
},
},
xAxis: {
data: [],
boundaryGap: false,
axisLabel: {
show: true,
textStyle: {
fontSize: widthRate * 22, //
color: $font_color,
},
},
axisLine: {
show: true,
lineStyle: {
color: '#31BCFF',
width: widthRate * 4,
},
},
axisTick: {
alignWithLabel: true,
},
splitLine: {
show: true,
interval: 0,
lineStyle: {
type: 'solid',
color: '#C3ECFF',
width: widthRate * 4,
},
},
},
yAxis: {
type: 'value',
minInterval: 1,
nameLocation: 'end',
nameTextStyle: {
color: $font_color,
fontSize: widthRate * 22,
},
axisLabel: {
show: true,
textStyle: {
fontSize: widthRate * 22, //
color: $font_color,
},
},
axisLine: {
show: false,
lineStyle: {
color: '#C3ECFF',
width: widthRate * 4,
},
},
splitLine: {
lineStyle: {
type: 'solid',
color: '#C3ECFF',
width: widthRate * 4,
},
},
},
series: [
{
data: [],
name: '我的得分能力',
type: 'line',
lineStyle: {
color: '#00AEFF',
width: widthRate * 4,
},
symbol: 'circle',
symbolSize: widthRate * 12,
itemStyle: {
color: '#fff',
borderColor: '#00AEFF',
borderWidth: widthRate * 4,
},
markLine: {
symbol: ['none', 'triangle'],
data: [
{
yAxis: 85,
lineStyle: {
type: [10, 15],
color: '#F3B0B4',
width: widthRate * 5,
},
label: {
formatter: '优秀',
color: '#F3B0B4',
fontSize: widthRate * 30,
padding: [0, widthRate * 16],
},
},
{
yAxis: 70,
lineStyle: {
type: [10, 15],
color: '#F3B0B4',
width: widthRate * 5,
},
label: {
formatter: '良好',
color: '#F3B0B4',
fontSize: widthRate * 30,
padding: [0, widthRate * 16],
},
},
{
yAxis: 60,
lineStyle: {
type: [10, 15],
color: '#F3B0B4',
width: widthRate * 5,
},
label: {
formatter: '合格',
color: '#F3B0B4',
fontSize: widthRate * 30,
padding: [0, widthRate * 16],
},
},
],
},
animation: false,
},
],
})
function drawGraph() {
if (myChart) {
myChart.dispose?.() //
myChart = echarts.init(echartRef.value)
}
option.xAxis.data =
props.data.jobScoringAbilityDataVoList?.map(i => dayjs(i.submitDate).format('MM/DD')) || []
option.series[0].data = props.data.jobScoringAbilityDataVoList?.map(i => i.scoreRate / 100) || []
myChart.setOption(option)
myChart.resize()
}
watch(
() => props.data,
() => {
nextTick(() => {
drawGraph()
})
},
{ immediate: true },
)
</script>
<template>
<div class="single-analysis hidden_scroll">
<div class="score-ability">
<div class="title" data-content="得分能力">得分能力</div>
<template v-if="data.jobScoringAbilityDataVoList?.length">
<div ref="echartRef" class="chartContainer"></div>
<div>
<div class="cards">
<Card
width="404"
height="160"
title="得分能力综合评估"
:text="levelConfig[data.level || 0].label"
></Card>
<Card
type="warning"
width="404"
height="160"
title="得分能力稳定性"
:text="stabilityConfig[data.stability || 0].label"
></Card>
</div>
<p class="tips">
得分能力是通过均分得分率等数据综合计算得出反映学生对该科目知识掌握程度和答题能力
</p>
</div>
</template>
<Empty v-else type="learn" style="height: 600px"></Empty>
</div>
<div class="knowledge-table">
<div class="title" data-content="知识点分析">知识点分析</div>
<div style="text-align: center">
<SelectTab
:modelValue="type"
:dataList="typeConfig"
@update:modelValue="emit('update:type', $event)"
></SelectTab>
</div>
<el-table v-if="table?.length" :data="table">
<el-table-column label="知识点名称" prop="knowledgeName" align="center" minWidth="42.8%">
</el-table-column>
<el-table-column
label="失分频率"
prop="lostFrequency"
sortable
align="center"
minWidth="22%"
>
<template #default="{ row }">
{{ lossFrequency(row) }} ({{ row.lostPointsNum }}/{{ row.jobNum }})
</template>
</el-table-column>
<el-table-column label="得分率" prop="scoreRate" sortable align="center" minWidth="17.6%">
<template #default="{ row }">
<span :style="{ color: configColor[getColor(row.scoreRate / 100)] }">
{{ row.scoreRate / 100 }}%
</span>
</template>
</el-table-column>
<el-table-column label="最近情况" prop="recentScoreType" align="center" minWidth="17.6%">
<template #default="{ row }">
<span :style="{ color: recentScoreType[row.recentScoreType].color }">
{{ recentScoreType[row.recentScoreType].label }}
</span>
</template>
</el-table-column>
</el-table>
<Empty v-else :type="tableLoading ? 'loading' : 'learn'" style="height: 600px"></Empty>
</div>
</div>
</template>
<style scoped lang="scss">
.single-analysis {
line-height: 1.5;
height: calc(100%);
.title {
color: #fff;
font-family: $font-special;
font-size: 3vh;
font-style: normal;
font-weight: 400;
text-align: center;
@include font-stroke($font-color, 4px);
}
.score-ability {
.chartContainer {
width: 100%;
margin-top: 4vh;
height: 60.83vh;
}
.cards {
display: flex;
justify-content: center;
.xxl-card {
margin: 0 16px;
}
}
.tips {
width: 1208px;
height: 9.67vh;
border-radius: 1.67vh;
background: #fff;
box-shadow: 0 0.33vh 0.17vh 0 rgba(0, 0, 0, 0.15);
color: #ff8d3b;
font-family: OPPOSans;
font-size: 2.67vh;
padding: 1vh 32px 0;
margin: 5vh auto 6vh;
}
}
.knowledge-table {
min-height: calc(100% + 210px);
:deep(.select-list) {
display: inline-flex;
margin: 4vh auto;
.data-item {
width: 298px;
&.active {
background: linear-gradient(269deg, #86e6ff 2.25%, #9effda 51.93%, #5fdcfd 99.65%);
}
}
}
:deep(.el-table) {
box-sizing: border-box;
border: 0.33vh #31bcff solid;
border-radius: 3.33vh;
background: transparent;
.cell {
line-height: 1.5;
color: $font-color;
font-size: 2.33vh;
padding: 0 4px;
// @include single_ellipsis;
}
th.el-table__cell {
background: #c2ebff;
font-family: $font-special;
padding: 1.5vh 0;
}
.el-table__cell {
height: 7.5vh;
color: $font-color;
font-family: OPPOSans;
font-size: 2.33vh;
position: relative;
border: none;
background-color: #fff;
.cell {
line-height: 1.5;
}
&::after {
position: absolute;
left: 0;
bottom: 0;
content: '';
display: block;
width: 100%;
height: 0.17vh;
background: repeating-linear-gradient(
to right,
#deedfc,
#deedfc 8px,
transparent 8px,
transparent 16px /* 线条间隔 */
);
}
&::before {
position: absolute;
right: 0;
top: 0;
content: '';
display: block;
width: 2px;
height: 100%;
background: repeating-linear-gradient(
to bottom,
#deedfc,
#deedfc 8px,
transparent 8px,
transparent 16px /* 线条间隔 */
);
}
&:last-child {
&::before {
display: none;
}
}
}
tr {
background: transparent;
td {
font-family: OPPOSans;
padding: 1.5vh 0;
}
}
}
}
}
</style>

View File

@ -1,148 +0,0 @@
<script setup lang="ts">
const props = defineProps<{
wholeAnalysis: anyObj
}>()
//
const wholeConfig: anyObj = {
0: {
label: '优秀',
icon: 'analysisStar_0',
},
1: {
label: '良好',
icon: 'analysisStar_1',
},
2: {
label: '合格',
icon: 'analysisStar_2',
},
3: {
label: '薄弱',
icon: 'analysisStar_3',
},
}
// -1- 0-退 1- 2-
const statusConfig: anyObj = {
0: {
label: '退步',
bg: '#FFD9D9',
color: '#F00A0A',
},
1: {
label: '稳定',
bg: '#E6E7FF',
color: '#7A7FF5',
},
2: {
label: '进步',
bg: '#F4FFDA',
color: '#8FCC00',
},
}
// // -1
// const analysisComputed = computed(() =>
// props.wholeAnalysis.map((i: anyObj) => {
// return {
// level: i?.level || 0,
// jobStatisticalDataVoList:
// i?.jobStatisticalDataVoList || [],
// };
// }),
// );
</script>
<template>
<div class="whole hidden_scroll">
<div v-for="i in wholeAnalysis" :key="i.level" class="whole-item">
<Icon :name="wholeConfig[i.level || 0].icon" size="120"> </Icon>
<div class="whole-item-right">
<template v-if="i.jobStatisticalDataVoList?.length">
<div
v-for="item in i.jobStatisticalDataVoList"
:key="item.subjectId"
class="whole-item-right-item"
>
<div class="name">{{ item.subjectName }}</div>
<div
v-if="statusConfig[item.status]"
class="status"
:style="{
color: statusConfig[item.status].color,
background: statusConfig[item.status].bg,
border: `3px solid ${statusConfig[item.status].color}`,
}"
>
{{ statusConfig[item.status].label }}
</div>
</div>
</template>
<div v-else class="empty">暂无</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.whole {
height: calc(100% - 48px);
margin-top: 4vh;
line-height: 1.5;
&-item {
display: flex;
margin: 2vh 0;
.x-icon {
margin-right: 40px;
}
&-right {
flex: 1;
border-radius: 1.67vh;
background: rgba(0, 0, 0, 0.1);
min-height: 10.67vh;
font-family: $font-special;
font-size: 3.33vh;
padding: 2vh;
display: grid;
// grid-template-columns: repeat(2, 1fr);
grid-template-columns: repeat(auto-fit, minmax(546px, 1fr));
grid-template-rows: 80px;
grid-auto-rows: 80px;
gap: 24px;
justify-content: center;
&-item {
// width: 546px;
display: flex;
padding: 0.83vh 32px;
justify-content: space-between;
align-items: center;
border-radius: 1.67vh;
background: #fff;
box-shadow: 0 0.33vh 0.17vh 0 rgba(0, 0, 0, 0.15);
.name {
flex: 1;
color: $font-color;
}
.status {
width: 100px;
height: 3.83vh;
border-radius: 3.33vh;
border: 0.25vh solid black;
font-family: OPPOSans;
font-size: 2.17vh;
display: flex;
align-items: center;
justify-content: center;
}
}
.empty {
color: #fff;
text-shadow: 0 0.17vh 0.17vh rgba(0, 0, 0, 0.45);
// margin: 10px auto;
text-align: center;
line-height: 6.67vh;
}
}
}
}
</style>

View File

@ -1,171 +0,0 @@
<script setup lang="ts">
import { userStore } from '@/stores'
import { storeToRefs } from 'pinia'
import homeWorkApi from '@/api/system/homeWork'
import Whole from './Whole.vue'
import SingleAnalysis from './SingleAnalysis.vue'
import { useRoute } from 'vue-router'
const OSS_URL = import.meta.env.VITE_OSS_URL
const route = useRoute()
let paramsStuId = route.query?.stuId || ''
const leftText = computed(() => route.query?.name || ' ')
const { userInfo } = storeToRefs(userStore())
const wholeAnalysis = ref<anyObj[]>([])
async function getAnalysis() {
const { data: res } = await homeWorkApi.getStatisticalAnalysis({
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
})
if (res.code === 200) {
wholeAnalysis.value = res.data
}
}
// const { subjectList, getSubjectAllList } = subjectStore();
const activeClass = ref('')
const classList = ref([{ id: '', name: '整体' }])
async function getClassList() {
const { data: res } = await homeWorkApi.getAnalyseSubjectList({
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
})
if (res.code === 200) {
classList.value.push(...res.data)
}
}
const singleAnalysis = ref<any>({})
async function getAnalysisSubject() {
const { data: res } = await homeWorkApi.getStatisticalSubject({
sourceType: 0,
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
subjectId: activeClass.value,
})
if (res.code === 200) {
singleAnalysis.value = res.data
}
}
const type = ref(0)
const knowledgeAnalysis = ref<{ loading: boolean; table: anyObj[] }[]>([])
async function getKnowledgeSubject() {
if (knowledgeAnalysis.value[type.value]?.loading) return
if (Array.isArray(knowledgeAnalysis.value[type.value]?.table)) return
const curType = type.value
knowledgeAnalysis.value[curType] = {
loading: true,
table: [],
}
try {
const { data: res } = await homeWorkApi.getKnowledgeSubject({
sourceType: 0,
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
subjectId: activeClass.value,
size: 300,
// orderByColumn: 'lostFrequency',
// isAsc: 'asc',
type: type.value,
})
if (res.code === 200) {
knowledgeAnalysis.value[curType].table = res.rows || []
}
} catch {}
knowledgeAnalysis.value[curType].loading = false
}
watch(type, () => getKnowledgeSubject())
watch(activeClass, () => {
knowledgeAnalysis.value = []
getKnowledgeSubject()
})
const loading = ref(false)
async function handleSelectClass(item: anyObj) {
if (item.id === '') {
getAnalysis()
} else {
loading.value = true
try {
await getAnalysisSubject()
} catch {}
loading.value = false
}
}
onActivated(async () => {
paramsStuId = route.query?.stuId || ''
classList.value = [{ id: '', name: '整体' }]
activeClass.value = ''
getClassList()
getAnalysis()
})
</script>
<template>
<div class="homework_statistics">
<div class="top">
<BackBar :leftText="leftText" />
</div>
<div class="main">
<div class="main_sidebar">
<Sidebar
v-model="activeClass"
:config="classList"
@handle-change="handleSelectClass"
></Sidebar>
</div>
<div
class="main_book"
:style="{ 'background-image': `url(${OSS_URL}/urm/homeWork/wrong_bg.svg)` }"
>
<template v-if="!loading">
<Whole v-if="activeClass === ''" :wholeAnalysis="wholeAnalysis"></Whole>
<SingleAnalysis
v-else
v-model:type="type"
:data="singleAnalysis"
:table="knowledgeAnalysis[type]?.table"
:tableLoading="knowledgeAnalysis[type]?.loading"
></SingleAnalysis>
</template>
<Empty v-else type="loading" style="height: 900px"></Empty>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.homework_statistics {
padding: 2.67vh 64px;
@include main_bg;
position: relative;
width: 100vw;
height: 100%;
overflow: hidden;
box-sizing: border-box;
line-height: 1.5;
.top {
position: absolute;
// top: 32;
// left: 64;
}
.main {
// margin-top: 66px;
width: 100%;
height: 100%;
padding-top: 2.83vh;
display: flex;
&_sidebar {
padding-top: 5.5vh;
width: 275px;
margin-right: 50px;
}
&_book {
width: 1470px;
height: 90.25vh;
flex-shrink: 0;
background-size: 100%;
padding: 8.67vh 64px 3.17vh;
}
}
}
</style>

View File

@ -1,278 +0,0 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { useRouter } from 'vue-router'
import { userStore } from '@/stores'
import { XMessage, debounce } from '@/hooks'
import homeWorkApi from '@/api/system/homeWork'
import LoadIcon from '@/page/system/mailbox/components/LoadIcon.vue'
import sass from '@/utils/sass'
const router = useRouter()
const { token } = userStore()
const OSS_URL = import.meta.env.VITE_OSS_URL
const itemBg = `url(${OSS_URL}/icon/homeWork_temporary_item_bg.svg)`
const subjectList: any = ref([])
const page = reactive({
pageSize: 11,
pageNum: 1,
total: 0,
})
const jobList: any = ref([])
const uploadLoading = ref(false)
const listLoading = ref(false) //
const moreLoading = ref(false) //
const uploadMsg = ref({
submitUrl: '',
subjectId: '',
clientType: 0,
})
function toLook(item: any) {
router.push(`/system/homeWork/temporaryHomeWork/${item.id}`)
}
function handleSelectClass() {
initList()
}
function handleBeforeUpload(rawFile: any) {
const fileType = ['image/jpeg', 'image/png']
if (!fileType.includes(rawFile.type)) {
XMessage.error('上传格式不正确只支持JPG/PNG')
return false
}
if (rawFile.size / 1024 / 1024 > 10) {
XMessage.error('图片过大')
return false
}
uploadLoading.value = true
return true
}
async function handleUploadSuccess(res: any, _file: any) {
uploadMsg.value.submitUrl = res.data.filePath
uploadLoading.value = false
try {
const { data } = await homeWorkApi.uploadTemporaryJob(uploadMsg.value)
if (data.code === 200) {
initList()
}
} catch (err) {
console.log(err)
}
}
function handleUploadError() {
XMessage.error('上传失败,请重新上传~')
uploadLoading.value = false
}
const debounceScroll = debounce(handleScroll, 200, false)
const scrollBoxRef = ref()
async function handleScroll(event: any) {
if (jobList.value.length >= page.total) return
if (moreLoading.value) return
if (event.target.scrollTop + event.target.offsetHeight >= scrollBoxRef.value?.offsetHeight - 50) {
page.pageNum++
try {
moreLoading.value = true
await initTemporaryJobList()
} catch {
page.pageNum--
}
moreLoading.value = false
}
}
async function initList() {
if (listLoading.value) return
listLoading.value = true
jobList.value = []
page.pageNum = 1
try {
await initTemporaryJobList()
} catch {
console.log('网络出问题了啦~')
}
listLoading.value = false
}
//
async function initTemporaryJobList() {
try {
const { data } = await homeWorkApi.temporaryJobList({
clientType: 0,
subjectId: uploadMsg.value.subjectId,
...page,
})
page.total = data.total
for (let i in data.rows) {
jobList.value.push(data.rows[i])
}
} catch (err) {
console.log(err)
}
}
//
async function getSubjectList() {
try {
const { data } = await homeWorkApi.getTemporarySubjectList()
subjectList.value = data.rows
} catch (err) {
console.log(err)
}
}
onMounted(async () => {
await getSubjectList()
uploadMsg.value.subjectId = subjectList.value[0].id
initList()
})
</script>
<template>
<div class="temporary">
<div class="top">
<BackBar leftText="临时作业" />
</div>
<div class="main">
<div class="main_sidebar">
<Sidebar
v-model="uploadMsg.subjectId"
:config="subjectList"
@handle-change="handleSelectClass"
></Sidebar>
</div>
<div class="main_answer">
<Empty v-if="listLoading" class="empty" type="loading"></Empty>
<div v-else class="main_answer_list hidden_scroll" @scroll="debounceScroll">
<div ref="scrollBoxRef" class="content">
<div>
<el-upload
class="pointer"
action="/api/main/sysFileInfo/tenUploadAll"
:data="{ type: 1 }"
:headers="{
Authorization: token,
'X-Tenant-Id': sass.platform.tenantId,
}"
accept="image/*"
:before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:show-file-list="false"
:disabled="uploadLoading"
>
<Icon name="add_book" size="80" />
<span class="text">{{ uploadLoading ? '上传中' : '上传图片' }}</span>
</el-upload>
</div>
<div v-for="(i, index) in jobList" :key="index" class="item pointer" @click="toLook(i)">
<img :src="i.submitUrl" alt="" class="sub_answer" />
<span class="sub_date">{{ dayjs(i.submitTime).format('YYYY/MM/DD HH:mm') }}</span>
<div v-if="i.status === 1" class="result">已批改</div>
</div>
</div>
<LoadIcon v-if="moreLoading" class="load-icon"></LoadIcon>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.temporary {
@include main_bg;
padding: 2.67vh 64px;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
.top {
position: absolute;
}
.main {
display: flex;
padding-top: 8.67vh;
width: 100%;
height: 100%;
&_sidebar {
padding-top: 2vh;
width: 214px;
margin-right: 98px;
}
&_answer {
width: 100%;
height: 100%;
padding: 2.5vh;
border-radius: 3.33vh;
border: 0.67vh solid #fff;
background: linear-gradient(179.32deg, #fdfdf9 0.69%, #dfebf7 99.53%);
box-sizing: border-box;
&_list {
height: 100%;
.content {
display: flex;
flex-wrap: wrap;
:deep(.el-upload) {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 24px;
width: 328px;
height: 33.5vh;
border-radius: 1.67vh;
background: #f2f3ff;
box-shadow: 0 0.33vh 0.33vh 0 #00000026;
.text {
font-family: $font-special;
margin-top: 2vh;
display: block;
font-size: 2.33vh;
line-height: 3.5vh;
color: #80b5fe;
}
}
.item {
position: relative;
padding: 2vh 24px 2.67vh 24px;
width: 332px;
height: 33.83vh;
margin: 0 24px 2vh 0;
border-radius: 1.67vh;
background-image: v-bind(itemBg);
background-size: 100%;
background-repeat: no-repeat;
background-position: right bottom;
.sub_answer {
width: 284px;
height: 23.67vh;
border-radius: 0.83vh;
}
.sub_date {
display: inline-block;
margin-top: 2vh;
font-size: 2.33vh;
line-height: 3.5vh;
color: #666;
}
&:nth-child(4n) {
margin-right: 0;
}
.result {
position: absolute;
top: 0;
right: 0;
padding: 0.67vh 14px;
color: #2f8a5b;
font-family: $font-special;
font-size: 2.33vh;
line-height: 3.5vh;
text-align: center;
background-color: #a2fb6e;
border-radius: 0 1.67vh 0 1.67vh;
}
}
}
}
}
}
}
</style>

View File

@ -1,314 +0,0 @@
<script setup lang="ts">
import homeWorkApi from '@/api/system/homeWork'
import { useRoute } from 'vue-router'
const route = useRoute()
const OSS_URL = import.meta.env.VITE_OSS_URL
const activeBg = `url(${OSS_URL}/urm/homeWork/homeWork_temporary_active_bg.svg)`
const nonactiveBg = `url(${OSS_URL}/urm/homeWork/homeWork_temporary_nonactive_bg.svg)`
const activeNav = ref({
name: '原图',
type: 0,
url: '',
})
const nav = ref([
{
name: '原图',
type: 0,
url: '',
},
])
const loading = ref(false)
const dragging = ref(false) //
const imgArea = ref()
const img = ref()
const movableData = ref({
x: 0,
y: 0,
scale: 1,
})
const scaleData = ref(1) //
const distance = ref({
startDistance: 0,
endDistance: 0,
})
function clickAnswerStatus(i: any) {
activeNav.value = i
movableData.value = {
x: 0,
y: 0,
scale: 1,
}
loading.value = true
setTimeout(() => {
onImageLoad()
}, 150)
}
//
function onImageLoad() {
loading.value = false
}
//
function tapEnlarge() {
if (movableData.value.scale < 2) {
movableData.value.scale += 0.1
}
}
//
function tapLessen() {
if (movableData.value.scale > 0.6) {
movableData.value.scale -= 0.1
}
}
function touchTwo(e: anyObj[], mode: 'startDistance' | 'endDistance') {
//
const one = {
x: e[0].pageX, //
y: e[0].pageY, //
} //
const two = {
x: e[1].pageX, //
y: e[1].pageY, //
}
distance.value[mode] = getDistance(one, two)
}
function getDistance(start: anyObj, stop: anyObj) {
return Math.sqrt(
Math.pow(Math.abs(start.x - stop.x), 2) + Math.pow(Math.abs(start.y - stop.y), 2),
)
}
const startDrag = (event: anyObj) => {
if (event.touches?.length === 2) {
//
touchTwo(event.touches, 'startDistance')
return
}
dragging.value = true
const x = (event.touches ? event.touches[0].pageX : event.pageX) - movableData.value.x
const y = (event.touches ? event.touches[0].pageY : event.pageY) - movableData.value.y
const move = (event: anyObj) => {
if (event.touches?.length === 2) {
//
touchTwo(event.touches, 'endDistance')
const scale2 =
scaleData.value + distance.value.endDistance / distance.value.startDistance - 1.01 // 1 0.01
movableData.value.scale = scale2 > 2 ? 2 : scale2 < 0.5 ? 0.5 : scale2 // 2 0.5
return
}
if (dragging.value) {
movableData.value.x = (event.touches ? event.touches[0].pageX : event.pageX) - x
movableData.value.y = (event.touches ? event.touches[0].pageY : event.pageY) - y
}
}
const stopDrag = () => {
distance.value.startDistance = distance.value.endDistance = 0
scaleData.value = movableData.value.scale
dragging.value = false
document.removeEventListener('mousemove', move)
document.removeEventListener('touchmove', move)
document.removeEventListener('mouseup', stopDrag)
document.removeEventListener('touchend', stopDrag)
}
document.addEventListener('mousemove', move)
document.addEventListener('touchmove', move)
document.addEventListener('mouseup', stopDrag)
document.addEventListener('touchend', stopDrag)
}
//
async function getTemporaryJobDetail() {
loading.value = true
try {
const { data } = await homeWorkApi.temporaryJobDetail(route.params.id)
if (data.data.status === 1) {
nav.value = [
{
name: '原图',
type: 0,
url: data.data.submitUrl,
},
{ name: '批改图', type: 1, url: data.data.markUrl },
]
} else {
nav.value = [
{
name: '原图',
type: 0,
url: data.data.submitUrl,
},
]
}
activeNav.value = nav.value[0]
loading.value = false
} catch (err) {
console.log(err)
loading.value = false
}
}
onUnmounted(() => {
document.removeEventListener('mousemove', () => {})
document.removeEventListener('mouseup', () => {})
document.removeEventListener('touchmove', () => {})
document.removeEventListener('touchend', () => {})
})
onActivated(() => {
getTemporaryJobDetail()
movableData.value = {
x: 0,
y: 0,
scale: 1,
}
})
</script>
<template>
<div class="sinle_temporary">
<div class="top">
<BackBar />
</div>
<div class="main">
<div
class="main_answer"
:style="{
'background-image': `url(${OSS_URL}/urm/homeWork/homeWork_temporary_single_bg.svg)`,
}"
>
<div class="nav">
<div
v-for="i in nav"
:key="i.type"
class="nav_item pointer"
:style="{
'background-image': i.type === activeNav.type ? activeBg : nonactiveBg,
color: i.type === activeNav.type ? '' : '#666',
}"
@click="clickAnswerStatus(i)"
>
{{ i.name }}
</div>
</div>
<Empty v-if="loading" class="empty" type="loading" content=""></Empty>
<div v-else class="area">
<div
ref="imgArea"
:style="{
transform: `translate(${movableData.x}px, ${movableData.y}px) scale(${movableData.scale})`,
}"
class="area-content"
@mousedown.prevent="startDrag"
@touchstart.prevent="startDrag"
>
<img ref="img" :src="activeNav.url" alt="" @load="onImageLoad" />
</div>
<div class="tool">
<Icon
name="homeWork_temporary_fangda"
width="48"
class="pointer tool_fangda"
@click="tapEnlarge"
></Icon>
<Icon
name="homeWork_temporary_suoxiao"
width="48"
class="pointer"
@click="tapLessen"
></Icon>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.sinle_temporary {
@include main_bg;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
.top {
padding: 3vh 64px 0;
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.main {
padding: 0 64px;
width: 100%;
height: 100%;
display: flex;
&_answer {
position: relative;
width: 1792px;
height: 85.25vh;
background-size: 100%;
.nav {
position: absolute;
top: -4.75vh;
width: calc(100% - 394px);
margin-left: 197px;
display: flex;
justify-content: center;
font-family: $font-special;
font-size: 3.33vh;
font-weight: 400;
line-height: 5vh;
text-align: center;
&_item {
width: 300px;
height: 7.67vh;
line-height: 7.67vh;
border-radius: 3.33vh 3.33vh 0 0;
background-size: 100%;
background-repeat: no-repeat;
&:first-child {
margin-right: 24px;
}
}
}
.area {
position: absolute;
top: 9.58vh;
left: 48px;
display: flex;
align-items: center;
justify-content: center;
width: calc(100% - 96px);
height: 71.92vh;
overflow: hidden;
.tool {
position: absolute;
bottom: 2vh;
display: flex;
align-items: center;
justify-content: center;
width: 188px;
height: 5.33vh;
border-radius: 4.5vh;
background: rgba(0, 0, 0, 0.45);
&_fangda {
margin-right: 32px;
}
}
.area-content {
width: 825px;
height: 71.92vh;
}
img {
width: 100%;
max-height: 100%;
object-fit: contain;
cursor: grab;
}
}
}
}
}
</style>

View File

@ -1,557 +0,0 @@
<script setup lang="ts">
import { useRouter, useRoute } from 'vue-router'
import RoundSplit from '@/page/subject/freeStudy/views/textbookDetail/RoundSplit.vue'
import LoadIcon from '@/page/system/mailbox/components/LoadIcon.vue'
import VideoFullBox from '@/page/subject/components/VideoFullBox/index.vue'
import homeWorkApi from '@/api/system/homeWork'
import { debounce, useAudio } from '@/hooks'
import { subjectStore } from '@/stores'
import { resource_type_analogy, resource_type_wrong_book } from '@/page/subject/doWork'
// import { userStore } from '@/stores';
const router = useRouter()
const route = useRoute()
// const { userInfo } = userStore();
const subjectId = ref(2)
const { getSubjectAllList } = subjectStore()
const { subjectList } = toRefs(subjectStore())
const subjectComputed = computed(() =>
subjectList.value.map(({ id, name }: anyObj) => ({ value: id, label: name })),
)
const subjectiveFlag = ref<0 | 1>(1)
const subjectiveConfig = reactive([
{ value: 1, label: '客观题' },
{ value: 0, label: '主观题' },
])
const correctFlag = ref<0 | 1>(0)
const correctConfig = reactive([
{ value: 0, label: '未订正' },
{ value: 1, label: '已订正' },
])
const unCorrectNum = ref(0)
async function getCorrectConfig() {
const { data: res } = await homeWorkApi.getWrongNum({
subjectId: subjectId.value,
subjectiveFlag: subjectiveFlag.value,
})
unCorrectNum.value = res.data.unCorrectNum
correctConfig[0].label = `未订正 ${res.data.unCorrectNum > 999 ? '999+' : res.data.unCorrectNum}`
correctConfig[1].label = `已订正 ${res.data.correctNum > 999 ? '999+' : res.data.correctNum}`
}
//
const list = ref<anyObj[]>([])
const page = reactive({
size: 10,
current: 1,
total: 0,
})
const loading = ref(false)
async function getList() {
const { data: res } = await homeWorkApi.getWrongList({
subjectId: subjectId.value,
subjectiveFlag: subjectiveFlag.value,
correctFlag: correctFlag.value,
...page,
})
if (res.code !== 200) return
list.value.push(...(res.rows || []))
page.total = res.total
}
const scrollBoxRef = ref()
async function handleScroll(event: any) {
if (list.value.length >= page.total) return
if (loading.value) return
if (event.target.scrollTop + event.target.offsetHeight >= scrollBoxRef.value?.offsetHeight - 50) {
page.current++
try {
loading.value = true
await getList()
} catch {
page.current--
}
loading.value = false
}
}
const debounceScroll = debounce(handleScroll, 200, false)
const pageLoading = ref(false)
const emptyText = ref('暂无错题')
//
async function initList(pageSize = 0) {
if (pageLoading.value) return
pageLoading.value = true
list.value = []
page.current = 1
pageSize && (page.size = pageSize)
try {
await getList()
emptyText.value = correctFlag.value ? '暂无已订正错题' : '暂无未订正错题'
} catch {
emptyText.value = '网络出问题了啦~'
}
pageLoading.value = false
}
async function init(size = 0) {
getCorrectConfig()
await initList(size)
}
/*
** 音频
*/
//
let player: anyObj = {}
const activeAudio = ref('')
function handlePlay(item: anyObj) {
activeAudio.value = item.subjectTitleVo.id
player.src = item.subjectTitleVo.voiceUrl
player.play()
}
//
function startTrain(item: anyObj) {
router.push({
path: '/subject/doWork',
query: {
resourceType: resource_type_analogy,
subjectId: subjectId.value,
titleId: item.id,
isHomework: resource_type_analogy,
},
})
}
const debounceStartTrain = debounce(startTrain, 500)
function handleStart() {
if (unCorrectNum.value <= 1) {
handleDiaOK()
} else {
showDia.value = true
}
}
//
const showDia = ref(false)
const correctId = ref<string>('') // id
const correctNum = ref(1)
async function handleDiaOK() {
if (!correctId.value) {
router.push({
path: '/subject/doWork',
query: {
resourceType: resource_type_wrong_book,
num: correctNum.value,
subjectId: subjectId.value,
isHomework: resource_type_wrong_book,
},
})
} else {
await homeWorkApi.errorEdition(correctId.value)
await init(list.value.length)
showDia.value = false
page.size = 10
}
}
function changeCorrectNum(type: 0 | 1) {
if (type) {
if (correctNum.value >= 10) return
correctNum.value++
} else {
if (correctNum.value <= 1) return
correctNum.value--
}
}
//
function handleCorrection(item: anyObj) {
correctId.value = item.userErrorEditionId
showDia.value = true
}
function handleDiaClosed() {
correctId.value = ''
}
//
const videoDia = reactive<anyObj>({
show: false,
knowledge: {},
})
function playVideo(item: anyObj) {
// videoDia.videoList = list;
videoDia.show = true
videoDia.knowledge.id = item.id
videoDia.knowledge.name = item.name
}
//
const imgDia = reactive({
show: false,
url: '',
})
function handleShow(url: string) {
imgDia.show = true
imgDia.url = url
}
onMounted(async () => {
player = useAudio({
autoplay: false,
})
if (subjectList.value?.length <= 0) {
await getSubjectAllList()
}
subjectId.value = subjectComputed.value[0].value
})
onActivated(async () => {
if (route.meta.subjectId) {
subjectId.value = Number(route.meta.subjectId as string)
}
pageLoading.value = false
loading.value = false
activeAudio.value = ''
correctFlag.value = 0
subjectiveFlag.value = 1
showDia.value = false
correctNum.value = 1
init()
})
onDeactivated(() => {
showDia.value = false
player.pause?.()
})
onUnmounted(() => {
player.destroy()
})
</script>
<template>
<div class="wrong-book">
<div class="top">
<BackBar leftText="错题本" />
<div class="top-select-tab">
<SelectTab v-model="subjectiveFlag" :dataList="subjectiveConfig" @change="init"></SelectTab>
</div>
<div class="top-select">
<Select v-model="subjectId" :selectList="subjectComputed" @selectChange="init"></Select>
</div>
</div>
<div class="main">
<div class="main-top">
<div class="main-top-select">
<SelectTab v-model="correctFlag" :dataList="correctConfig" @change="initList"></SelectTab>
</div>
</div>
<RoundSplit :length="88"></RoundSplit>
<div class="main-box">
<template v-if="list.length">
<div class="main-list hidden_scroll" @scroll="debounceScroll">
<div ref="scrollBoxRef" class="scroll-box QuesItemBox">
<template v-for="i in list" :key="i.id">
<WrongQuesItem
:correctFlag="correctFlag"
:subjectiveFlag="subjectiveFlag"
:data="i"
:time="i.createTime"
:activeAudio="activeAudio"
@startTrain="debounceStartTrain(i)"
@play="handlePlay(i)"
@playVideo="playVideo"
@showPic="handleShow"
>
<template #customBtn>
<Btn
v-if="subjectiveFlag === 1"
class="custom-btn"
width="192"
height="64"
size="32"
@click="debounceStartTrain(i)"
>
举一反三
</Btn>
<Btn
v-else-if="subjectiveFlag === 0 && correctFlag === 0"
class="custom-btn"
width="192"
height="64"
size="32"
@click="handleCorrection(i)"
>
订正错题
</Btn>
<div v-else></div>
</template>
</WrongQuesItem>
</template>
</div>
</div>
<LoadIcon v-if="loading" class="load-icon"></LoadIcon>
</template>
<Empty
v-else
class="empty"
:type="pageLoading ? 'loading' : 'learn'"
:content="emptyText"
></Empty>
</div>
</div>
<Btn
v-if="unCorrectNum && subjectiveFlag === 1"
class="start_train-btn"
type="success"
width="232"
height="80"
@click="handleStart"
>
订正错题
</Btn>
<mj-dialog
v-model="showDia"
hide-cancel
:ok-text="!correctId ? '开始' : '确定'"
title="订正错题"
:ok="handleDiaOK"
:closed="handleDiaClosed"
>
<div class="dia_body">
<template v-if="!correctId">
<div class="dia_body-title">系统将从您的错题本中随机选择题目</div>
<div class="dia_body-slider">
<div class="dia_body-slider-box">
<Icon name="slider_btn_reduce" width="56" @click="changeCorrectNum(0)"></Icon>
<el-slider
v-model="correctNum"
:show-tooltip="false"
:step="1"
:max="unCorrectNum > 10 ? 10 : unCorrectNum"
:min="1"
></el-slider>
<Icon name="slider_btn_add" width="56" @click="changeCorrectNum(1)"></Icon>
</div>
<div class="dia_body-slider-text">
数量{{ correctNum }}/{{ unCorrectNum > 10 ? 10 : unCorrectNum }}
</div>
</div>
</template>
<template v-else>
<div class="dia_body-title text">请确认该题已掌握订正后将移动至 已订正模块</div>
</template>
</div>
</mj-dialog>
<VideoFullBox
v-model:show="videoDia.show"
:knowledge="videoDia.knowledge"
:data="{
subjectId,
resourceType: 2,
origin: 2,
}"
></VideoFullBox>
<ImgView v-model="imgDia.show" :url="imgDia.url"></ImgView>
</div>
</template>
<style scoped lang="scss">
.wrong-book {
@include main_bg;
position: relative;
width: 100vw;
height: 100%;
overflow: hidden;
box-sizing: border-box;
line-height: 1.5;
// @include hidden_scroll;
.top {
width: 100%;
padding: 2.67vh 64px 0;
position: absolute;
top: 0;
left: 0;
z-index: 1;
margin-bottom: 2vh;
display: flex;
align-items: center;
justify-content: space-between;
z-index: auto;
&-select-tab {
flex: 1;
margin-left: 62px;
:deep(.select-list) {
width: 968px;
.data-item {
min-width: 50%;
&.active {
background: linear-gradient(269deg, #86e6ff 2.25%, #9effda 51.93%, #5fdcfd 99.65%);
backdrop-filter: blur(3px);
}
}
}
}
}
.main {
position: relative;
margin: 11.33vh auto 0;
background-size: 100%;
width: 1792px;
height: 86vh;
// padding: 91px 24px 24px;
&-top {
width: 100%;
height: 8.5vh;
border-radius: 1.67vh;
background: linear-gradient(269deg, #86e6ff 2.25%, #9effda 51.93%, #5fdcfd 99.65%);
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 32px;
&-select {
:deep(.select-list) {
border-radius: 4.5vh;
.data-item {
min-width: 258px;
width: 50%;
border-radius: 4.5vh;
&.active {
background: linear-gradient(90deg, #86e6ff 0%, #9effda 50%, #86e6ff 100%);
}
}
}
}
}
.round-split {
position: absolute;
width: calc(100% - 50px);
left: 50%;
transform: translate(-50%);
// z-index: 1;
top: 7.92vh;
:deep(.dot) {
background: linear-gradient(180deg, #798498 0%, #a1b1cb 100%);
}
}
&-box {
height: calc(100% - 110px);
border-radius: 1.67vh;
background: linear-gradient(180deg, #f0faff 0%, #fafdff 100%);
padding: 2.67vh;
}
&-list {
height: 100%;
// margin: 0 32px;
// .scroll-box {
// height: 100%;
//}
.custom-btn {
min-height: 5.67vh;
margin-left: 24px;
border-radius: 4.42vh;
border: 0.17vh solid #fff;
box-shadow: 0 0.17vh 0.17vh 0 rgba(0, 0, 0, 0.25);
color: $font-color;
.x-icon {
margin-left: 16px;
transform: rotate(180deg);
}
}
}
.custom_load_icon {
position: absolute;
bottom: 3.17vh;
left: 50%;
transform: translate(-50%);
}
.empty {
height: 75vh;
}
}
.start_train-btn {
position: absolute;
bottom: 13.33vh;
right: -35px;
z-index: 100;
:deep(.x-button-text) {
transform: translate(-8px);
}
}
}
.dia_body {
// height: 392px;
padding: 3.33vh 0;
text-align: center;
color: $font-color;
line-height: 1.5;
&-title {
text-align: center;
font-family: $font-special;
font-size: 2.67vh;
&.text {
width: 510px;
margin: 4.17vh auto 0;
}
}
&-slider {
margin-top: 3.33vh;
&-box {
display: flex;
align-items: center;
padding: 0 24px;
.x-icon {
margin: 0 32px;
flex-shrink: 0;
}
}
:deep(.el-slider) {
height: 4.67vh;
.el-slider__runway {
height: 3vh;
background: #9a9ea0;
border-radius: 2.5vh;
}
.el-slider__bar {
height: 3vh;
border-radius: 2.5vh;
background: linear-gradient(249deg, #4de798 26.9%, #a2fb6e 79.86%);
}
.el-slider__button-wrapper {
width: 56px;
height: 4.67vh;
background: radial-gradient(189.06% 189.06% at 49.22% 50.78%, #fff 0%, #becadf 100%);
border-radius: 50%;
filter: drop-shadow(0px 3.5px 3.5px rgba(0, 0, 0, 0.15));
top: -0.83vh;
}
.el-slider__button {
width: 56px;
height: 4.67vh;
border: none;
}
}
&-text {
margin-top: 1.33vh;
font-family: OPPOSans;
font-size: 2vh;
}
}
}
</style>

View File

@ -242,12 +242,7 @@ onUnmounted(() => {
})
</script>
<template>
<div
class="uncomplete"
:style="{
'background-image': `url(${OSS_URL}/urm/mail_bg.svg)`,
}"
>
<div class="uncomplete">
<div v-if="!initLoading" class="hidden_scroll">
<div
class="book"
@ -463,6 +458,7 @@ onUnmounted(() => {
box-sizing: border-box;
background-repeat: no-repeat;
background-size: cover;
background-color: $theme-color-bg;
.book {
box-sizing: border-box;
@include book_style;

View File

@ -122,12 +122,11 @@ function handleGetDynamic(item: any) {
<style lang="scss" scoped>
.dynamic-list {
width: 127.33vh;
// 1240 +168 +120
display: flex;
justify-content: space-between;
position: absolute;
top: 86vh;
left: 4vh;
top: 85vh;
left: 1vh;
height: 10.83vh;
&-item,
.day,
@ -229,7 +228,7 @@ function handleGetDynamic(item: any) {
}
.achievement-reward {
width: 97.17vh;
margin-left: 15vh;
margin-left: 14vh;
.bg-slider {
width: 78.33vh;
left: 9.33vh;

View File

@ -476,7 +476,7 @@ onUnmounted(() => {
width: 100%;
height: 100%;
position: relative;
background: $blue-gradient;
background: $theme-color-bg;
display: flex;
gap: 10px;
padding-left: $space-6;
@ -493,6 +493,7 @@ onUnmounted(() => {
$right_w: 131.92vh;
.right {
width: $right_w;
margin-left: 2vh;
position: relative;
}
.button-allReward {

View File

@ -1,8 +1,11 @@
// 主题
$white-gradient: #fdfdf8; // 白系背景
$blue-gradient: #7CABFF;// 蓝色背景
$theme-color: #ffe60f; // 品牌色/主操作按钮/按钮移入/文字链
$theme-minor-color: #fffbd2; // 品牌色/主操作按钮/按钮移入/文字链
// 主题色背景
$theme-color-bg: #D4E0F5;
$theme-color-bg: $blue-gradient; // #b1c3e3;
// 辅助颜色
$success-color: #07cc89;
@ -79,8 +82,6 @@ $border-input-color: #e8eaec;
$gradient: linear-gradient(180deg, #4de798 0%, #a2fb6e 100%); // 主渐变
$gray_bg: $theme-color-bg; // 灰系背景
$grey-gradient: $theme-color-bg; // 灰系背景
$white-gradient: $theme-color-bg; // 白系背景
$blue-gradient: #7CABFF;// 蓝色背景
$page-fill: #fafafa;
$bg-fill: #fff;