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

305 lines
7.5 KiB
Vue

<template>
<aside class="training-ai-sidebar">
<section class="ai-panel ai-panel--study">
<h3>
<el-icon><Reading /></el-icon>
AI助学
</h3>
<div class="ai-output">
<div class="output-glow"></div>
<p>{{ displayAssistMessage }}</p>
<div v-if="referenceText" class="reference-block">
<strong>{{ referenceTitle }}</strong>
<span v-html="referenceText"></span>
</div>
</div>
<div class="ai-input-shell">
<textarea v-model="assistQuestion" placeholder="可以根据任务要求问我相关问题"></textarea>
<button type="button" class="send-btn" aria-label="" @click="requestAssist">
<el-icon><Top /></el-icon>
</button>
</div>
</section>
<section class="ai-panel ai-panel--review">
<h3>
<el-icon><Star /></el-icon>
AI助评
</h3>
<div class="ai-output">
<div class="output-glow"></div>
<p>{{ displayEvalMessage }}</p>
<div v-if="displayScoreVisible" class="score-badge">
综合评分<strong>{{ displayFinalScore }}</strong> / 100
</div>
</div>
<div class="ai-input-shell">
<textarea v-model="reviewQuestion" placeholder="请求AI助评智能打分"></textarea>
<button type="button" class="send-btn" aria-label="AI" @click="requestEvaluation">
<el-icon><Top /></el-icon>
</button>
</div>
</section>
</aside>
</template>
<script setup>
import { Reading, Star, Top } from "@element-plus/icons-vue";
const props = defineProps({
studyTip: {
type: String,
default: "当前任务建议:先明确任务要求,再结合真实业务场景逐步补全实训内容。",
},
referenceTitle: {
type: String,
default: "填写提醒",
},
referenceText: {
type: String,
default: "",
},
assistText: {
type: String,
default: "建议已生成:请围绕任务目标补充关键数据、分析过程和结论依据。",
},
evalMessage: {
type: String,
default: "完成实训内容后可以请求AI助评获取智能评分和改进建议。",
},
finalScore: {
type: [String, Number],
default: "--",
},
scoreVisible: {
type: Boolean,
default: false,
},
progressItems: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(["request-assist", "request-evaluation"]);
const assistQuestion = ref("");
const reviewQuestion = ref("");
const assistMessage = ref("");
const internalEvalMessage = ref("");
const internalScore = ref("--");
const internalScoreVisible = ref(false);
const displayAssistMessage = computed(() => assistMessage.value || props.studyTip);
const displayScoreVisible = computed(() => props.scoreVisible || internalScoreVisible.value);
const displayFinalScore = computed(() => (props.scoreVisible ? props.finalScore : internalScore.value));
const displayEvalMessage = computed(() => {
if (props.scoreVisible) return props.evalMessage;
return internalEvalMessage.value || props.evalMessage;
});
function requestAssist() {
emit("request-assist", assistQuestion.value);
assistMessage.value = assistQuestion.value
? `围绕“${assistQuestion.value}”,建议先对照任务目标检查信息是否完整,再补充数据依据和分析结论。`
: props.assistText;
}
function requestEvaluation() {
emit("request-evaluation", reviewQuestion.value);
internalScoreVisible.value = true;
internalScore.value = 88;
internalEvalMessage.value = "已完成初步评估:内容结构基本完整,建议继续补充真实数据来源、分析过程和可执行结论。";
}
</script>
<style lang="scss" scoped>
.training-ai-sidebar {
display: flex;
flex-direction: column;
gap: 22px;
align-self: start;
min-height: calc(100vh - 106px);
padding: 10px;
border: 1px solid rgba(0, 174, 255, 0.22);
border-radius: 26px;
background:
linear-gradient(180deg, rgba(13, 30, 44, 0.92), rgba(5, 15, 26, 0.96)),
radial-gradient(circle at 50% 0%, rgba(0, 220, 255, 0.16), transparent 42%);
box-shadow:
inset 0 1px 0 rgba(169, 229, 255, 0.12),
0 24px 56px rgba(0, 0, 0, 0.22);
}
.ai-panel {
position: relative;
overflow: hidden;
border: 1px solid rgba(72, 207, 255, 0.34);
border-radius: 22px;
padding: 18px;
background:
linear-gradient(180deg, rgba(12, 32, 49, 0.88), rgba(6, 19, 31, 0.94)),
repeating-linear-gradient(90deg, rgba(95, 221, 255, 0.04) 0 1px, transparent 1px 26px);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.08),
0 18px 36px rgba(0, 0, 0, 0.2);
&::before {
content: "";
position: absolute;
left: 18px;
top: 0;
width: 38px;
height: 2px;
background: linear-gradient(90deg, #5ef2ff, transparent);
}
h3 {
display: flex;
align-items: center;
gap: 10px;
margin: 0 0 14px;
color: #ffffff;
font-size: 20px;
font-weight: 900;
letter-spacing: 0;
.el-icon {
color: #77eaff;
}
}
}
.ai-output {
position: relative;
min-height: 220px;
padding: 18px;
border: 1px solid rgba(129, 211, 255, 0.4);
border-radius: 14px;
color: #d9efff;
background:
linear-gradient(180deg, rgba(8, 27, 42, 0.88), rgba(4, 17, 29, 0.92)),
radial-gradient(circle at 18% 12%, rgba(38, 205, 255, 0.12), transparent 32%);
box-shadow: inset 0 0 28px rgba(55, 183, 255, 0.08);
font-size: 14px;
line-height: 1.75;
p {
position: relative;
z-index: 1;
margin: 0;
}
}
.output-glow {
position: absolute;
inset: 0;
pointer-events: none;
background:
linear-gradient(90deg, rgba(96, 237, 255, 0.1), transparent 22%),
linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 26%);
opacity: 0.75;
}
.reference-block {
position: relative;
z-index: 1;
margin-top: 14px;
padding-top: 14px;
border-top: 1px solid rgba(129, 211, 255, 0.18);
color: #c9e8fb;
strong {
display: block;
margin-bottom: 8px;
color: #ffffff;
}
}
.ai-input-shell {
position: relative;
margin-top: 14px;
textarea {
display: block;
width: 100%;
min-height: 142px;
resize: vertical;
padding: 14px 54px 14px 14px;
border: 1px solid rgba(129, 211, 255, 0.36);
border-radius: 12px;
outline: none;
color: #eef8ff;
background: rgba(3, 16, 28, 0.86);
font-size: 14px;
line-height: 1.7;
transition: border-color 0.2s, box-shadow 0.2s;
&::placeholder {
color: rgba(198, 225, 244, 0.62);
}
&:focus {
border-color: rgba(100, 229, 255, 0.78);
box-shadow: 0 0 0 3px rgba(0, 174, 255, 0.14);
}
}
}
.send-btn {
position: absolute;
right: 12px;
bottom: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border: 1px solid rgba(116, 232, 255, 0.5);
border-radius: 50%;
color: #ffffff;
background: linear-gradient(135deg, #1da2ff, #6b5cff);
box-shadow: 0 10px 24px rgba(0, 136, 255, 0.34);
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 14px 30px rgba(0, 174, 255, 0.44);
}
}
.score-badge {
position: relative;
z-index: 1;
display: inline-flex;
align-items: center;
gap: 6px;
margin-top: 14px;
padding: 8px 12px;
border: 1px solid rgba(126, 233, 255, 0.38);
border-radius: 999px;
color: #dff8ff;
background: rgba(11, 42, 56, 0.78);
strong {
color: #7ee9ff;
font-size: 20px;
}
}
@media (max-width: 1280px) {
.training-ai-sidebar {
min-height: auto;
}
.ai-output {
min-height: 160px;
}
}
</style>