fix: 修复一些问题

This commit is contained in:
阿梦 2026-05-31 14:17:51 +08:00
parent 3f826a1a67
commit 9badd93bfe
7 changed files with 435 additions and 43 deletions

View File

@ -140,6 +140,7 @@ export const getPaperRecordDetail = (data: { recordId: string; reportFlag: numbe
export interface JobReportVo { export interface JobReportVo {
actualScore?: number; actualScore?: number;
markScore?: number;
beginTime?: string; beginTime?: string;
costTime?: number; costTime?: number;
jobId?: number; jobId?: number;
@ -179,17 +180,24 @@ export interface JobAnalyseVo {
stuRankVoList?: StuRankVoItem[]; stuRankVoList?: StuRankVoItem[];
} }
/** 作业整体分析 */ /** 作业整体分析(家长端查看孩子报告时需传 userId */
export const getJobAnalyse = (params: { recordId: string }) => export const getJobAnalyse = (params: { recordId: string; userId?: string }) =>
request<JobAnalyseVo>({ request<JobAnalyseVo>({
url: '/sc/job/getJobAnalyse', url: '/sc/job/getJobAnalyse',
method: 'GET', method: 'GET',
params, params,
}); });
/** 知识点分析 */ export interface KnowledgeAnalyzeItem {
export const getKnowledgeAnalyse = (params: { recordId: string }) => knowledgeId?: string;
request<any[]>({ knowledgeName?: string;
answerScore?: number;
titleScore?: number;
}
/** 知识点分析(家长端查看孩子报告时需传 userId */
export const getKnowledgeAnalyse = (params: { recordId: string; userId?: string }) =>
request<KnowledgeAnalyzeItem[]>({
url: '/sc/job/statisticalKnowledge', url: '/sc/job/statisticalKnowledge',
method: 'GET', method: 'GET',
params, params,

View File

@ -80,6 +80,7 @@ export const parentSmsLogin = (data: ParentSmsLoginParams) =>
data: { data: {
bindMaOpenid: true, bindMaOpenid: true,
adminType: 16, adminType: 16,
clientType: 'PARENT',
phone: data.phone, phone: data.phone,
verifyCode: data.verifyCode, verifyCode: data.verifyCode,
wxUser: { wxUser: {

View File

@ -109,7 +109,7 @@ export interface KpointScoreItem {
kpointId?: number; kpointId?: number;
kpointName?: string; kpointName?: string;
actualScore?: number; actualScore?: number;
fullscore?: number; fullScore?: number;
scoreRate?: number; scoreRate?: number;
scoreRatePercent?: number; scoreRatePercent?: number;
levelCode?: number; levelCode?: number;

View File

@ -240,7 +240,7 @@ const levelKnowledge = computed<ScoreViewItem[]>(() => {
return (level?.items || []).map((item) => ({ return (level?.items || []).map((item) => ({
kpointId: item.kpointId ?? 0, kpointId: item.kpointId ?? 0,
kpointName: item.kpointName || '--', kpointName: item.kpointName || '--',
fullScore: item.fullscore ?? 0, fullScore: item.fullScore ?? 0,
score: item.actualScore ?? 0, score: item.actualScore ?? 0,
scoreRatePercent: item.scoreRatePercent ?? 0, scoreRatePercent: item.scoreRatePercent ?? 0,
})); }));

View File

@ -43,24 +43,100 @@
<template v-if="activeTab === 'analysis'"> <template v-if="activeTab === 'analysis'">
<ParentCard class="section-card" title="题型分布" subtitle="按题型拆解本次卷面构成"> <ParentCard class="section-card" title="题型分布" subtitle="按题型拆解本次卷面构成">
<view v-if="pageLoading && !analysisRows.length" class="answers-loading"> <view v-if="pageLoading && !channelAnalyzeList.length" class="answers-loading">
<xy-Empty type="loading" content="加载分析数据..." /> <xy-Empty type="loading" content="加载分析数据..." />
</view> </view>
<view v-else-if="analysisRows.length" class="data-table"> <view v-else-if="channelAnalyzeList.length" class="chart-block">
<view class="table-row table-head"> <view class="pie-row">
<text>题型</text> <view class="pie">
<text>题量</text> <view class="pie-circle" :style="channelPieStyle"></view>
<text>占比</text> <view class="pie-hole">
<text class="pie-hole-num">{{ totalQuestionCount }}</text>
<text class="pie-hole-label"></text>
</view>
</view>
<view class="pie-legend">
<view
v-for="(item, idx) in channelAnalyzeList"
:key="idx"
class="legend-item"
>
<view
class="legend-dot"
:style="{ background: channelPieColors[idx % channelPieColors.length] }"
></view>
<text class="legend-name">{{ item.chanelTypeName }}</text>
<text class="legend-count">{{ item.titleCount }}</text>
</view>
</view>
</view> </view>
<view v-for="row in analysisRows" :key="row.type" class="table-row channel-row"> <view class="data-table chart-table">
<text>{{ row.type }}</text> <view class="table-row table-head channel-row">
<text class="strong">{{ row.count }}</text> <text>题型</text>
<text class="rate-good">{{ row.rate }}</text> <text>题量</text>
<text>占比</text>
</view>
<view v-for="row in analysisRows" :key="row.type" class="table-row channel-row">
<text>{{ row.type }}</text>
<text class="strong">{{ row.count }}</text>
<text class="rate-good">{{ row.rate }}</text>
</view>
</view> </view>
</view> </view>
<xy-Empty v-else type="task" content="暂无题型分析数据" /> <xy-Empty v-else type="task" content="暂无题型分析数据" />
</ParentCard> </ParentCard>
<ParentCard class="section-card" title="难度分布" subtitle="各难度档位题量占比">
<view v-if="pageLoading && !difficultyAnalyzeList.length" class="answers-loading">
<xy-Empty type="loading" content="加载分析数据..." />
</view>
<view v-else-if="difficultyAnalyzeList.length" class="chart-block">
<view class="pie-row">
<view class="pie">
<view class="pie-circle" :style="difficultyPieStyle"></view>
<view class="pie-hole">
<text class="pie-hole-num">{{ totalDifficultyCount }}</text>
<text class="pie-hole-label"></text>
</view>
</view>
<view class="pie-legend">
<view
v-for="(item, idx) in difficultyAnalyzeList"
:key="idx"
class="legend-item"
>
<view
class="legend-dot"
:style="{ background: getDifficultyColor(item.difficultyName) }"
></view>
<text class="legend-name">{{ item.difficultyName }}</text>
<text class="legend-count">{{ item.titleCount }}</text>
</view>
</view>
</view>
<view class="bars">
<view
v-for="(item, idx) in difficultyAnalyzeList"
:key="idx"
class="bar-row"
>
<view class="bar-name">{{ item.difficultyName }}</view>
<view class="bar-track">
<view
class="bar-fill"
:style="{
width: getDifficultyBarWidth(item.score) + '%',
background: getDifficultyColor(item.difficultyName),
}"
></view>
</view>
<view class="bar-value">{{ item.score }}<text class="bar-value-unit"></text></view>
</view>
</view>
</view>
<xy-Empty v-else type="task" content="暂无难度分析数据" />
</ParentCard>
<ParentCard class="section-card" title="难度得分" subtitle="关注中难题稳定性,决定后续练习重点"> <ParentCard class="section-card" title="难度得分" subtitle="关注中难题稳定性,决定后续练习重点">
<view v-if="pageLoading && !difficultyRows.length" class="answers-loading"> <view v-if="pageLoading && !difficultyRows.length" class="answers-loading">
<xy-Empty type="loading" content="加载分析数据..." /> <xy-Empty type="loading" content="加载分析数据..." />
@ -79,7 +155,7 @@
<text :class="row.rateValue >= 80 ? 'rate-good' : 'rate-warn'">{{ row.rate }}</text> <text :class="row.rateValue >= 80 ? 'rate-good' : 'rate-warn'">{{ row.rate }}</text>
</view> </view>
</view> </view>
<xy-Empty v-else type="task" content="暂无难度数据" /> <xy-Empty v-else type="task" content="暂无难度分数据" />
</ParentCard> </ParentCard>
<ParentCard <ParentCard
@ -247,13 +323,21 @@
<template v-if="activeTab === 'knowledge'"> <template v-if="activeTab === 'knowledge'">
<ParentCard class="section-card" title="错题知识点占比" subtitle="用于判断最近应优先补强的知识点"> <ParentCard class="section-card" title="错题知识点占比" subtitle="用于判断最近应优先补强的知识点">
<view class="knowledge-summary"> <view v-if="pageLoading && !knowledgeList.length" class="answers-loading">
<xy-Empty type="loading" content="加载知识点..." />
</view>
<view v-else-if="knowledgeRatios.length" class="knowledge-summary">
<view class="wrong-total"> <view class="wrong-total">
<text class="wrong-number">{{ wrongCount }}</text> <text class="wrong-number">{{ knowledgeWeakCount }}</text>
<text class="wrong-label">错题数</text> <text class="wrong-label">薄弱知识点</text>
</view> </view>
<view class="ratio-list"> <scroll-view
<view v-for="item in detail.knowledgeRatios" :key="item.name" class="ratio-row"> scroll-y
class="ratio-list"
:style="{ height: ratioListHeight }"
:show-scrollbar="false"
>
<view v-for="item in knowledgeRatios" :key="item.name" class="ratio-row">
<view class="ratio-meta"> <view class="ratio-meta">
<text>{{ item.name }}</text> <text>{{ item.name }}</text>
<text>{{ item.percent }}%</text> <text>{{ item.percent }}%</text>
@ -262,11 +346,16 @@
<view class="ratio-fill" :class="item.tone" :style="{ width: `${item.percent}%` }"></view> <view class="ratio-fill" :class="item.tone" :style="{ width: `${item.percent}%` }"></view>
</view> </view>
</view> </view>
</view> </scroll-view>
</view> </view>
<xy-Empty v-else type="learn" content="暂无知识点占比数据" />
</ParentCard> </ParentCard>
<ParentCard class="section-card" title="知识点得分"> <ParentCard class="section-card" title="知识点得分">
<view v-if="pageLoading && !knowledgeList.length" class="answers-loading">
<xy-Empty type="loading" content="加载知识点..." />
</view>
<template v-else-if="knowledgeList.length">
<view class="level-tabs"> <view class="level-tabs">
<view <view
v-for="level in knowledgeTabs" v-for="level in knowledgeTabs"
@ -287,10 +376,12 @@
<view v-for="item in filteredKnowledgeScores" :key="item.name" class="table-row knowledge-row"> <view v-for="item in filteredKnowledgeScores" :key="item.name" class="table-row knowledge-row">
<text class="knowledge-name">{{ item.name }}</text> <text class="knowledge-name">{{ item.name }}</text>
<text>{{ item.fullScore }}</text> <text>{{ item.fullScore }}</text>
<text class="score-danger">{{ item.score }}[{{ knowledgeRate(item) }}%]</text> <text :class="knowledgeScoreClass(item)">{{ item.score }}[{{ knowledgeRate(item) }}%]</text>
</view> </view>
</view> </view>
<xy-Empty v-if="!filteredKnowledgeScores.length" type="learn" content="当前分段暂无知识点" /> <xy-Empty v-if="!filteredKnowledgeScores.length" type="learn" content="当前分段暂无知识点" />
</template>
<xy-Empty v-else type="learn" content="暂无知识点数据" />
</ParentCard> </ParentCard>
</template> </template>
</view> </view>
@ -308,7 +399,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { getJobAnalyse, getPaperRecordDetail, type JobAnalyseVo } from '@/api/homework'; import { getJobAnalyse, getKnowledgeAnalyse, getPaperRecordDetail, type JobAnalyseVo, type KnowledgeAnalyzeItem } from '@/api/homework';
import { useParentStore } from '@/state/modules/parent';
import { storeToRefs } from 'pinia';
import ParentCard from '../components/ParentCard.vue'; import ParentCard from '../components/ParentCard.vue';
import ParentPage from '../components/ParentPage.vue'; import ParentPage from '../components/ParentPage.vue';
import { import {
@ -317,6 +410,7 @@ import {
type ReportQuestion, type ReportQuestion,
type ReportQuestionSummary, type ReportQuestionSummary,
type ReportScoreBlock, type ReportScoreBlock,
type ReportKnowledgeRatio,
} from '../utils/data'; } from '../utils/data';
import { import {
mapPaperQuestion, mapPaperQuestion,
@ -342,7 +436,23 @@ const knowledgeTabs: { key: KnowledgeLevel; label: string }[] = [
const REPORT_FLAG = 1; const REPORT_FLAG = 1;
const parentStore = useParentStore();
const { activeChild } = storeToRefs(parentStore);
const channelPieColors = ['#2563eb', '#22c55e', '#f59e0b', '#ef4444', '#0ea5e9', '#a855f7', '#ec4899'];
const difficultyBaseColorMap: Record<string, string> = {
容易: '#22c55e',
较易: '#0ea5e9',
一般: '#f59e0b',
普通: '#f59e0b',
中等: '#f59e0b',
较难: '#f97316',
困难: '#ef4444',
};
const difficultyFallbackColors = ['#6366f1', '#14b8a6', '#f472b6', '#84cc16'];
const recordId = ref(''); const recordId = ref('');
const childUserId = ref('');
const reportName = ref(''); const reportName = ref('');
const reportSubject = ref(''); const reportSubject = ref('');
const reportCategory = ref(''); const reportCategory = ref('');
@ -360,9 +470,11 @@ const questionSummaries = ref<ReportQuestionSummary[]>([]);
const questionDetailCache = ref<Record<number, ReportQuestion>>({}); const questionDetailCache = ref<Record<number, ReportQuestion>>({});
const paperDetail = ref<Record<string, unknown>>({}); const paperDetail = ref<Record<string, unknown>>({});
const jobAnalyse = ref<JobAnalyseVo>({}); const jobAnalyse = ref<JobAnalyseVo>({});
const knowledgeList = ref<KnowledgeAnalyzeItem[]>([]);
onLoad((query) => { onLoad((query) => {
recordId.value = String(query?.id || ''); recordId.value = String(query?.id || '');
childUserId.value = String(query?.userId || '');
reportName.value = query?.name ? decodeURIComponent(String(query.name)) : ''; reportName.value = query?.name ? decodeURIComponent(String(query.name)) : '';
reportSubject.value = query?.subject ? decodeURIComponent(String(query.subject)) : ''; reportSubject.value = query?.subject ? decodeURIComponent(String(query.subject)) : '';
reportCategory.value = query?.category ? decodeURIComponent(String(query.category)) : ''; reportCategory.value = query?.category ? decodeURIComponent(String(query.category)) : '';
@ -387,9 +499,60 @@ const formatDifficultyScoreRate = (actual?: number, total?: number) => {
return { text: `${Math.round(rate * 10) / 10}%`, value: rate }; return { text: `${Math.round(rate * 10) / 10}%`, value: rate };
}; };
const channelAnalyzeList = computed(() => jobAnalyse.value.paperChanelAnalyzeList || []);
const difficultyAnalyzeList = computed(() => jobAnalyse.value.paperDifficultyAnalyzeList || []);
const getDifficultyColor = (name?: string) => {
if (!name) return difficultyFallbackColors[0];
return difficultyBaseColorMap[name] || difficultyFallbackColors[name.length % difficultyFallbackColors.length];
};
const totalQuestionCount = computed(() =>
channelAnalyzeList.value.reduce((sum, item) => sum + (Number(item.titleCount) || 0), 0),
);
const totalDifficultyCount = computed(() =>
difficultyAnalyzeList.value.reduce((sum, item) => sum + (Number(item.titleCount) || 0), 0),
);
const buildPieStyle = (
list: { titleCount?: number }[],
colorFn: (idx: number) => string,
) => {
if (!list.length) return {};
const total = list.reduce((sum, item) => sum + (Number(item.titleCount) || 0), 0);
if (total <= 0) return {};
const parts: string[] = [];
let cur = 0;
list.forEach((item, idx) => {
const pct = ((Number(item.titleCount) || 0) / total) * 100;
const color = colorFn(idx);
const next = cur + pct;
parts.push(`${color} ${cur}% ${next}%`);
cur = next;
});
return { background: `conic-gradient(${parts.join(', ')})` };
};
const channelPieStyle = computed(() =>
buildPieStyle(channelAnalyzeList.value, (idx) => channelPieColors[idx % channelPieColors.length]),
);
const difficultyPieStyle = computed(() =>
buildPieStyle(difficultyAnalyzeList.value, (idx) =>
getDifficultyColor(difficultyAnalyzeList.value[idx]?.difficultyName),
),
);
const getDifficultyBarWidth = (score?: number) => {
const max = Math.max(...difficultyAnalyzeList.value.map((item) => Number(item.score) || 0), 1);
return Math.round(((Number(score) || 0) / max) * 100);
};
const scoreBlocks = computed<ReportScoreBlock[]>(() => { const scoreBlocks = computed<ReportScoreBlock[]>(() => {
const vo = jobAnalyse.value.jobReportVo || {}; const vo = jobAnalyse.value.jobReportVo || {};
const mark = Number(vo.actualScore ?? paperDetail.value.score ?? 0); const mark = Number(vo.actualScore ?? vo.markScore ?? paperDetail.value.score ?? 0);
const total = Number(vo.totalScore ?? paperDetail.value.totalScore ?? 0); const total = Number(vo.totalScore ?? paperDetail.value.totalScore ?? 0);
const rateFromScore = const rateFromScore =
total > 0 ? `${Math.round((mark / total) * 1000) / 10}%` : reportScoreRate.value ? `${reportScoreRate.value}%` : '--'; total > 0 ? `${Math.round((mark / total) * 1000) / 10}%` : reportScoreRate.value ? `${reportScoreRate.value}%` : '--';
@ -418,7 +581,7 @@ const ranks = computed(() => {
}); });
const analysisRows = computed(() => { const analysisRows = computed(() => {
const list = jobAnalyse.value.paperChanelAnalyzeList || []; const list = channelAnalyzeList.value;
const totalCount = list.reduce((sum, item) => sum + (Number(item.titleCount) || 0), 0); const totalCount = list.reduce((sum, item) => sum + (Number(item.titleCount) || 0), 0);
return list.map((item) => { return list.map((item) => {
const count = Number(item.titleCount) || 0; const count = Number(item.titleCount) || 0;
@ -476,6 +639,52 @@ const detail = computed(() => ({
ranks: ranks.value, ranks: ranks.value,
})); }));
const knowledgeScores = computed<ReportKnowledgeScore[]>(() =>
knowledgeList.value.map((item) => ({
name: item.knowledgeName || '--',
fullScore: Number(item.titleScore) || 0,
score: Number(item.answerScore) || 0,
})),
);
const ratioTones: ReportKnowledgeRatio['tone'][] = ['blue', 'orange', 'green'];
const knowledgeRatios = computed<ReportKnowledgeRatio[]>(() => {
const lostItems = knowledgeList.value
.map((item) => ({
name: item.knowledgeName || '--',
lost: Math.max(0, (Number(item.titleScore) || 0) - (Number(item.answerScore) || 0)),
}))
.filter((item) => item.lost > 0)
.sort((a, b) => b.lost - a.lost);
const totalLost = lostItems.reduce((sum, item) => sum + item.lost, 0);
if (totalLost <= 0) return [];
return lostItems.map((item, idx) => ({
name: item.name,
percent: Math.round((item.lost / totalLost) * 100),
tone: ratioTones[idx % ratioTones.length],
}));
});
const knowledgeWeakCount = computed(
() =>
knowledgeList.value.filter(
(item) => (Number(item.answerScore) || 0) < (Number(item.titleScore) || 0),
).length,
);
const RATIO_ROW_HEIGHT = 72;
const RATIO_LIST_MIN = 200;
const RATIO_LIST_MAX = 480;
const ratioListHeight = computed(() => {
const count = knowledgeRatios.value.length || 1;
const height = Math.min(Math.max(count * RATIO_ROW_HEIGHT, RATIO_LIST_MIN), RATIO_LIST_MAX);
return `${height}rpx`;
});
const selectedQuestionSummary = computed( const selectedQuestionSummary = computed(
() => questionSummaries.value.find((item) => item.no === selectedNo.value) || null, () => questionSummaries.value.find((item) => item.no === selectedNo.value) || null,
); );
@ -556,13 +765,32 @@ const loadReportData = async () => {
pageLoading.value = true; pageLoading.value = true;
try { try {
const [detailRes, analyseRes] = await Promise.all([ const analyseParams: { recordId: string; userId?: string } = {
getPaperRecordDetail({ recordId: recordId.value, reportFlag: REPORT_FLAG }), recordId: recordId.value,
getJobAnalyse({ recordId: recordId.value }).catch(() => ({ data: {} })), };
]); const studentId = activeChild.value.id || childUserId.value;
if (studentId) {
analyseParams.userId = studentId;
}
const detailRes = await getPaperRecordDetail({ recordId: recordId.value, reportFlag: REPORT_FLAG });
paperDetail.value = (detailRes.data || {}) as Record<string, unknown>; paperDetail.value = (detailRes.data || {}) as Record<string, unknown>;
jobAnalyse.value = analyseRes.data || {};
try {
const analyseRes = await getJobAnalyse(analyseParams);
jobAnalyse.value = analyseRes.data || {};
} catch (analyseErr) {
console.error('[parent-report-detail] analyse err', analyseErr);
jobAnalyse.value = {};
}
try {
const knowledgeRes = await getKnowledgeAnalyse(analyseParams);
knowledgeList.value = Array.isArray(knowledgeRes.data) ? knowledgeRes.data : [];
} catch (knowledgeErr) {
console.error('[parent-report-detail] knowledge err', knowledgeErr);
knowledgeList.value = [];
}
const titleList = paperDetail.value.titleVoList || paperDetail.value.subjectTitleVoList; const titleList = paperDetail.value.titleVoList || paperDetail.value.subjectTitleVoList;
rawTitleItems.value = Array.isArray(titleList) ? (titleList as Record<string, unknown>[]) : []; rawTitleItems.value = Array.isArray(titleList) ? (titleList as Record<string, unknown>[]) : [];
@ -585,7 +813,17 @@ const loadReportData = async () => {
} }
}; };
const knowledgeRate = (item: ReportKnowledgeScore) => Math.round((item.score / item.fullScore) * 100); const knowledgeRate = (item: ReportKnowledgeScore) => {
if (!item.fullScore) return 0;
return Math.round((item.score / item.fullScore) * 100);
};
const knowledgeScoreClass = (item: ReportKnowledgeScore) => {
const rate = knowledgeRate(item);
if (rate >= 80) return 'rate-good';
if (rate >= 60) return 'rate-warn';
return 'score-danger';
};
const knowledgeLevelOf = (item: ReportKnowledgeScore): KnowledgeLevel => { const knowledgeLevelOf = (item: ReportKnowledgeScore): KnowledgeLevel => {
const rate = knowledgeRate(item); const rate = knowledgeRate(item);
@ -595,8 +833,8 @@ const knowledgeLevelOf = (item: ReportKnowledgeScore): KnowledgeLevel => {
}; };
const filteredKnowledgeScores = computed(() => { const filteredKnowledgeScores = computed(() => {
if (activeKnowledgeLevel.value === 'all') return detail.value.knowledgeScores; if (activeKnowledgeLevel.value === 'all') return knowledgeScores.value;
return detail.value.knowledgeScores.filter((item) => knowledgeLevelOf(item) === activeKnowledgeLevel.value); return knowledgeScores.value.filter((item) => knowledgeLevelOf(item) === activeKnowledgeLevel.value);
}); });
const previewStemImage = (index: number) => { const previewStemImage = (index: number) => {
@ -772,6 +1010,144 @@ const goHome = () => {
border-radius: 26rpx; border-radius: 26rpx;
} }
.chart-block {
display: flex;
flex-direction: column;
gap: 28rpx;
}
.chart-table {
margin-top: 4rpx;
}
.pie-row {
display: flex;
align-items: center;
gap: 24rpx;
}
.pie {
position: relative;
width: 200rpx;
height: 200rpx;
flex-shrink: 0;
}
.pie-circle {
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: 0 8rpx 16rpx rgba(37, 99, 235, 0.12);
}
.pie-hole {
position: absolute;
inset: 28rpx;
border-radius: 50%;
background: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow:
inset 0 4rpx 8rpx rgba(0, 0, 0, 0.06),
0 2rpx 4rpx rgba(0, 0, 0, 0.04);
}
.pie-hole-num {
font-size: 44rpx;
font-weight: 900;
color: #0f172a;
line-height: 1;
}
.pie-hole-label {
font-size: 20rpx;
color: #94a3b8;
margin-top: 4rpx;
}
.pie-legend {
flex: 1;
display: flex;
flex-direction: column;
gap: 12rpx;
min-width: 0;
}
.legend-item {
display: flex;
align-items: center;
gap: 12rpx;
}
.legend-name {
flex: 1;
font-size: 24rpx;
color: #475569;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.legend-count {
font-size: 24rpx;
color: #2563eb;
font-weight: 800;
flex-shrink: 0;
}
.bars {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.bar-row {
display: flex;
align-items: center;
gap: 16rpx;
}
.bar-name {
width: 88rpx;
flex-shrink: 0;
font-size: 24rpx;
color: #475569;
font-weight: 700;
text-align: right;
}
.bar-track {
flex: 1;
height: 24rpx;
border-radius: 999rpx;
background: #f1f5f9;
overflow: hidden;
}
.bar-fill {
height: 100%;
border-radius: 999rpx;
transition: width 0.3s ease;
}
.bar-value {
width: 88rpx;
flex-shrink: 0;
font-size: 26rpx;
font-weight: 900;
color: #0f172a;
text-align: right;
}
.bar-value-unit {
font-size: 20rpx;
font-weight: 600;
color: #94a3b8;
}
.table-row.channel-row { .table-row.channel-row {
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
} }
@ -1057,7 +1433,7 @@ const goHome = () => {
.knowledge-summary { .knowledge-summary {
display: flex; display: flex;
align-items: center; align-items: flex-start;
gap: 28rpx; gap: 28rpx;
} }
@ -1088,12 +1464,18 @@ const goHome = () => {
.ratio-list { .ratio-list {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
min-height: 200rpx;
max-height: 480rpx;
} }
.ratio-row + .ratio-row { .ratio-row + .ratio-row {
margin-top: 18rpx; margin-top: 18rpx;
} }
.ratio-row:last-child {
padding-bottom: 4rpx;
}
.ratio-meta { .ratio-meta {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -217,8 +217,9 @@ const subjectClass = (subject: string) => {
}; };
const openReport = (item: ReportCard) => { const openReport = (item: ReportCard) => {
const userId = activeChild.value.id ? `&userId=${activeChild.value.id}` : '';
uni.navigateTo({ uni.navigateTo({
url: `/pages/parent/reports/detail?id=${item.id}&name=${encodeURIComponent(item.title)}&subject=${encodeURIComponent(item.subject)}&category=${encodeURIComponent(item.category)}&date=${item.date}&score=${item.score}`, url: `/pages/parent/reports/detail?id=${item.id}&name=${encodeURIComponent(item.title)}&subject=${encodeURIComponent(item.subject)}&category=${encodeURIComponent(item.category)}&date=${item.date}&score=${item.score}${userId}`,
}); });
}; };

View File

@ -83,7 +83,7 @@ const skipFirstOnShow = ref(true);
const totalWrong = computed(() => { const totalWrong = computed(() => {
const subjects = summaryData.value?.subjects || []; const subjects = summaryData.value?.subjects || [];
if (subjects.length) { if (subjects.length) {
return subjects.reduce((sum, item) => sum + (item.totalCount || 0), 0); return subjects.reduce((sum, item) => sum + (Number(item.totalCount) || 0), 0);
} }
return 0; return 0;
}); });
@ -94,10 +94,10 @@ const subjectStats = computed(() => {
...buildSubjectStatRow( ...buildSubjectStatRow(
item.subjectName || '其他', item.subjectName || '其他',
{ {
total: item.totalCount || 0, total: Number(item.totalCount) || 0,
newCount: item.newCount || 0, newCount: Number(item.newCount) || 0,
reviewCount: item.reviewCount || 0, reviewCount: Number(item.reviewCount) || 0,
masteredCount: item.masteredCount || 0, masteredCount: Number(item.masteredCount) || 0,
}, },
index, index,
), ),