feat: add school default task configuration page
parent
bad2437914
commit
2ad3ee929e
@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container default-task-page">
|
||||||
|
<el-alert
|
||||||
|
v-if="!isReady"
|
||||||
|
:title="statusTitle"
|
||||||
|
:description="lastError || '正在初始化,稍后刷新。初始化完成前暂以平台基线任务只读展示。'"
|
||||||
|
:type="status === 'FAILED' ? 'error' : 'warning'"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
class="mb16"
|
||||||
|
/>
|
||||||
|
<div class="task-toolbar">
|
||||||
|
<div>
|
||||||
|
<h3>默认实训任务配置</h3>
|
||||||
|
<p>为本校教学班尚未单独配置任务时提供默认任务。教学班已配置的任务不受这里改动影响。</p>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-actions">
|
||||||
|
<el-button :loading="loading" @click="loadTasks">刷新</el-button>
|
||||||
|
<el-button v-if="!isReady" type="warning" :loading="retrying" @click="retryInitialization">立即重试</el-button>
|
||||||
|
<el-button type="primary" :disabled="!isReady" :loading="saving" @click="saveTasks">保存配置</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="loading" :data="tasks" border row-key="id" class="task-table">
|
||||||
|
<el-table-column label="实训任务" prop="module" min-width="260" />
|
||||||
|
<el-table-column label="是否启用" width="180" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-switch
|
||||||
|
v-model="row.disabledStatus"
|
||||||
|
:active-value="0"
|
||||||
|
:inactive-value="1"
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="停用"
|
||||||
|
:disabled="!isReady"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="排序" prop="sort" width="100" align="center" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, getCurrentInstance, onMounted, ref } from "vue";
|
||||||
|
import * as teacherApi from "@/api/teacher";
|
||||||
|
import useUserStore from "@/store/modules/user";
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const tasks = ref([]);
|
||||||
|
const status = ref("INITIALIZING");
|
||||||
|
const lastError = ref("");
|
||||||
|
const loading = ref(false);
|
||||||
|
const saving = ref(false);
|
||||||
|
const retrying = ref(false);
|
||||||
|
const isManagerTeacher = computed(() => userStore.userInfo.teacherAdmin === true);
|
||||||
|
const isReady = computed(() => status.value === "READY");
|
||||||
|
const statusTitle = computed(() => (status.value === "FAILED" ? "默认任务初始化失败" : "默认任务正在初始化"));
|
||||||
|
|
||||||
|
const loadTasks = async () => {
|
||||||
|
if (!isManagerTeacher.value) {
|
||||||
|
proxy.$modal.msgError("仅管理员教师可访问默认实训任务配置");
|
||||||
|
proxy.$router.replace("/index");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await teacherApi.getSchoolDefaultTasks();
|
||||||
|
status.value = res.data?.status || "INITIALIZING";
|
||||||
|
lastError.value = res.data?.lastError || "";
|
||||||
|
tasks.value = res.data?.tasks || [];
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveTasks = async () => {
|
||||||
|
saving.value = true;
|
||||||
|
try {
|
||||||
|
await teacherApi.saveSchoolDefaultTasks(tasks.value);
|
||||||
|
proxy.$modal.msgSuccess("默认实训任务已保存");
|
||||||
|
await loadTasks();
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const retryInitialization = async () => {
|
||||||
|
retrying.value = true;
|
||||||
|
try {
|
||||||
|
await teacherApi.retrySchoolDefaultTaskInitialization();
|
||||||
|
proxy.$modal.msgSuccess("已提交初始化任务,请稍后刷新");
|
||||||
|
await loadTasks();
|
||||||
|
} finally {
|
||||||
|
retrying.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(loadTasks);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.default-task-page { min-height: 640px; }
|
||||||
|
.task-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 24px; margin-bottom: 16px; }
|
||||||
|
.task-toolbar h3 { margin: 0 0 8px; }
|
||||||
|
.task-toolbar p { margin: 0; color: var(--el-text-color-secondary); }
|
||||||
|
.toolbar-actions { display: flex; gap: 10px; flex-shrink: 0; }
|
||||||
|
.task-table { width: 100%; }
|
||||||
|
</style>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const root = path.resolve(__dirname, "..");
|
||||||
|
const read = (file) => fs.readFileSync(path.join(root, file), "utf8");
|
||||||
|
|
||||||
|
const routerSource = read("src/router/index.js");
|
||||||
|
const sidebarSource = read("src/layout/components/Sidebar/index.vue");
|
||||||
|
const apiSource = read("src/api/teacher.js");
|
||||||
|
const pageSource = read("src/views/teacherEnd/defaultTask/index.vue");
|
||||||
|
|
||||||
|
assert.match(routerSource, /requiresManagerTeacher:\s*true/);
|
||||||
|
assert.match(sidebarSource, /route\.meta\?\.requiresManagerTeacher/);
|
||||||
|
assert.match(apiSource, /\/api\/school-default-tasks/);
|
||||||
|
assert.match(pageSource, /正在初始化,稍后刷新/);
|
||||||
|
assert.match(pageSource, /立即重试/);
|
||||||
|
assert.match(pageSource, /:disabled="!isReady"/);
|
||||||
|
|
||||||
|
console.log("school default task front-end contract passed");
|
||||||
Loading…
Reference in New Issue