2026-02-05 12:47:24 +08:00

526 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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)}` : '' }}
&nbsp;&nbsp;
{{ item.submitEndTime ? `截止时间:${formatTime(item.submitEndTime)}` : '' }}
&nbsp;&nbsp;
{{ 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[]>([]);
// 当前选中的学科 idstring与 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>