526 lines
16 KiB
Vue
526 lines
16 KiB
Vue
<template>
|
||
<view class="history-page">
|
||
<!-- 顶部栏 -->
|
||
<view class="top">
|
||
<qy-BackBar leftText="作业记录" />
|
||
<qy-Select
|
||
v-model="activeStatus"
|
||
:selectList="statusList"
|
||
@selectChange="handleChangeStatus"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 有学科数据时显示主体内容 -->
|
||
<view v-if="subjectData.length" class="main">
|
||
<!-- 侧边栏 - 学科选择 -->
|
||
<view class="main-sidebar">
|
||
<qy-Sidebar
|
||
v-model="activeClass"
|
||
:config="subjectData"
|
||
@handleChange="handleSelectClass"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 作业列表区域 -->
|
||
<view class="list-box">
|
||
<template v-if="jobList.length">
|
||
<scroll-view class="list" scroll-y :show-scrollbar="false">
|
||
<view
|
||
v-for="item in jobList"
|
||
:key="item.id"
|
||
class="list-item"
|
||
@click="toReports(item)"
|
||
>
|
||
<view class="list-item-info">
|
||
<view class="list-item-title">{{ activeSubjectName }}</view>
|
||
<view class="list-item-name">{{ item.paperName }}</view>
|
||
<view class="list-item-time">
|
||
{{ item.createTime ? `发布时间:${formatTime(item.createTime)}` : '' }}
|
||
|
||
{{ item.submitEndTime ? `截止时间:${formatTime(item.submitEndTime)}` : '' }}
|
||
|
||
{{ item.markTime ? `批改时间:${formatTime(item.markTime)}` : '' }}
|
||
</view>
|
||
<!-- 已批改时显示得分率 -->
|
||
<view v-if="item.gradingStatus === 1" class="list-item-rate" :style="{ color: getRateConfig(item).color }">
|
||
<text>得分率:{{ getScoreRate(item) }}%</text>
|
||
<qy-ProgressBar
|
||
:rate="item.score / item.totalScore"
|
||
width="196"
|
||
v-bind="getRateConfig(item)"
|
||
/>
|
||
<!-- 奖励显示 -->
|
||
<view class="reward-list">
|
||
<view v-if="item.addDiamond" class="reward-list-item">
|
||
<image :src="`${OSS_URL}/icon/diamond_nobg.svg`" mode="aspectFit" />
|
||
<text class="text">+{{ item.addDiamond }}</text>
|
||
</view>
|
||
<view v-if="item.addExp" class="reward-list-item">
|
||
<image :src="`${OSS_URL}/icon/exp_nobg.svg`" mode="aspectFit" />
|
||
<text class="text">+{{ item.addExp }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view
|
||
:class="['list-item-status', { active: item.gradingStatus === 1 }]"
|
||
:style="{
|
||
backgroundImage: item.gradingStatus === 1
|
||
? `url(${OSS_URL}/urm/homeWork/corrected.svg)`
|
||
: `url(${OSS_URL}/urm/homeWork/uncorrected.svg)`
|
||
}"
|
||
>
|
||
<text>{{ item.gradingStatus === 1 ? '已批改' : '未批改' }}</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 分页 -->
|
||
<view class="page" v-if="page.total > page.pageSize">
|
||
<view v-if="page.pageNum > 1" class="page-btn" @click="changePage(0)">
|
||
<text>上一页</text>
|
||
</view>
|
||
<text class="page-info">{{ page.pageNum }}/{{ Math.ceil(page.total / page.pageSize) }}</text>
|
||
<view
|
||
v-if="page.pageNum * page.pageSize < page.total"
|
||
class="page-btn success"
|
||
@click="changePage(1)"
|
||
>
|
||
<text>下一页</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<!-- 空状态 -->
|
||
<qy-Empty
|
||
v-else
|
||
class="empty"
|
||
:type="listLoading ? 'loading' : pageError ? 'network' : 'learn'"
|
||
:content="pageError ? '网络出问题了啦' : '暂无作业记录,快去做作业吧!'"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 无学科数据时空状态 -->
|
||
<qy-Empty
|
||
v-else
|
||
class="empty-page"
|
||
:type="pageLoading ? 'loading' : 'learn'"
|
||
content="暂无作业记录,快去做作业吧!"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, reactive, onMounted } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import { getListCompleteRecord } from '@/api/homework';
|
||
import { user } from '@/store';
|
||
import { storeToRefs } from 'pinia';
|
||
import dayjs from 'dayjs';
|
||
|
||
const OSS_URL = 'https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com';
|
||
|
||
const userStore = user();
|
||
const { userInfo } = storeToRefs(userStore);
|
||
|
||
// 颜色配置
|
||
const colorConfig: Record<string, string[]> = {
|
||
primary: ['#2D39BC', '#2D39BC', '#6B73E5', '#90FCF7'],
|
||
success: ['#2F8A5B', '#27A363', '#25BF6E', '#1FFF8A'],
|
||
error: ['#BC2D37', '#B22731', '#CB3741', '#FF3542'],
|
||
warning: ['#a13c00', '#C08129', '#CC8A33', '#FFFE36'],
|
||
};
|
||
|
||
// 状态选项
|
||
const statusList = [
|
||
{ label: '全部', value: '' },
|
||
{ label: '已批改', value: '1' },
|
||
{ label: '未批改', value: '0' },
|
||
];
|
||
|
||
// 筛选状态
|
||
const activeStatus = ref('');
|
||
// 学科数据(与 pages/homework/index 保持一致结构)
|
||
const subjectData = ref<any[]>([]);
|
||
// 当前选中的学科 id(string,与 homework/index 一致)
|
||
const activeClass = ref<string>('');
|
||
// 作业列表
|
||
const jobList = ref<any[]>([]);
|
||
// 分页
|
||
const page = reactive({
|
||
pageSize: 10,
|
||
pageNum: 1,
|
||
total: 0,
|
||
});
|
||
// 加载状态
|
||
const listLoading = ref(false);
|
||
const pageLoading = ref(false);
|
||
const pageError = ref(false);
|
||
|
||
// 格式化时间
|
||
const formatTime = (time: string) => {
|
||
return time ? dayjs(time).format('YYYY/MM/DD HH:mm') : '';
|
||
};
|
||
|
||
// 计算得分率
|
||
const getScoreRate = (item: any) => {
|
||
if (!item.totalScore) return 0;
|
||
return ((item.score / item.totalScore) * 100).toFixed(0);
|
||
};
|
||
|
||
// 获取得分率类型
|
||
const rateType = (rate: number) => {
|
||
if (rate >= 80) return 'success';
|
||
if (rate >= 60) return 'primary';
|
||
if (rate >= 40) return 'warning';
|
||
return 'error';
|
||
};
|
||
|
||
// 获取颜色配置
|
||
const getRateConfig = (item: any) => {
|
||
const rate = item.totalScore ? (item.score / item.totalScore) * 100 : 0;
|
||
const type = rateType(rate);
|
||
const config = colorConfig[type] || colorConfig.primary;
|
||
return {
|
||
color: config[0],
|
||
border: config[1],
|
||
bg: config[2],
|
||
activeBg: config[3],
|
||
};
|
||
};
|
||
|
||
// 当前选中学科名称
|
||
const activeSubjectName = computed(() => {
|
||
return subjectData.value.find(i => i.id === activeClass.value)?.name || '';
|
||
});
|
||
|
||
// 初始化学科:从已完成作业记录中按学科分组,结构与 pages/homework/index 一致
|
||
const initSubject = async () => {
|
||
try {
|
||
// 只按 subjectId 维度拉一遍记录,用于构造学科列表
|
||
const res = await getListCompleteRecord({});
|
||
const records = res.data || [];
|
||
|
||
const subjectsMap = new Map<string, any>();
|
||
const subjects: any[] = [];
|
||
|
||
records.forEach((item: any) => {
|
||
const sid = String(item.subjectId);
|
||
if (!subjectsMap.has(sid)) {
|
||
subjectsMap.set(sid, true);
|
||
subjects.push({
|
||
id: sid,
|
||
name: item.subjectName,
|
||
warning: false,
|
||
});
|
||
}
|
||
});
|
||
|
||
subjectData.value = subjects;
|
||
if (subjects.length && !activeClass.value) {
|
||
activeClass.value = subjects[0].id;
|
||
}
|
||
} catch (error) {
|
||
console.error('初始化学科失败', error);
|
||
}
|
||
};
|
||
|
||
// 获取作业列表
|
||
const getJobList = async () => {
|
||
try {
|
||
const params: Record<string, any> = {};
|
||
// 只有选择了“已批改/未批改”时才传 gradingStatus,避免出现 gradingStatus=undefined
|
||
if (activeStatus.value !== '') {
|
||
params.gradingStatus = activeStatus.value;
|
||
}
|
||
if (activeClass.value) {
|
||
params.subjectId = activeClass.value;
|
||
}
|
||
|
||
const res = await getListCompleteRecord(params);
|
||
jobList.value = res.data || [];
|
||
page.total = res.data?.length || 0;
|
||
} catch (error) {
|
||
console.error('获取作业记录失败', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// 切换状态
|
||
const handleChangeStatus = () => {
|
||
refreshJobList();
|
||
};
|
||
|
||
// 切换学科
|
||
const handleSelectClass = (item: any) => {
|
||
refreshJobList();
|
||
};
|
||
|
||
// 分页切换
|
||
const changePage = async (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;
|
||
await getJobList();
|
||
} catch {
|
||
pageError.value = true;
|
||
}
|
||
listLoading.value = false;
|
||
};
|
||
|
||
// 刷新列表
|
||
const refreshJobList = async () => {
|
||
pageError.value = false;
|
||
listLoading.value = true;
|
||
jobList.value = [];
|
||
page.pageNum = 1;
|
||
try {
|
||
await getJobList();
|
||
} catch {
|
||
pageError.value = true;
|
||
}
|
||
listLoading.value = false;
|
||
};
|
||
|
||
// 跳转报告页
|
||
const toReports = (item: any) => {
|
||
if (item.gradingStatus !== 1) {
|
||
uni.showToast({
|
||
title: '作业未批改,请耐心等待老师批改',
|
||
icon: 'none',
|
||
});
|
||
return;
|
||
}
|
||
uni.navigateTo({
|
||
url: `/pages/homework/reports/index?id=${item.id}&name=${encodeURIComponent(item.paperName)}`,
|
||
});
|
||
};
|
||
|
||
onMounted(async () => {
|
||
pageLoading.value = true;
|
||
listLoading.value = true;
|
||
try {
|
||
await initSubject();
|
||
if (subjectData.value.length) {
|
||
await getJobList();
|
||
}
|
||
} catch (error) {
|
||
console.error('初始化失败', error);
|
||
}
|
||
listLoading.value = false;
|
||
pageLoading.value = false;
|
||
});
|
||
|
||
// 页面显示时刷新
|
||
onShow(() => {
|
||
if (subjectData.value.length) {
|
||
refreshJobList();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.history-page {
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
background-image: url('https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com/urm/main_bg.svg');
|
||
background-size: cover;
|
||
}
|
||
|
||
.top {
|
||
padding: 12rpx 20rpx 0;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.main {
|
||
height: calc(100vh - 52rpx);
|
||
padding-left: 12rpx;
|
||
padding-right: 12rpx;
|
||
padding-top: 8rpx;
|
||
position: relative;
|
||
display: flex;
|
||
|
||
&-sidebar {
|
||
width: 100rpx;
|
||
height: 100%;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.list-box {
|
||
flex: 1;
|
||
height: 100%;
|
||
margin-left: 10rpx;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.page {
|
||
margin-top: 6rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
padding: 0 8rpx;
|
||
|
||
&-btn {
|
||
padding: 6rpx 14rpx;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
border-radius: 10rpx;
|
||
margin: 0 10rpx;
|
||
|
||
&.success {
|
||
background: linear-gradient(180deg, #4cd964 0%, #2db84d 100%);
|
||
|
||
text {
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
text {
|
||
font-family: $font-special;
|
||
font-size: 12rpx;
|
||
color: $font-color;
|
||
}
|
||
}
|
||
|
||
&-info {
|
||
font-family: $font-special;
|
||
font-size: 12rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
|
||
.list-item {
|
||
display: flex;
|
||
align-items: center;
|
||
border-radius: 10rpx;
|
||
background: #fff;
|
||
overflow: hidden;
|
||
margin-bottom: 8rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
&-info {
|
||
flex: 1;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
&-title {
|
||
display: inline-block;
|
||
padding: 0 8rpx;
|
||
height: 32rpx;
|
||
line-height: 32rpx;
|
||
background: #7effff;
|
||
margin-bottom: 6rpx;
|
||
width: auto;
|
||
max-width: 200rpx;
|
||
box-sizing: border-box;
|
||
text-align: center;
|
||
font-family: $font-special;
|
||
font-size: 12rpx;
|
||
color: $font-color;
|
||
border-top-left-radius: 10rpx;
|
||
@include single-ellipsis;
|
||
}
|
||
|
||
&-name {
|
||
font-family: 'OPPO Sans';
|
||
font-size: 12rpx;
|
||
color: #666;
|
||
padding-left: 12rpx;
|
||
margin-bottom: 4rpx;
|
||
@include single-ellipsis;
|
||
}
|
||
|
||
&-time {
|
||
font-family: 'OPPO Sans';
|
||
font-size: 10rpx;
|
||
color: #999;
|
||
padding-left: 12rpx;
|
||
padding-bottom: 8rpx;
|
||
}
|
||
|
||
&-status {
|
||
width: 100rpx;
|
||
height: 100%;
|
||
min-height: 70rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
background-repeat: no-repeat;
|
||
background-size: 100% 100%;
|
||
background-position: center;
|
||
|
||
text {
|
||
font-family: $font-special;
|
||
font-size: 12rpx;
|
||
color: $font-color;
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
&-rate {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
padding-left: 12rpx;
|
||
padding-bottom: 6rpx;
|
||
font-family: $font-special;
|
||
font-size: 11rpx;
|
||
|
||
.reward-list {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-left: 10rpx;
|
||
|
||
&-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-right: 8rpx;
|
||
|
||
image {
|
||
width: 18rpx;
|
||
height: 18rpx;
|
||
margin-right: 4rpx;
|
||
}
|
||
|
||
.text {
|
||
font-family: $font-special;
|
||
font-size: 11rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty {
|
||
flex: 1;
|
||
}
|
||
|
||
.empty-page {
|
||
height: calc(100vh - 200rpx);
|
||
}
|
||
</style>
|