From 9278dcc6108b2667210b556d1bfcacb4d771670c Mon Sep 17 00:00:00 2001 From: ywyonui Date: Tue, 17 Jun 2025 16:57:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=88=90=E7=BB=A9=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E9=A1=B5=E9=9D=A2=E6=8E=A5=E5=8F=A3=E8=B0=83=E7=94=A8?= =?UTF-8?q?=EF=BC=8C=E6=A0=B9=E6=8D=AE=E6=8E=A5=E5=8F=A3=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E6=96=B0=E7=BB=84=E5=90=88=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/base/server.ts | 14 + .../BasicListLayout/hooks/useLayout.ts | 5 +- src/pages/base/grades/detail.vue | 349 +++++++- src/pages/base/grades/list.vue | 823 +++--------------- src/pages/base/home/index.vue | 4 +- 5 files changed, 480 insertions(+), 715 deletions(-) diff --git a/src/api/base/server.ts b/src/api/base/server.ts index 981fe9f..b1201db 100644 --- a/src/api/base/server.ts +++ b/src/api/base/server.ts @@ -56,3 +56,17 @@ export const drpkkbApi = async (params: any) => { export const xsXkListApi = async (params: any) => { return await get("/mobile/jz/xsxk/list", params); }; + +/** + * 查询学生考试场次 + */ +export const xsKsccApi = async (params: any) => { + return await get("/mobile/jz/kscc", params); +}; + +/** + * 查询学生考试成绩 + */ +export const xsKscjApi = async (params: any) => { + return await get("/mobile/jz/kscj", params); +}; diff --git a/src/components/BasicListLayout/hooks/useLayout.ts b/src/components/BasicListLayout/hooks/useLayout.ts index e298721..b55d0b7 100644 --- a/src/components/BasicListLayout/hooks/useLayout.ts +++ b/src/components/BasicListLayout/hooks/useLayout.ts @@ -17,10 +17,11 @@ export function useLayout(options: LayoutOptions): UseLayoutInterfaceReturn { async function requestApi(pageNo: number, pageSize: number) { if (isFunction(options.api)) { try { - const result = await options.api(Object.assign({}, { + let params = Object.assign({}, { rows: pageSize, page: pageNo - }, options.param)) + }, options.param) + const result = await options.api(params) await nextTick() if (methods.value) { // @ts-ignore diff --git a/src/pages/base/grades/detail.vue b/src/pages/base/grades/detail.vue index a7fcf2c..b0b0927 100644 --- a/src/pages/base/grades/detail.vue +++ b/src/pages/base/grades/detail.vue @@ -3,20 +3,20 @@ - 2024学年第一学期二年级期中 + {{ kscc.ksmc }} 教学质量监测成绩报告 - 全科(满分700分) + 共{{kscjList.length}}科(满分{{ kscjList.length * 100 }}分) - A + {{ curKsdj.dfbs }} 等级说明 区域等级 - 相对较好 + {{ curKsdj.dsms }} @@ -28,6 +28,7 @@ - {{ subject.name }}(满分{{ subject.fullScore }}) + {{ kscj.kmmc }} + (分数{{ kscj.ksfs }}) - {{ subject.grade }} - 详情 + {{ kscj.ksdj.dfbs }} + {{ kscj.ksdj.dsms }} @@ -73,7 +73,11 @@ - + + import { ref, onMounted, watch, nextTick } from "vue"; -import { navigateBack } from "@/utils/uniapp"; import uCharts from "@/components/charts/u-charts.js"; +import dayjs from "dayjs"; +import { xsKscjApi } from "@/api/base/server"; +import { useUserStore } from "@/store/modules/user"; +import { useDataStore } from "@/store/modules/data"; +const { getCurXs } = useUserStore(); +const { getData } = useDataStore(); +dayjs.locale("zh-cn"); + +const kmTabsRef = ref(null) + +const fsScale = 100; + +const curXs = ref({}) +const kscc = ref({}) + +const kscjList = ref([]) +const curKsdj = ref({}) + +const xqKmList = ref([]) +const curKm = ref({}) +const curKmIndex = ref(0) +const xqKmmcList = ref([]) // 当前选中的选项卡 const activeTab = ref("scores"); @@ -165,15 +190,26 @@ function switchTab(tab: string) { // 科目数据 const subjects = ref([ - { name: "语文", fullScore: 98, grade: "B" }, - { name: "数学", fullScore: 96, grade: "A" }, - { name: "英语", fullScore: 98, grade: "A" }, - { name: "科学", fullScore: 94, grade: "A" }, - { name: "音乐", fullScore: 93, grade: "A" }, - { name: "美术", fullScore: 93, grade: "A" }, - { name: "体育", fullScore: 93, grade: "A" }, + { id: "1", kmmc: "语文", fullScore: 98, grade: "B" }, + { id: "2", kmmc: "数学", fullScore: 96, grade: "A" }, + { id: "3", kmmc: "英语", fullScore: 98, grade: "A" }, + { id: "4", kmmc: "科学", fullScore: 94, grade: "A" }, + // { id: "5", kmmc: "音乐", fullScore: 93, grade: "A" }, + // { id: "6", kmmc: "美术", fullScore: 93, grade: "A" }, + { id: "7", kmmc: "体育", fullScore: 93, grade: "A" }, ]); +type ColorMapType = { + [key: string]: string; +}; +const colorMap: ColorMapType = { + "A": "#FF6B6B", + "B": "#4D96FF", + "C": "#6BCB77", + "D": "#FFD93D", + "E": "#B8B8B8", +}; + // 雷达图数据 const radarData = { categories: ["语文", "数学", "英语", "科学", "音乐", "美术", "体育"], @@ -181,7 +217,7 @@ const radarData = { }; // 趋势图数据 -const trendData = { +let trendData = { categories: ["01-04", "02-04", "03-04", "04-04", "05-04"], series: [ { @@ -191,6 +227,33 @@ const trendData = { ], }; +// 后端查询到的所有科目列表 +const srcKmList = ref([]); +// 后端查询到的考试等级列表 +const srcKsdjList = ref([]); +// 后端查询到的当前学生本次考试成绩列表 +const srcKsccKscjList = ref([]); +// 后端查询到的当前学生本次学期所有成绩列表 +const srcXqKscjList = ref([]); + +const switchKm = (index : number) => { + curKmIndex.value = index; + curKm.value = xqKmList.value[index]; + trendData = { + categories: curKm.value.rqList, + series: [ + { + name: "成绩趋势", + data: curKm.value.fsList, + }, + ], + }; + if (activeTab.value === "trend") { + // 重绘趋势图 + drawTrendChart(); + } +}; + // 绘制雷达图 const drawRadarChart = () => { nextTick(() => { @@ -326,7 +389,118 @@ watch(activeTab, (newValue) => { } }); +// Float乘以倍数,然后四舍五入转int +const floatToInt = (floatNum: number, scale: number) => { + return Math.round(floatNum * scale); +} + +// 初始化考试等级列表 +const initKsdj = () => { + srcKsdjList.value.forEach((ksdj: any) => { + ksdj.djfsd = floatToInt(ksdj.djfsd, fsScale); // 等级最低分 + ksdj.djfsg = floatToInt(ksdj.djfsg, fsScale); // 等级最高分 + ksdj.djclr = colorMap[ksdj.dfbs] || ''; // 等级颜色 + }); +}; + +// 单科科目成绩构建 +const buildKmKscj = (km: any, cjList: any) => { + let cj = cjList.find((item: any) => item.kmId === km.id); + if (cj) { + km.ksfs = cj.ksfs; // 记录分数 + const fs = floatToInt(cj.ksfs, fsScale); + // 查询考试等级 + km.ksdj = srcKsdjList.value.find((item: any) => item.djfsd <= fs && item.djfsg >= fs); + } else { + km.ksfs = 0.00; + km.ksdj = { + id: '', + dfbs: '无', + dsms: '无' + } + } + return km; +} + +// 构建当前学期所有科目成绩(对象是按照{ 科目id:{ 日期:成绩 } }的结构拼接) +const buildXqKmKscjList = () => { + const xqKscjMap = new Map(); + const len = srcXqKscjList.value.length; + for (let i = 0; i < len; i++) { + const exam = srcXqKscjList.value[i]; + exam.ksrq = dayjs(exam.kskstime).format('MM-DD'); + if (!xqKscjMap.has(exam.kmId)) { + xqKscjMap.set(exam.kmId, {}); + } + xqKscjMap.get(exam.kmId)[exam.ksrq] = exam.ksfs; + } + xqKmList.value = []; + // 遍历map + const keys = Array.from(xqKscjMap.keys()); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = xqKscjMap.get(key); + // 目前value是Object,改成map + const dataMap = new Map(Object.entries(value)); + let km = { + id: key, + kmmc: srcKmList.value.find((item: any) => item.id === key)?.kmmc, + rqList: Array.from(dataMap.keys()).map(String), + fsList: Array.from(dataMap.values()).map(Number) + }; + xqKmList.value.push(km); + } + xqKmmcList.value = xqKmList.value.map((item: any) => item.kmmc); + switchKm(0); +} + +const rebuildData = () => { + // 根据考试场次,分解对应的科目 + let ksccKmmcList = getData.kmmc.split(","); + let ksccKmIdList = getData.kmId.split(","); + let ksfsList = []; + let totalFs = 0.00; + kscjList.value = []; + for (let i = 0; i < ksccKmIdList.length; i++) { + let km = { id: ksccKmIdList[i], kmmc: ksccKmmcList[i] }; + let kmKscj = buildKmKscj(km, srcKsccKscjList.value); + ksfsList.push(kmKscj.ksfs); + totalFs += kmKscj.ksfs; + kscjList.value.push(kmKscj); + } + // 雷达图数据 + radarData.categories = ksccKmmcList; + radarData.series = [{ name: "分数", data: ksfsList }]; + // 当前成绩列表 + totalFs = floatToInt(totalFs, fsScale) / ksccKmIdList.length; + curKsdj.value = srcKsdjList.value.find((item: any) => item.djfsd <= totalFs && item.djfsg >= totalFs); + // 构建趋势图所需的成绩列表信息 + buildXqKmKscjList(); +} + onMounted(() => { + curXs.value = getCurXs; + kscc.value = getData; + xsKscjApi({ + xsId: getCurXs.id, + ksccId: getData.id, + njmcId: getData.njmcId + }).then(res => { + if (res.resultCode == 1) { + srcKmList.value = res.result.kmList; + srcKsdjList.value = res.result.ksdjList; + srcKsccKscjList.value = res.result.ksccKscjList; + srcXqKscjList.value = res.result.xqKscjList; + // 初始化考试等级 + initKsdj(); + // 重构显示数据 + rebuildData(); + } + }) + .catch((error) => { + // 接口调用失败 + console.error("调用查询成绩接口失败:", error); + }); // 默认绘制学科成绩视图 // 延迟执行确保DOM已经渲染 setTimeout(() => { @@ -601,6 +775,21 @@ function hideGradeInfo() { font-size: 16px; } } + .km-info { + margin-bottom: 10px; + display: flex; + align-items: center; + .km-mc { + flex: 1 0 1px; + } + .switch-btn { + padding: 4px 12px; + background-color: rgba(25, 118, 210, 0.8); + color: #fff; + border-radius: 20px; + font-size: 13px; + } + } } .radar-placeholder { height: 300px; @@ -721,4 +910,124 @@ function hideGradeInfo() { } } } + + +/* 学生选择器弹窗样式 */ +.student-selector { + background: linear-gradient(135deg, #ffffff 0%, #f8faff 100%); + border-top-left-radius: 20px; + border-top-right-radius: 20px; + padding-bottom: 30px; + + .selector-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px; + border-bottom: 1px solid #f0f2f5; + + .selector-title { + font-size: 18px; + font-weight: 600; + color: #303133; + line-height: 1; + } + + .close-btn { + width: 32px; + height: 32px; + background-color: #f5f7fa; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + flex-shrink: 0; + + &:active { + transform: scale(0.9); + background-color: #e6e8eb; + } + } + } + + .student-list { + padding: 0 20px; + max-height: 50%; + overflow-y: auto; + + .student-item { + display: flex; + align-items: center; + padding: 16px 0; + border-bottom: 1px solid #f5f7fa; + transition: all 0.3s ease; + border-radius: 12px; + margin-bottom: 8px; + + &:last-child { + border-bottom: none; + margin-bottom: 0; + } + + &-active { + background: linear-gradient(135deg, rgba(74, 144, 226, 0.08) 0%, rgba(53, 122, 189, 0.05) 100%); + padding: 16px 12px; + } + + .student-avatar { + width: 50px; + height: 50px; + border-radius: 50%; + overflow: hidden; + flex-shrink: 0; + box-shadow: 0 2px 8px rgba(74, 144, 226, 0.15); + border: 2px solid #ffffff; + + .avatar-img { + width: 100%; + height: 100%; + object-fit: cover; + } + } + + .student-info { + flex: 1; + margin-left: 15px; + display: flex; + flex-direction: column; + justify-content: center; + min-width: 0; + + .student-name { + font-size: 16px; + font-weight: 600; + color: #303133; + margin-bottom: 4px; + line-height: 1.2; + } + + .student-class { + font-size: 13px; + color: #606266; + font-weight: 400; + line-height: 1; + } + } + + .check-icon { + width: 24px; + height: 24px; + border-radius: 50%; + background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%); + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 2px 8px rgba(74, 144, 226, 0.3); + flex-shrink: 0; + } + } + } +} + diff --git a/src/pages/base/grades/list.vue b/src/pages/base/grades/list.vue index a7fcf2c..1b128e9 100644 --- a/src/pages/base/grades/list.vue +++ b/src/pages/base/grades/list.vue @@ -1,724 +1,165 @@ + +.card-footer { + padding: 12px 15px; + border-top: 1px solid #f0f0f0; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 14px; + color: #888; + cursor: pointer; + + .arrow { + font-size: 16px; + color: #ccc; + } +} + +// 新增按钮样式示例 (可根据 BasicListLayout 的 bottom 插槽调整) +.button { + padding: 10px 15px; + background-color: #447ade; + color: white; + text-align: center; + border-radius: 5px; + margin: 10px 15px; // 示例外边距 + font-size: 16px; +} + \ No newline at end of file diff --git a/src/pages/base/home/index.vue b/src/pages/base/home/index.vue index 608ffee..672d37f 100644 --- a/src/pages/base/home/index.vue +++ b/src/pages/base/home/index.vue @@ -147,7 +147,7 @@ const menuItems = ref([ { title: "成绩查询", icon: "/static/base/home/file-search-line.png", - path: "/pages/base/grades/detail", + path: "/pages/base/grades/list", }, // { // title: "在线请假", @@ -244,7 +244,7 @@ function switchStudent(student: any) { // 这里可以添加切换学生后的其他操作,如刷新页面数据等 uni.showToast({ - title: `已切换到${student.name}`, + title: `已切换到${student.xm}`, icon: "none", }); }