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/TrainingTaskBrief.vue

319 lines
7.7 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<section class="goal-bg training-task-brief">
<div class="brief-toolbar">
<div class="brief-tabs" role="tablist" aria-label="实训任务说明">
<button
class="brief-tab brief-tab--background"
:class="{ 'is-active': activeTab === 'background' }"
type="button"
role="tab"
:aria-selected="activeTab === 'background'"
@click="activeTab = 'background'"
>
<span class="brief-tab-icon"><el-icon><Clock /></el-icon></span>
<span>实训背景</span>
</button>
<button
class="brief-tab brief-tab--goals"
:class="{ 'is-active': activeTab === 'goals' }"
type="button"
role="tab"
:aria-selected="activeTab === 'goals'"
@click="activeTab = 'goals'"
>
<span class="brief-tab-icon"><el-icon><Aim /></el-icon></span>
<span>实训目标</span>
</button>
<button
class="brief-tab brief-tab--requirements"
:class="{ 'is-active': activeTab === 'requirements' }"
type="button"
role="tab"
:aria-selected="activeTab === 'requirements'"
@click="activeTab = 'requirements'"
>
<span class="brief-tab-icon"><el-icon><DataAnalysis /></el-icon></span>
<span>实训要求</span>
</button>
</div>
<TrainingTaskToolbar
class="brief-material"
:material-name="materialName"
:material-url="materialUrl"
/>
</div>
<article class="brief-panel" role="tabpanel">
<div class="brief-panel-title">
<span class="brief-panel-icon"><el-icon><Clock v-if="activeTab === 'background'" /><Aim v-else-if="activeTab === 'goals'" /><DataAnalysis v-else /></el-icon></span>
<span>{{ activeTitle }}</span>
</div>
<div class="brief-scroll">
<p v-if="activeTab === 'background'" class="brief-content">{{ displayBackground }}</p>
<ul v-else-if="activeTab === 'goals' && displayGoals.length" class="brief-content brief-goals">
<li v-for="goal in displayGoals" :key="goal">{{ goal }}</li>
</ul>
<p v-else-if="activeTab === 'goals'" class="brief-content">请根据教师端配置的实训目标完成本次实训。</p>
<p v-else class="brief-content">{{ displayRequirements }}</p>
</div>
</article>
</section>
</template>
<script setup>
import { Aim, Clock, DataAnalysis } from "@element-plus/icons-vue";
import TrainingTaskToolbar from "@/views/components/TrainingTaskToolbar.vue";
const activeTab = ref("background");
const props = defineProps({
background: {
type: String,
default: "",
},
goals: {
type: [Array, String],
default: () => [],
},
requirements: {
type: String,
default: "",
},
materialName: {
type: String,
default: "",
},
materialUrl: {
type: String,
default: "",
},
showMaterial: {
type: Boolean,
default: true,
},
});
const { materialName, materialUrl } = toRefs(props);
const displayBackground = computed(() => props.background || "请根据教师端配置的实训背景理解本次任务场景。");
const displayGoals = computed(() => normalizeGoals(props.goals));
const displayRequirements = computed(() => props.requirements || "请根据教师端配置的实训要求完成本次实训。");
const activeTitle = computed(() => {
const titleMap = {
background: "实训背景",
goals: "实训目标",
requirements: "实训要求",
};
return titleMap[activeTab.value];
});
function normalizeGoals(value) {
if (Array.isArray(value)) {
return value.map((item) => String(item || "").trim()).filter(Boolean);
}
if (!value) {
return [];
}
try {
const parsed = JSON.parse(value);
if (Array.isArray(parsed)) {
return parsed.map((item) => String(item || "").trim()).filter(Boolean);
}
} catch (error) {
// Fall through to delimiter parsing.
}
return String(value)
.split(/[;;||]/)
.map((item) => item.trim())
.filter(Boolean);
}
</script>
<style lang="scss" scoped>
.training-task-brief {
/* 令牌继承宿主页面主题(教育蓝页传蓝,墨黑页/无令牌时回退墨黑) */
--brief-accent: var(--student-accent, #1f2937);
--brief-accent-deep: var(--student-accent-deep, #111827);
--brief-accent-soft: var(--student-accent-soft, #f3f4f6);
--brief-panel-border: var(--student-panel-border, #e5e7eb);
--brief-text: var(--student-text, #1f2937);
--brief-text-secondary: var(--student-text-secondary, #6b7280);
margin-bottom: 10px;
padding: 12px 16px;
border: 1px solid var(--brief-panel-border);
border-radius: 12px;
background: #ffffff;
box-shadow: none;
}
.brief-toolbar {
display: flex;
align-items: stretch;
gap: 10px;
margin-bottom: 10px;
}
.brief-tabs {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
flex: 1 1 auto;
min-width: 0;
}
.brief-material {
flex: 0 0 auto;
}
.brief-tab {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
min-width: 0;
height: 36px;
padding: 0 12px;
border: 1px solid var(--brief-panel-border);
border-radius: 8px;
color: var(--brief-text-secondary);
background: var(--brief-accent-soft);
cursor: pointer;
font-size: var(--student-fs-body-sm);
font-weight: 600;
line-height: 1;
transition: border-color 0.16s ease, color 0.16s ease, background 0.16s ease;
&:hover {
border-color: var(--brief-accent);
color: var(--brief-accent);
background: #ffffff;
}
&.is-active {
border-color: var(--brief-accent);
color: #ffffff;
background: var(--brief-accent);
box-shadow: 0 2px 6px rgba(17, 24, 39, 0.18);
}
}
.brief-tab-icon,
.brief-panel-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
flex: 0 0 16px;
line-height: 1;
.el-icon {
display: block;
width: 16px;
height: 16px;
font-size: 18px;
line-height: 1;
}
}
.brief-tab .brief-tab-icon {
color: inherit;
}
.brief-tab.is-active .brief-tab-icon {
color: inherit;
}
.brief-panel {
border: 1px solid var(--brief-panel-border);
border-radius: 10px;
background: var(--brief-accent-soft);
overflow: hidden;
}
.brief-panel-title {
display: flex;
align-items: center;
gap: 8px;
height: 36px;
padding: 0 14px;
border-bottom: 1px solid var(--brief-panel-border);
color: var(--brief-text);
font-size: var(--student-fs-body);
font-weight: 700;
line-height: 1;
}
.brief-scroll {
max-height: 160px;
min-height: 100px;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: var(--brief-panel-border) var(--brief-accent-soft);
}
.brief-content {
margin: 0;
padding: 10px 16px 12px;
color: var(--brief-text-secondary);
font-size: var(--student-fs-body);
line-height: 1.6;
}
.brief-goals {
padding-left: 16px;
list-style: none;
li {
padding: 4px 0 4px 12px;
border-left: 2px solid var(--brief-panel-border);
color: var(--brief-text-secondary);
font-size: var(--student-fs-body);
line-height: 1.55;
}
li + li {
margin-top: 2px;
}
}
.brief-scroll::-webkit-scrollbar {
width: 6px;
}
.brief-scroll::-webkit-scrollbar-track {
background: var(--brief-accent-soft);
}
.brief-scroll::-webkit-scrollbar-thumb {
border-radius: 6px;
background: var(--brief-panel-border);
}
@media (max-width: 768px) {
.training-task-brief {
padding: 12px;
}
.brief-tabs {
gap: 8px;
}
.brief-toolbar {
flex-direction: column;
}
.brief-tab {
height: 34px;
padding: 0 8px;
font-size: var(--student-fs-caption);
}
.brief-scroll {
max-height: 140px;
min-height: 90px;
}
}
</style>