fix: 修复一些问题
This commit is contained in:
parent
3f826a1a67
commit
9badd93bfe
@ -140,6 +140,7 @@ export const getPaperRecordDetail = (data: { recordId: string; reportFlag: numbe
|
||||
|
||||
export interface JobReportVo {
|
||||
actualScore?: number;
|
||||
markScore?: number;
|
||||
beginTime?: string;
|
||||
costTime?: number;
|
||||
jobId?: number;
|
||||
@ -179,17 +180,24 @@ export interface JobAnalyseVo {
|
||||
stuRankVoList?: StuRankVoItem[];
|
||||
}
|
||||
|
||||
/** 作业整体分析 */
|
||||
export const getJobAnalyse = (params: { recordId: string }) =>
|
||||
/** 作业整体分析(家长端查看孩子报告时需传 userId) */
|
||||
export const getJobAnalyse = (params: { recordId: string; userId?: string }) =>
|
||||
request<JobAnalyseVo>({
|
||||
url: '/sc/job/getJobAnalyse',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
|
||||
/** 知识点分析 */
|
||||
export const getKnowledgeAnalyse = (params: { recordId: string }) =>
|
||||
request<any[]>({
|
||||
export interface KnowledgeAnalyzeItem {
|
||||
knowledgeId?: string;
|
||||
knowledgeName?: string;
|
||||
answerScore?: number;
|
||||
titleScore?: number;
|
||||
}
|
||||
|
||||
/** 知识点分析(家长端查看孩子报告时需传 userId) */
|
||||
export const getKnowledgeAnalyse = (params: { recordId: string; userId?: string }) =>
|
||||
request<KnowledgeAnalyzeItem[]>({
|
||||
url: '/sc/job/statisticalKnowledge',
|
||||
method: 'GET',
|
||||
params,
|
||||
|
||||
@ -80,6 +80,7 @@ export const parentSmsLogin = (data: ParentSmsLoginParams) =>
|
||||
data: {
|
||||
bindMaOpenid: true,
|
||||
adminType: 16,
|
||||
clientType: 'PARENT',
|
||||
phone: data.phone,
|
||||
verifyCode: data.verifyCode,
|
||||
wxUser: {
|
||||
|
||||
@ -109,7 +109,7 @@ export interface KpointScoreItem {
|
||||
kpointId?: number;
|
||||
kpointName?: string;
|
||||
actualScore?: number;
|
||||
fullscore?: number;
|
||||
fullScore?: number;
|
||||
scoreRate?: number;
|
||||
scoreRatePercent?: number;
|
||||
levelCode?: number;
|
||||
|
||||
@ -240,7 +240,7 @@ const levelKnowledge = computed<ScoreViewItem[]>(() => {
|
||||
return (level?.items || []).map((item) => ({
|
||||
kpointId: item.kpointId ?? 0,
|
||||
kpointName: item.kpointName || '--',
|
||||
fullScore: item.fullscore ?? 0,
|
||||
fullScore: item.fullScore ?? 0,
|
||||
score: item.actualScore ?? 0,
|
||||
scoreRatePercent: item.scoreRatePercent ?? 0,
|
||||
}));
|
||||
|
||||
@ -43,24 +43,100 @@
|
||||
|
||||
<template v-if="activeTab === 'analysis'">
|
||||
<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="加载分析数据..." />
|
||||
</view>
|
||||
<view v-else-if="analysisRows.length" class="data-table">
|
||||
<view class="table-row table-head">
|
||||
<text>题型</text>
|
||||
<text>题量</text>
|
||||
<text>占比</text>
|
||||
<view v-else-if="channelAnalyzeList.length" class="chart-block">
|
||||
<view class="pie-row">
|
||||
<view class="pie">
|
||||
<view class="pie-circle" :style="channelPieStyle"></view>
|
||||
<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 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 class="data-table chart-table">
|
||||
<view class="table-row table-head channel-row">
|
||||
<text>题型</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>
|
||||
<xy-Empty v-else type="task" content="暂无题型分析数据" />
|
||||
</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="关注中难题稳定性,决定后续练习重点">
|
||||
<view v-if="pageLoading && !difficultyRows.length" class="answers-loading">
|
||||
<xy-Empty type="loading" content="加载分析数据..." />
|
||||
@ -79,7 +155,7 @@
|
||||
<text :class="row.rateValue >= 80 ? 'rate-good' : 'rate-warn'">{{ row.rate }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<xy-Empty v-else type="task" content="暂无难度分析数据" />
|
||||
<xy-Empty v-else type="task" content="暂无难度得分数据" />
|
||||
</ParentCard>
|
||||
|
||||
<ParentCard
|
||||
@ -247,13 +323,21 @@
|
||||
|
||||
<template v-if="activeTab === 'knowledge'">
|
||||
<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">
|
||||
<text class="wrong-number">{{ wrongCount }}</text>
|
||||
<text class="wrong-label">错题数</text>
|
||||
<text class="wrong-number">{{ knowledgeWeakCount }}</text>
|
||||
<text class="wrong-label">薄弱知识点</text>
|
||||
</view>
|
||||
<view class="ratio-list">
|
||||
<view v-for="item in detail.knowledgeRatios" :key="item.name" class="ratio-row">
|
||||
<scroll-view
|
||||
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">
|
||||
<text>{{ item.name }}</text>
|
||||
<text>{{ item.percent }}%</text>
|
||||
@ -262,11 +346,16 @@
|
||||
<view class="ratio-fill" :class="item.tone" :style="{ width: `${item.percent}%` }"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<xy-Empty v-else type="learn" content="暂无知识点占比数据" />
|
||||
</ParentCard>
|
||||
|
||||
<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
|
||||
v-for="level in knowledgeTabs"
|
||||
@ -287,10 +376,12 @@
|
||||
<view v-for="item in filteredKnowledgeScores" :key="item.name" class="table-row knowledge-row">
|
||||
<text class="knowledge-name">{{ item.name }}</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>
|
||||
<xy-Empty v-if="!filteredKnowledgeScores.length" type="learn" content="当前分段暂无知识点" />
|
||||
</template>
|
||||
<xy-Empty v-else type="learn" content="暂无知识点数据" />
|
||||
</ParentCard>
|
||||
</template>
|
||||
</view>
|
||||
@ -308,7 +399,9 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
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 ParentPage from '../components/ParentPage.vue';
|
||||
import {
|
||||
@ -317,6 +410,7 @@ import {
|
||||
type ReportQuestion,
|
||||
type ReportQuestionSummary,
|
||||
type ReportScoreBlock,
|
||||
type ReportKnowledgeRatio,
|
||||
} from '../utils/data';
|
||||
import {
|
||||
mapPaperQuestion,
|
||||
@ -342,7 +436,23 @@ const knowledgeTabs: { key: KnowledgeLevel; label: string }[] = [
|
||||
|
||||
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 childUserId = ref('');
|
||||
const reportName = ref('');
|
||||
const reportSubject = ref('');
|
||||
const reportCategory = ref('');
|
||||
@ -360,9 +470,11 @@ const questionSummaries = ref<ReportQuestionSummary[]>([]);
|
||||
const questionDetailCache = ref<Record<number, ReportQuestion>>({});
|
||||
const paperDetail = ref<Record<string, unknown>>({});
|
||||
const jobAnalyse = ref<JobAnalyseVo>({});
|
||||
const knowledgeList = ref<KnowledgeAnalyzeItem[]>([]);
|
||||
|
||||
onLoad((query) => {
|
||||
recordId.value = String(query?.id || '');
|
||||
childUserId.value = String(query?.userId || '');
|
||||
reportName.value = query?.name ? decodeURIComponent(String(query.name)) : '';
|
||||
reportSubject.value = query?.subject ? decodeURIComponent(String(query.subject)) : '';
|
||||
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 };
|
||||
};
|
||||
|
||||
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 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 rateFromScore =
|
||||
total > 0 ? `${Math.round((mark / total) * 1000) / 10}%` : reportScoreRate.value ? `${reportScoreRate.value}%` : '--';
|
||||
@ -418,7 +581,7 @@ const ranks = 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);
|
||||
return list.map((item) => {
|
||||
const count = Number(item.titleCount) || 0;
|
||||
@ -476,6 +639,52 @@ const detail = computed(() => ({
|
||||
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(
|
||||
() => questionSummaries.value.find((item) => item.no === selectedNo.value) || null,
|
||||
);
|
||||
@ -556,13 +765,32 @@ const loadReportData = async () => {
|
||||
|
||||
pageLoading.value = true;
|
||||
try {
|
||||
const [detailRes, analyseRes] = await Promise.all([
|
||||
getPaperRecordDetail({ recordId: recordId.value, reportFlag: REPORT_FLAG }),
|
||||
getJobAnalyse({ recordId: recordId.value }).catch(() => ({ data: {} })),
|
||||
]);
|
||||
const analyseParams: { recordId: string; userId?: string } = {
|
||||
recordId: recordId.value,
|
||||
};
|
||||
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>;
|
||||
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;
|
||||
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 rate = knowledgeRate(item);
|
||||
@ -595,8 +833,8 @@ const knowledgeLevelOf = (item: ReportKnowledgeScore): KnowledgeLevel => {
|
||||
};
|
||||
|
||||
const filteredKnowledgeScores = computed(() => {
|
||||
if (activeKnowledgeLevel.value === 'all') return detail.value.knowledgeScores;
|
||||
return detail.value.knowledgeScores.filter((item) => knowledgeLevelOf(item) === activeKnowledgeLevel.value);
|
||||
if (activeKnowledgeLevel.value === 'all') return knowledgeScores.value;
|
||||
return knowledgeScores.value.filter((item) => knowledgeLevelOf(item) === activeKnowledgeLevel.value);
|
||||
});
|
||||
|
||||
const previewStemImage = (index: number) => {
|
||||
@ -772,6 +1010,144 @@ const goHome = () => {
|
||||
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 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
@ -1057,7 +1433,7 @@ const goHome = () => {
|
||||
|
||||
.knowledge-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
gap: 28rpx;
|
||||
}
|
||||
|
||||
@ -1088,12 +1464,18 @@ const goHome = () => {
|
||||
.ratio-list {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 200rpx;
|
||||
max-height: 480rpx;
|
||||
}
|
||||
|
||||
.ratio-row + .ratio-row {
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.ratio-row:last-child {
|
||||
padding-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.ratio-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@ -217,8 +217,9 @@ const subjectClass = (subject: string) => {
|
||||
};
|
||||
|
||||
const openReport = (item: ReportCard) => {
|
||||
const userId = activeChild.value.id ? `&userId=${activeChild.value.id}` : '';
|
||||
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}`,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -83,7 +83,7 @@ const skipFirstOnShow = ref(true);
|
||||
const totalWrong = computed(() => {
|
||||
const subjects = summaryData.value?.subjects || [];
|
||||
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;
|
||||
});
|
||||
@ -94,10 +94,10 @@ const subjectStats = computed(() => {
|
||||
...buildSubjectStatRow(
|
||||
item.subjectName || '其他',
|
||||
{
|
||||
total: item.totalCount || 0,
|
||||
newCount: item.newCount || 0,
|
||||
reviewCount: item.reviewCount || 0,
|
||||
masteredCount: item.masteredCount || 0,
|
||||
total: Number(item.totalCount) || 0,
|
||||
newCount: Number(item.newCount) || 0,
|
||||
reviewCount: Number(item.reviewCount) || 0,
|
||||
masteredCount: Number(item.masteredCount) || 0,
|
||||
},
|
||||
index,
|
||||
),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user