From 0562efd6e820033b0a6d285ca32c1072e908a15d Mon Sep 17 00:00:00 2001 From: hebo Date: Sat, 14 Feb 2026 11:28:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AD=A6=E7=94=9F=E5=AE=9E=E8=B7=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/base/server.ts | 7 + .../ImageVideoUpload/ImageVideoUpload.vue | 75 +- src/pages/base/grades/detail.vue | 776 ++++++------------ src/pages/base/home/index.vue | 2 +- src/utils/filePreview.ts | 111 ++- 5 files changed, 412 insertions(+), 559 deletions(-) diff --git a/src/api/base/server.ts b/src/api/base/server.ts index 1bc70fc..fc8d7fa 100644 --- a/src/api/base/server.ts +++ b/src/api/base/server.ts @@ -74,6 +74,13 @@ export const xsKsccApi = async (params: any) => { export const xsKscjApi = async (params: any) => { return await get("/mobile/jz/kscj", params); }; + +/** + * 根据考试场次ID和学生ID查询评价(班主任寄语) + */ +export const ksccPjFindByKsccAndXsApi = async (params: { ksccId: string; xsId: string }) => { + return await get("/api/ksccPj/findByKsccAndXs", params); +}; /** * 家长接龙查询 */ diff --git a/src/components/ImageVideoUpload/ImageVideoUpload.vue b/src/components/ImageVideoUpload/ImageVideoUpload.vue index cdd22d6..aad4f1c 100644 --- a/src/components/ImageVideoUpload/ImageVideoUpload.vue +++ b/src/components/ImageVideoUpload/ImageVideoUpload.vue @@ -111,14 +111,15 @@ {{ file.originalName || file.name }} - {{ formatFileSize(file.size) }} - {{ file.extension?.toUpperCase() || 'FILE' }} - + + + + @@ -140,7 +141,8 @@ import { ref, computed, watch } from 'vue' import { imagUrl } from '@/utils' import { previewFile as previewFileUtil, - previewVideo as previewVideoUtil + previewVideo as previewVideoUtil, + downloadFile as downloadFileUtil } from '@/utils/filePreview' // 接口定义 @@ -736,6 +738,24 @@ const previewFile = (index: number) => { } } +// 下载文件(与选项统计导出一致:H5 用 location.href,APP/小程序用 uni.downloadFile) +const downloadFileItem = (index: number) => { + const file = fileList.value[index] + if (!file) return + const pathOrUrl = (file as any).url ?? (file as any).filePath + if (!pathOrUrl) { + showToast({ title: '文件未上传完成,无法下载', icon: 'none' }) + return + } + const fileUrl = /^https?:\/\//i.test(pathOrUrl) ? pathOrUrl : imagUrl(pathOrUrl) + const baseName = file.originalName || file.name || '下载文件' + const ext = file.extension || String(pathOrUrl).split('.').pop() || '' + const fileName = ext && !baseName.includes('.') ? `${baseName}.${ext}` : baseName + downloadFileUtil(fileUrl, fileName).catch(() => { + showToast({ title: '下载失败', icon: 'none' }) + }) +} + // 移除文件 const removeFile = (index: number) => { fileList.value.splice(index, 1) @@ -1253,13 +1273,13 @@ defineExpose({ border-radius: 8rpx; padding: 16rpx; border: 1rpx solid #e9ecef; - position: relative; } .file-preview { display: flex; align-items: center; flex: 1; + min-width: 0; cursor: pointer; } @@ -1289,38 +1309,37 @@ defineExpose({ font-size: 28rpx; color: #333; display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + word-break: break-all; + white-space: normal; margin-bottom: 4rpx; } -.file-size { - font-size: 24rpx; - color: #999; - display: block; - margin-bottom: 2rpx; -} - -.file-type { - font-size: 20rpx; - color: #666; - display: block; -} - +/* 右侧操作区单独一列,与文件名不重叠 */ .file-actions { - position: absolute; - top: 8rpx; - right: 8rpx; + flex-shrink: 0; + margin-left: 24rpx; + display: flex; + flex-direction: column; + align-items: center; + gap: 12rpx; } +.file-actions .download-btn, .file-actions .delete-btn { - width: 32rpx; - height: 32rpx; - background: rgba(255, 59, 48, 0.8); - border-radius: 50%; + width: 56rpx; + height: 56rpx; + border-radius: 12rpx; display: flex; align-items: center; justify-content: center; + flex-shrink: 0; +} + +.file-actions .download-btn { + background: rgba(0, 122, 255, 0.9); +} + +.file-actions .delete-btn { + background: rgba(255, 59, 48, 0.9); } diff --git a/src/pages/base/grades/detail.vue b/src/pages/base/grades/detail.vue index d257b03..6823c9e 100644 --- a/src/pages/base/grades/detail.vue +++ b/src/pages/base/grades/detail.vue @@ -1,34 +1,39 @@ @@ -162,7 +105,7 @@ import { ref, onMounted, watch, nextTick } from "vue"; import uCharts from "@/components/charts/u-charts.js"; import dayjs from "dayjs"; -import { xsKscjApi } from "@/api/base/server"; +import { xsKscjApi, ksccPjFindByKsccAndXsApi } from "@/api/base/server"; import { useUserStore } from "@/store/modules/user"; import { useDataStore } from "@/store/modules/data"; const { getCurXs } = useUserStore(); @@ -173,19 +116,18 @@ const kmTabsRef = ref(null) const fsScale = 100; -const curXs = ref({}) +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"); +// 页面数据加载中(显示遮罩) +const pageLoading = ref(false); +// 班主任寄语(家长端只读) +const pjBzr = ref(""); // 切换选项卡 const switchTab = (tab: string) => { @@ -195,12 +137,13 @@ const switchTab = (tab: string) => { type ColorMapType = { [key: string]: string; }; +// 等级颜色与教师端一致 const colorMap: ColorMapType = { - "A": "#FFD700", // 优秀 - 金黄色 - "B": "#00FF00", // 良好 - 亮绿色 - "C": "#FF8C00", // 中等 - 橙色 - "D": "#666666", // 及格 - 灰色 - "E": "#FF0000", // 不及格 - 红色 + "A": "#4bb604", // 优秀 - 绿色 + "B": "#FF8C00", // 良好 - 橙色 + "C": "#FFD700", // 中等 - 黄色 + "D": "#FF0000", // 及格 - 红色 + "E": "#666666", // 不及格 - 灰色 }; // 雷达图数据 @@ -209,17 +152,6 @@ const radarData = { series: [{ name: "分数", data: [85, 90, 88, 92, 86, 91, 89] }], }; -// 趋势图数据 -let trendData = { - categories: ["01-04", "02-04", "03-04", "04-04", "05-04"], - series: [ - { - name: "成绩趋势", - data: [84, 81, 89, 92, 99], - }, - ], -}; - // 所有科目列表 const sysKmList = ref([]); // 当前考试科目列表 @@ -233,26 +165,6 @@ const kmDjList = ref([]); const djList = ref([]); // 考试场次成绩列表 const ksccKscjList = ref([]); -// 学期成绩 -const xqKscjList = 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 = () => { @@ -310,85 +222,10 @@ const drawRadarChart = () => { }); }; -// 绘制趋势图 -const drawTrendChart = () => { - nextTick(() => { - try { - // 获取容器信息 - const query = uni.createSelectorQuery(); - query - .select("#chart-container1") - .boundingClientRect((data) => { - if (!data) { - console.error("未找到趋势图容器"); - return; - } - - // 安全类型判断 - const rect = data as any; - const canvasWidth = rect.width || 300; - const canvasHeight = rect.height || 300; - - const ctx = uni.createCanvasContext("trendCanvas"); - - // 绘制折线图配置 - const options = { - type: "line", - context: ctx, - width: canvasWidth, - height: canvasHeight, - categories: trendData.categories, - series: trendData.series, - animation: true, - background: "#FFFFFF", - padding: [15, 15, 0, 15], - dataLabel: kscc.value.sfXsFs, // 是否显示分数 - dataPointShape: true, - enableScroll: false, - legend: { - show: false, - }, - xAxis: { - disableGrid: true, - axisLine: true, - axisLineColor: "#CCCCCC", - }, - yAxis: { - gridType: "dash", - dashLength: 2, - data: [ - { - min: 0, - max: 100, - }, - ], - format: (val : number) => { - return val == 0 || val == 100 ? val : ''; - }, - }, - extra: { - line: { - type: "straight", - width: 2, - }, - }, - }; - - new uCharts(options); - }) - .exec(); - } catch (error) { - console.error("绘制趋势图出错:", error); - } - }); -}; - // 监听选项卡变化,重新渲染对应图表 watch(activeTab, (newValue) => { if (newValue === "diagnosis") { setTimeout(drawRadarChart, 50); - } else if (newValue === "trend") { - setTimeout(drawTrendChart, 50); } }); @@ -418,38 +255,6 @@ const initKsdj = () => { singleFlag = maxDj < maxKmDj + 100; }; -// 构建当前学期所有科目成绩(对象是按照{ 科目id:{ 日期:成绩 } }的结构拼接) -const buildXqKmKscjList = () => { - const xqKscjMap = new Map(); - const len = xqKscjList.value.length; - for (let i = 0; i < len; i++) { - const exam = xqKscjList.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: sysKmList.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 ksfsList: any[] = []; let totalFs = 0.00; @@ -474,100 +279,101 @@ const rebuildData = () => { totalFs = totalFs / ksccKmList.value.length; } curKsdj.value = djList.value.find((item: any) => item.zdf <= totalFs && item.zgf >= totalFs) || {}; - console.log("考试场次等级", curKsdj.value); - // 构建趋势图所需的成绩列表信息 - buildXqKmKscjList(); } onMounted(async () => { - curXs.value = getCurXs; - kscc.value = getData; - const res = await xsKscjApi({ - xsId: getCurXs.id, - ksccId: getData.id, - njmcId: getData.njmcId - }); - if (res.resultCode == 1) { - sysKmList.value = res.result.kmList; - ksccKmList.value = res.result.ksccKmList; - kmDjList.value = res.result.kmDjList; - djList.value = res.result.djList; - ksccKscjList.value = res.result.ksccKscjList; - xqKscjList.value = res.result.xqKscjList; - // 初始化考试等级 - initKsdj(); - // 重构显示数据 - rebuildData(); - } - // 默认绘制学科成绩视图 - // 延迟执行确保DOM已经渲染 - setTimeout(() => { - if (activeTab.value === "diagnosis") { - drawRadarChart(); - } else if (activeTab.value === "trend") { - drawTrendChart(); + pageLoading.value = true; + try { + curXs.value = getCurXs; + kscc.value = getData; + const res = await xsKscjApi({ + xsId: getCurXs.id, + ksccId: getData.id, + njmcId: getData.njmcId + }); + if (res.resultCode == 1) { + sysKmList.value = res.result.kmList || []; + ksccKmList.value = res.result.ksccKmList || []; + kmDjList.value = res.result.kmDjList || []; + djList.value = res.result.djList || []; + ksccKscjList.value = res.result.ksccKscjList || []; + initKsdj(); + rebuildData(); } - }, 300); + // 加载班主任寄语 + try { + const pjRes = await ksccPjFindByKsccAndXsApi({ + ksccId: getData.id, + xsId: getCurXs.id, + }); + if (pjRes && pjRes.resultCode === 1 && pjRes.result && pjRes.result.pjBzr) { + pjBzr.value = pjRes.result.pjBzr; + } + } catch (_) {} + setTimeout(() => { + if (activeTab.value === "diagnosis") { + drawRadarChart(); + } + }, 300); + } finally { + pageLoading.value = false; + } }); - -// 等级说明弹窗状态 -const gradeInfoPopupVisible = ref(false); - -// 等级说明数据 -const gradeInfoList = ref([ - { - grade: "A+", - color: "#FF6B6B", - desc: "优异", - description: "超过90%同学,处于年级前列", - }, - { - grade: "A", - color: "#4D96FF", - desc: "相对较好", - description: "超过80%同学,表现良好", - }, - { - grade: "B", - color: "#6BCB77", - desc: "良好", - description: "超过60%同学,达到年级中上水平", - }, - { - grade: "C", - color: "#FFD93D", - desc: "一般", - description: "达到年级平均水平", - }, - { - grade: "D", - color: "#B8B8B8", - desc: "待提高", - description: "未达到年级平均水平,需要加强", - }, -]); - -// 显示等级说明弹窗 -function showGradeInfo() { - gradeInfoPopupVisible.value = true; -} - -// 隐藏等级说明弹窗 -function hideGradeInfo() { - gradeInfoPopupVisible.value = false; -}