From f945f2fe9dae35c3b5fd4beea2b182904df0e16e Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 29 四月 2026 10:56:25 +0800
Subject: [PATCH] feat(AIChatSidebar): 添加AI助手欢迎界面和快速提示功能
---
src/components/AIChatSidebar/index.vue | 1014 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 989 insertions(+), 25 deletions(-)
diff --git a/src/components/AIChatSidebar/index.vue b/src/components/AIChatSidebar/index.vue
index bab4dea..548ec31 100644
--- a/src/components/AIChatSidebar/index.vue
+++ b/src/components/AIChatSidebar/index.vue
@@ -41,18 +41,18 @@
</div>
<div class="header-actions">
<el-tooltip content="浼氳瘽鍘嗗彶" placement="bottom">
- <el-button link @click="toggleHistory">
+ <el-button link class="header-action-btn" @click="handleToggleHistory">
<el-icon :size="18"><Timer /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="寮�鍚柊浼氳瘽" placement="bottom">
- <el-button link @click="newChat">
+ <el-button link class="header-action-btn" @click="handleNewChat">
<el-icon :size="18"><Plus /></el-icon>
</el-button>
</el-tooltip>
<div class="action-divider"></div>
<el-tooltip content="鍏抽棴" placement="bottom">
- <el-button link class="close-btn" @click="visible = false">
+ <el-button link class="header-action-btn close-btn" @click="handleManualClose">
<el-icon :size="18"><Close /></el-icon>
</el-button>
</el-tooltip>
@@ -99,6 +99,80 @@
</div>
<div v-else class="chat-main">
+ <div :class="['chat-hero', { compact: hasMessages }]">
+ <div :class="['assistant-stand', { thinking: isSending, compact: hasMessages }]">
+ <div class="assistant-halo"></div>
+ <div class="assistant-scan-ring"></div>
+ <div class="assistant-orbit assistant-orbit-a"></div>
+ <div class="assistant-orbit assistant-orbit-b"></div>
+ <div class="assistant-bot">
+ <div class="assistant-bot-antenna assistant-bot-antenna-left"></div>
+ <div class="assistant-bot-antenna assistant-bot-antenna-right"></div>
+ <div class="assistant-bot-head">
+ <div class="assistant-bot-head-glow"></div>
+ <div class="assistant-bot-eye assistant-bot-eye-left"></div>
+ <div class="assistant-bot-eye assistant-bot-eye-right"></div>
+ <div class="assistant-bot-mouth"></div>
+ </div>
+ <div class="assistant-bot-neck"></div>
+ <div class="assistant-bot-body">
+ <div class="assistant-bot-core">
+ <div class="assistant-bot-core-ring"></div>
+ <el-icon :size="22"><component :is="currentAssistant.icon" /></el-icon>
+ </div>
+ <div class="assistant-bot-arm assistant-bot-arm-left"></div>
+ <div class="assistant-bot-arm assistant-bot-arm-right"></div>
+ </div>
+ </div>
+ <div class="assistant-status">
+ <span class="assistant-status-dot"></span>
+ {{ isSending ? '鎬濊�冧腑...' : currentAssistant.label }}
+ </div>
+ <div class="assistant-base assistant-base-lg"></div>
+ <div class="assistant-base assistant-base-md"></div>
+ <div class="assistant-base assistant-base-sm"></div>
+ </div>
+
+ <div :class="['welcome-card', { compact: hasMessages }]">
+ <div class="welcome-eyebrow">鏅鸿兘鍔╂墜</div>
+ <h3 class="welcome-title">
+ 鎮ㄥソ
+ <br />
+ 鎴戞槸{{ currentAssistant.label }}鍒嗘瀽瑙h鍔╂墜
+ </h3>
+ <p class="welcome-desc">
+ {{ currentAssistant.description || '鎴戝彲浠ュ洿缁曚笟鍔¢棶棰樻彁渚涜В璇汇�佹煡璇㈠缓璁拰鍒嗘瀽鏀寔锛屽府鍔╀綘鏇村揩瀹屾垚鍒ゆ柇涓庡鐞嗐��' }}
+ </p>
+
+ <div class="quick-prompt-list">
+ <button
+ v-for="prompt in displayedQuickPrompts"
+ :key="prompt"
+ type="button"
+ class="quick-prompt-btn"
+ :disabled="isSending"
+ @click="sendQuickPrompt(prompt)"
+ >
+ {{ prompt }}
+ </button>
+ </div>
+
+ <button
+ v-if="quickPrompts.length > quickPromptLimit"
+ type="button"
+ class="more-prompts-btn"
+ @click="refreshQuickPrompts"
+ >
+ <el-icon><RefreshRight /></el-icon>
+ <span>鎹竴鎹�</span>
+ </button>
+ </div>
+ </div>
+
+ <div v-show="!hasMessages" class="hero-dot-grid" aria-hidden="true">
+ <span v-for="dot in 28" :key="dot"></span>
+ </div>
+
<div class="message-list" ref="messageListRef">
<div
v-for="(message, index) in messages"
@@ -149,10 +223,10 @@
<div class="input-area">
<div class="input-actions">
- <el-button link type="primary" size="small" @click="newChat">
+ <el-button link class="utility-action-btn" type="primary" size="small" @click="handleNewChat">
<el-icon><Plus /></el-icon>鏂颁細璇�
</el-button>
- <el-button v-if="isSending" link type="danger" size="small" @click="stopGeneration">
+ <el-button v-if="isSending" link class="utility-action-btn stop-action-btn" type="danger" size="small" @click="stopGeneration">
<el-icon><VideoPause /></el-icon>鍋滄鐢熸垚
</el-button>
<el-upload
@@ -164,7 +238,7 @@
:on-change="handleFileChange"
:disabled="isSending"
>
- <el-button link type="primary" size="small" :disabled="isSending">
+ <el-button link class="utility-action-btn upload-action-btn" type="primary" size="small" :disabled="isSending">
<el-icon><Upload /></el-icon>鍒嗘瀽鏂囦欢
</el-button>
</el-upload>
@@ -188,8 +262,9 @@
class="send-btn"
:disabled="isSending || (!inputMessage.trim() && !selectedFile)"
@click="sendMessage"
+ aria-label="鍙戦��"
>
- 鍙戦��
+ <el-icon><Promotion /></el-icon>
</el-button>
</div>
</div>
@@ -203,7 +278,7 @@
import { ref, onMounted, onUnmounted, nextTick, watch, computed } from 'vue'
import request from '@/utils/request'
import * as echarts from 'echarts'
-import { Cpu, User, Plus, Timer, Delete, ChatDotSquare, VideoPause, Upload, Document, Close, ShoppingCart } from '@element-plus/icons-vue'
+import { Cpu, User, Plus, Timer, Delete, ChatDotSquare, VideoPause, Upload, Document, Close, ShoppingCart, Promotion, RefreshRight } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
const props = defineProps({
@@ -228,6 +303,7 @@
storageKey: 'ai_chat_uuid',
placeholder: '璇疯緭鍏ユ偍鐨勯棶棰�... (Enter 鍙戦��, Shift+Enter 鎹㈣)',
welcomeMessage: '浣犲ソ',
+ description: '鎴戝彲浠ュ洖绛斾綘鐨勯棶棰橈紝涓轰綘鎻愪緵涓氬姟鏁版嵁瑙h淇℃伅銆佸鐞嗗缓璁拰杈呭姪鍐崇瓥鏀寔銆�',
allowFileUpload: true,
emptySessionText: '鏆傛棤鍘嗗彶浼氳瘽'
},
@@ -241,6 +317,7 @@
storageKey: 'purchase_ai_chat_uuid',
placeholder: '璇疯緭鍏ラ噰璐棶棰�... (Enter 鍙戦��, Shift+Enter 鎹㈣)',
welcomeMessage: '浣犲ソ',
+ description: '鎴戝彲浠ュ崗鍔╀綘鍒嗘瀽閲囪喘璁㈠崟銆佸埌璐ц繘搴︺�佷緵搴斿晢琛ㄧ幇鍜屼粯娆炬儏鍐碉紝甯姪浣犲揩閫熷畾浣嶉噰璐紓甯搞��',
allowFileUpload: false,
emptySessionText: '鏆傛棤閲囪喘浼氳瘽'
}
@@ -250,6 +327,52 @@
const selectedAssistantKey = ref(props.defaultAssistant || assistants.value[0]?.key || 'general')
const currentAssistant = computed(() => assistants.value.find(item => item.key === selectedAssistantKey.value) || assistants.value[0] || builtInAssistants[0])
const showAssistantSwitch = computed(() => assistants.value.length > 1)
+const assistantQuickPromptMap = {
+ general: [
+ '鎴戝綋鍓嶆湁鍝簺瀹℃壒寰呭姙闇�瑕佸鐞嗭紵',
+ '甯垜鍒楀嚭浠婂ぉ鏂板鐨勫鎵瑰緟鍔炪��',
+ '褰撳墠寰呮垜瀹℃壒鐨勫崟鎹紝鎸夋椂闂村�掑簭鍒楀嚭鏉ャ��',
+ '鎴戝彂璧风殑瀹℃壒閲岋紝鍝簺杩樺湪澶勭悊涓紵',
+ '鏌ヨ娴佺▼缂栧彿 XXX 鐨勫鎵硅鎯呫��',
+ '娴佺▼缂栧彿 XXX 鐜板湪鍗″湪鍝釜瀹℃壒鑺傜偣锛熷綋鍓嶅鎵逛汉鏄皝锛�',
+ '甯垜鏌ョ湅娴佺▼缂栧彿 XXX 鐨勫鎵规祦杞褰曘��',
+ '杩�7澶╂垜鐨勫鎵瑰緟鍔炵粺璁℃儏鍐垫�庝箞鏍凤紵',
+ '鏈湀鎴戠殑瀹℃壒涓紝閫氳繃銆侀┏鍥炪�佸鐞嗕腑鍚勬湁澶氬皯锛�',
+ '杩�30澶╁悇绫诲瀷瀹℃壒鏁伴噺鍒嗗竷鏄粈涔堬紵',
+ '甯垜瀹℃壒閫氳繃娴佺▼缂栧彿 XXX锛屽娉ㄢ�滃悓鎰忊�濄��',
+ '甯垜椹冲洖娴佺▼缂栧彿 XXX锛屽娉ㄢ�滆琛ュ厖璇存槑鈥濄��',
+ '鎾ら攢鎴戝垰鍒氬娴佺▼缂栧彿 XXX 鐨勫鎵规搷浣溿��',
+ '甯垜淇敼娴佺▼缂栧彿 XXX 鐨勫娉ㄤ负鈥滃凡琛ュ厖闄勪欢鈥濄��',
+ '鍒犻櫎鎴戝彂璧风殑娴佺▼缂栧彿 XXX銆�'
+ ],
+ purchase: [
+ '鏈湀閲囪喘閲戦鎺掑悕鍓嶅崄鐨勭墿鏂欐湁鍝簺锛�',
+ '鍝簺閲囪喘璁㈠崟杩樻湭鍏ュ簱锛�',
+ '鏈�杩�7澶╀緵搴斿晢鍒拌揣寮傚父鏈夊摢浜涳紵',
+ '甯垜缁熻寰呬粯娆鹃噰璐崟',
+ '鍒楀嚭鏈湀閲囪喘閫�璐ф儏鍐�'
+ ]
+}
+const quickPromptLimit = 3
+const quickPromptStart = ref(0)
+const quickPrompts = computed(() => {
+ const assistant = currentAssistant.value || {}
+ if (Array.isArray(assistant.quickPrompts) && assistant.quickPrompts.length) {
+ return assistant.quickPrompts
+ }
+ return assistantQuickPromptMap[assistant.key] || assistantQuickPromptMap.general
+})
+const displayedQuickPrompts = computed(() => {
+ const prompts = quickPrompts.value || []
+ if (prompts.length <= quickPromptLimit) return prompts
+
+ const result = []
+ for (let i = 0; i < quickPromptLimit; i++) {
+ result.push(prompts[(quickPromptStart.value + i) % prompts.length])
+ }
+ return result
+})
+const hasMessages = computed(() => messages.value.length > 0)
const visible = ref(false)
const windowWidth = ref(window.innerWidth)
@@ -274,11 +397,31 @@
const sessions = ref([])
const loadingSessions = ref(false)
+const abortCurrentRequest = () => {
+ if (!currentAbortController.value) return
+
+ currentAbortController.value.abort()
+ currentAbortController.value = null
+ isSending.value = false
+
+ const lastMsg = messages.value[messages.value.length - 1]
+ if (lastMsg && !lastMsg.isUser) {
+ lastMsg.isTyping = false
+ }
+}
+
const toggleHistory = () => {
showHistory.value = !showHistory.value
if (showHistory.value) {
loadSessions()
}
+}
+
+const handleToggleHistory = () => {
+ if (isSending.value) {
+ abortCurrentRequest()
+ }
+ toggleHistory()
}
const loadSessions = async () => {
@@ -395,12 +538,7 @@
watch(selectedAssistantKey, (nextKey, prevKey) => {
if (!prevKey || nextKey === prevKey) return
- if (currentAbortController.value) {
- currentAbortController.value.abort()
- currentAbortController.value = null
- }
-
- isSending.value = false
+ abortCurrentRequest()
disposeCharts()
messages.value = []
outputState.value = {}
@@ -408,6 +546,7 @@
showHistory.value = false
selectedFile.value = null
inputMessage.value = ''
+ quickPromptStart.value = 0
initUUID()
hello()
})
@@ -425,6 +564,13 @@
const handleClose = () => {
visible.value = false
+}
+
+const handleManualClose = () => {
+ if (isSending.value) {
+ abortCurrentRequest()
+ }
+ handleClose()
}
const initUUID = () => {
@@ -447,9 +593,29 @@
sessions.value = []
showHistory.value = false
selectedFile.value = null
+ quickPromptStart.value = 0
localStorage.removeItem(currentAssistant.value.storageKey)
initUUID()
hello()
+}
+
+const handleNewChat = () => {
+ if (isSending.value) {
+ abortCurrentRequest()
+ }
+ newChat()
+}
+
+const sendQuickPrompt = (prompt) => {
+ if (!prompt || isSending.value) return
+ inputMessage.value = prompt
+ sendMessage()
+}
+
+const refreshQuickPrompts = () => {
+ const prompts = quickPrompts.value || []
+ if (prompts.length <= quickPromptLimit) return
+ quickPromptStart.value = (quickPromptStart.value + quickPromptLimit) % prompts.length
}
const disposeCharts = () => {
@@ -673,17 +839,7 @@
}
const stopGeneration = () => {
- if (currentAbortController.value) {
- currentAbortController.value.abort()
- currentAbortController.value = null
- isSending.value = false
-
- // 灏嗘渶鍚庝竴鏉℃秷鎭爣璁颁负闈炴墦瀛楃姸鎬�
- const lastMsg = messages.value[messages.value.length - 1]
- if (lastMsg && !lastMsg.isUser) {
- lastMsg.isTyping = false
- }
- }
+ abortCurrentRequest()
}
const sendRequest = (message) => {
@@ -1376,6 +1532,40 @@
}
}
}
+
+ :deep(.header-action-btn) {
+ position: relative;
+ overflow: hidden;
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.08));
+ border: 1px solid rgba(255, 255, 255, 0.16);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 10px 18px rgba(0, 0, 0, 0.12);
+
+ &::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.22), transparent 55%);
+ pointer-events: none;
+ }
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: -120%;
+ left: -40%;
+ width: 60%;
+ height: 260%;
+ background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.28), transparent);
+ transform: rotate(24deg);
+ opacity: 0;
+ transition: all 0.35s ease;
+ }
+
+ &:hover::after {
+ left: 100%;
+ opacity: 1;
+ }
+ }
}
.assistant-switcher {
@@ -1392,6 +1582,11 @@
gap: 6px;
flex-wrap: wrap;
justify-content: center;
+ padding: 4px;
+ border-radius: 999px;
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.08));
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 10px 18px rgba(0, 0, 0, 0.1);
}
:deep(.el-radio-button__inner) {
@@ -1839,6 +2034,40 @@
display: inline-flex;
align-items: center;
}
+
+ :deep(.utility-action-btn) {
+ position: relative;
+ height: 34px;
+ padding: 0 14px;
+ border-radius: 999px;
+ border: 1px solid rgba(92, 119, 255, 0.18);
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(236, 243, 255, 0.98));
+ color: $primary-blue;
+ font-weight: 600;
+ box-shadow: 0 10px 20px rgba(0, 85, 212, 0.08);
+ transition: all 0.25s ease;
+
+ .el-icon {
+ margin-right: 5px;
+ }
+
+ &:hover:not(.is-disabled) {
+ color: #fff;
+ border-color: transparent;
+ background: linear-gradient(135deg, #1f6dff 0%, #6b38ef 100%);
+ box-shadow: 0 14px 24px rgba(64, 90, 255, 0.2);
+ transform: translateY(-1px);
+ }
+ }
+
+ :deep(.stop-action-btn) {
+ border-color: rgba(255, 99, 123, 0.18);
+ color: #d33e5e;
+
+ &:hover:not(.is-disabled) {
+ background: linear-gradient(135deg, #f5536e 0%, #a33cff 100%);
+ }
+ }
}
.input-box {
@@ -1944,6 +2173,10 @@
box-shadow: 0 6px 20px rgba(0, 85, 212, 0.4);
transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275);
overflow: hidden;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ letter-spacing: 0.3px;
&::before {
content: '';
@@ -1973,6 +2206,11 @@
background: linear-gradient(145deg, #b0b0b0, #c5c5c5);
box-shadow: none;
cursor: not-allowed;
+ }
+
+ .el-icon {
+ font-size: 15px;
+ transform: translateY(-1px);
}
}
}
@@ -2034,4 +2272,730 @@
color: #5ac8fa;
}
}
+
+.chat-main {
+ background:
+ radial-gradient(circle at top left, rgba(46, 140, 224, 0.12) 0%, transparent 34%),
+ linear-gradient(180deg, #fff 0%, #f7fbff 46%, #fff 100%);
+}
+
+.chat-hero {
+ display: grid;
+ grid-template-columns: 164px minmax(0, 1fr);
+ gap: 18px;
+ align-items: start;
+ padding: 14px 18px 6px;
+
+ &.compact {
+ grid-template-columns: 122px minmax(0, 1fr);
+ gap: 12px;
+ padding: 8px 18px 2px;
+ }
+}
+
+.assistant-stand {
+ position: relative;
+ min-height: 252px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ padding-top: 18px;
+ overflow: hidden;
+
+ &.compact {
+ min-height: 176px;
+ padding-top: 8px;
+ }
+
+ &.thinking {
+ .assistant-halo {
+ opacity: 1;
+ transform: scale(1.08);
+ filter: blur(8px);
+ }
+
+ .assistant-scan-ring {
+ opacity: 1;
+ animation-duration: 1.6s;
+ }
+
+ .assistant-orbit {
+ opacity: 1;
+ }
+
+ .assistant-bot {
+ transform: translateY(-4px) scale(1.02);
+ }
+
+ .assistant-bot-head {
+ box-shadow: 0 0 30px rgba(80, 157, 255, 0.36);
+ }
+
+ .assistant-bot-eye {
+ animation: robotBlinkFast 1.1s infinite;
+ box-shadow: 0 0 16px rgba(72, 186, 255, 0.95);
+ }
+
+ .assistant-bot-mouth {
+ width: 28px;
+ opacity: 1;
+ animation: robotTalk 1.2s ease-in-out infinite;
+ }
+
+ .assistant-bot-core {
+ animation: corePulse 1.4s ease-in-out infinite;
+ box-shadow: 0 0 24px rgba(78, 120, 255, 0.26);
+ }
+
+ .assistant-bot-core-ring {
+ animation: coreRotate 3s linear infinite;
+ }
+
+ .assistant-status {
+ color: #6a3bee;
+ box-shadow: 0 10px 22px rgba(106, 59, 238, 0.14);
+ }
+
+ .assistant-status-dot {
+ background: #6a3bee;
+ box-shadow: 0 0 12px rgba(106, 59, 238, 0.9);
+ animation: thinkingDot 1s ease-in-out infinite;
+ }
+
+ .assistant-base-sm {
+ box-shadow: 0 0 24px rgba(255, 93, 122, 0.48);
+ }
+ }
+}
+
+.assistant-halo {
+ position: absolute;
+ top: 22px;
+ width: 130px;
+ height: 130px;
+ border-radius: 50%;
+ background: radial-gradient(circle, rgba(46, 140, 224, 0.3) 0%, rgba(0, 85, 212, 0.18) 42%, rgba(113, 54, 244, 0.12) 60%, transparent 78%);
+ filter: blur(6px);
+ opacity: 0.82;
+ transition: all 0.35s ease;
+}
+
+.assistant-scan-ring {
+ position: absolute;
+ top: 40px;
+ width: 132px;
+ height: 132px;
+ border-radius: 50%;
+ border: 1px solid rgba(90, 159, 224, 0.22);
+ box-shadow: inset 0 0 16px rgba(255, 255, 255, 0.25);
+ opacity: 0.55;
+ animation: scanRing 4s linear infinite;
+}
+
+.assistant-orbit {
+ position: absolute;
+ top: 52px;
+ width: 150px;
+ height: 150px;
+ border-radius: 50%;
+ border: 1px dashed rgba(92, 135, 255, 0.22);
+ opacity: 0.45;
+}
+
+.assistant-orbit-a {
+ animation: orbitRotate 8s linear infinite;
+}
+
+.assistant-orbit-b {
+ width: 118px;
+ height: 118px;
+ top: 68px;
+ border-color: rgba(255, 108, 150, 0.22);
+ animation: orbitRotateReverse 5.6s linear infinite;
+}
+
+.assistant-bot {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ margin-top: 12px;
+ transition: transform 0.35s ease;
+}
+
+.assistant-bot-antenna {
+ position: absolute;
+ top: -4px;
+ width: 4px;
+ height: 20px;
+ border-radius: 999px;
+ background: linear-gradient(180deg, #fefefe, #aac9ff);
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: -6px;
+ left: 50%;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ transform: translateX(-50%);
+ background: linear-gradient(135deg, #54bfff, #7a41ff);
+ box-shadow: 0 0 14px rgba(84, 191, 255, 0.65);
+ }
+}
+
+.assistant-bot-antenna-left {
+ left: 36px;
+ transform: rotate(-14deg);
+}
+
+.assistant-bot-antenna-right {
+ right: 36px;
+ transform: rotate(14deg);
+}
+
+.assistant-bot-head {
+ position: relative;
+ width: 92px;
+ height: 78px;
+ border-radius: 28px;
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, #e8f1ff 100%);
+ border: 1px solid rgba(0, 85, 212, 0.14);
+ box-shadow: 0 16px 32px rgba(0, 85, 212, 0.14);
+}
+
+.assistant-bot-head-glow {
+ position: absolute;
+ inset: 10px 16px auto;
+ height: 20px;
+ border-radius: 999px;
+ background: linear-gradient(180deg, rgba(0, 85, 212, 0.16), transparent);
+}
+
+.assistant-bot-eye {
+ position: absolute;
+ top: 30px;
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ background: radial-gradient(circle, #8ef0ff 0%, #56c0ff 42%, #2869ff 100%);
+ box-shadow: 0 0 12px rgba(72, 186, 255, 0.72);
+ animation: robotBlink 3.2s infinite;
+}
+
+.assistant-bot-eye-left {
+ left: 22px;
+}
+
+.assistant-bot-eye-right {
+ right: 22px;
+}
+
+.assistant-bot-mouth {
+ position: absolute;
+ left: 50%;
+ bottom: 16px;
+ width: 22px;
+ height: 4px;
+ transform: translateX(-50%);
+ border-radius: 999px;
+ background: linear-gradient(90deg, rgba(72, 186, 255, 0.2), rgba(72, 186, 255, 0.9), rgba(72, 186, 255, 0.2));
+}
+
+.assistant-bot-neck {
+ width: 16px;
+ height: 10px;
+ border-radius: 0 0 10px 10px;
+ background: linear-gradient(180deg, #dceaff, #bdd5ff);
+ margin-top: -2px;
+}
+
+.assistant-bot-body {
+ position: relative;
+ width: 104px;
+ height: 92px;
+ margin-top: 2px;
+ border-radius: 28px 28px 34px 34px;
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, #e3eeff 100%);
+ border: 1px solid rgba(0, 85, 212, 0.14);
+ box-shadow: 0 18px 36px rgba(0, 85, 212, 0.16);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.assistant-bot-arm {
+ position: absolute;
+ top: 18px;
+ width: 16px;
+ height: 44px;
+ border-radius: 999px;
+ background: linear-gradient(180deg, #eff5ff, #c7dbff);
+ border: 1px solid rgba(0, 85, 212, 0.12);
+}
+
+.assistant-bot-arm-left {
+ left: -10px;
+ transform: rotate(16deg);
+}
+
+.assistant-bot-arm-right {
+ right: -10px;
+ transform: rotate(-16deg);
+}
+
+.assistant-bot-core {
+ position: relative;
+ width: 46px;
+ height: 46px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: $primary-blue;
+ background: radial-gradient(circle, rgba(255, 255, 255, 1) 0%, #dae8ff 55%, #adc7ff 100%);
+}
+
+.assistant-bot-core-ring {
+ position: absolute;
+ inset: -6px;
+ border-radius: 50%;
+ border: 1px solid rgba(88, 135, 255, 0.3);
+ border-top-color: rgba(255, 96, 139, 0.85);
+ border-right-color: rgba(79, 145, 255, 0.9);
+}
+
+.assistant-status {
+ position: relative;
+ z-index: 1;
+ margin-top: 14px;
+ padding: 6px 12px;
+ border-radius: 999px;
+ font-size: 12px;
+ font-weight: 600;
+ color: $deep-blue;
+ background: rgba(255, 255, 255, 0.95);
+ border: 1px solid rgba(0, 85, 212, 0.12);
+ box-shadow: 0 8px 20px rgba(0, 85, 212, 0.08);
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.assistant-status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #2e8ce0;
+ box-shadow: 0 0 10px rgba(46, 140, 224, 0.72);
+}
+
+.assistant-base {
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ border-radius: 50%;
+ border: 2px solid rgba(255, 93, 122, 0.22);
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(255, 111, 145, 0.1) 70%, transparent 100%);
+}
+
+.assistant-base-lg {
+ width: 118px;
+ height: 30px;
+}
+
+.assistant-base-md {
+ bottom: 6px;
+ width: 88px;
+ height: 20px;
+ border-color: rgba(255, 93, 122, 0.34);
+}
+
+.assistant-base-sm {
+ bottom: 11px;
+ width: 54px;
+ height: 10px;
+ background: linear-gradient(90deg, rgba(255, 93, 122, 0.95), rgba(255, 173, 188, 0.9));
+ border: none;
+ box-shadow: 0 0 18px rgba(255, 93, 122, 0.38);
+}
+
+@keyframes robotBlink {
+ 0%, 44%, 48%, 100% {
+ transform: scaleY(1);
+ }
+ 46% {
+ transform: scaleY(0.14);
+ }
+}
+
+@keyframes robotBlinkFast {
+ 0%, 100% {
+ transform: scaleY(1);
+ }
+ 50% {
+ transform: scaleY(0.3);
+ }
+}
+
+@keyframes robotTalk {
+ 0%, 100% {
+ transform: translateX(-50%) scaleX(1);
+ }
+ 50% {
+ transform: translateX(-50%) scaleX(1.35);
+ }
+}
+
+@keyframes corePulse {
+ 0%, 100% {
+ transform: scale(1);
+ filter: brightness(1);
+ }
+ 50% {
+ transform: scale(1.08);
+ filter: brightness(1.08);
+ }
+}
+
+@keyframes coreRotate {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes orbitRotate {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes orbitRotateReverse {
+ from {
+ transform: rotate(360deg);
+ }
+ to {
+ transform: rotate(0deg);
+ }
+}
+
+@keyframes scanRing {
+ 0%, 100% {
+ transform: scale(0.96);
+ opacity: 0.42;
+ }
+ 50% {
+ transform: scale(1.04);
+ opacity: 0.86;
+ }
+}
+
+@keyframes thinkingDot {
+ 0%, 100% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(1.35);
+ }
+}
+
+.welcome-card {
+ position: relative;
+ padding: 14px 14px 12px;
+ border-radius: 16px;
+ background:
+ linear-gradient(#fff, #fff) padding-box,
+ linear-gradient(135deg, rgba(255, 64, 96, 0.85), rgba(117, 65, 255, 0.9)) border-box;
+ border: 1px solid transparent;
+ box-shadow: 0 16px 36px rgba(0, 85, 212, 0.12);
+
+ &.compact {
+ padding: 10px 12px;
+ border-radius: 12px;
+ box-shadow: 0 8px 16px rgba(0, 85, 212, 0.07);
+
+ .welcome-eyebrow {
+ margin-bottom: 4px;
+ }
+
+ .welcome-title {
+ font-size: 17px;
+ line-height: 1.3;
+
+ br {
+ display: none;
+ }
+ }
+
+ .welcome-desc {
+ margin-top: 6px;
+ font-size: 12px;
+ line-height: 1.55;
+ }
+
+ .quick-prompt-list {
+ margin-top: 10px;
+ gap: 6px;
+ }
+
+ .quick-prompt-btn {
+ padding: 8px 10px;
+ font-size: 12px;
+ border-radius: 7px;
+ }
+
+ .more-prompts-btn {
+ margin-top: 8px;
+ font-size: 12px;
+ }
+ }
+}
+
+.welcome-eyebrow {
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 2px;
+ color: rgba(0, 85, 212, 0.58);
+ margin-bottom: 8px;
+}
+
+.welcome-title {
+ margin: 0;
+ font-size: 26px;
+ line-height: 1.2;
+ font-weight: 800;
+ color: #172033;
+}
+
+.welcome-desc {
+ margin: 10px 0 0;
+ font-size: 13px;
+ line-height: 1.7;
+ color: #5f6980;
+}
+
+.quick-prompt-list {
+ display: grid;
+ gap: 8px;
+ margin-top: 14px;
+}
+
+.quick-prompt-btn {
+ width: 100%;
+ border: none;
+ border-radius: 10px;
+ padding: 11px 14px;
+ text-align: left;
+ font-size: 13px;
+ font-weight: 600;
+ color: #fff;
+ cursor: pointer;
+ background: linear-gradient(90deg, #ff4c55 0%, #7c38ef 100%);
+ box-shadow: 0 12px 22px rgba(124, 56, 239, 0.18);
+ transition: transform 0.25s ease, box-shadow 0.25s ease, opacity 0.2s ease;
+ position: relative;
+ overflow: hidden;
+
+ &::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.22), transparent 56%);
+ pointer-events: none;
+ }
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: -120%;
+ left: -30%;
+ width: 45%;
+ height: 260%;
+ background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.3), transparent);
+ transform: rotate(22deg);
+ opacity: 0;
+ transition: all 0.35s ease;
+ }
+
+ &:hover:not(:disabled) {
+ transform: translateY(-2px) scale(1.01);
+ box-shadow: 0 16px 28px rgba(124, 56, 239, 0.24);
+
+ &::after {
+ left: 100%;
+ opacity: 1;
+ }
+ }
+
+ &:disabled {
+ cursor: not-allowed;
+ opacity: 0.65;
+ }
+}
+
+.more-prompts-btn {
+ margin-top: 10px;
+ padding: 0 12px;
+ height: 32px;
+ border: 1px solid rgba(208, 65, 81, 0.12);
+ border-radius: 999px;
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(255, 241, 245, 0.96));
+ color: #d04151;
+ font-size: 13px;
+ font-weight: 600;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ box-shadow: 0 10px 18px rgba(208, 65, 81, 0.08);
+ transition: all 0.25s ease;
+
+ &:hover {
+ transform: translateY(-1px);
+ background: linear-gradient(135deg, #ff5570 0%, #8a3df6 100%);
+ border-color: transparent;
+ color: #fff;
+ box-shadow: 0 14px 24px rgba(138, 61, 246, 0.18);
+ }
+}
+
+.hero-dot-grid {
+ display: grid;
+ grid-template-columns: repeat(14, 1fr);
+ gap: 7px;
+ padding: 0 18px 14px;
+
+ span {
+ display: block;
+ width: 100%;
+ aspect-ratio: 1;
+ border-radius: 2px;
+ background: linear-gradient(135deg, rgba(255, 110, 138, 0.95), rgba(255, 190, 201, 0.55));
+ }
+}
+
+.message-list {
+ padding: 8px 18px 18px;
+ gap: 16px;
+ background: transparent;
+}
+
+.input-area {
+ padding: 12px 18px 16px;
+ background: #fff;
+ border-top: none;
+
+ &::before {
+ display: none;
+ }
+
+ .input-box {
+ padding: 14px 16px 16px;
+ border: 1px solid rgba(123, 56, 239, 0.9);
+ border-radius: 22px;
+ margin: 0;
+ transition: all 0.25s ease;
+ box-shadow: 0 14px 34px rgba(0, 85, 212, 0.08);
+
+ &:focus-within {
+ border-color: #7c38ef;
+ box-shadow: 0 0 0 3px rgba(124, 56, 239, 0.1), 0 18px 40px rgba(0, 85, 212, 0.12);
+ transform: none;
+ }
+
+ :deep(.el-textarea__inner) {
+ padding-right: 58px;
+ padding-bottom: 0;
+ min-height: 104px;
+
+ &::placeholder {
+ color: #a0a9bc;
+ }
+ }
+
+ .send-btn {
+ right: 25px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 36px;
+ min-width: 36px;
+ height: 36px;
+ padding: 0;
+ background: linear-gradient(135deg, #ff5570 0%, #7a36f2 58%, #2d79ff 100%);
+ border-radius: 50%;
+ box-shadow: 0 12px 24px rgba(109, 50, 236, 0.24);
+ transition: all 0.25s ease;
+ gap: 0;
+
+ &:hover:not(:disabled) {
+ transform: translateY(calc(-50% - 1px)) scale(1.04);
+ box-shadow: 0 16px 28px rgba(109, 50, 236, 0.3);
+ }
+
+ &:active:not(:disabled) {
+ transform: translateY(-50%) scale(0.96);
+ }
+
+ .el-icon {
+ margin: 0;
+ font-size: 16px;
+ transform: translate(0, -1px);
+ }
+ }
+ }
+}
+
+@media (max-width: 767px) {
+ .chat-hero {
+ grid-template-columns: 1fr;
+ gap: 10px;
+ padding: 14px 14px 6px;
+
+ &.compact {
+ padding: 8px 14px 4px;
+ }
+ }
+
+ .assistant-stand {
+ min-height: 184px;
+ }
+
+ .welcome-card {
+ padding: 12px 12px 10px;
+ }
+
+ .welcome-title {
+ font-size: 21px;
+ }
+
+ .hero-dot-grid {
+ grid-template-columns: repeat(12, 1fr);
+ gap: 6px;
+ padding: 0 14px 12px;
+ }
+
+ .message-list {
+ padding: 8px 14px 14px;
+ }
+
+ .input-area {
+ padding: 10px 14px 14px;
+ }
+
+ .input-area .input-actions {
+ gap: 10px;
+ flex-wrap: wrap;
+ }
+}
</style>
--
Gitblit v1.9.3