|
|
@@ -5,6 +5,7 @@
|
|
|
<h3>和AI一起学你想学!</h3>
|
|
|
</div>
|
|
|
<div class="input-section">
|
|
|
+ <!-- 发送消息输入框 -->
|
|
|
<div class="dialogue-card user-input-card">
|
|
|
<div class="dialogue-content">
|
|
|
<textarea
|
|
|
@@ -44,22 +45,66 @@
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- 生成/进度 -->
|
|
|
+ <div class="new-container" v-if="showNewContainer">
|
|
|
+ <!-- 全部完成时显示成功状态 -->
|
|
|
+ <div v-if="allStepsCompleted" class="success-container">
|
|
|
+ <div class="success-checkmark">✓</div>
|
|
|
+ <div class="success-text">完成</div>
|
|
|
+ </div>
|
|
|
+ <!-- 未完成时显示进度 -->
|
|
|
+ <template v-else>
|
|
|
+ <div class="left-box">
|
|
|
+ <div class="loading-container">
|
|
|
+ <div class="loading-spinner"></div>
|
|
|
+ <div class="loading-text">正在生成中</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="right-box">
|
|
|
+ <div class="progress-container">
|
|
|
+ <div class="progress-title">正在生成课程</div>
|
|
|
+ <div class="progress-steps">
|
|
|
+ <div class="progress-step" v-for="(step, index) in visibleProgressSteps" :key="index" :style="{ animationDelay: `${index * 0.2}s` }">
|
|
|
+ <span class="step-text">{{ step.text }}</span>
|
|
|
+ <span class="step-status" :class="{ 'completed': step.completed, 'active': step.active }">
|
|
|
+ <span v-if="step.completed" class="checkmark">✓</span>
|
|
|
+ <span v-else-if="step.active" class="loading-dot"></span>
|
|
|
+ <span v-else class="step-placeholder"></span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import {ref} from "vue";
|
|
|
+import {ref, onMounted, computed} from "vue";
|
|
|
import { Top, Search, User,Memo,ArrowUpBold,ArrowDownBold } from '@element-plus/icons-vue'
|
|
|
|
|
|
// 发送消息输入框
|
|
|
const prompt = ref(""); // prompt
|
|
|
|
|
|
+// 控制生成进度显示状态
|
|
|
+const showNewContainer = ref(false);
|
|
|
+
|
|
|
+// 生成进度步骤
|
|
|
+const progressSteps = ref([
|
|
|
+ { text: '生成课程信息...', completed: false, active: false, visible: false },
|
|
|
+ { text: '生成课程小节...', completed: false, active: false, visible: false },
|
|
|
+ { text: '生成背景图...', completed: false, active: false, visible: false },
|
|
|
+ { text: '生成配音...', completed: false, active: false, visible: false }
|
|
|
+]);
|
|
|
+
|
|
|
// 下拉框配置
|
|
|
const dropdowns = ref([
|
|
|
{
|
|
|
key: 'course',
|
|
|
- value: '诗词课',
|
|
|
+ value: '诗词',
|
|
|
visible: false,
|
|
|
icon: Search,
|
|
|
options: [
|
|
|
@@ -177,7 +222,69 @@ const doSendMessage = async (content) => {
|
|
|
// 清空输入框
|
|
|
prompt.value = "";
|
|
|
|
|
|
+ // 显示new-container
|
|
|
+ showNewContainer.value = true;
|
|
|
+
|
|
|
+ // 开始模拟生成进度
|
|
|
+ simulateProgress();
|
|
|
+};
|
|
|
+
|
|
|
+// 模拟生成进度
|
|
|
+const simulateProgress = () => {
|
|
|
+ // 重置所有步骤
|
|
|
+ progressSteps.value.forEach(step => {
|
|
|
+ step.completed = false;
|
|
|
+ step.active = false;
|
|
|
+ step.visible = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 逐个处理步骤
|
|
|
+ let currentStep = 0;
|
|
|
+
|
|
|
+ const processStep = () => {
|
|
|
+ if (currentStep < progressSteps.value.length) {
|
|
|
+ // 显示当前步骤
|
|
|
+ progressSteps.value[currentStep].visible = true;
|
|
|
+ // 激活当前步骤
|
|
|
+ progressSteps.value[currentStep].active = true;
|
|
|
+
|
|
|
+ // 模拟步骤完成
|
|
|
+ setTimeout(() => {
|
|
|
+ // 标记当前步骤为完成
|
|
|
+ progressSteps.value[currentStep].completed = true;
|
|
|
+ progressSteps.value[currentStep].active = false;
|
|
|
+
|
|
|
+ // 处理下一步
|
|
|
+ currentStep++;
|
|
|
+ processStep();
|
|
|
+ }, 1500); // 每个步骤持续1.5秒
|
|
|
+ } else {
|
|
|
+ // 所有步骤完成后,3秒后隐藏容器
|
|
|
+ setTimeout(() => {
|
|
|
+ showNewContainer.value = false;
|
|
|
+ }, 2000);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 开始处理第一个步骤
|
|
|
+ processStep();
|
|
|
};
|
|
|
+
|
|
|
+// 计算属性:返回可见的进度步骤
|
|
|
+const visibleProgressSteps = computed(() => {
|
|
|
+ return progressSteps.value.filter(step => step.visible);
|
|
|
+});
|
|
|
+
|
|
|
+// 计算属性:检查是否所有步骤都已完成
|
|
|
+const allStepsCompleted = computed(() => {
|
|
|
+ return progressSteps.value.every(step => step.completed);
|
|
|
+});
|
|
|
+
|
|
|
+// 组件挂载时示
|
|
|
+onMounted(() => {
|
|
|
+ // 初始状态下不显示生成进度
|
|
|
+ showNewContainer.value = false;
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
@@ -189,10 +296,9 @@ const doSendMessage = async (content) => {
|
|
|
|
|
|
.self-directed-learning {
|
|
|
width: 100%;
|
|
|
- // min-height: rpx(100); /* 设置最小高度 */
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- flex-grow: 1; /* 允许组件在容器中生长 */
|
|
|
+ flex-grow: 1;
|
|
|
}
|
|
|
|
|
|
.title-section {
|
|
|
@@ -204,11 +310,13 @@ const doSendMessage = async (content) => {
|
|
|
font-size: rpx(12);
|
|
|
color: black;
|
|
|
font-weight: bold;
|
|
|
+ letter-spacing: rpx(5);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.input-section {
|
|
|
display: flex;
|
|
|
+ flex-direction: column;
|
|
|
justify-content: flex-start;
|
|
|
margin-left: rpx(10);
|
|
|
width: calc(100% - rpx(10));
|
|
|
@@ -221,17 +329,17 @@ const doSendMessage = async (content) => {
|
|
|
padding: rpx(8);
|
|
|
max-width: 50%;
|
|
|
min-width: rpx(200);
|
|
|
- // box-shadow: 0 rpx(3.5) rpx(10) rgba(0, 0, 0, 0.15);
|
|
|
width: auto;
|
|
|
display: inline-block;
|
|
|
z-index: 2;
|
|
|
}
|
|
|
|
|
|
.user-input-card {
|
|
|
- width: 90%;
|
|
|
+ width: 93%;
|
|
|
margin-left: rpx(20);
|
|
|
max-width: none;
|
|
|
animation: dialogueEnterCenter 0.6s ease forwards;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
.user-input-card::before {
|
|
|
@@ -384,13 +492,13 @@ const doSendMessage = async (content) => {
|
|
|
position: absolute;
|
|
|
bottom: rpx(0);
|
|
|
right: rpx(0);
|
|
|
- width: rpx(20);
|
|
|
- height: rpx(20);
|
|
|
+ width: rpx(18);
|
|
|
+ height: rpx(18);
|
|
|
border-radius: 50%;
|
|
|
background: transparent;
|
|
|
color: #a7a4ed;
|
|
|
border: rpx(1) solid #a7a4ed;
|
|
|
- font-size: rpx(3);
|
|
|
+ font-size: rpx(2);
|
|
|
font-weight: bold;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
@@ -443,4 +551,236 @@ const doSendMessage = async (content) => {
|
|
|
display: block;
|
|
|
}
|
|
|
|
|
|
+.new-container {
|
|
|
+ width: 93%;
|
|
|
+ margin-left: rpx(20);
|
|
|
+ margin-top: rpx(10);
|
|
|
+ display: flex;
|
|
|
+ gap: rpx(5);
|
|
|
+ background-color: #f1f0ff;
|
|
|
+ border: rpx(1) solid #a7a4ed;
|
|
|
+ border-radius: rpx(6);
|
|
|
+ padding: rpx(2);
|
|
|
+ max-width: none;
|
|
|
+ align-self: flex-start;
|
|
|
+ box-sizing: border-box;
|
|
|
+ min-height: rpx(50);
|
|
|
+}
|
|
|
+
|
|
|
+.success-container {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: rpx(10);
|
|
|
+ gap: rpx(5);
|
|
|
+}
|
|
|
+
|
|
|
+.success-checkmark {
|
|
|
+ width: rpx(20);
|
|
|
+ height: rpx(20);
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: #4caf50;
|
|
|
+ color: white;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: rpx(12);
|
|
|
+ font-weight: bold;
|
|
|
+ animation: successScale 0.5s ease-out;
|
|
|
+}
|
|
|
+
|
|
|
+.success-text {
|
|
|
+ font-size: rpx(10);
|
|
|
+ color: #666;
|
|
|
+ font-weight: bold;
|
|
|
+ animation: successFadeIn 0.5s ease-out 0.2s both;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes successScale {
|
|
|
+ 0% {
|
|
|
+ transform: scale(0);
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
+ 50% {
|
|
|
+ transform: scale(1.2);
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ transform: scale(1);
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes successFadeIn {
|
|
|
+ from {
|
|
|
+ opacity: 0;
|
|
|
+ transform: translateY(rpx(5));
|
|
|
+ }
|
|
|
+ to {
|
|
|
+ opacity: 1;
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.left-box {
|
|
|
+ flex: 0.5;
|
|
|
+ // background-color: rgba(241, 240, 255, 0.8);
|
|
|
+ backdrop-filter: blur(rpx(5));
|
|
|
+ // border: rpx(1) solid rgba(167, 164, 237);
|
|
|
+ border-radius: rpx(6);
|
|
|
+ padding: rpx(2);
|
|
|
+ min-height: rpx(50);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.loading-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: rpx(5);
|
|
|
+}
|
|
|
+
|
|
|
+.loading-spinner {
|
|
|
+ width: rpx(12);
|
|
|
+ height: rpx(12);
|
|
|
+ border: rpx(2) solid rgba(167, 164, 237, 0.3);
|
|
|
+ border-top: rpx(2) solid #a7a4ed;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
+}
|
|
|
+
|
|
|
+.loading-text {
|
|
|
+ font-size: rpx(8);
|
|
|
+ color: #666;
|
|
|
+ animation: pulse 1.5s ease-in-out infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes spin {
|
|
|
+ 0% { transform: rotate(0deg); }
|
|
|
+ 100% { transform: rotate(360deg); }
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes pulse {
|
|
|
+ 0%, 100% { opacity: 1; }
|
|
|
+ 50% { opacity: 0.6; }
|
|
|
+}
|
|
|
+
|
|
|
+.right-box {
|
|
|
+ flex: 1;
|
|
|
+ // background-color: rgba(241, 240, 255, 0.8);
|
|
|
+ backdrop-filter: blur(rpx(5));
|
|
|
+ // border: rpx(1) solid rgba(167, 164, 237, 0.5);
|
|
|
+ border-radius: rpx(6);
|
|
|
+ padding: rpx(8);
|
|
|
+ min-height: rpx(50);
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ justify-content: flex-start;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-container {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: rpx(5);
|
|
|
+}
|
|
|
+
|
|
|
+.progress-title {
|
|
|
+ font-size: rpx(10);
|
|
|
+ font-weight: bold;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: rpx(3);
|
|
|
+}
|
|
|
+
|
|
|
+.progress-steps {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: rpx(2);
|
|
|
+}
|
|
|
+
|
|
|
+.progress-step {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-start;
|
|
|
+ align-items: center;
|
|
|
+ text-align: left;
|
|
|
+ font-size: rpx(8);
|
|
|
+ color: #666;
|
|
|
+ padding: rpx(1) 0;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ opacity: 0;
|
|
|
+ transform: translateY(rpx(5));
|
|
|
+ animation: stepEnter 0.5s ease forwards;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes stepEnter {
|
|
|
+ to {
|
|
|
+ opacity: 1;
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.progress-step.active .step-text {
|
|
|
+ color: #a7a4ed;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.step-text {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ transition: color 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.step-status {
|
|
|
+ width: rpx(8);
|
|
|
+ height: rpx(8);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border-radius: 50%;
|
|
|
+ margin-left: rpx(10);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.step-placeholder {
|
|
|
+ width: rpx(4);
|
|
|
+ height: rpx(4);
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: rgba(167, 164, 237, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.step-status.completed {
|
|
|
+ background-color: #4caf50;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.step-status.active {
|
|
|
+ background-color: rgba(167, 164, 237, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.checkmark {
|
|
|
+ font-size: rpx(6);
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.loading-dot {
|
|
|
+ width: rpx(4);
|
|
|
+ height: rpx(4);
|
|
|
+ background-color: #a7a4ed;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: pulse 1.5s ease-in-out infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes pulse {
|
|
|
+ 0%, 100% { opacity: 1; transform: scale(1); }
|
|
|
+ 50% { opacity: 0.6; transform: scale(1.2); }
|
|
|
+}
|
|
|
+
|
|
|
</style>
|