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

307 lines
7.0 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
v-if="showToolbar"
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,
},
showToolbar: {
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 {
margin-bottom: 14px;
padding: 12px 16px;
border: 1px solid #2d5f7e;
border-radius: 20px;
background: rgba(0, 35, 55, 0.6);
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: 40px;
padding: 0 12px;
border: 1px solid #2d5f7e;
border-radius: 6px;
color: #b9e2ff;
background: rgba(13, 64, 88, 0.62);
cursor: pointer;
font-size: 15px;
font-weight: 800;
line-height: 1;
transition: border-color 0.18s ease, color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}
.brief-tab:hover,
.brief-tab.is-active {
border-color: #46b5e7;
color: #f1fbff;
background: rgba(18, 85, 116, 0.78);
box-shadow: none;
}
.brief-tab-icon,
.brief-panel-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
flex: 0 0 18px;
line-height: 1;
.el-icon {
display: block;
width: 18px;
height: 18px;
font-size: 18px;
line-height: 1;
}
}
.brief-tab-icon,
.brief-panel-icon,
.brief-tab--background .brief-tab-icon,
.brief-tab--goals .brief-tab-icon,
.brief-tab--requirements .brief-tab-icon {
color: #7ddcff;
}
.brief-panel {
border: 1px solid rgba(0, 200, 255, 0.2);
border-radius: 20px;
background: rgba(0, 20, 35, 0.65);
overflow: hidden;
}
.brief-panel-title {
display: flex;
align-items: center;
gap: 8px;
height: 40px;
padding: 0 14px;
border-bottom: 1px solid rgba(0, 180, 255, 0.2);
color: #e9f8ff;
font-size: 15px;
font-weight: 900;
line-height: 1;
}
.brief-scroll {
max-height: 160px;
min-height: 110px;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: rgba(92, 197, 245, 0.55) rgba(0, 13, 24, 0.5);
}
.brief-content {
margin: 0;
padding: 10px 16px 12px;
color: #d9efff;
font-size: 15px;
line-height: 1.55;
}
.brief-goals {
padding-left: 16px;
list-style: none;
li {
padding: 5px 0 5px 12px;
border-left: 2px solid rgba(105, 221, 255, 0.5);
}
li + li {
margin-top: 2px;
}
}
.brief-scroll::-webkit-scrollbar {
width: 8px;
}
.brief-scroll::-webkit-scrollbar-track {
background: rgba(0, 13, 24, 0.5);
}
.brief-scroll::-webkit-scrollbar-thumb {
border-radius: 8px;
background: rgba(92, 197, 245, 0.55);
}
@media (max-width: 768px) {
.training-task-brief {
padding: 12px;
}
.brief-tabs {
gap: 8px;
}
.brief-toolbar {
flex-direction: column;
}
.brief-tab {
height: 36px;
padding: 0 8px;
font-size: 14px;
}
.brief-scroll {
height: 140px;
}
}
</style>