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;
-}