|
|
|
|
import request from "@/utils/request";
|
|
|
|
|
import { getUserInfo } from "@/utils/auth";
|
|
|
|
|
import { getTrainingReviewPreviewContext } from "@/utils/trainingReviewPreview";
|
|
|
|
|
|
|
|
|
|
// 学生页面和全局“补充步骤”导航会在同一轮路由渲染中读取同一任务配置。
|
|
|
|
|
// 仅合并进行中的请求:教师保存后下一次读取仍会获得最新配置,不引入陈旧缓存。
|
|
|
|
|
const pendingTaskRequests = new Map();
|
|
|
|
|
const pendingStudentTaskListRequests = new Map();
|
|
|
|
|
const STUDENT_TASK_NAVIGATION_CACHE_PREFIX = "student-training-task-navigation:v1:";
|
|
|
|
|
|
|
|
|
|
function notifyStudentTaskConfigLoading(taskKey, loading) {
|
|
|
|
|
if (typeof window === "undefined") return;
|
|
|
|
|
window.dispatchEvent(new CustomEvent("student-task-config-loading", { detail: { taskKey, loading } }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function listTrainingTasks(params) {
|
|
|
|
|
return request({
|
|
|
|
|
url: "/api/training-tasks",
|
|
|
|
|
method: "get",
|
|
|
|
|
params,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getStudentTaskNavigationCacheKey() {
|
|
|
|
|
try {
|
|
|
|
|
const userInfo = JSON.parse(getUserInfo() || "{}");
|
|
|
|
|
const userId = userInfo.userId || userInfo.user_id || userInfo.username || userInfo.userName;
|
|
|
|
|
return userId ? `${STUDENT_TASK_NAVIGATION_CACHE_PREFIX}${userId}` : "";
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getCachedStudentTrainingTasks() {
|
|
|
|
|
const cacheKey = getStudentTaskNavigationCacheKey();
|
|
|
|
|
if (!cacheKey || typeof window === "undefined") return [];
|
|
|
|
|
try {
|
|
|
|
|
const cached = JSON.parse(window.sessionStorage.getItem(cacheKey) || "[]");
|
|
|
|
|
return Array.isArray(cached) ? cached : [];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The navigation and top bar both need the same enabled task list. Reuse one
|
|
|
|
|
* in-flight request and retain the last successful response for immediate menu rendering.
|
|
|
|
|
*/
|
|
|
|
|
export function loadStudentTrainingTasks() {
|
|
|
|
|
const cacheKey = getStudentTaskNavigationCacheKey() || "anonymous";
|
|
|
|
|
if (pendingStudentTaskListRequests.has(cacheKey)) {
|
|
|
|
|
return pendingStudentTaskListRequests.get(cacheKey);
|
|
|
|
|
}
|
|
|
|
|
const requestPromise = listTrainingTasks({ enabledOnly: true }).then((res) => {
|
|
|
|
|
const tasks = Array.isArray(res?.data) ? res.data : [];
|
|
|
|
|
if (cacheKey !== "anonymous" && typeof window !== "undefined") {
|
|
|
|
|
window.sessionStorage.setItem(cacheKey, JSON.stringify(tasks));
|
|
|
|
|
}
|
|
|
|
|
return { ...res, data: tasks };
|
|
|
|
|
});
|
|
|
|
|
pendingStudentTaskListRequests.set(cacheKey, requestPromise);
|
|
|
|
|
requestPromise.then(
|
|
|
|
|
() => pendingStudentTaskListRequests.delete(cacheKey),
|
|
|
|
|
() => pendingStudentTaskListRequests.delete(cacheKey)
|
|
|
|
|
);
|
|
|
|
|
return requestPromise;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getTrainingTaskByKey(taskKey) {
|
|
|
|
|
const normalizedTaskKey = String(taskKey || "").trim();
|
|
|
|
|
const preview = getTrainingReviewPreviewContext();
|
|
|
|
|
if (preview) {
|
|
|
|
|
if (preview.taskKey !== normalizedTaskKey) {
|
|
|
|
|
return Promise.resolve({ code: 200, data: null });
|
|
|
|
|
}
|
|
|
|
|
if (!preview.taskConfig) {
|
|
|
|
|
return Promise.reject(new Error("评阅案例配置未加载,不能展示通用案例"));
|
|
|
|
|
}
|
|
|
|
|
return Promise.resolve({ code: 200, data: preview.taskConfig });
|
|
|
|
|
}
|
|
|
|
|
if (pendingTaskRequests.has(normalizedTaskKey)) {
|
|
|
|
|
return pendingTaskRequests.get(normalizedTaskKey);
|
|
|
|
|
}
|
|
|
|
|
notifyStudentTaskConfigLoading(normalizedTaskKey, true);
|
|
|
|
|
const requestPromise = request({
|
|
|
|
|
url: `/api/training-tasks/key/${normalizedTaskKey}`,
|
|
|
|
|
method: "get",
|
|
|
|
|
});
|
|
|
|
|
pendingTaskRequests.set(normalizedTaskKey, requestPromise);
|
|
|
|
|
requestPromise.then(
|
|
|
|
|
() => {
|
|
|
|
|
pendingTaskRequests.delete(normalizedTaskKey);
|
|
|
|
|
notifyStudentTaskConfigLoading(normalizedTaskKey, false);
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
pendingTaskRequests.delete(normalizedTaskKey);
|
|
|
|
|
notifyStudentTaskConfigLoading(normalizedTaskKey, false);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return requestPromise;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function updateTrainingTask(id, data) {
|
|
|
|
|
return request({
|
|
|
|
|
url: `/api/training-tasks/${id}`,
|
|
|
|
|
method: "put",
|
|
|
|
|
data,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createTrainingTask(data) {
|
|
|
|
|
return request({
|
|
|
|
|
url: "/api/training-tasks",
|
|
|
|
|
method: "post",
|
|
|
|
|
data,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function importTrainingTasks(file) {
|
|
|
|
|
const data = new FormData();
|
|
|
|
|
data.append("file", file);
|
|
|
|
|
return request({
|
|
|
|
|
url: "/api/training-tasks/import",
|
|
|
|
|
method: "post",
|
|
|
|
|
data,
|
|
|
|
|
headers: { "Content-Type": "multipart/form-data" },
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function downloadTrainingTaskTemplate() {
|
|
|
|
|
return request({
|
|
|
|
|
url: "/api/training-tasks/template",
|
|
|
|
|
method: "get",
|
|
|
|
|
responseType: "blob",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function uploadTrainingTaskMaterial(data) {
|
|
|
|
|
return request({
|
|
|
|
|
url: "/common/upload",
|
|
|
|
|
method: "post",
|
|
|
|
|
data,
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "multipart/form-data",
|
|
|
|
|
repeatSubmit: false,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function listClassTrainingTasks(teachingClassId, params) {
|
|
|
|
|
return request({
|
|
|
|
|
url: `/api/training-tasks/classes/${teachingClassId}`,
|
|
|
|
|
method: "get",
|
|
|
|
|
params,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getClassTrainingTaskByKey(teachingClassId, taskKey) {
|
|
|
|
|
return request({
|
|
|
|
|
url: `/api/training-tasks/classes/${teachingClassId}/key/${taskKey}`,
|
|
|
|
|
method: "get",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function saveClassTrainingTask(teachingClassId, taskKey, data) {
|
|
|
|
|
return request({
|
|
|
|
|
url: `/api/training-tasks/classes/${teachingClassId}/${taskKey}`,
|
|
|
|
|
method: "put",
|
|
|
|
|
data,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function restoreClassTrainingTaskDefault(teachingClassId, taskKey) {
|
|
|
|
|
return request({
|
|
|
|
|
url: `/api/training-tasks/classes/${teachingClassId}/${taskKey}/override`,
|
|
|
|
|
method: "delete",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function restoreClassTrainingTasksDefaults(teachingClassId) {
|
|
|
|
|
return request({
|
|
|
|
|
url: `/api/training-tasks/classes/${teachingClassId}/overrides`,
|
|
|
|
|
method: "delete",
|
|
|
|
|
});
|
|
|
|
|
}
|