You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dianshang-qianduan/src/views/components/TrainingTaskToolbar.vue

100 lines
4.1 KiB
Vue

<template>
<div class="training-task-toolbar">
<TrainingMaterialButton :material-name="resolvedMaterial.name" :material-url="resolvedMaterial.url" />
<el-button class="task-toolbar-button task-toolbar-button--submit" @click="triggerExistingAction('submit')">
<el-icon><CircleCheck /></el-icon>提交任务
</el-button>
<el-button class="task-toolbar-button" @click="triggerExistingAction('reset')">
<el-icon><RefreshLeft /></el-icon>重新实训
</el-button>
<el-button class="task-toolbar-button" @click="triggerExistingAction('export')">
<el-icon><Download /></el-icon>导出实训成果
</el-button>
</div>
</template>
<script setup>
import { computed, onMounted, ref, watch } from "vue";
import { CircleCheck, Download, RefreshLeft } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import { getTrainingTaskByKey } from "@/api/trainingTask";
import { resolveTrainingTaskKey } from "@/views/training/taskKeyMap";
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
const props = defineProps({
materialName: { type: String, default: "" },
materialUrl: { type: String, default: "" },
taskKey: { type: String, default: "" },
onExport: { type: Function, default: null },
});
const route = useRoute();
const loadedMaterial = ref({ name: "", url: "" });
const resolvedTaskKey = computed(() => resolveTrainingTaskKey(route, props.taskKey));
const resolvedMaterial = computed(() => ({
name: String(props.materialName || loadedMaterial.value.name || ""),
url: String(props.materialUrl || loadedMaterial.value.url || ""),
}));
onMounted(loadMaterial);
watch(resolvedTaskKey, loadMaterial);
async function loadMaterial() {
if (props.materialName || props.materialUrl || !resolvedTaskKey.value) return;
try {
const response = await getTrainingTaskByKey(resolvedTaskKey.value);
const task = response?.data || response || {};
loadedMaterial.value = {
name: String(task.materialName || ""),
url: String(task.materialUrl || ""),
};
} catch {
// The material button keeps its unavailable state when a task has no configured case file.
}
}
function triggerExistingAction(action) {
if (action === "export" && props.onExport) {
props.onExport();
return;
}
const labelPriority = {
submit: [[/提交任务/], [/完成实训/, /保存并完成/, /校验并完成/]],
reset: [[/重新实训/], [/清空填写/]],
export: [[/导出实训成果/]],
};
const buttons = Array.from(document.querySelectorAll("button"))
.filter((button) => !button.closest(".training-task-toolbar") && isVisible(button) && !button.disabled);
const target = labelPriority[action]
.map((matchers) => buttons.find((button) => matchers.some((matcher) => matcher.test(button.innerText || ""))))
.find(Boolean);
if (target) {
target.click();
return;
}
const messages = {
submit: "请先完成全部实训步骤,再提交任务。",
reset: "当前步骤没有可重新实训的内容。",
export: "当前任务暂无可导出的实训成果。",
};
ElMessage.warning(messages[action]);
}
function isVisible(element) {
const style = window.getComputedStyle(element);
return style.display !== "none" && style.visibility !== "hidden" && element.getClientRects().length > 0;
}
</script>
<style lang="scss" scoped>
.training-task-toolbar { display:flex; flex-wrap:wrap; align-items:center; justify-content:flex-end; gap:8px; }
.task-toolbar-button { min-height:32px; padding:0 10px; border-color:rgba(99,217,255,.55); border-radius:7px; color:#e9fbff; background:rgba(17,126,178,.36); font-size:13px; font-weight:800; }
.task-toolbar-button:hover { border-color:rgba(116,235,255,.85); color:#e9fbff; background:rgba(18,146,204,.5); }
.task-toolbar-button--submit { border-color:rgba(66,224,187,.7); background:rgba(13,137,111,.34); }
.task-toolbar-button--submit:hover { border-color:rgba(100,245,208,.9); background:rgba(14,164,132,.45); }
@media (max-width:768px) { .training-task-toolbar { justify-content:stretch; } .training-task-toolbar > * { flex:1 1 auto; } }
</style>