tabbar样式改进

main
heiferleaf 2 months ago
parent 700e5370b7
commit 1e0cb425f1

@ -5,7 +5,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/wx_miniapp?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai url: jdbc:mysql://localhost:3306/wx_miniapp?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root username: root
password: 123456 password: 1234
mybatis: mybatis:
mapper-locations: classpath:mapper/*.xml mapper-locations: classpath:mapper/*.xml

@ -5,7 +5,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/wx_miniapp?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai url: jdbc:mysql://localhost:3306/wx_miniapp?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root username: root
password: 123456 password: 1234
mybatis: mybatis:
mapper-locations: classpath:mapper/*.xml mapper-locations: classpath:mapper/*.xml

@ -1,8 +1,29 @@
<template> <template>
<view class="argument-component"> <view class="argument-component">
<view class="content-card"> <!-- 动态背景元素 -->
<view class="card-title">立论功能</view> <view class="animated-bg">
<view class="card-content">在这里可以进行辩题立论和论点整理</view> <view v-for="i in 6" :key="i" class="bg-circle" :style="getRandomCircleStyle()"></view>
</view>
<!-- 顶部卡片 - 可收缩 -->
<view class="content-card" :class="{ 'collapsed': isCardCollapsed }">
<view class="card-header" @click="toggleCard">
<view class="card-icon-wrapper">
<view class="card-icon">
<image src="/static/icons/brain-line.png" mode="aspectFit"></image>
</view>
</view>
<view class="card-text">
<view class="card-title">立论助手</view>
<view class="card-content" v-if="!isCardCollapsed">AI</view>
</view>
<view class="collapse-icon">
<image
:src="isCardCollapsed ? '/static/icons/arrow-down-s-line.png' : '/static/icons/arrow-up-s-line.png'"
mode="aspectFit"
></image>
</view>
</view>
</view> </view>
<!-- 聊天展示区 --> <!-- 聊天展示区 -->
@ -19,19 +40,25 @@
class="chat-message" class="chat-message"
:class="msg.role === 'user' ? 'from-user' : 'from-ai'" :class="msg.role === 'user' ? 'from-user' : 'from-ai'"
> >
<view class="avatar" v-if="msg.role === 'ai'">AI</view>
<view class="bubble"> <view class="bubble">
<block v-if="msg.loading"> <block v-if="msg.loading">
<view class="dot-flashing"></view> <view class="loading-animation">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
</block> </block>
<block v-else> <block v-else>
{{ msg.content }} {{ msg.content }}
</block> </block>
</view> </view>
<view class="avatar user-avatar" v-if="msg.role === 'user'"></view>
</view> </view>
</scroll-view> </scroll-view>
<!-- 输入框与发送按钮 --> <!-- 输入框与发送按钮 -->
<view class="chat-input"> <view class="chat-input">
<view class="input-group"> <view class="input-group">
<input <input
class="input-box" class="input-box"
@ -39,15 +66,22 @@
v-model="position" v-model="position"
placeholder="请输入论方,例如:正方" placeholder="请输入论方,例如:正方"
/> />
<view class="textarea-container">
<textarea <textarea
class="textarea-box" class="textarea-box"
v-model="question" v-model="question"
placeholder="请输入辩题,例如:应不应该取消作业" placeholder="请输入辩题,例如:应不应该取消作业"
auto-height auto-height
/> />
<button class="send-button-embedded" @click="sendMessage()">
<image src="/static/icons/send-plane-fill.png" mode="aspectFit"></image>
</button>
</view> </view>
<button class="send-button" @click="sendMessage()"></button>
</view> </view>
</view>
<!-- 底部安全区域 -->
<view class="safe-area-bottom"></view>
</view> </view>
</template> </template>
@ -63,12 +97,35 @@ export default {
} }
], ],
scrollToView: "", scrollToView: "",
position: "", position: "",
question: "", question: "",
isCardCollapsed: false, //
}; };
}, },
methods: { methods: {
// /
toggleCard() {
this.isCardCollapsed = !this.isCardCollapsed;
},
//
getRandomCircleStyle() {
const size = Math.random() * 300 + 100;
const x = Math.random() * 100;
const y = Math.random() * 100;
const delay = Math.random() * 5;
const duration = Math.random() * 10 + 15;
return {
width: `${size}rpx`,
height: `${size}rpx`,
left: `${x}%`,
top: `${y}%`,
animationDelay: `${delay}s`,
animationDuration: `${duration}s`
};
},
async sendMessage() { async sendMessage() {
// //
this.messages.push({ this.messages.push({
@ -83,13 +140,14 @@ export default {
this.messages.push({ role: "ai", content: "", loading: true }); this.messages.push({ role: "ai", content: "", loading: true });
this.scrollToBottom(); this.scrollToBottom();
// AI // AI
const reply = await this.callAI(this.position, this.question); const reply = await this.callAI(this.position, this.question);
// loading AI // loading AI
this.messages.splice(aiIndex, 1, { role: "ai", content: reply }); this.messages.splice(aiIndex, 1, { role: "ai", content: reply });
this.scrollToBottom(); this.scrollToBottom();
}, },
async callAI(topic, stance) { async callAI(topic, stance) {
this.position = ""; this.position = "";
this.question = ""; this.question = "";
@ -97,7 +155,7 @@ export default {
console.log("callAI:", topic, stance); console.log("callAI:", topic, stance);
uni.request({ uni.request({
url: "http://localhost:8080/api/ai/argument", // URL url: "http://localhost:8080/api/ai/argument",
method: "POST", method: "POST",
header: { header: {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -136,175 +194,461 @@ export default {
<style scoped> <style scoped>
.argument-component { .argument-component {
width: 100%; width: 100%;
padding: 20rpx; height: 100vh;
padding: 30rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 78vh; background: linear-gradient(135deg, #4338CA 0%, #7C3AED 100%);
background: linear-gradient(135deg, #2c3e50, #4ca1af); overflow-x: hidden;
box-sizing: border-box;
padding-bottom: 180rpx; /* 为底部TabBar留出空间 */
position: relative;
}
/* 动态背景 */
.animated-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
}
.bg-circle {
position: absolute;
border-radius: 50%;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
animation: float 20s infinite ease-in-out;
opacity: 0.4;
/* 添加过渡效果 */
transition: all 1.5s ease-in-out;
}
overflow-x: hidden; /* 禁止横向滚动 */ @keyframes float {
box-sizing: border-box; /* 避免 padding 导致溢出 */ 0%, 100% {
transform: translate(0, 0) scale(1);
}
25% {
transform: translate(5%, 10%) scale(1.05);
}
50% {
transform: translate(10%, 5%) scale(0.95);
}
75% {
transform: translate(5%, 15%) scale(1.1);
}
} }
/* 顶部卡片 */
.content-card { .content-card {
background-color: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.15);
border-radius: 20rpx;
padding: 30rpx;
width: 90%;
margin: 0 auto 30rpx;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
border-radius: 28rpx;
padding: 20rpx;
width: 100%;
margin-bottom: 30rpx;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
animation: slideDown 0.6s ease-out;
transition: all 0.3s ease;
}
.content-card.collapsed {
padding: 16rpx 20rpx;
margin-bottom: 20rpx;
}
.card-header {
display: flex;
align-items: center;
cursor: pointer;
}
.collapse-icon {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s ease;
}
.collapse-icon image {
width: 30rpx;
height: 30rpx;
filter: brightness(0) invert(1);
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card-icon-wrapper {
position: relative;
margin-right: 24rpx;
}
.card-icon {
width: 80rpx;
height: 80rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.collapsed .card-icon {
width: 70rpx;
height: 70rpx;
}
.card-icon::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0) 100%
);
transform: rotate(45deg);
animation: shine 3s infinite;
}
@keyframes shine {
0% {
transform: translateX(-100%) rotate(45deg);
}
20%, 100% {
transform: translateX(100%) rotate(45deg);
}
}
.card-icon image {
width: 45rpx;
height: 45rpx;
filter: brightness(0) invert(1);
z-index: 1;
transition: all 0.3s ease;
}
.collapsed .card-icon image {
width: 40rpx;
height: 40rpx;
}
.card-text {
flex: 1;
transition: all 0.3s ease;
} }
.card-title { .card-title {
font-size: 36rpx; font-size: 36rpx;
color: #ffffff; color: #FFFFFF;
font-weight: bold; font-weight: 700;
margin-bottom: 20rpx; margin-bottom: 10rpx;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.collapsed .card-title {
font-size: 32rpx;
margin-bottom: 0;
} }
.card-content { .card-content {
font-size: 28rpx; font-size: 26rpx;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
line-height: 1.5;
transition: all 0.3s ease;
} }
/* 聊天区域 */ /* 聊天区域 */
.chat-area { .chat-area {
flex: 1; flex: 1;
width: 90%; width: 100%;
margin: 0 auto; background: rgba(255, 255, 255, 0.1);
background-color: rgba(255, 255, 255, 0.05); border-radius: 28rpx;
border-radius: 20rpx; padding: 30rpx;
padding: 20rpx; margin-bottom: 30rpx;
overflow-y: auto; overflow-y: auto;
border: 1px solid rgba(255, 255, 255, 0.15);
position: relative;
z-index: 1;
transition: all 0.3s ease;
} }
/* 消息通用样式 */ /* 消息通用样式 */
.chat-message { .chat-message {
display: flex; display: flex;
margin-bottom: 20rpx; margin-bottom: 30rpx;
align-items: flex-start;
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
} }
/* 用户消息靠右 */
.from-user { .from-user {
justify-content: flex-end; justify-content: flex-end;
} }
/* AI消息靠左 */
.from-ai { .from-ai {
justify-content: flex-start; justify-content: flex-start;
} }
/* 头像样式 */
.avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24rpx;
font-weight: bold;
margin-right: 16rpx;
flex-shrink: 0;
}
.user-avatar {
background: #F59E0B;
margin-left: 16rpx;
margin-right: 0;
}
/* 气泡样式 */ /* 气泡样式 */
.bubble { .bubble {
max-width: 70%; max-width: 70%;
padding: 20rpx; padding: 20rpx 24rpx;
border-radius: 20rpx; border-radius: 24rpx;
font-size: 28rpx; font-size: 28rpx;
line-height: 1.6; line-height: 1.6;
word-break: break-word; word-break: break-word;
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
position: relative;
transition: all 0.3s ease; /* 添加过渡效果 */
} }
/* 用户气泡颜色 */
.from-user .bubble { .from-user .bubble {
background-color: #00c4ff; background: #F59E0B;
color: white; color: #FFFFFF;
border-bottom-right-radius: 0; border-bottom-right-radius: 4rpx;
font-weight: 500;
}
.from-user .bubble::after {
content: '';
position: absolute;
right: -12rpx;
bottom: 0;
width: 0;
height: 0;
border-left: 16rpx solid #F59E0B;
border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */
} }
/* AI气泡颜色 */
.from-ai .bubble { .from-ai .bubble {
background-color: #ffd54f; background: rgba(255, 255, 255, 0.2);
color: #333; color: #FFFFFF;
border-bottom-left-radius: 0; border-bottom-left-radius: 4rpx;
min-width: 80rpx;
}
.from-ai .bubble::after {
content: '';
position: absolute;
left: -12rpx;
bottom: 0;
width: 0;
height: 0;
border-right: 16rpx solid rgba(255, 255, 255, 0.2);
border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */
}
/* 加载动画 */
.loading-animation {
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
height: 30rpx;
}
.loading-animation .dot {
width: 12rpx;
height: 12rpx;
background-color: #FFFFFF;
border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out both;
}
min-width: 80px; .loading-animation .dot:nth-child(1) {
animation-delay: -0.32s;
}
.loading-animation .dot:nth-child(2) {
animation-delay: -0.16s;
}
@keyframes bounce {
0%, 80%, 100% {
transform: scale(0);
}
40% {
transform: scale(1.0);
}
} }
/* 输入区域 */ /* 输入区域 */
.chat-input { .chat-input {
display: flex; display: flex;
padding: 20rpx; padding: 16rpx;
background-color: rgba(255, 255, 255, 0.05); background: rgba(255, 255, 255, 0.15);
border-top: 1rpx solid #ccc; border-radius: 28rpx;
align-items: flex-end; align-items: flex-end;
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
margin-bottom: 20rpx;
}
.send-button.left-send {
margin-right: 12rpx;
margin-left: 0;
background: #F59E0B;
width: 70rpx;
height: 70rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 15rpx rgba(245, 158, 11, 0.4);
transition: all 0.3s;
padding: 0;
}
.send-button.left-send:active {
transform: scale(0.95) rotate(-15deg);
box-shadow: 0 2rpx 8rpx rgba(245, 158, 11, 0.3);
}
.send-button.left-send image {
width: 36rpx;
height: 36rpx;
filter: brightness(0) invert(1);
} }
.input-group { .input-group {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10rpx; gap: 16rpx;
} }
.input-box { .input-box {
background-color: #fff; background: rgba(255, 255, 255, 0.2);
border-radius: 10rpx; border-radius: 20rpx;
padding: 10rpx 20rpx; padding: 16rpx 20rpx;
margin-bottom: 20rpx;
font-size: 28rpx; font-size: 28rpx;
border: none; color: #FFFFFF;
border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s;
height: 70rpx;
} }
.textarea-box { .textarea-container {
background-color: #fff; position: relative;
border-radius: 10rpx; width: 100%;
padding: 10rpx 20rpx;
font-size: 28rpx;
min-height: 80rpx;
border: none;
} }
.send-button { .textarea-box {
margin-left: 20rpx; background: rgba(255, 255, 255, 0.2);
background-color: #00bcd4; border-radius: 20rpx;
color: #fff; padding: 16rpx 20rpx 16rpx 90rpx; /* 增加左侧内边距 */
border-radius: 10rpx;
padding: 20rpx;
font-size: 28rpx; font-size: 28rpx;
white-space: nowrap; color: #FFFFFF;
border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s;
min-height: 70rpx;
max-height: 180rpx;
width: 100%;
box-sizing: border-box;
} }
/* AI loading 动画 */ .send-button-embedded {
.dot-flashing { position: absolute;
width: 20rpx; bottom: 16rpx;
height: 20rpx; left: 16rpx;
background: transparent;
width: 60rpx;
height: 60rpx;
border-radius: 50%; border-radius: 50%;
background-color: #333; display: flex;
animation: dotFlashing 1s infinite linear alternate; align-items: center;
position: relative; justify-content: center;
transition: all 0.3s;
padding: 0;
z-index: 2;
}
margin-left: auto; .send-button-embedded:active {
margin-right: auto; transform: scale(1.2) rotate(-15deg);
} }
.dot-flashing::before,
.dot-flashing::after { .send-button-embedded image {
content: ""; width: 40rpx;
display: inline-block; height: 40rpx;
position: absolute; filter: invert(72%) sepia(87%) saturate(1242%) hue-rotate(325deg) brightness(101%) contrast(96%);
top: 0;
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background-color: #333;
} }
.dot-flashing::before {
left: -30rpx; .input-box::placeholder,
animation: dotFlashing 1s infinite linear alternate; .textarea-box::placeholder {
animation-delay: 0.2s; color: burlywood;
} }
.dot-flashing::after {
left: 30rpx; .input-box:focus,
animation: dotFlashing 1s infinite linear alternate; .textarea-box:focus {
animation-delay: 0.4s; border-color: #F59E0B;
outline: none;
background: rgba(255, 255, 255, 0.25);
} }
@keyframes dotFlashing { /* 底部安全区域 */
0% { .safe-area-bottom {
background-color: #333; height: 20rpx;
} height: calc(20rpx + constant(safe-area-inset-bottom)); /* iOS 11.0-11.2 */
50%, height: calc(20rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */
100% {
background-color: rgba(51, 51, 51, 0.3);
}
} }
</style> </style>

@ -1,8 +1,43 @@
<template> <template>
<view class="argument-component"> <view class="debate-component">
<view class="content-card"> <!-- 动态背景元素 -->
<view class="card-title">辩论功能</view> <view class="animated-bg">
<view class="card-content">在这里可以和AI进行激烈的交流~</view> <view v-for="i in 6" :key="i" class="bg-circle" :style="getRandomCircleStyle()"></view>
</view>
<!-- 顶部卡片 - 可收缩 -->
<view class="content-card" :class="{ 'collapsed': isCardCollapsed }">
<view class="card-header" @click="toggleCard">
<view class="card-icon-wrapper">
<view class="card-icon">
<image src="/static/icons/robot-2-line.png" mode="aspectFit"></image>
</view>
</view>
<view class="card-text">
<view class="card-title">模拟辩论</view>
<view class="card-content" v-if="!isCardCollapsed">AI</view>
</view>
<view class="collapse-icon">
<image
:src="isCardCollapsed ? '/static/icons/arrow-down-s-line.png' : '/static/icons/arrow-up-s-line.png'"
mode="aspectFit"
></image>
</view>
</view>
</view>
<!-- 辩论主题选择器 -->
<view class="topic-selector" v-if="!isCardCollapsed">
<scroll-view scroll-x class="topic-scroll" show-scrollbar="false">
<view
v-for="(topic, idx) in debateTopics"
:key="idx"
:class="['topic-pill', { active: selectedTopic === idx }]"
@click="selectTopic(idx)"
>
{{ topic }}
</view>
</scroll-view>
</view> </view>
<!-- 聊天展示区 --> <!-- 聊天展示区 -->
@ -19,30 +54,43 @@
class="chat-message" class="chat-message"
:class="msg.role === 'user' ? 'from-user' : 'from-ai'" :class="msg.role === 'user' ? 'from-user' : 'from-ai'"
> >
<view class="avatar" v-if="msg.role === 'ai'">AI</view>
<view class="bubble"> <view class="bubble">
<block v-if="msg.loading"> <block v-if="msg.loading">
<view class="dot-flashing"></view> <view class="loading-animation">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
</block> </block>
<block v-else> <block v-else>
{{ msg.content }} {{ msg.content }}
</block> </block>
</view> </view>
<view class="avatar user-avatar" v-if="msg.role === 'user'"></view>
</view> </view>
</scroll-view> </scroll-view>
<!-- 输入框与发送按钮 --> <!-- 输入框与发送按钮 -->
<view class="chat-input"> <view class="chat-input">
<view class="input-group"> <view class="input-group">
<view class="textarea-container">
<textarea <textarea
class="textarea-box" class="textarea-box"
v-model="content" v-model="content"
placeholder="请输入辩论的过程" placeholder="输入你的辩论观点与AI进行思辨交锋"
auto-height auto-height
maxlength="1000" maxlength="1000"
/> />
<button class="send-button-embedded" @click="sendMessage()">
<image src="/static/icons/send-plane-fill.png" mode="aspectFit"></image>
</button>
</view>
</view> </view>
<button class="send-button" @click="sendMessage()"></button>
</view> </view>
<!-- 底部安全区域 -->
<view class="safe-area-bottom"></view>
</view> </view>
</template> </template>
@ -54,15 +102,61 @@ export default {
messages: [ messages: [
{ {
role: "ai", role: "ai",
content: "在这里你可以和我进行辩论,现在,辩论开始!" content: "在这里你可以和我进行辩论,现在,辩论开始!请选择一个话题或提出你想讨论的观点。"
} }
], ],
scrollToView: "", scrollToView: "",
content: "", content: "",
debateTopics: [
"教育改革", "环境保护", "人工智能", "社会公平", "文化传承", "科技伦理"
],
selectedTopic: -1,
isCardCollapsed: false, //
}; };
}, },
methods: { methods: {
// /
toggleCard() {
this.isCardCollapsed = !this.isCardCollapsed;
},
//
getRandomCircleStyle() {
const size = Math.random() * 300 + 100;
const x = Math.random() * 100;
const y = Math.random() * 100;
const delay = Math.random() * 5;
const duration = Math.random() * 10 + 15;
return {
width: `${size}rpx`,
height: `${size}rpx`,
left: `${x}%`,
top: `${y}%`,
animationDelay: `${delay}s`,
animationDuration: `${duration}s`
};
},
//
selectTopic(index) {
if (this.selectedTopic === index) return;
this.selectedTopic = index;
const topic = this.debateTopics[index];
//
this.messages.push({
role: "ai",
content: `已选择「${topic}」作为辩论主题。请分享你对这个话题的观点,我们开始辩论吧!`
});
this.scrollToBottom();
},
async sendMessage() { async sendMessage() {
if (!this.content.trim()) return;
// //
this.messages.push({ this.messages.push({
role: "user", role: "user",
@ -97,6 +191,7 @@ export default {
this.messages.splice(aiIndex, 1, { role: "ai", content: reply }); this.messages.splice(aiIndex, 1, { role: "ai", content: reply });
this.scrollToBottom(); this.scrollToBottom();
}, },
async callAI(history, content) { async callAI(history, content) {
this.content = ""; this.content = "";
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -141,177 +236,478 @@ export default {
</script> </script>
<style scoped> <style scoped>
.argument-component { .debate-component {
width: 100%; width: 100%;
padding: 20rpx; height: 100vh;
padding: 30rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 78vh; background: linear-gradient(135deg, #4338CA 0%, #7C3AED 100%);
background: linear-gradient(135deg, #2c3e50, #c6c333); overflow-x: hidden;
box-sizing: border-box;
padding-bottom: 180rpx; /* 为底部TabBar留出空间 */
position: relative;
}
/* 动态背景 */
.animated-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
}
overflow-x: hidden; /* 禁止横向滚动 */ .bg-circle {
box-sizing: border-box; /* 避免 padding 导致溢出 */ position: absolute;
border-radius: 50%;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
animation: float 20s infinite ease-in-out;
opacity: 0.4;
/* 添加过渡效果 */
transition: all 1.5s ease-in-out;
} }
@keyframes float {
0%, 100% {
transform: translate(0, 0) scale(1);
}
25% {
transform: translate(5%, 10%) scale(1.05);
}
50% {
transform: translate(10%, 5%) scale(0.95);
}
75% {
transform: translate(5%, 15%) scale(1.1);
}
}
/* 顶部卡片 */
.content-card { .content-card {
background-color: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.15);
border-radius: 20rpx;
padding: 30rpx;
width: 90%;
margin: 0 auto 30rpx;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
border-radius: 28rpx;
padding: 20rpx;
width: 100%;
margin-bottom: 30rpx;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
animation: slideDown 0.6s ease-out;
transition: all 0.3s ease;
}
.content-card.collapsed {
padding: 16rpx 20rpx;
margin-bottom: 20rpx;
}
.card-header {
display: flex;
align-items: center;
cursor: pointer;
}
.collapse-icon {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s ease;
}
.collapse-icon image {
width: 30rpx;
height: 30rpx;
filter: brightness(0) invert(1);
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card-icon-wrapper {
position: relative;
margin-right: 24rpx;
}
.card-icon {
width: 80rpx;
height: 80rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.collapsed .card-icon {
width: 70rpx;
height: 70rpx;
}
.card-icon::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0) 100%
);
transform: rotate(45deg);
animation: shine 3s infinite;
}
@keyframes shine {
0% {
transform: translateX(-100%) rotate(45deg);
}
20%, 100% {
transform: translateX(100%) rotate(45deg);
}
}
.card-icon image {
width: 45rpx;
height: 45rpx;
filter: brightness(0) invert(1);
z-index: 1;
transition: all 0.3s ease;
}
.collapsed .card-icon image {
width: 40rpx;
height: 40rpx;
}
.card-text {
flex: 1;
transition: all 0.3s ease;
} }
.card-title { .card-title {
font-size: 36rpx; font-size: 36rpx;
color: #ffffff; color: #FFFFFF;
font-weight: bold; font-weight: 700;
margin-bottom: 20rpx; margin-bottom: 10rpx;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.collapsed .card-title {
font-size: 32rpx;
margin-bottom: 0;
} }
.card-content { .card-content {
font-size: 28rpx; font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
line-height: 1.5;
transition: all 0.3s ease;
}
/* 主题选择器 */
.topic-selector {
margin-bottom: 30rpx;
position: relative;
z-index: 1;
transition: all 0.3s ease;
}
.topic-scroll {
white-space: nowrap;
padding: 10rpx 0;
}
.topic-pill {
display: inline-block;
padding: 16rpx 30rpx;
background: rgba(255, 255, 255, 0.15);
border-radius: 50rpx;
margin-right: 20rpx;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
font-size: 26rpx;
transition: all 0.3s;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.topic-pill.active {
background: #F59E0B;
color: #FFFFFF;
box-shadow: 0 4rpx 15rpx rgba(245, 158, 11, 0.4);
transform: scale(1.05);
border-color: #F59E0B;
} }
/* 聊天区域 */ /* 聊天区域 */
.chat-area { .chat-area {
flex: 1; flex: 1;
width: 90%; width: 100%;
margin: 0 auto; background: rgba(255, 255, 255, 0.1);
background-color: rgba(255, 255, 255, 0.05); border-radius: 28rpx;
border-radius: 20rpx; padding: 30rpx;
padding: 20rpx; margin-bottom: 30rpx;
overflow-y: auto; overflow-y: auto;
border: 1px solid rgba(255, 255, 255, 0.15);
position: relative;
z-index: 1;
transition: all 0.3s ease;
} }
/* 消息通用样式 */ /* 消息通用样式 */
.chat-message { .chat-message {
display: flex; display: flex;
margin-bottom: 20rpx; margin-bottom: 30rpx;
align-items: flex-start;
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
} }
/* 用户消息靠右 */
.from-user { .from-user {
justify-content: flex-end; justify-content: flex-end;
} }
/* AI消息靠左 */
.from-ai { .from-ai {
justify-content: flex-start; justify-content: flex-start;
} }
/* 头像样式 */
.avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24rpx;
font-weight: bold;
margin-right: 16rpx;
flex-shrink: 0;
}
.user-avatar {
background: #F59E0B;
margin-left: 16rpx;
margin-right: 0;
}
/* 气泡样式 */ /* 气泡样式 */
.bubble { .bubble {
max-width: 70%; max-width: 70%;
padding: 20rpx; padding: 20rpx 24rpx;
border-radius: 20rpx; border-radius: 24rpx;
font-size: 28rpx; font-size: 28rpx;
line-height: 1.6; line-height: 1.6;
word-break: break-word; word-break: break-word;
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
position: relative;
transition: all 0.3s ease; /* 添加过渡效果 */
} }
/* 用户气泡颜色 */
.from-user .bubble { .from-user .bubble {
background-color: #00c4ff; background: #F59E0B;
color: white; color: #FFFFFF;
border-bottom-right-radius: 0; border-bottom-right-radius: 4rpx;
font-weight: 500;
}
.from-user .bubble::after {
content: '';
position: absolute;
right: -12rpx;
bottom: 0;
width: 0;
height: 0;
border-left: 16rpx solid #F59E0B;
border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */
} }
/* AI气泡颜色 */
.from-ai .bubble { .from-ai .bubble {
background-color: #ffd54f; background: rgba(255, 255, 255, 0.2);
color: #333; color: #FFFFFF;
border-bottom-left-radius: 0; border-bottom-left-radius: 4rpx;
min-width: 80rpx;
}
min-width: 80px; .from-ai .bubble::after {
content: '';
position: absolute;
left: -12rpx;
bottom: 0;
width: 0;
height: 0;
border-right: 16rpx solid rgba(255, 255, 255, 0.2);
border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */
} }
/* 输入区域 */ /* 加载动画 */
.chat-input { .loading-animation {
display: flex; display: flex;
padding: 20rpx; align-items: center;
background-color: rgba(255, 255, 255, 0.05); justify-content: center;
border-top: 1rpx solid #ccc; gap: 8rpx;
align-items: flex-end; height: 30rpx;
} }
.input-group { .loading-animation .dot {
flex: 1; width: 12rpx;
display: flex; height: 12rpx;
flex-direction: column; background-color: #FFFFFF;
gap: 10rpx; border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out both;
}
.loading-animation .dot:nth-child(1) {
animation-delay: -0.32s;
} }
.input-box { .loading-animation .dot:nth-child(2) {
background-color: #fff; animation-delay: -0.16s;
border-radius: 10rpx; }
padding: 10rpx 20rpx;
@keyframes bounce {
0%, 80%, 100% {
transform: scale(0);
}
40% {
transform: scale(1.0);
}
}
/* 输入区域 */
.chat-input {
display: flex;
padding: 16rpx;
background: rgba(255, 255, 255, 0.15);
border-radius: 28rpx;
align-items: flex-end;
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
margin-bottom: 20rpx; margin-bottom: 20rpx;
font-size: 28rpx;
border: none;
} }
.textarea-box { .textarea-box {
background-color: #fff; background: rgba(255, 255, 255, 0.2);
border-radius: 10rpx; border-radius: 20rpx;
padding: 10rpx 20rpx; padding: 16rpx 20rpx 16rpx 90rpx; /* 增加左侧内边距为发送按钮留出空间 */
font-size: 28rpx; font-size: 28rpx;
min-height: 80rpx; color: #FFFFFF;
border: none; border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s;
min-height: 70rpx;
max-height: 180rpx; /* 限制最大高度 */
width: 100%;
box-sizing: border-box;
} }
.send-button { .textarea-box::placeholder {
margin-left: 20rpx; color: hsla(0, 6%, 15%, 0.877);
background-color: #00bcd4;
color: #fff;
border-radius: 10rpx;
padding: 20rpx;
font-size: 28rpx;
white-space: nowrap;
} }
/* AI loading 动画 */ .textarea-box:focus {
.dot-flashing { border-color: #F59E0B;
width: 20rpx; outline: none;
height: 20rpx; background: rgba(255, 255, 255, 0.25);
}
.send-button.left-send {
margin-right: 12rpx;
margin-left: 0;
background: #F59E0B;
width: 70rpx;
height: 70rpx;
border-radius: 50%; border-radius: 50%;
background-color: #333; display: flex;
animation: dotFlashing 1s infinite linear alternate; align-items: center;
position: relative; justify-content: center;
box-shadow: 0 4rpx 15rpx rgba(245, 158, 11, 0.4);
transition: all 0.3s;
padding: 0;
}
margin-left: auto; .send-button.left-send:active {
margin-right: auto; transform: scale(0.95) rotate(-15deg);
box-shadow: 0 2rpx 8rpx rgba(245, 158, 11, 0.3);
} }
.dot-flashing::before,
.dot-flashing::after { .input-group {
content: ""; flex: 1;
display: inline-block; display: flex;
flex-direction: column;
gap: 16rpx;
}
.textarea-container {
position: relative;
width: 100%;
}
.send-button-embedded {
position: absolute; position: absolute;
top: 0; bottom: 16rpx;
width: 20rpx; left: 16rpx;
height: 20rpx; background: transparent;
width: 60rpx;
height: 60rpx;
border-radius: 50%; border-radius: 50%;
background-color: #333; display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
padding: 0;
z-index: 2;
} }
.dot-flashing::before {
left: -30rpx; .send-button-embedded:active {
animation: dotFlashing 1s infinite linear alternate; transform: scale(1.2) rotate(-15deg);
animation-delay: 0.2s;
} }
.dot-flashing::after {
left: 30rpx; .send-button-embedded image {
animation: dotFlashing 1s infinite linear alternate; width: 40rpx;
animation-delay: 0.4s; height: 40rpx;
filter: invert(72%) sepia(87%) saturate(1242%) hue-rotate(325deg) brightness(101%) contrast(96%);
} }
@keyframes dotFlashing { /* 底部安全区域 */
0% { .safe-area-bottom {
background-color: #333; height: 20rpx;
} height: calc(20rpx + constant(safe-area-inset-bottom)); /* iOS 11.0-11.2 */
50%, height: calc(20rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */
100% {
background-color: rgba(51, 51, 51, 0.3);
}
} }
</style> </style>

@ -1,387 +1,471 @@
<template> <template>
<view class="home-component"> <view class="home-component">
<!-- 添加云的背景图像 --> <!-- 动态背景元素 -->
<image class="cloud-image" src="/static/cloud.png" mode="aspectFit"></image> <view class="animated-bg">
<view v-for="i in 6" :key="i" class="bg-circle" :style="getRandomCircleStyle()"></view>
</view>
<!-- 欢迎标题保留 --> <!-- 顶部欢迎卡片 -->
<view class="welcome-section"> <view class="welcome-card">
<view class="welcome-title">欢迎使用智辩云枢</view> <view class="welcome-content">
<view class="welcome-subtitle">AI驱动的辩论助手平台</view> <view class="welcome-title">智辩云枢</view>
<view class="welcome-subtitle">AI驱动的辩论训练与分析平台</view>
</view>
</view> </view>
<!-- 三个功能卡片 - 错落布局 --> <!-- 功能卡片区域 -->
<view class="feature-cards"> <view class="feature-grid">
<!-- 第一张卡片 (奇数 - 靠左) --> <view class="feature-card" @click="switchTab(1)">
<view class="feature-card card-odd" @click="navigateToFeature(0)"> <view class="feature-icon">
<view class="card-left"> <image src="/static/icons/chat-1-line.png" mode="aspectFit"></image>
<view class="card-icon">🧠</view> </view>
<view class="feature-text">
<view class="feature-title">模拟辩论</view>
<view class="feature-desc">与AI进行实时辩论对练提升应变能力</view>
</view> </view>
<view class="card-right"> <view class="feature-arrow">
<view class="card-title">立论助手</view> <image src="/static/icons/arrow-right-s-line.png" mode="aspectFit"></image>
<view class="card-description">AI辅助构建辩论框架智能生成论点与论据</view>
</view> </view>
</view> </view>
<!-- 第二张卡片 (偶数 - 靠右) --> <view class="feature-card" @click="switchTab(2)">
<view class="feature-card card-even" @click="navigateToFeature(1)"> <view class="feature-icon">
<view class="card-left"> <image src="/static/icons/lightbulb-line.png" mode="aspectFit"></image>
<view class="card-title">复盘分析</view>
<view class="card-description">智能分析辩论过程提供专业化改进建议</view>
</view> </view>
<view class="card-right"> <view class="feature-text">
<view class="card-icon">📊</view> <view class="feature-title">立论助手</view>
<view class="feature-desc">AI辅助构建辩论框架智能生成论点与论据</view>
</view>
<view class="feature-arrow">
<image src="/static/icons/arrow-right-s-line.png" mode="aspectFit"></image>
</view> </view>
</view> </view>
<!-- 第三张卡片 (奇数 - 靠左) --> <view class="feature-card" @click="switchTab(3)">
<view class="feature-card card-odd" @click="navigateToFeature(2)"> <view class="feature-icon">
<view class="card-left"> <image src="/static/icons/file-chart-line.png" mode="aspectFit"></image>
<view class="card-icon">🤖</view>
</view> </view>
<view class="card-right"> <view class="feature-text">
<view class="card-title">模拟辩论</view> <view class="feature-title">复盘分析</view>
<view class="card-description">与AI进行实时辩论对练提升应变能力</view> <view class="feature-desc">分析辩论过程提供改进建议与优化方向</view>
</view> </view>
<view class="feature-arrow">
<image src="/static/icons/arrow-right-s-line.png" mode="aspectFit"></image>
</view> </view>
</view> </view>
<!-- 底部名句展示 -->
<view class="quote-container">
<view class="quote-icon"></view>
<view class="quote-content">
<text class="quote-text">{{ currentQuote.text }}</text>
<text class="quote-author"> {{ currentQuote.author }}</text>
</view> </view>
<!-- 最近活动区域 -->
<view class="recent-section">
<view class="section-header">
<view class="section-title">最近活动</view>
<view class="section-more" @click="showMoreActivities">
<text>查看更多</text>
<image src="/static/icons/arrow-right-s-line.png" mode="aspectFit"></image>
</view> </view>
</view> </view>
</template>
<script setup> <view class="activity-list">
import { ref, onMounted, onUnmounted } from 'vue'; <view class="activity-item" v-for="(activity, index) in recentActivities" :key="index">
<view class="activity-icon" :class="activity.type">
<image :src="getActivityIcon(activity.type)" mode="aspectFit"></image>
</view>
<view class="activity-content">
<view class="activity-title">{{ activity.title }}</view>
<view class="activity-time">{{ activity.time }}</view>
</view>
<view class="activity-arrow">
<image src="/static/icons/arrow-right-s-line.png" mode="aspectFit"></image>
</view>
</view>
</view>
</view>
const emit = defineEmits(['change-tab', 'change-index']); <!-- 底部安全区域 -->
<view class="safe-area-bottom"></view>
</view>
</template>
const tabList = [ <script>
export default {
data() {
return {
recentActivities: [
{ {
pageName:'立论', type: 'debate',
component:'PageOfArgument' title: '关于"教育改革"的辩论',
time: '今天 14:30'
}, },
{ {
pageName:'复盘', type: 'argument',
component:'PageOfReview' title: '关于"环境保护"的立论',
time: '昨天 09:15'
}, },
{ {
pageName:'辩论', type: 'review',
component:'PageOfDebate' title: '关于"人工智能"的辩论复盘',
time: '3天前'
} }
]; ]
};
const changeTab = (index) => {
emit('change-tab', tabList[index].component);
}
const changeIndex = (index) => {
emit('change-index', index);
}
//
const navigateToFeature = (index) => {
changeTab(index);
changeIndex(index + 1);
};
//
const debateQuotes = [
{
text: "辩论的艺术不在于赢得争论,而在于不失去朋友",
author: "爱因斯坦"
},
{
text: "真理越辩越明,正义自在人心",
author: "中国古语"
},
{
text: "不要用事实来干扰我的论点",
author: "辩论格言"
},
{
text: "辩论是发现真理的艺术,而不是为了胜利",
author: "苏格拉底"
},
{
text: "思辨是灵魂的对话",
author: "柏拉图"
},
{
text: "辩论不是为了证明谁对谁错,而是为了共同接近真理",
author: "亚里士多德"
}, },
{ methods: {
text: "最好的论点不是说服对手,而是让对手思考", //
author: "辩论艺术" getRandomCircleStyle() {
const size = Math.random() * 300 + 100;
const x = Math.random() * 100;
const y = Math.random() * 100;
const delay = Math.random() * 5;
const duration = Math.random() * 10 + 15;
return {
width: `${size}rpx`,
height: `${size}rpx`,
left: `${x}%`,
top: `${y}%`,
animationDelay: `${delay}s`,
animationDuration: `${duration}s`
};
}, },
{
text: "辩论的目的不是为了赢,而是为了真理",
author: "苏格拉底"
}
];
//
const currentQuote = ref(debateQuotes[0]);
let quoteInterval = null;
// //
const changeQuote = () => { getActivityIcon(type) {
const randomIndex = Math.floor(Math.random() * debateQuotes.length); const icons = {
currentQuote.value = debateQuotes[randomIndex]; 'debate': '/static/icons/chat-1-line.png',
}; 'argument': '/static/icons/lightbulb-line.png',
'review': '/static/icons/file-chart-line.png'
onMounted(() => { };
// return icons[type] || icons.debate;
changeQuote(); },
// 15 //
quoteInterval = setInterval(changeQuote, 15000); switchTab(index) {
}); this.$emit('tab-change', index);
},
onUnmounted(() => { //
// showMoreActivities() {
if (quoteInterval) { uni.showToast({
clearInterval(quoteInterval); title: '功能开发中',
icon: 'none'
});
}
} }
}); };
</script> </script>
<style scoped> <style scoped>
.home-component { .home-component {
width: 100%; width: 100%;
padding: 20rpx; min-height: 100vh;
padding: 30rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; background: linear-gradient(135deg, #4338CA 0%, #7C3AED 100%);
position: relative;
z-index: 5;
border-radius: 20rpx;
box-sizing: border-box;
max-width: 100vw;
overflow-x: hidden; overflow-x: hidden;
box-sizing: border-box;
padding-bottom: 180rpx; /* 为底部TabBar留出空间 */
position: relative;
} }
/* 云图像样式 */ /* 动态背景 */
.cloud-image { .animated-bg {
position: absolute; position: absolute;
top: 20rpx; top: 0;
right: 40rpx; left: 0;
width: 180rpx; width: 100%;
height: 120rpx; height: 100%;
opacity: 0.6; overflow: hidden;
z-index: 1; z-index: 0;
animation: float 6s ease-in-out infinite; }
.bg-circle {
position: absolute;
border-radius: 50%;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
animation: float 20s infinite ease-in-out;
opacity: 0.4;
transition: all 1.5s ease-in-out;
} }
@keyframes float { @keyframes float {
0% { 0%, 100% {
transform: translateY(0px) translateX(0px); transform: translate(0, 0) scale(1);
}
25% {
transform: translate(5%, 10%) scale(1.05);
} }
50% { 50% {
transform: translateY(-15px) translateX(10px); transform: translate(10%, 5%) scale(0.95);
} }
100% { 75% {
transform: translateY(0px) translateX(0px); transform: translate(5%, 15%) scale(1.1);
} }
} }
.welcome-section { /* 欢迎卡片 */
width: 90%; .welcome-card {
padding: 40rpx 0; background: rgba(255, 255, 255, 0.15);
text-align: center; backdrop-filter: blur(10px);
margin-bottom: 40rpx; border-radius: 28rpx;
padding: 30rpx;
width: 100%;
margin-bottom: 30rpx;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative; position: relative;
z-index: 2; z-index: 1;
display: flex;
justify-content: space-between;
align-items: center;
animation: slideDown 0.6s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.welcome-content {
flex: 1;
} }
.welcome-title { .welcome-title {
font-size: 42rpx; font-size: 48rpx;
color: #ffffff; color: #FFFFFF;
font-weight: bold; font-weight: 700;
margin-bottom: 10rpx; margin-bottom: 16rpx;
text-shadow: 0 0 10rpx rgba(0, 214, 185, 0.3); text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
} }
.welcome-subtitle { .welcome-subtitle {
font-size: 28rpx; font-size: 28rpx;
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.8);
line-height: 1.5;
} }
.feature-cards {
width: 100%; /* 功能卡片区域 */
.feature-grid {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 40rpx; gap: 20rpx;
margin-bottom: 50rpx; margin-bottom: 40rpx;
padding: 0 20rpx;
box-sizing: border-box;
position: relative; position: relative;
z-index: 3; z-index: 1;
} }
.feature-card { .feature-card {
width: 90%; background: rgba(255, 255, 255, 0.1);
border-radius: 24rpx;
padding: 24rpx;
display: flex; display: flex;
background-color: rgba(255, 255, 255, 0.08); align-items: center;
border-radius: 20rpx; border: 1px solid rgba(255, 255, 255, 0.15);
padding: 30rpx 20rpx; transition: all 0.3s;
transition: all 0.3s ease; animation: fadeIn 0.5s ease-out;
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1); animation-fill-mode: both;
border: 1px solid rgba(0, 214, 185, 0.2);
min-height: 160rpx;
box-sizing: border-box;
} }
/* 悬停/触摸效果 */ .feature-card:nth-child(1) {
.card-hover { animation-delay: 0.1s;
transform: translateY(-5rpx) scale(1.02);
background-color: rgba(0, 214, 185, 0.15);
box-shadow: 0 12rpx 25rpx rgba(0, 0, 0, 0.2), 0 0 20rpx rgba(0, 214, 185, 0.3);
border-color: rgba(0, 214, 185, 0.5);
} }
/* 奇数卡片靠左 */ .feature-card:nth-child(2) {
.card-odd { animation-delay: 0.2s;
align-self: flex-start;
margin-left: 0;
border-top-left-radius: 0;
border-left: 3px solid #00D6B9;
} }
/* 偶数卡片靠右 */ .feature-card:nth-child(3) {
.card-even { animation-delay: 0.3s;
align-self: flex-end;
margin-right: 0;
border-top-right-radius: 0;
border-right: 3px solid #00D6B9;
} }
.feature-card:active { .feature-card:active {
transform: scale(0.98); transform: scale(0.98);
background-color: rgba(0, 214, 185, 0.2); background: rgba(255, 255, 255, 0.15);
} }
.card-left, .card-right { @keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.feature-icon {
width: 80rpx;
height: 80rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 20rpx;
display: flex; display: flex;
flex-direction: column; align-items: center;
justify-content: center; justify-content: center;
margin-right: 20rpx;
} }
.card-left { .feature-icon image {
width: 30%; width: 44rpx;
align-items: center; height: 44rpx;
filter: brightness(0) invert(1);
} }
.card-right { .feature-text {
width: 70%; flex: 1;
padding-left: 20rpx;
} }
/* 在偶数卡片中,反转左右区域的宽度 */ .feature-title {
.card-even .card-left { font-size: 32rpx;
width: 70%; color: #FFFFFF;
align-items: flex-start; font-weight: 600;
padding-right: 20rpx; margin-bottom: 8rpx;
} }
.card-even .card-right { .feature-desc {
width: 30%; font-size: 24rpx;
align-items: center; color: rgba(255, 255, 255, 0.7);
padding-left: 0; line-height: 1.4;
} }
.card-icon { .feature-arrow {
font-size: 70rpx; width: 40rpx;
background: rgba(0, 214, 185, 0.1); height: 40rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
margin-bottom: 10rpx; justify-content: center;
box-shadow: 0 0 15rpx rgba(0, 214, 185, 0.3);
transition: all 0.3s ease;
} }
.card-hover .card-icon { .feature-arrow image {
background: rgba(0, 214, 185, 0.25); width: 30rpx;
box-shadow: 0 0 20rpx rgba(0, 214, 185, 0.5); height: 30rpx;
transform: scale(1.1); filter: brightness(0) invert(1);
opacity: 0.7;
} }
.card-title { /* 最近活动区域 */
font-size: 32rpx; .recent-section {
color: #ffffff; position: relative;
font-weight: bold; z-index: 1;
margin-bottom: 15rpx;
} }
.card-description { .section-header {
font-size: 26rpx; display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.section-title {
font-size: 36rpx;
color: #FFFFFF;
font-weight: 600;
}
.section-more {
display: flex;
align-items: center;
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
line-height: 1.5; font-size: 26rpx;
} }
/* 名句容器样式 */ .section-more image {
.quote-container { width: 24rpx;
width: 90%; height: 24rpx;
margin-left: 6rpx;
filter: brightness(0) invert(1);
opacity: 0.7;
}
.activity-list {
display: flex; display: flex;
padding: 30rpx; flex-direction: column;
background-color: rgba(0, 214, 185, 0.1); gap: 16rpx;
}
.activity-item {
background: rgba(255, 255, 255, 0.1);
border-radius: 20rpx; border-radius: 20rpx;
margin-top: 20rpx; padding: 20rpx;
position: relative; display: flex;
z-index: 2; align-items: center;
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.1); border: 1px solid rgba(255, 255, 255, 0.15);
border: 1px solid rgba(0, 214, 185, 0.2); transition: all 0.3s;
transition: all 0.5s ease;
overflow: hidden;
} }
.quote-icon { .activity-item:active {
font-size: 60rpx; background: rgba(255, 255, 255, 0.15);
color: rgba(0, 214, 185, 0.5);
margin-right: 15rpx;
align-self: flex-start;
line-height: 1;
} }
.quote-content { .activity-icon {
width: 60rpx;
height: 60rpx;
border-radius: 16rpx;
display: flex; display: flex;
flex-direction: column; align-items: center;
justify-content: center;
margin-right: 16rpx;
}
.activity-icon.debate {
background: rgba(245, 158, 11, 0.3);
}
.activity-icon.argument {
background: rgba(16, 185, 129, 0.3);
}
.activity-icon.review {
background: rgba(79, 70, 229, 0.3);
}
.activity-icon image {
width: 36rpx;
height: 36rpx;
filter: brightness(0) invert(1);
}
.activity-content {
flex: 1; flex: 1;
} }
.quote-text { .activity-title {
font-size: 28rpx; font-size: 28rpx;
color: #ffffff; color: #FFFFFF;
line-height: 1.5; margin-bottom: 6rpx;
font-style: italic;
margin-bottom: 10rpx;
animation: fadeIn 0.5s ease;
} }
.quote-author { .activity-time {
font-size: 24rpx; font-size: 22rpx;
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
text-align: right;
animation: fadeIn 0.5s ease;
} }
@keyframes fadeIn { .activity-arrow {
from { width: 40rpx;
opacity: 0; height: 40rpx;
transform: translateY(10rpx); display: flex;
} align-items: center;
to { justify-content: center;
opacity: 1; }
transform: translateY(0);
} .activity-arrow image {
width: 24rpx;
height: 24rpx;
filter: brightness(0) invert(1);
opacity: 0.7;
}
/* 底部安全区域 */
.safe-area-bottom {
height: 20rpx;
height: calc(20rpx + constant(safe-area-inset-bottom)); /* iOS 11.0-11.2 */
height: calc(20rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */
} }
</style> </style>

@ -1,8 +1,29 @@
<template> <template>
<view class="argument-component"> <view class="review-component">
<view class="content-card"> <!-- 动态背景元素 -->
<view class="card-title">复盘功能</view> <view class="animated-bg">
<view class="card-content">在这里可以进行辩论过程的复盘</view> <view v-for="i in 6" :key="i" class="bg-circle" :style="getRandomCircleStyle()"></view>
</view>
<!-- 顶部卡片 - 可收缩 -->
<view class="content-card" :class="{ 'collapsed': isCardCollapsed }">
<view class="card-header" @click="toggleCard">
<view class="card-icon-wrapper">
<view class="card-icon">
<image src="/static/icons/file-chart-line.png" mode="aspectFit"></image>
</view>
</view>
<view class="card-text">
<view class="card-title">复盘分析</view>
<view class="card-content" v-if="!isCardCollapsed"></view>
</view>
<view class="collapse-icon">
<image
:src="isCardCollapsed ? '/static/icons/arrow-down-s-line.png' : '/static/icons/arrow-up-s-line.png'"
mode="aspectFit"
></image>
</view>
</view>
</view> </view>
<!-- 聊天展示区 --> <!-- 聊天展示区 -->
@ -19,31 +40,44 @@
class="chat-message" class="chat-message"
:class="msg.role === 'user' ? 'from-user' : 'from-ai'" :class="msg.role === 'user' ? 'from-user' : 'from-ai'"
> >
<view class="avatar" v-if="msg.role === 'ai'">AI</view>
<view class="bubble"> <view class="bubble">
<block v-if="msg.loading"> <block v-if="msg.loading">
<view class="dot-flashing"></view> <view class="loading-animation">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
</block> </block>
<block v-else> <block v-else>
{{ msg.content }} {{ msg.content }}
</block> </block>
</view> </view>
<view class="avatar user-avatar" v-if="msg.role === 'user'"></view>
</view> </view>
</scroll-view> </scroll-view>
<!-- 输入框与发送按钮 --> <!-- 输入框与发送按钮 -->
<view class="chat-input"> <view class="chat-input">
<view class="input-group"> <view class="input-group">
<view class="textarea-container">
<textarea <textarea
class="textarea-box" class="textarea-box"
v-model="content" v-model="content"
placeholder="请输入辩论的过程" placeholder="粘贴你的辩论记录AI将为你分析优缺点并提供改进建议"
auto-height auto-height
maxlength="20000" maxlength="5000"
/> />
<button class="send-button-embedded" @click="sendMessage()">
<image src="/static/icons/send-plane-fill.png" mode="aspectFit"></image>
</button>
</view> </view>
<button class="send-button" @click="sendMessage()"></button>
</view> </view>
</view> </view>
<!-- 底部安全区域 -->
<view class="safe-area-bottom"></view>
</view>
</template> </template>
<script> <script>
@ -54,16 +88,41 @@ export default {
messages: [ messages: [
{ {
role: "ai", role: "ai",
content: content: "欢迎使用辩论复盘分析!请粘贴你的辩论记录,我将为你分析优缺点并提供改进建议。"
"辩论过程复盘功能,可以帮你回顾和总结辩论过程中的关键点、亮点以及可能的改进之处。", }
},
], ],
scrollToView: "", scrollToView: "",
content: "", content: "",
isCardCollapsed: false, //
}; };
}, },
methods: { methods: {
// /
toggleCard() {
this.isCardCollapsed = !this.isCardCollapsed;
},
//
getRandomCircleStyle() {
const size = Math.random() * 300 + 100;
const x = Math.random() * 100;
const y = Math.random() * 100;
const delay = Math.random() * 5;
const duration = Math.random() * 10 + 15;
return {
width: `${size}rpx`,
height: `${size}rpx`,
left: `${x}%`,
top: `${y}%`,
animationDelay: `${delay}s`,
animationDuration: `${duration}s`
};
},
async sendMessage() { async sendMessage() {
if (!this.content.trim()) return;
// //
this.messages.push({ this.messages.push({
role: "user", role: "user",
@ -77,13 +136,14 @@ export default {
this.messages.push({ role: "ai", content: "", loading: true }); this.messages.push({ role: "ai", content: "", loading: true });
this.scrollToBottom(); this.scrollToBottom();
// AI // AI
const reply = await this.callAI(this.content); const reply = await this.callAI(this.content);
// loading AI // loading AI
this.messages.splice(aiIndex, 1, { role: "ai", content: reply }); this.messages.splice(aiIndex, 1, { role: "ai", content: reply });
this.scrollToBottom(); this.scrollToBottom();
}, },
async callAI(content) { async callAI(content) {
this.content = ""; this.content = "";
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -127,177 +187,424 @@ export default {
</script> </script>
<style scoped> <style scoped>
.argument-component { .review-component {
width: 100%; width: 100%;
padding: 20rpx; height: 100vh;
padding: 30rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 78vh; background: linear-gradient(135deg, #4338CA 0%, #7C3AED 100%);
background: linear-gradient(135deg, #2c3e50, #337fc6); overflow-x: hidden;
box-sizing: border-box;
padding-bottom: 180rpx; /* 为底部TabBar留出空间 */
position: relative;
}
/* 动态背景 */
.animated-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
}
.bg-circle {
position: absolute;
border-radius: 50%;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
animation: float 20s infinite ease-in-out;
opacity: 0.4;
/* 添加过渡效果 */
transition: all 1.5s ease-in-out;
}
overflow-x: hidden; /* 禁止横向滚动 */ @keyframes float {
box-sizing: border-box; /* 避免 padding 导致溢出 */ 0%, 100% {
transform: translate(0, 0) scale(1);
}
25% {
transform: translate(5%, 10%) scale(1.05);
}
50% {
transform: translate(10%, 5%) scale(0.95);
}
75% {
transform: translate(5%, 15%) scale(1.1);
}
} }
/* 顶部卡片 */
.content-card { .content-card {
background-color: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.15);
border-radius: 20rpx;
padding: 30rpx;
width: 90%;
margin: 0 auto 30rpx;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
border-radius: 28rpx;
padding: 20rpx;
width: 100%;
margin-bottom: 30rpx;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
animation: slideDown 0.6s ease-out;
transition: all 0.3s ease;
}
.content-card.collapsed {
padding: 16rpx 20rpx;
margin-bottom: 20rpx;
}
.card-header {
display: flex;
align-items: center;
cursor: pointer;
}
.collapse-icon {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s ease;
}
.collapse-icon image {
width: 30rpx;
height: 30rpx;
filter: brightness(0) invert(1);
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card-icon-wrapper {
position: relative;
margin-right: 24rpx;
}
.card-icon {
width: 80rpx;
height: 80rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.collapsed .card-icon {
width: 70rpx;
height: 70rpx;
}
.card-icon::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0) 100%
);
transform: rotate(45deg);
animation: shine 3s infinite;
}
@keyframes shine {
0% {
transform: translateX(-100%) rotate(45deg);
}
20%, 100% {
transform: translateX(100%) rotate(45deg);
}
}
.card-icon image {
width: 45rpx;
height: 45rpx;
filter: brightness(0) invert(1);
z-index: 1;
transition: all 0.3s ease;
}
.collapsed .card-icon image {
width: 40rpx;
height: 40rpx;
}
.card-text {
flex: 1;
transition: all 0.3s ease;
} }
.card-title { .card-title {
font-size: 36rpx; font-size: 36rpx;
color: #ffffff; color: #FFFFFF;
font-weight: bold; font-weight: 700;
margin-bottom: 20rpx; margin-bottom: 10rpx;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.collapsed .card-title {
font-size: 32rpx;
margin-bottom: 0;
} }
.card-content { .card-content {
font-size: 28rpx; font-size: 26rpx;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
line-height: 1.5;
transition: all 0.3s ease;
} }
/* 聊天区域 */ /* 聊天区域 */
.chat-area { .chat-area {
flex: 1; flex: 1;
width: 90%; width: 100%;
margin: 0 auto; background: rgba(255, 255, 255, 0.1);
background-color: rgba(255, 255, 255, 0.05); border-radius: 28rpx;
border-radius: 20rpx; padding: 30rpx;
padding: 20rpx; margin-bottom: 30rpx;
overflow-y: auto; overflow-y: auto;
border: 1px solid rgba(255, 255, 255, 0.15);
position: relative;
z-index: 1;
transition: all 0.3s ease;
} }
/* 消息通用样式 */ /* 消息通用样式 */
.chat-message { .chat-message {
display: flex; display: flex;
margin-bottom: 20rpx; margin-bottom: 30rpx;
align-items: flex-start;
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
} }
/* 用户消息靠右 */
.from-user { .from-user {
justify-content: flex-end; justify-content: flex-end;
} }
/* AI消息靠左 */
.from-ai { .from-ai {
justify-content: flex-start; justify-content: flex-start;
} }
/* 头像样式 */
.avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24rpx;
font-weight: bold;
margin-right: 16rpx;
flex-shrink: 0;
}
.user-avatar {
background: #F59E0B;
margin-left: 16rpx;
margin-right: 0;
}
/* 气泡样式 */ /* 气泡样式 */
.bubble { .bubble {
max-width: 70%; max-width: 70%;
padding: 20rpx; padding: 20rpx 24rpx;
border-radius: 20rpx; border-radius: 24rpx;
font-size: 28rpx; font-size: 28rpx;
line-height: 1.6; line-height: 1.6;
word-break: break-word; word-break: break-word;
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
position: relative;
transition: all 0.3s ease; /* 添加过渡效果 */
} }
/* 用户气泡颜色 */
.from-user .bubble { .from-user .bubble {
background-color: #00c4ff; background: #F59E0B;
color: white; color: #FFFFFF;
border-bottom-right-radius: 0; border-bottom-right-radius: 4rpx;
font-weight: 500;
}
.from-user .bubble::after {
content: '';
position: absolute;
right: -12rpx;
bottom: 0;
width: 0;
height: 0;
border-left: 16rpx solid #F59E0B;
border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */
} }
/* AI气泡颜色 */
.from-ai .bubble { .from-ai .bubble {
background-color: #ffd54f; background: rgba(255, 255, 255, 0.2);
color: #333; color: #FFFFFF;
border-bottom-left-radius: 0; border-bottom-left-radius: 4rpx;
min-width: 80rpx;
}
.from-ai .bubble::after {
content: '';
position: absolute;
left: -12rpx;
bottom: 0;
width: 0;
height: 0;
border-right: 16rpx solid rgba(255, 255, 255, 0.2);
border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */
}
min-width: 80px; /* 加载动画 */
.loading-animation {
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
height: 30rpx;
}
.loading-animation .dot {
width: 12rpx;
height: 12rpx;
background-color: #FFFFFF;
border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out both;
}
.loading-animation .dot:nth-child(1) {
animation-delay: -0.32s;
}
.loading-animation .dot:nth-child(2) {
animation-delay: -0.16s;
}
@keyframes bounce {
0%, 80%, 100% {
transform: scale(0);
}
40% {
transform: scale(1.0);
}
} }
/* 输入区域 */ /* 输入区域 */
.chat-input { .chat-input {
display: flex; display: flex;
padding: 20rpx; padding: 16rpx;
background-color: rgba(255, 255, 255, 0.05); background: rgba(255, 255, 255, 0.15);
border-top: 1rpx solid #ccc; border-radius: 28rpx;
align-items: flex-end; align-items: flex-end;
border: 1px solid rgba(255, 255, 255, 0.2);
position: relative;
z-index: 1;
margin-bottom: 20rpx;
} }
.input-group { .input-group {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10rpx; gap: 16rpx;
} }
.input-box { .textarea-container {
background-color: #fff; position: relative;
border-radius: 10rpx; width: 100%;
padding: 10rpx 20rpx;
margin-bottom: 20rpx;
font-size: 28rpx;
border: none;
} }
.textarea-box { .send-button-embedded {
background-color: #fff; position: absolute;
border-radius: 10rpx; bottom: 16rpx;
padding: 10rpx 20rpx; left: 16rpx;
font-size: 28rpx; background: transparent;
min-height: 80rpx; width: 60rpx;
border: none; height: 60rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
padding: 0;
z-index: 2;
} }
.send-button { .send-button-embedded:active {
margin-left: 20rpx; transform: scale(1.2) rotate(-15deg);
background-color: #00bcd4;
color: #fff;
border-radius: 10rpx;
padding: 20rpx;
font-size: 28rpx;
white-space: nowrap;
} }
/* AI loading 动画 */ .send-button-embedded image {
.dot-flashing { width: 40rpx;
width: 20rpx; height: 40rpx;
height: 20rpx; filter: invert(72%) sepia(87%) saturate(1242%) hue-rotate(325deg) brightness(101%) contrast(96%);
border-radius: 50%;
background-color: #333;
animation: dotFlashing 1s infinite linear alternate;
position: relative;
margin-left: auto;
margin-right: auto;
} }
.dot-flashing::before,
.dot-flashing::after { .textarea-box {
content: ""; background: rgba(255, 255, 255, 0.2);
display: inline-block; border-radius: 20rpx;
position: absolute; padding: 16rpx 20rpx 16rpx 90rpx; /* 增加左侧内边距为发送按钮留出空间 */
top: 0; font-size: 28rpx;
width: 20rpx; color: #FFFFFF;
height: 20rpx; border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 50%; transition: all 0.3s;
background-color: #333; min-height: 120rpx;
max-height: 300rpx; /* 增加最大高度,适合复盘分析的长文本 */
width: 100%;
box-sizing: border-box;
} }
.dot-flashing::before {
left: -30rpx; .textarea-box::placeholder {
animation: dotFlashing 1s infinite linear alternate; color: #362d2de1;
animation-delay: 0.2s;
} }
.dot-flashing::after {
left: 30rpx; .textarea-box:focus {
animation: dotFlashing 1s infinite linear alternate; border-color: #F59E0B;
animation-delay: 0.4s; outline: none;
background: rgba(255, 255, 255, 0.25);
} }
@keyframes dotFlashing { /* 底部安全区域 */
0% { .safe-area-bottom {
background-color: #333; height: 20rpx;
} height: calc(20rpx + constant(safe-area-inset-bottom)); /* iOS 11.0-11.2 */
50%, height: calc(20rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */
100% {
background-color: rgba(51, 51, 51, 0.3);
}
} }
</style> </style>

@ -1,136 +1,164 @@
<template> <template>
<view class="tab-bar"> <view class="tab-bar">
<!-- 添加移动的背景块 -->
<view class="active-bg" :style="{ transform: `translateX(${100 * props.currentComponentIndex}%)` }"></view>
<view <view
class="tab-item" v-for="(tab, index) in tabs"
:class="{
'active': props.currentComponentIndex === index,
'is-first-tab': index === 0
}"
v-for="(item, index) in tabList"
:key="index" :key="index"
@click="changeTab(index)"> class="tab-item"
<text :class="{'active': props.currentComponentIndex === index}">{{ item.pageName }}</text> :class="{ active: activeTab === index }"
@click="switchTab(index)"
>
<view class="tab-icon-container" :class="{ active: activeTab === index }">
<view class="tab-icon">
<image :src="activeTab === index ? tab.activeIcon : tab.icon" mode="aspectFit"></image>
</view>
</view>
<view class="tab-label" :class="{ active: activeTab === index }">{{ tab.label }}</view>
</view> </view>
</view> </view>
</template> </template>
<script setup> <script>
export default {
// emit props: {
const emit = defineEmits(['change-tab', 'change-index']); activeTab: {
const props = defineProps(['currentComponentIndex']); type: Number,
default: 0
const tabList = [ }
},
data() {
return {
tabs: [
{ {
pageName:'首页', label: '首页',
component:'PageOfHome' icon: '/static/icons/home.png',
activeIcon: '/static/icons/home-fill.png'
}, },
{ {
pageName:'立论', label: '辩论',
component:'PageOfArgument' icon: '/static/icons/chat-1-line.png',
activeIcon: '/static/icons/chat-1-fill.png'
}, },
{ {
pageName:'复盘', label: '立论',
component:'PageOfReview' icon: '/static/icons/lightbulb-line.png',
activeIcon: '/static/icons/lightbulb-fill.png'
}, },
{ {
pageName:'辩论', label: '复盘',
component:'PageOfDebate' icon: '/static/icons/file-chart-line.png',
activeIcon: '/static/icons/file-chart-fill.png'
}
]
};
},
methods: {
switchTab(index) {
if (this.activeTab === index) return;
this.$emit('tab-change', index);
}
} }
];
const changeTab = (index) => {
if(props.currentComponentIndex === index) return;
//
emit('change-tab', tabList[index].component);
emit('change-index', index);
}; };
</script> </script>
<style scoped> <style scoped>
.tab-bar { .tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 120rpx;
background: rgba(255, 255, 255, 0.5);
display: flex; display: flex;
width: 70%; justify-content: space-around;
margin: 0 auto; align-items: center;
height: 70%; border-top: 1px solid rgba(255, 255, 255, 0.1);
background-color: #1A2C42; padding-bottom: constant(safe-area-inset-bottom);
border: none; padding-bottom: env(safe-area-inset-bottom);
position: relative; z-index: 100;
border-radius: 25rpx; }
overflow: hidden;
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.2);
transform: translateZ(0);
-webkit-transform: translateZ(0);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
/* 移动的背景块 */ .tab-item {
.active-bg { display: flex;
position: absolute; flex-direction: column;
width: 25%; align-items: center;
justify-content: center;
position: relative;
flex: 1;
height: 100%; height: 100%;
background-color: #00D6B9; transition: all 0.3s;
border-radius: 25rpx; }
transition: transform 0.3s ease;
z-index: 1;
box-shadow: 0 0 20rpx rgba(0, 214, 185, 0.5);
/* 添加硬件加速 */
will-change: transform;
}
.tab-item { .tab-icon-container {
flex: 1; width: 100rpx;
height: 100rpx;
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
height: 100%; justify-content: center;
position: relative; position: relative;
transition: all 0.3s ease;
z-index: 2; z-index: 2;
} border-radius: 50%;
min-width: 80rpx; /* 防止 flex 压缩 */
min-height: 80rpx;
flex-shrink: 0; /* 禁止压缩 */
}
.tab-item::before { .tab-icon-container.active {
content: ''; width: 120rpx;
position: absolute; height: 120rpx;
left: 0; border-radius: 50%;
top: 50%; background-color: #B59DF1;
transform: translateY(-50%); transform: translateY(-45rpx);
width: 1px; border: 14rpx solid #6839E0;
height: 60%; position: relative;
z-index: 2; box-sizing: border-box;
background: linear-gradient( box-shadow: 0 4rpx 20rpx rgba(104, 57, 224, 0.4);
to bottom, }
transparent,
rgba(0, 214, 185, 0.1) 15%,
rgba(0, 214, 185, 0.5) 50%,
rgba(0, 214, 185, 0.1) 85%,
transparent
);
}
.tab-item.is-first-tab::before { .tab-icon {
display: none; width: 60rpx;
} height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.tab-item.active { .tab-icon image {
background-color: transparent; width: 50rpx;
} height: 50rpx;
filter: brightness(0) invert(0.8);
opacity: 0.7;
transition: all 0.3s;
}
.tab-item text { .tab-label {
font-size: 24rpx; font-size: 24rpx;
color: rgba(0, 214, 185, 0.7); font-weight: 400;
transition: all 0.3s ease; color: rgba(255, 255, 255, 0.7);
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2); transition: all 0.3s;
} margin-top: 6rpx;
opacity: 0.7;
font-family: 'Poppins' , 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
letter-spacing: 1rpx;
}
.tab-item text.active { .tab-label.active {
color: #ffffff; color: #FFFFFF;
font-weight: bold; font-weight: 600;
text-shadow: 0 1rpx 8rpx rgba(255, 255, 255, 0.4); opacity: 1;
transform: scale(1.05); text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
} transform: translateY(-15rpx) scale(1.1);
}
.tab-icon-container.active .tab-icon image {
opacity: 1;
filter: brightness(0) invert(1);
transform: scale(1.1);
}
.tab-item:active {
opacity: 0.7;
}
</style> </style>

@ -18,5 +18,8 @@
"navigationBarTitleText": "智辩云枢", "navigationBarTitleText": "智辩云枢",
"navigationBarBackgroundColor": "#1a2a6c", "navigationBarBackgroundColor": "#1a2a6c",
"backgroundColor": "#000000" "backgroundColor": "#000000"
},
"style": {
"scoped": false
} }
} }

@ -1,154 +1,52 @@
<template> <template>
<view class="home-page"> <view class="container">
<view class="home-header"> <HomeCom v-if="activeTab === 0" @tab-change="handleTabChange"/>
<text class="home-header-title"></text> <DebateCom v-if="activeTab === 1" />
<text class="home-header-title"></text> <ArgumentCom v-if="activeTab === 2" />
<text class="home-header-title"></text> <ReviewCom v-if="activeTab === 3" />
<text class="home-header-title"></text>
</view>
<view class="home-content">
<!-- 使用条件渲染替代动态组件 -->
<PageOfHome v-if="currentComponent === 'PageOfHome'" @change-tab="handleTabChange" @change-index="handleIndexChange" />
<PageOfArgument v-else-if="currentComponent === 'PageOfArgument'" />
<PageOfReview v-else-if="currentComponent === 'PageOfReview'" />
<PageOfDebate v-else-if="currentComponent === 'PageOfDebate'" />
</view>
<view class="home-footer"> <TabBar :activeTab="activeTab" @tab-change="handleTabChange" />
<TabBar @change-tab="handleTabChange" @change-index="handleIndexChange" :currentComponentIndex="currentComponentIndex"/>
</view>
</view> </view>
</template> </template>
<script setup> <script>
import TabBar from '../../components/TabBar.vue'; import HomeCom from '@/components/HomeCom.vue';
import { ref } from 'vue'; import DebateCom from '@/components/DebateCom.vue';
import ArgumentCom from '@/components/ArgumentCom.vue';
// import ReviewCom from '@/components/ReviewCom.vue';
import PageOfHome from '../../components/HomeCom.vue'; import TabBar from '@/components/TabBar.vue';
import PageOfArgument from '../../components/ArgumentCom.vue';
import PageOfReview from '../../components/ReviewCom.vue'; export default {
import PageOfDebate from '../../components/DebateCom.vue' components: {
HomeCom,
// DebateCom,
const currentComponent = ref('PageOfHome'); ArgumentCom,
// index ReviewCom,
const currentComponentIndex = ref(0); TabBar
},
// data() {
const handleTabChange = (componentName) => { return {
currentComponent.value = componentName; activeTab: 0
}; };
const handleIndexChange = (index) => { },
currentComponentIndex.value = index; methods: {
} handleTabChange(index) {
</script> this.activeTab = index;
<style scoped> //
.home-page { const titles = ['首页', '辩论', '立论', '复盘'];
position: relative; uni.setNavigationBarTitle({
width: 100vw; title: titles[index]
height: 100vh; });
background: linear-gradient(135deg, #1a2a6c, #000000);
display: flex;
flex-direction: column;
align-items: center;
}
.home-header {
width: 100%;
height: min(100rpx, fit-content);
/* background-color: #000000; */
margin-top: 20rpx;
margin-left: 25rpx;
margin-bottom: 20rpx;
display: flex;
}
.home-header::after{
content: '';
width: 100%;
height: 1px;
background: linear-gradient(to right, transparent, rgba(0, 214, 185, 0.1) 15%, rgba(0, 214, 185, 0.5) 50%, rgba(0, 214, 185, 0.1) 85%, transparent);
position: absolute;
top: 100rpx; /* 将分割线定位在header的底部 */
left: 0;
z-index: 1;
transform: translateY(20rpx);
}
.home-header-title {
font-size: 40rpx;
color: #ffffff;
text-align: left;
margin-left: 20rpx;
font-weight: bold;
margin-top: 20rpx;
}
.home-header-title:nth-child(odd) {
animation: float-up-to-down 3s infinite;
}
.home-header-title:nth-child(even) {
animation: float-down-to-up 3s infinite;
}
@keyframes float-up-to-down {
0%{
transform: translateY(-10rpx);
}
20%{
transform: translateY(-7rpx);
}
50%{
transform: translateY(0rpx);
}
70%{
transform: translateY(7rpx);
}
100%{
transform: translateY(-10rpx);
}
}
@keyframes float-down-to-up {
0%{
transform: translateY(10rpx);
}
20%{
transform: translateY(7rpx);
}
50%{
transform: translateY(0rpx);
}
70%{
transform: translateY(-7rpx);
}
100%{
transform: translateY(10rpx);
} }
} }
};
</script>
.home-content { <style>
flex: 1; .container {
width: 100%; width: 100%;
padding: 30rpx 0; min-height: 100vh;
box-sizing: border-box;
overflow-y: auto;
min-height: 400rpx;
position: relative; position: relative;
z-index: 2; }
margin-bottom: 140rpx;
}
.home-footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 120rpx;
z-index: 100;
}
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Loading…
Cancel
Save