feat(router): 添加AI工业大脑路由并优化AI聊天侧边栏功能
- 新增AI工业大脑路由(/ai-industrial-brain)及对应视图组件
- 重构AI聊天侧边栏组件,增加多种配置选项包括隐藏触发器、自动打开等功能
- 更新AI助手头像资源并实现动态头像显示机制
- 优化AI聊天侧边栏UI样式和动画效果
- 在布局组件中添加全局AI聊天显示控制逻辑
- 修改权限模块中的路由过滤逻辑以支持AI功能路由
- 新增AI助手工作空间组件用于特定场景下的AI交互
| | |
| | | <template> |
| | | <div class="ai-chat-sidebar-wrapper"> |
| | | <!-- æ¬æµ®å¾æ --> |
| | | <div class="ai-chat-trigger" @click="toggleSidebar" v-show="!visible"> |
| | | <div v-if="!hideTrigger" class="ai-chat-trigger" @click="toggleSidebar" v-show="!visible"> |
| | | <el-tooltip :content="currentAssistant.tooltip" placement="left"> |
| | | <div class="trigger-icon"> |
| | | <el-icon :size="30" color="#fff"><component :is="currentAssistant.icon" /></el-icon> |
| | |
| | | <!-- ä¾§è¾¹æ å¯¹è¯æ¡ --> |
| | | <el-drawer |
| | | v-model="visible" |
| | | :size="drawerSize" |
| | | direction="rtl" |
| | | :size="computedDrawerSize" |
| | | :direction="drawerDirection" |
| | | :with-header="true" |
| | | class="ai-chat-drawer" |
| | | :modal="false" |
| | | modal-class="ai-chat-overlay" |
| | | :show-close="false" |
| | | :append-to-body="false" |
| | | :close-on-press-escape="!hideTrigger" |
| | | :close-on-click-modal="!hideTrigger" |
| | | @close="handleClose" |
| | | > |
| | | <template #header> |
| | |
| | | <el-icon :size="18"><Plus /></el-icon> |
| | | </el-button> |
| | | </el-tooltip> |
| | | <div class="action-divider"></div> |
| | | <el-tooltip content="å
³é" placement="bottom"> |
| | | <el-button |
| | | v-if="headerExtraActionText" |
| | | link |
| | | class="header-action-btn header-action-btn--text" |
| | | @click="handleHeaderExtraAction" |
| | | > |
| | | {{ headerExtraActionText }} |
| | | </el-button> |
| | | <div v-if="!hideTrigger" class="action-divider"></div> |
| | | <el-tooltip v-if="!hideTrigger" content="å
³é" placement="bottom"> |
| | | <el-button link class="header-action-btn close-btn" @click="handleManualClose"> |
| | | <el-icon :size="18"><Close /></el-icon> |
| | | </el-button> |
| | |
| | | <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 class="assistant-model-shell"> |
| | | <div class="assistant-model-cut"> |
| | | <img |
| | | v-if="currentAssistantAvatar" |
| | | class="assistant-model-img" |
| | | :src="currentAssistantAvatar" |
| | | :alt="currentAssistant.label" |
| | | /> |
| | | <div v-else class="assistant-model-fallback"> |
| | | <el-icon :size="30"><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>æ¢ä¸æ¢</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"> |
| | |
| | | import { Cpu, User, Plus, Timer, Delete, ChatDotSquare, VideoPause, Upload, Document, Close, Promotion, RefreshRight } from '@element-plus/icons-vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { builtInAssistants, generalAssistant } from './assistants' |
| | | import todoAssistantAvatar from '@/assets/AI/å¾
å婿.png' |
| | | import salesAssistantAvatar from '@/assets/AI/éå®å©æ.png' |
| | | import purchaseAssistantAvatar from '@/assets/AI/éè´å©æ.png' |
| | | import productionAssistantAvatar from '@/assets/AI/çäº§å©æ.png' |
| | | import financeAssistantAvatar from '@/assets/AI/è´¢å¡å©æ.png' |
| | | import bossAssistantAvatar from '@/assets/AI/èæ¿å©æ.png' |
| | | |
| | | const emit = defineEmits(['header-extra-action']) |
| | | |
| | | const props = defineProps({ |
| | | assistants: { |
| | |
| | | defaultAssistant: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | hideTrigger: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | autoOpen: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | drawerSize: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | drawerDirection: { |
| | | type: String, |
| | | default: 'rtl' |
| | | }, |
| | | headerExtraActionText: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }) |
| | | |
| | | const hideTrigger = computed(() => props.hideTrigger) |
| | | const headerExtraActionText = computed(() => String(props.headerExtraActionText || '').trim()) |
| | | const drawerDirection = computed(() => (props.drawerDirection === 'ttb' || props.drawerDirection === 'btt' || props.drawerDirection === 'ltr' || props.drawerDirection === 'rtl') |
| | | ? props.drawerDirection |
| | | : 'rtl') |
| | | const assistants = computed(() => props.assistants?.length ? props.assistants : builtInAssistants) |
| | | 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 assistantAvatarByKey = { |
| | | general: todoAssistantAvatar, |
| | | todo: todoAssistantAvatar, |
| | | purchase: purchaseAssistantAvatar, |
| | | sales: salesAssistantAvatar, |
| | | production: productionAssistantAvatar, |
| | | finance: financeAssistantAvatar, |
| | | boss: bossAssistantAvatar |
| | | } |
| | | const currentAssistantAvatar = computed(() => { |
| | | const assistant = currentAssistant.value || {} |
| | | return assistant.avatar || assistantAvatarByKey[assistant.key] || '' |
| | | }) |
| | | const showAssistantSwitch = computed(() => assistants.value.length > 1) |
| | | const quickPromptLimit = 3 |
| | | const quickPromptStart = ref(0) |
| | |
| | | |
| | | const visible = ref(false) |
| | | const windowWidth = ref(window.innerWidth) |
| | | const drawerSize = computed(() => { |
| | | const responsiveDrawerSize = computed(() => { |
| | | if (windowWidth.value < 768) return '100%' |
| | | if (windowWidth.value < 1200) return '50%' |
| | | return '50%' |
| | | }) |
| | | const computedDrawerSize = computed(() => props.drawerSize || responsiveDrawerSize.value) |
| | | const messageListRef = ref(null) |
| | | const isSending = ref(false) |
| | | const currentAbortController = ref(null) |
| | |
| | | } |
| | | |
| | | onMounted(() => { |
| | | initUUID() |
| | | // åå§æ¬¢è¿ |
| | | if (messages.value.length === 0) { |
| | | hello() |
| | | if (props.autoOpen) { |
| | | visible.value = true |
| | | } |
| | | initUUID() |
| | | window.addEventListener('resize', handleWindowResize) |
| | | }) |
| | | |
| | |
| | | inputMessage.value = '' |
| | | quickPromptStart.value = 0 |
| | | initUUID() |
| | | hello() |
| | | }) |
| | | |
| | | watch(() => props.defaultAssistant, (nextKey) => { |
| | | if (!nextKey || nextKey === selectedAssistantKey.value) return |
| | | if (!assistants.value.some(item => item.key === nextKey)) return |
| | | selectedAssistantKey.value = nextKey |
| | | }) |
| | | |
| | | watch(() => props.autoOpen, (nextValue) => { |
| | | if (nextValue) { |
| | | visible.value = true |
| | | } |
| | | }) |
| | | |
| | | const handleWindowResize = () => { |
| | | windowWidth.value = window.innerWidth |
| | | } |
| | | |
| | | const handleHeaderExtraAction = () => { |
| | | emit('header-extra-action') |
| | | } |
| | | |
| | | const toggleSidebar = () => { |
| | |
| | | } |
| | | |
| | | const handleClose = () => { |
| | | if (hideTrigger.value) return |
| | | visible.value = false |
| | | } |
| | | |
| | | const handleManualClose = () => { |
| | | if (hideTrigger.value) return |
| | | if (isSending.value) { |
| | | abortCurrentRequest() |
| | | } |
| | |
| | | uuid.value = storedUUID |
| | | } |
| | | |
| | | const hello = () => { |
| | | sendRequest(currentAssistant.value.welcomeMessage || 'ä½ å¥½') |
| | | } |
| | | |
| | | const newChat = () => { |
| | | revokeMessageLocalFileSnapshots(messages.value) |
| | | disposeCharts() |
| | |
| | | quickPromptStart.value = 0 |
| | | localStorage.removeItem(currentAssistant.value.storageKey) |
| | | initUUID() |
| | | hello() |
| | | } |
| | | |
| | | const handleNewChat = () => { |
| | |
| | | height: 100%; |
| | | } |
| | | :deep(.el-drawer__header) { |
| | | margin-bottom: 0; |
| | | padding: 0; |
| | | margin-bottom: 0 !important; |
| | | padding: 0 !important; |
| | | border-bottom: 1px solid rgba(255, 255, 255, 0.12); |
| | | background: $gradient-dark; |
| | | color: #fff; |
| | | } |
| | |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | width: 100%; |
| | | padding: 18px 20px; |
| | | padding: 12px 18px; |
| | | background: $gradient-dark; |
| | | position: relative; |
| | | overflow: hidden; |
| | |
| | | opacity: 1; |
| | | } |
| | | } |
| | | |
| | | :deep(.header-action-btn--text) { |
| | | width: auto !important; |
| | | min-width: 104px; |
| | | padding: 8px 14px !important; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | |
| | | .assistant-switcher { |
| | |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | height: 240px; |
| | | height: 128px; |
| | | background: linear-gradient(180deg, rgba(0, 85, 212, 0.06) 0%, transparent 100%); |
| | | pointer-events: none; |
| | | } |
| | |
| | | |
| | | .chat-hero { |
| | | display: grid; |
| | | grid-template-columns: 164px minmax(0, 1fr); |
| | | gap: 18px; |
| | | align-items: start; |
| | | padding: 14px 18px 6px; |
| | | grid-template-columns: 176px minmax(0, 1fr); |
| | | gap: 14px; |
| | | align-items: stretch; |
| | | padding: 8px 18px 4px; |
| | | |
| | | &.compact { |
| | | grid-template-columns: 122px minmax(0, 1fr); |
| | | gap: 12px; |
| | | padding: 8px 18px 2px; |
| | | grid-template-columns: 132px minmax(0, 1fr); |
| | | gap: 10px; |
| | | padding: 4px 18px 2px; |
| | | } |
| | | } |
| | | |
| | | .assistant-stand { |
| | | position: relative; |
| | | min-height: 252px; |
| | | min-height: 206px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-direction: column; |
| | | padding-top: 18px; |
| | | padding-top: 8px; |
| | | overflow: hidden; |
| | | |
| | | &.compact { |
| | | min-height: 176px; |
| | | padding-top: 8px; |
| | | min-height: 160px; |
| | | padding-top: 4px; |
| | | } |
| | | |
| | | &.thinking { |
| | | .assistant-halo { |
| | | opacity: 1; |
| | | transform: scale(1.08); |
| | | filter: blur(8px); |
| | | transform: scale(1.12); |
| | | filter: blur(9px); |
| | | } |
| | | |
| | | .assistant-scan-ring { |
| | | opacity: 1; |
| | | animation-duration: 1.6s; |
| | | opacity: 0.95; |
| | | animation-duration: 1.5s; |
| | | } |
| | | |
| | | .assistant-orbit { |
| | | opacity: 1; |
| | | opacity: 0.76; |
| | | } |
| | | |
| | | .assistant-bot { |
| | | transform: translateY(-4px) scale(1.02); |
| | | .assistant-model-shell { |
| | | transform: translateY(-5px) scale(1.02); |
| | | } |
| | | |
| | | .assistant-bot-head { |
| | | box-shadow: 0 0 30px rgba(80, 157, 255, 0.36); |
| | | .assistant-model-cut { |
| | | animation-duration: 2.2s; |
| | | } |
| | | |
| | | .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-model-img { |
| | | filter: saturate(1.06) drop-shadow(0 18px 20px rgba(22, 48, 80, 0.22)); |
| | | } |
| | | |
| | | .assistant-status { |
| | |
| | | animation: thinkingDot 1s ease-in-out infinite; |
| | | } |
| | | |
| | | .assistant-base-lg { |
| | | animation-duration: 1.8s; |
| | | } |
| | | |
| | | .assistant-base-md { |
| | | animation-duration: 1.5s; |
| | | } |
| | | |
| | | .assistant-base-sm { |
| | | box-shadow: 0 0 24px rgba(255, 93, 122, 0.48); |
| | | box-shadow: 0 0 24px rgba(30, 91, 255, 0.36); |
| | | animation-duration: 1.25s; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .assistant-halo { |
| | | position: absolute; |
| | | top: 22px; |
| | | width: 130px; |
| | | height: 130px; |
| | | top: 24px; |
| | | width: 146px; |
| | | height: 146px; |
| | | 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%); |
| | | background: radial-gradient(circle, rgba(31, 122, 114, 0.26) 0%, rgba(30, 91, 255, 0.2) 42%, rgba(109, 65, 237, 0.12) 66%, transparent 80%); |
| | | filter: blur(6px); |
| | | opacity: 0.82; |
| | | opacity: 0.78; |
| | | transition: all 0.35s ease; |
| | | } |
| | | |
| | | .assistant-scan-ring { |
| | | position: absolute; |
| | | top: 40px; |
| | | width: 132px; |
| | | height: 132px; |
| | | top: 44px; |
| | | width: 136px; |
| | | height: 136px; |
| | | border-radius: 50%; |
| | | border: 1px solid rgba(90, 159, 224, 0.22); |
| | | border: 1px solid rgba(67, 145, 223, 0.24); |
| | | box-shadow: inset 0 0 16px rgba(255, 255, 255, 0.25); |
| | | opacity: 0.55; |
| | | opacity: 0.52; |
| | | animation: scanRing 4s linear infinite; |
| | | } |
| | | |
| | | .assistant-orbit { |
| | | position: absolute; |
| | | top: 52px; |
| | | width: 150px; |
| | | height: 150px; |
| | | width: 156px; |
| | | height: 156px; |
| | | border-radius: 50%; |
| | | border: 1px dashed rgba(92, 135, 255, 0.22); |
| | | opacity: 0.45; |
| | | border: 1px dashed rgba(92, 135, 255, 0.24); |
| | | opacity: 0.42; |
| | | } |
| | | |
| | | .assistant-orbit-a { |
| | | animation: orbitRotate 8s linear infinite; |
| | | animation: orbitRotate 8.6s linear infinite; |
| | | } |
| | | |
| | | .assistant-orbit-b { |
| | | width: 118px; |
| | | height: 118px; |
| | | width: 124px; |
| | | height: 124px; |
| | | top: 68px; |
| | | border-color: rgba(255, 108, 150, 0.22); |
| | | animation: orbitRotateReverse 5.6s linear infinite; |
| | | border-color: rgba(31, 122, 114, 0.24); |
| | | animation: orbitRotateReverse 6.2s linear infinite; |
| | | } |
| | | |
| | | .assistant-bot { |
| | | .assistant-model-shell { |
| | | position: relative; |
| | | z-index: 1; |
| | | width: 148px; |
| | | height: 178px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | align-items: flex-end; |
| | | justify-content: center; |
| | | margin-top: 12px; |
| | | margin-top: 4px; |
| | | 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%; |
| | | bottom: 2px; |
| | | width: 164px; |
| | | height: 42px; |
| | | transform: translateX(-50%); |
| | | background: linear-gradient(135deg, #54bfff, #7a41ff); |
| | | box-shadow: 0 0 14px rgba(84, 191, 255, 0.65); |
| | | border-radius: 50%; |
| | | background: radial-gradient( |
| | | ellipse at center, |
| | | rgba(43, 126, 211, 0.32) 0%, |
| | | rgba(43, 126, 211, 0.14) 46%, |
| | | rgba(43, 126, 211, 0) 74% |
| | | ); |
| | | filter: blur(2.6px); |
| | | animation: baseGlow 4.6s ease-in-out infinite; |
| | | z-index: 1; |
| | | } |
| | | |
| | | &::after { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: 10px; |
| | | width: 138px; |
| | | height: 28px; |
| | | transform: translateX(-50%); |
| | | border-radius: 50%; |
| | | border: 1px solid rgba(36, 116, 198, 0.6); |
| | | box-shadow: |
| | | inset 0 0 0 1px rgba(255, 255, 255, 0.58), |
| | | 0 0 22px rgba(42, 116, 196, 0.24); |
| | | animation: basePulse 3.2s ease-in-out infinite; |
| | | z-index: 4; |
| | | } |
| | | } |
| | | |
| | | .assistant-bot-antenna-left { |
| | | left: 36px; |
| | | transform: rotate(-14deg); |
| | | } |
| | | |
| | | .assistant-bot-antenna-right { |
| | | right: 36px; |
| | | transform: rotate(14deg); |
| | | } |
| | | |
| | | .assistant-bot-head { |
| | | .assistant-model-cut { |
| | | position: relative; |
| | | width: 132px; |
| | | height: 178px; |
| | | z-index: 6; |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: center; |
| | | transform-origin: center 84%; |
| | | animation: avatarFloat 3.2s ease-in-out infinite; |
| | | } |
| | | |
| | | .assistant-model-img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; |
| | | object-position: center bottom; |
| | | display: block; |
| | | filter: saturate(1.03) drop-shadow(0 14px 18px rgba(22, 49, 79, 0.2)); |
| | | transition: filter 0.35s ease; |
| | | } |
| | | |
| | | .assistant-model-fallback { |
| | | 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); |
| | | height: 92px; |
| | | border-radius: 24px; |
| | | color: #fff; |
| | | background: linear-gradient(145deg, rgba(31, 122, 114, 0.9), rgba(30, 91, 255, 0.9)); |
| | | border: 1px solid rgba(255, 255, 255, 0.3); |
| | | box-shadow: 0 12px 24px rgba(31, 85, 173, 0.22); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .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 { |
| | | .assistant-base { |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: 16px; |
| | | width: 22px; |
| | | height: 4px; |
| | | bottom: 8px; |
| | | 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); |
| | | border: 1px solid rgba(36, 116, 198, 0.28); |
| | | background: radial-gradient( |
| | | ellipse at center, |
| | | rgba(255, 255, 255, 0.94) 0%, |
| | | rgba(81, 164, 233, 0.16) 58%, |
| | | rgba(30, 91, 255, 0.06) 100% |
| | | ); |
| | | box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); |
| | | } |
| | | |
| | | .assistant-status { |
| | | position: relative; |
| | | z-index: 1; |
| | | margin-top: 14px; |
| | | padding: 6px 12px; |
| | | margin-top: 7px; |
| | | padding: 5px 10px; |
| | | border-radius: 999px; |
| | | font-size: 12px; |
| | | font-size: 11px; |
| | | font-weight: 600; |
| | | color: $deep-blue; |
| | | background: rgba(255, 255, 255, 0.95); |
| | |
| | | } |
| | | |
| | | .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; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .assistant-base-md { |
| | | bottom: 6px; |
| | | width: 88px; |
| | | height: 20px; |
| | | border-color: rgba(255, 93, 122, 0.34); |
| | | bottom: 15px; |
| | | width: 104px; |
| | | height: 22px; |
| | | border-color: rgba(36, 116, 198, 0.48); |
| | | animation: basePulse 2.8s ease-in-out infinite; |
| | | } |
| | | |
| | | .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)); |
| | | bottom: 20px; |
| | | width: 68px; |
| | | height: 14px; |
| | | background: linear-gradient(90deg, rgba(31, 122, 114, 0.82), rgba(45, 124, 255, 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); |
| | | } |
| | | box-shadow: 0 0 18px rgba(45, 124, 255, 0.34); |
| | | animation: basePulse 2.2s ease-in-out infinite; |
| | | } |
| | | |
| | | @keyframes orbitRotate { |
| | |
| | | } |
| | | } |
| | | |
| | | .assistant-base-lg { |
| | | width: 142px; |
| | | height: 32px; |
| | | animation: basePulse 3.4s ease-in-out infinite; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | width: 130px; |
| | | height: 130px; |
| | | transform: translate(-50%, -50%); |
| | | border-radius: 50%; |
| | | background: conic-gradient( |
| | | from 180deg, |
| | | transparent 0deg, |
| | | rgba(36, 116, 198, 0.65) 48deg, |
| | | transparent 114deg, |
| | | rgba(36, 116, 198, 0.55) 212deg, |
| | | transparent 286deg, |
| | | rgba(31, 122, 114, 0.45) 334deg, |
| | | transparent 360deg |
| | | ); |
| | | -webkit-mask: radial-gradient(circle, transparent 61%, #000 62%, #000 68%, transparent 70%); |
| | | mask: radial-gradient(circle, transparent 61%, #000 62%, #000 68%, transparent 70%); |
| | | opacity: 0.62; |
| | | animation: baseSpin 9s linear infinite; |
| | | } |
| | | } |
| | | |
| | | @keyframes avatarFloat { |
| | | 0%, |
| | | 100% { |
| | | transform: translateY(0); |
| | | } |
| | | 50% { |
| | | transform: translateY(-7px); |
| | | } |
| | | } |
| | | |
| | | @keyframes basePulse { |
| | | 0%, |
| | | 100% { |
| | | transform: translateX(-50%) scale(1); |
| | | opacity: 0.88; |
| | | } |
| | | 50% { |
| | | transform: translateX(-50%) scale(1.05); |
| | | opacity: 0.98; |
| | | } |
| | | } |
| | | |
| | | @keyframes baseSpin { |
| | | from { |
| | | transform: translate(-50%, -50%) rotate(0deg); |
| | | } |
| | | to { |
| | | transform: translate(-50%, -50%) rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes baseGlow { |
| | | 0%, |
| | | 100% { |
| | | transform: translateX(-50%) scaleX(1); |
| | | opacity: 0.82; |
| | | } |
| | | 50% { |
| | | transform: translateX(-50%) scaleX(1.06); |
| | | opacity: 0.96; |
| | | } |
| | | } |
| | | |
| | | .welcome-card { |
| | | position: relative; |
| | | padding: 14px 14px 12px; |
| | | align-self: stretch; |
| | | min-height: 206px; |
| | | padding: 9px 10px 8px; |
| | | border-radius: 16px; |
| | | background: |
| | | linear-gradient(#fff, #fff) padding-box, |
| | |
| | | box-shadow: 0 16px 36px rgba(0, 85, 212, 0.12); |
| | | |
| | | &.compact { |
| | | padding: 10px 12px; |
| | | min-height: 160px; |
| | | padding: 8px 9px 7px; |
| | | border-radius: 12px; |
| | | box-shadow: 0 8px 16px rgba(0, 85, 212, 0.07); |
| | | |
| | |
| | | } |
| | | |
| | | .welcome-title { |
| | | font-size: 17px; |
| | | line-height: 1.3; |
| | | font-size: 16px; |
| | | line-height: 1.25; |
| | | |
| | | br { |
| | | display: none; |
| | |
| | | } |
| | | |
| | | .welcome-desc { |
| | | margin-top: 6px; |
| | | font-size: 12px; |
| | | line-height: 1.55; |
| | | margin-top: 4px; |
| | | font-size: 11px; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .quick-prompt-list { |
| | | margin-top: 10px; |
| | | gap: 6px; |
| | | margin-top: 8px; |
| | | gap: 5px; |
| | | } |
| | | |
| | | .quick-prompt-btn { |
| | | padding: 8px 10px; |
| | | font-size: 12px; |
| | | padding: 7px 9px; |
| | | font-size: 11px; |
| | | border-radius: 7px; |
| | | } |
| | | |
| | | .more-prompts-btn { |
| | | margin-top: 8px; |
| | | font-size: 12px; |
| | | margin-top: 6px; |
| | | font-size: 11px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .welcome-eyebrow { |
| | | font-size: 11px; |
| | | font-size: 10px; |
| | | font-weight: 700; |
| | | letter-spacing: 2px; |
| | | color: rgba(0, 85, 212, 0.58); |
| | | margin-bottom: 8px; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .welcome-title { |
| | | margin: 0; |
| | | font-size: 26px; |
| | | line-height: 1.2; |
| | | font-size: 20px; |
| | | line-height: 1.15; |
| | | font-weight: 800; |
| | | color: #172033; |
| | | |
| | | br { |
| | | display: none; |
| | | } |
| | | } |
| | | |
| | | .welcome-desc { |
| | | margin: 10px 0 0; |
| | | font-size: 13px; |
| | | line-height: 1.7; |
| | | margin: 5px 0 0; |
| | | font-size: 12px; |
| | | line-height: 1.5; |
| | | color: #5f6980; |
| | | } |
| | | |
| | | .quick-prompt-list { |
| | | display: grid; |
| | | gap: 8px; |
| | | margin-top: 14px; |
| | | gap: 6px; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .quick-prompt-btn { |
| | | width: 100%; |
| | | border: none; |
| | | border-radius: 10px; |
| | | padding: 11px 14px; |
| | | border-radius: 9px; |
| | | padding: 7px 10px; |
| | | text-align: left; |
| | | font-size: 13px; |
| | | font-size: 12px; |
| | | font-weight: 600; |
| | | color: #fff; |
| | | cursor: pointer; |
| | |
| | | } |
| | | |
| | | .more-prompts-btn { |
| | | margin-top: 10px; |
| | | padding: 0 12px; |
| | | height: 32px; |
| | | margin-top: 6px; |
| | | padding: 0 10px; |
| | | height: 26px; |
| | | 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-size: 12px; |
| | | font-weight: 600; |
| | | cursor: pointer; |
| | | display: inline-flex; |
| | |
| | | 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)); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | .welcome-title { |
| | | font-size: 21px; |
| | | } |
| | | |
| | | .hero-dot-grid { |
| | | grid-template-columns: repeat(12, 1fr); |
| | | gap: 6px; |
| | | padding: 0 14px 12px; |
| | | } |
| | | |
| | | .message-list { |
| | |
| | | <app-main /> |
| | | <settings ref="settingRef" /> |
| | | </div> |
| | | <AIChatSidebar v-if="aiEnabled" /> |
| | | <AIChatSidebar v-if="showGlobalAiChat" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { useWindowSize } from "@vueuse/core"; |
| | | import { useRoute } from "vue-router"; |
| | | import Sidebar from "./components/Sidebar/index.vue"; |
| | | import { AppMain, Navbar, Settings, TagsView } from "./components"; |
| | | import AIChatSidebar from "@/components/AIChatSidebar/index.vue"; |
| | |
| | | |
| | | const settingsStore = useSettingsStore(); |
| | | const userStore = useUserStore(); |
| | | const route = useRoute(); |
| | | const theme = computed(() => settingsStore.theme); |
| | | const sideTheme = computed(() => settingsStore.sideTheme); |
| | | const sidebar = computed(() => useAppStore().sidebar); |
| | |
| | | const needTagsView = computed(() => settingsStore.tagsView); |
| | | const fixedHeader = computed(() => settingsStore.fixedHeader); |
| | | const aiEnabled = computed(() => Number(userStore.aiEnabled) === 1); |
| | | const showGlobalAiChat = computed(() => { |
| | | const isIndustrialBrainRoute = String(route.path || "").startsWith("/ai-industrial-brain"); |
| | | return !isIndustrialBrainRoute && aiEnabled.value; |
| | | }); |
| | | |
| | | const classObj = computed(() => ({ |
| | | hideSidebar: !sidebar.value.opened, |
| | |
| | | ], |
| | | }, |
| | | { |
| | | path: "/ai-industrial-brain", |
| | | component: Layout, |
| | | children: [ |
| | | { |
| | | path: "index", |
| | | component: () => import("@/views/aiIndustrialBrain/index.vue"), |
| | | name: "AiIndustrialBrain", |
| | | meta: { title: "AIå·¥ä¸å¤§è", icon: "skill" }, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | path: "/user", |
| | | component: Layout, |
| | | hidden: true, |
| | |
| | | const defaultData = JSON.parse(JSON.stringify(rawRoutes)) |
| | | const sidebarRoutes = filterAsyncRouter(sdata)
|
| | | const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
| | | const defaultRoutes = filterAsyncRouter(defaultData)
|
| | | const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
|
| | | asyncRoutes.forEach(route => { router.addRoute(route) })
|
| | | this.setRoutes(rewriteRoutes)
|
| | | // å°è´¢å¡ç®¡çè·¯ç±åå¹¶å°ä¾§è¾¹æ
|
| | | this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
|
| | | this.setDefaultRoutes(sidebarRoutes)
|
| | | this.setTopbarRoutes(defaultRoutes)
|
| | | resolve(rewriteRoutes)
|
| | | })
|
| | | })
|
| | | const defaultRoutes = filterAsyncRouter(defaultData) |
| | | const asyncRoutes = filterDynamicRoutes(dynamicRoutes) |
| | | asyncRoutes.forEach(route => { router.addRoute(route) }) |
| | | this.setRoutes(rewriteRoutes) |
| | | const constantSidebarRoutes = filterAiFeatureRoutes(constantRoutes, aiEnabled) |
| | | // å°è´¢å¡ç®¡çè·¯ç±åå¹¶å°ä¾§è¾¹æ |
| | | this.setSidebarRouters(constantSidebarRoutes.concat(sidebarRoutes)) |
| | | this.setDefaultRoutes(sidebarRoutes) |
| | | this.setTopbarRoutes(defaultRoutes) |
| | | resolve(rewriteRoutes) |
| | | }) |
| | | }) |
| | | }
|
| | | }
|
| | | })
|
| | |
| | | })
|
| | | }
|
| | |
|
| | | function filterChildren(childrenMap, lastRouter = false) {
|
| | | function filterChildren(childrenMap, lastRouter = false) { |
| | | var children = []
|
| | | childrenMap.forEach(el => {
|
| | | el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path
|
| | |
| | | children.push(el)
|
| | | }
|
| | | })
|
| | | return children
|
| | | }
|
| | |
|
| | | // å¨æè·¯ç±éåï¼éªè¯æ¯å¦å
·å¤æé
|
| | | export function filterDynamicRoutes(routes) {
|
| | | return children |
| | | } |
| | | |
| | | // å¨æè·¯ç±éåï¼éªè¯æ¯å¦å
·å¤æé |
| | | export function filterDynamicRoutes(routes) { |
| | | const res = []
|
| | | routes.forEach(route => {
|
| | | if (route.permissions) {
|
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <transition name="fade"> |
| | | <section v-if="visible" class="assistant-workspace"> |
| | | <div class="assistant-workspace__panel"> |
| | | <button |
| | | v-if="assistantMode === 'pending'" |
| | | type="button" |
| | | class="workspace-back-btn" |
| | | @click="$emit('close')" |
| | | > |
| | | <el-icon><ArrowLeftBold /></el-icon> |
| | | <span>è¿åå·¥ä¸å¤§å±</span> |
| | | </button> |
| | | |
| | | <div class="assistant-workspace__body"> |
| | | <AIChatSidebar |
| | | v-if="assistantMode !== 'pending'" |
| | | :key="assistantMode" |
| | | class="workspace-chat" |
| | | :assistants="assistantMode === 'purchase' ? [purchaseAssistant] : [generalAssistant]" |
| | | :default-assistant="assistantMode" |
| | | :hide-trigger="true" |
| | | :auto-open="true" |
| | | drawer-size="100%" |
| | | drawer-direction="ttb" |
| | | header-extra-action-text="è¿åå·¥ä¸å¤§å±" |
| | | @header-extra-action="$emit('close')" |
| | | /> |
| | | |
| | | <div v-else class="workspace-pending"> |
| | | <div class="workspace-pending__content"> |
| | | <h3>{{ agentTitle }}</h3> |
| | | <p>æ£å¨å¼åï¼æ¬è¯·æå¾
......</p> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | </transition> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | import { ArrowLeftBold } from "@element-plus/icons-vue"; |
| | | import AIChatSidebar from "@/components/AIChatSidebar/index.vue"; |
| | | import { generalAssistant, purchaseAssistant } from "@/components/AIChatSidebar/assistants"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | agent: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | defineEmits(["close"]); |
| | | |
| | | const agentKey = computed(() => String(props.agent?.key || "")); |
| | | const agentTitle = computed(() => String(props.agent?.name || "AI婿")); |
| | | const assistantMode = computed(() => { |
| | | if (agentKey.value === "purchase") return "purchase"; |
| | | if (agentKey.value === "general") return "general"; |
| | | return "pending"; |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .assistant-workspace { |
| | | position: fixed; |
| | | inset: 0; |
| | | z-index: 2100; |
| | | padding: 12px; |
| | | background: rgba(33, 49, 63, 0.24); |
| | | backdrop-filter: blur(2px); |
| | | } |
| | | |
| | | .assistant-workspace__panel { |
| | | position: relative; |
| | | height: 100%; |
| | | border-radius: 22px; |
| | | border: 1px solid var(--surface-border); |
| | | background: linear-gradient(180deg, #f9fcfb 0%, #f0f5f2 100%); |
| | | box-shadow: var(--shadow-md); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .assistant-workspace__body { |
| | | height: 100%; |
| | | min-height: 100%; |
| | | } |
| | | |
| | | .workspace-back-btn { |
| | | position: absolute; |
| | | top: 16px; |
| | | right: 20px; |
| | | z-index: 5; |
| | | height: 36px; |
| | | padding: 0 14px; |
| | | border: 1px solid rgba(38, 112, 183, 0.3); |
| | | border-radius: 10px; |
| | | background: rgba(255, 255, 255, 0.92); |
| | | color: #25528f; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | cursor: pointer; |
| | | transition: all 0.2s ease; |
| | | } |
| | | |
| | | .workspace-back-btn:hover { |
| | | border-color: rgba(31, 122, 114, 0.45); |
| | | color: #1f5ddf; |
| | | box-shadow: 0 8px 16px rgba(31, 122, 114, 0.14); |
| | | transform: translateY(-1px); |
| | | } |
| | | |
| | | .workspace-chat { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .workspace-chat :deep(.ai-chat-sidebar-wrapper) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .workspace-chat :deep(.ai-chat-drawer) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .workspace-chat :deep(.el-drawer) { |
| | | height: 100% !important; |
| | | width: 100% !important; |
| | | } |
| | | |
| | | .workspace-pending { |
| | | height: 100%; |
| | | display: grid; |
| | | place-items: center; |
| | | padding: 20px; |
| | | color: var(--text-secondary); |
| | | } |
| | | |
| | | .workspace-pending__content { |
| | | display: grid; |
| | | gap: 12px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .workspace-pending__content h3 { |
| | | margin: 0; |
| | | font-size: 36px; |
| | | color: var(--text-primary); |
| | | } |
| | | |
| | | .workspace-pending__content p { |
| | | margin: 0; |
| | | font-size: 24px; |
| | | } |
| | | |
| | | .fade-enter-active, |
| | | .fade-leave-active { |
| | | transition: opacity 0.2s ease; |
| | | } |
| | | |
| | | .fade-enter-from, |
| | | .fade-leave-to { |
| | | opacity: 0; |
| | | } |
| | | |
| | | @media (max-width: 1600px) { |
| | | .workspace-back-btn { |
| | | top: 12px; |
| | | right: 14px; |
| | | height: 32px; |
| | | padding: 0 12px; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .workspace-pending__content h3 { |
| | | font-size: 30px; |
| | | } |
| | | |
| | | .workspace-pending__content p { |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div ref="screenRef" class="ai-brain-screen"> |
| | | <section class="brain-stage"> |
| | | <header class="brain-head"> |
| | | <div class="head-date"> |
| | | <p>{{ weekLabel }}</p> |
| | | <p>{{ dateLabel }}</p> |
| | | </div> |
| | | |
| | | <div class="head-title"> |
| | | <span>AIå·¥ä¸å¤§è</span> |
| | | </div> |
| | | |
| | | <div class="head-actions"> |
| | | <button type="button" class="head-back-btn" @click="goBack"> |
| | | <el-icon><ArrowLeftBold /></el-icon> |
| | | <span>è¿å</span> |
| | | </button> |
| | | </div> |
| | | </header> |
| | | |
| | | <section class="brain-intro"> |
| | | <h2>å·¥ä¸AIæ°ååå·¥ï¼èµè½æºé æ°çºªå
</h2> |
| | | <p>å
大AI婿ååä¼ä¸ç®¡çãéå®ãéè´ãç产ãè´¢å¡åæ°æ®å
¨é¾è·¯</p> |
| | | <div class="intro-sign">é¿éäº Ã åé®å¤§æ¨¡å à æºè½ä½AI</div> |
| | | </section> |
| | | |
| | | <section class="carousel-area"> |
| | | <button type="button" class="nav-btn nav-btn--left" @click="prevCard"> |
| | | <el-icon><ArrowLeftBold /></el-icon> |
| | | </button> |
| | | |
| | | <div class="carousel-track"> |
| | | <article |
| | | v-for="card in visibleCards" |
| | | :key="card.agent.key" |
| | | class="agent-card" |
| | | :class="{ 'agent-card--active': card.offset === 0 }" |
| | | :style="getCardStyle(card.offset)" |
| | | @click="openAssistant(card.realIndex)" |
| | | > |
| | | <div class="agent-card__head" :class="{ 'agent-card__head--active': card.offset === 0 }"> |
| | | {{ card.agent.name }} |
| | | </div> |
| | | |
| | | <div class="agent-card__body" :class="{ 'agent-card__body--active': card.offset === 0 }"> |
| | | <div class="avatar-shell" :class="{ 'avatar-shell--active': card.offset === 0 }"> |
| | | <div class="avatar-base"></div> |
| | | <div class="avatar-cut"> |
| | | <img v-if="card.agent.avatar" class="avatar-cut__img" :src="card.agent.avatar" :alt="card.agent.name" /> |
| | | </div> |
| | | </div> |
| | | <div v-if="card.offset === 0" class="highlight-list"> |
| | | <div |
| | | v-for="highlight in card.agent.highlights" |
| | | :key="highlight" |
| | | class="highlight-item" |
| | | > |
| | | {{ highlight }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </article> |
| | | </div> |
| | | |
| | | <button type="button" class="nav-btn nav-btn--right" @click="nextCard"> |
| | | <el-icon><ArrowRightBold /></el-icon> |
| | | </button> |
| | | </section> |
| | | |
| | | <section class="brain-footer"> |
| | | <div class="footer-grid-overlay"></div> |
| | | |
| | | <div class="footer-metrics"> |
| | | <article class="footer-metric"> |
| | | <span class="footer-metric__label">å¨çº¿æºè½ä½</span> |
| | | <strong class="footer-metric__value">{{ agents.length }}个</strong> |
| | | <small class="footer-metric__hint">å
¨é¾è·¯ååè¿è¡</small> |
| | | </article> |
| | | |
| | | <article class="footer-metric footer-metric--focus"> |
| | | <span class="footer-metric__label">å½åç¦ç¹</span> |
| | | <strong class="footer-metric__value">{{ getFooterAgentName(focusAgent.name) }}</strong> |
| | | <small class="footer-metric__hint">{{ focusAgent.highlights?.[0] || "æºè½åæèå¨" }}</small> |
| | | </article> |
| | | |
| | | <article class="footer-metric footer-metric--period"> |
| | | <span class="footer-metric__label">è½®æå¨æ</span> |
| | | <strong class="footer-metric__value">{{ carouselSecondsText }}</strong> |
| | | <div class="footer-period-control"> |
| | | <button type="button" class="period-btn" @click="adjustCarouselSeconds(-0.5)">-</button> |
| | | <input |
| | | v-model.number="carouselSeconds" |
| | | class="period-input" |
| | | type="number" |
| | | min="2" |
| | | max="12" |
| | | step="0.5" |
| | | /> |
| | | <span class="period-unit">s</span> |
| | | <button type="button" class="period-btn" @click="adjustCarouselSeconds(0.5)">+</button> |
| | | </div> |
| | | <input |
| | | v-model.number="carouselSeconds" |
| | | class="footer-period-slider" |
| | | type="range" |
| | | min="2" |
| | | max="12" |
| | | step="0.5" |
| | | /> |
| | | <small class="footer-metric__hint">坿å¨è®¾ç½® 2.0s - 12.0s</small> |
| | | </article> |
| | | </div> |
| | | |
| | | <div class="footer-rail"> |
| | | <div class="footer-rail__line"> |
| | | <span class="footer-rail__flow"></span> |
| | | </div> |
| | | <div class="footer-rail__nodes"> |
| | | <button |
| | | v-for="node in footerNodes" |
| | | :key="node.key" |
| | | type="button" |
| | | class="footer-node" |
| | | :class="{ 'footer-node--active': node.index === carouselIndex }" |
| | | @click="openAssistant(node.index)" |
| | | > |
| | | <span class="footer-node__dot"></span> |
| | | <span class="footer-node__name">{{ getFooterAgentName(node.name) }}</span> |
| | | </button> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | </section> |
| | | |
| | | <AiAssistantWorkspace |
| | | :visible="fullscreenVisible" |
| | | :agent="currentAgent" |
| | | @close="closeFullscreen" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import { ArrowLeftBold, ArrowRightBold } from "@element-plus/icons-vue"; |
| | | import AiAssistantWorkspace from "./components/AiAssistantWorkspace.vue"; |
| | | import todoAvatar from "@/assets/AI/å¾
å婿.png"; |
| | | import salesAvatar from "@/assets/AI/éå®å©æ.png"; |
| | | import purchaseAvatar from "@/assets/AI/éè´å©æ.png"; |
| | | import productionAvatar from "@/assets/AI/çäº§å©æ.png"; |
| | | import financeAvatar from "@/assets/AI/è´¢å¡å©æ.png"; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const agents = [ |
| | | { |
| | | key: "general", |
| | | name: "AIå¾
å婿", |
| | | highlights: ["è·¨æ¨¡åæµç¨è¯æ", "ç»è¥é£é©æºè½æé"], |
| | | }, |
| | | { |
| | | key: "sales", |
| | | name: "AIéå®å©æ", |
| | | highlights: ["å®¢æ·æµå¤±é£é©åæ", "忬¾ä¸æ¥ä»·çç¥å»ºè®®"], |
| | | }, |
| | | { |
| | | key: "purchase", |
| | | name: "AIéè´å©æ", |
| | | highlights: ["ä¾åºé¾ææ åæ", "éè´è®¢åæºè½çæ"], |
| | | }, |
| | | { |
| | | key: "production", |
| | | name: "AIçäº§å©æ", |
| | | highlights: ["å·¥åºç¶é¢å®ä½", "产è½ä¸æ¥åºæºè½é¢è¦"], |
| | | }, |
| | | { |
| | | key: "finance", |
| | | name: "AIè´¢å¡å©æ", |
| | | highlights: ["ç°éæµååé¢å¤", "è´¹ç¨ç»ææºè½åæ"], |
| | | }, |
| | | ]; |
| | | |
| | | const avatarByAgentKey = { |
| | | general: todoAvatar, |
| | | sales: salesAvatar, |
| | | purchase: purchaseAvatar, |
| | | production: productionAvatar, |
| | | finance: financeAvatar, |
| | | }; |
| | | |
| | | for (let i = agents.length - 1; i >= 0; i -= 1) { |
| | | const agent = agents[i]; |
| | | const avatar = avatarByAgentKey[agent.key]; |
| | | if (!avatar) { |
| | | agents.splice(i, 1); |
| | | continue; |
| | | } |
| | | agent.avatar = avatar; |
| | | } |
| | | |
| | | const carouselIndex = ref(Math.min(2, Math.max(agents.length - 1, 0))); |
| | | const fullscreenVisible = ref(false); |
| | | const screenRef = ref(null); |
| | | const carouselIntervalMs = ref(4500); |
| | | |
| | | let carouselTimer = null; |
| | | |
| | | const fallbackAgent = { |
| | | key: "fallback", |
| | | name: "AI婿", |
| | | avatar: "", |
| | | highlights: [], |
| | | }; |
| | | |
| | | const currentAgent = computed(() => agents[carouselIndex.value] || agents[0] || fallbackAgent); |
| | | const focusAgent = computed(() => currentAgent.value || fallbackAgent); |
| | | const footerNodes = computed(() => |
| | | agents.map((agent, index) => ({ |
| | | key: agent.key, |
| | | name: agent.name, |
| | | index, |
| | | })) |
| | | ); |
| | | const carouselSeconds = computed({ |
| | | get: () => Number((carouselIntervalMs.value / 1000).toFixed(1)), |
| | | set: (value) => { |
| | | const next = Number(value); |
| | | if (!Number.isFinite(next)) return; |
| | | const clamped = Math.max(2, Math.min(12, Math.round(next * 2) / 2)); |
| | | carouselIntervalMs.value = Math.round(clamped * 1000); |
| | | }, |
| | | }); |
| | | const carouselSecondsText = computed(() => `${carouselSeconds.value.toFixed(1)}s`); |
| | | |
| | | const weekLabel = computed(() => { |
| | | const weekMap = ["æææ¥", "ææä¸", "ææäº", "ææä¸", "ææå", "ææäº", "ææå
"]; |
| | | return weekMap[new Date().getDay()]; |
| | | }); |
| | | |
| | | const dateLabel = computed(() => { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | return `${year}å¹´${month}æ${day}æ¥`; |
| | | }); |
| | | |
| | | const visibleCards = computed(() => { |
| | | const total = agents.length; |
| | | return agents |
| | | .map((agent, index) => { |
| | | let offset = index - carouselIndex.value; |
| | | if (offset > total / 2) offset -= total; |
| | | if (offset < -total / 2) offset += total; |
| | | return { agent, offset, realIndex: index }; |
| | | }) |
| | | .filter((item) => Math.abs(item.offset) <= 2) |
| | | .sort((a, b) => a.offset - b.offset); |
| | | }); |
| | | |
| | | function getCardStyle(offset) { |
| | | const distance = Math.abs(offset); |
| | | const scale = distance === 0 ? 1 : distance === 1 ? 0.88 : 0.78; |
| | | const opacity = distance === 0 ? 1 : distance === 1 ? 0.92 : 0.76; |
| | | return { |
| | | transform: `translateX(${offset * 340}px) scale(${scale})`, |
| | | zIndex: String(50 - distance), |
| | | opacity, |
| | | }; |
| | | } |
| | | |
| | | function getFooterAgentName(name) { |
| | | return String(name || "AI婿").replace(/^AI/, ""); |
| | | } |
| | | |
| | | function adjustCarouselSeconds(delta) { |
| | | carouselSeconds.value = carouselSeconds.value + delta; |
| | | } |
| | | |
| | | function prevCard() { |
| | | const total = agents.length; |
| | | if (!total) return; |
| | | carouselIndex.value = (carouselIndex.value - 1 + total) % total; |
| | | } |
| | | |
| | | function nextCard() { |
| | | const total = agents.length; |
| | | if (!total) return; |
| | | carouselIndex.value = (carouselIndex.value + 1) % total; |
| | | } |
| | | |
| | | async function enterBrowserFullscreen() { |
| | | if (document.fullscreenElement) return; |
| | | const target = screenRef.value || document.documentElement; |
| | | if (!target || typeof target.requestFullscreen !== "function") return; |
| | | try { |
| | | await target.requestFullscreen(); |
| | | } catch (error) { |
| | | // Ignore: browser may block fullscreen when there is no direct user activation. |
| | | } |
| | | } |
| | | |
| | | async function exitBrowserFullscreen() { |
| | | if (!document.fullscreenElement || typeof document.exitFullscreen !== "function") return; |
| | | try { |
| | | await document.exitFullscreen(); |
| | | } catch (error) { |
| | | // Ignore fullscreen exit failures. |
| | | } |
| | | } |
| | | |
| | | function goBack() { |
| | | closeFullscreen(); |
| | | exitBrowserFullscreen(); |
| | | if (window.history.length > 1) { |
| | | router.back(); |
| | | return; |
| | | } |
| | | router.push("/index"); |
| | | } |
| | | |
| | | function openAssistant(index) { |
| | | if (!agents.length) return; |
| | | carouselIndex.value = index; |
| | | fullscreenVisible.value = true; |
| | | } |
| | | |
| | | function closeFullscreen() { |
| | | fullscreenVisible.value = false; |
| | | } |
| | | |
| | | function startCarousel() { |
| | | stopCarousel(); |
| | | if (fullscreenVisible.value) return; |
| | | carouselTimer = window.setInterval(() => { |
| | | nextCard(); |
| | | }, carouselIntervalMs.value); |
| | | } |
| | | |
| | | function stopCarousel() { |
| | | if (carouselTimer) { |
| | | window.clearInterval(carouselTimer); |
| | | carouselTimer = null; |
| | | } |
| | | } |
| | | |
| | | function handleEscClose(event) { |
| | | if (event.key === "Escape" && fullscreenVisible.value) { |
| | | closeFullscreen(); |
| | | } |
| | | } |
| | | |
| | | watch( |
| | | () => fullscreenVisible.value, |
| | | (opened) => { |
| | | if (opened) { |
| | | stopCarousel(); |
| | | } else { |
| | | startCarousel(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => carouselIntervalMs.value, |
| | | () => { |
| | | if (!fullscreenVisible.value) { |
| | | startCarousel(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | onMounted(() => { |
| | | startCarousel(); |
| | | window.addEventListener("keydown", handleEscClose); |
| | | window.requestAnimationFrame(() => { |
| | | enterBrowserFullscreen(); |
| | | }); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | stopCarousel(); |
| | | window.removeEventListener("keydown", handleEscClose); |
| | | exitBrowserFullscreen(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .ai-brain-screen { |
| | | position: fixed; |
| | | inset: 0; |
| | | z-index: 1900; |
| | | padding: 10px; |
| | | overflow: hidden; |
| | | background: var(--app-bg); |
| | | } |
| | | |
| | | .brain-stage { |
| | | position: relative; |
| | | height: 100%; |
| | | min-height: 100%; |
| | | border-radius: 22px; |
| | | border: 1px solid var(--surface-border); |
| | | background: |
| | | radial-gradient(circle at 14% 8%, rgba(31, 122, 114, 0.14), transparent 40%), |
| | | radial-gradient(circle at 86% 12%, rgba(30, 91, 255, 0.1), transparent 42%), |
| | | linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(245, 249, 247, 0.94)), |
| | | repeating-linear-gradient( |
| | | 135deg, |
| | | rgba(255, 255, 255, 0.05) 0, |
| | | rgba(255, 255, 255, 0.05) 14px, |
| | | rgba(31, 122, 114, 0.03) 14px, |
| | | rgba(31, 122, 114, 0.03) 28px |
| | | ); |
| | | box-shadow: var(--shadow-sm); |
| | | } |
| | | |
| | | .brain-head { |
| | | display: grid; |
| | | grid-template-columns: 220px minmax(0, 1fr) 180px; |
| | | align-items: center; |
| | | padding: 12px 18px 0; |
| | | } |
| | | |
| | | .head-date { |
| | | color: var(--text-secondary); |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .head-date p { |
| | | margin: 0; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .head-title { |
| | | justify-self: center; |
| | | width: min(760px, 95%); |
| | | height: 68px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-radius: 0 0 46px 46px; |
| | | color: #fff; |
| | | font-size: 42px; |
| | | font-style: italic; |
| | | font-weight: 700; |
| | | letter-spacing: 1px; |
| | | background: linear-gradient(135deg, #1f7a72 0%, #1e5bff 100%); |
| | | box-shadow: 0 16px 30px rgba(31, 122, 114, 0.24); |
| | | } |
| | | |
| | | .head-actions { |
| | | justify-self: end; |
| | | } |
| | | |
| | | .head-back-btn { |
| | | height: 40px; |
| | | padding: 0 14px; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | border: none; |
| | | border-radius: 999px; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: var(--colorPrimary); |
| | | background: var(--surface-base); |
| | | box-shadow: 0 8px 18px rgba(31, 49, 38, 0.12); |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .brain-intro { |
| | | text-align: center; |
| | | margin-top: 34px; |
| | | } |
| | | |
| | | .brain-intro h2 { |
| | | margin: 0; |
| | | font-size: 44px; |
| | | font-style: italic; |
| | | font-weight: 700; |
| | | color: var(--text-primary); |
| | | } |
| | | |
| | | .brain-intro p { |
| | | margin: 12px 0 10px; |
| | | font-size: 28px; |
| | | color: var(--text-secondary); |
| | | } |
| | | |
| | | .intro-sign { |
| | | display: inline-block; |
| | | padding: 6px 18px; |
| | | border-radius: 999px; |
| | | font-size: 24px; |
| | | font-weight: 700; |
| | | color: #1e5bff; |
| | | background: rgba(255, 255, 255, 0.82); |
| | | border: 1px solid rgba(30, 91, 255, 0.18); |
| | | } |
| | | |
| | | .carousel-area { |
| | | position: relative; |
| | | margin-top: 34px; |
| | | padding: 0 72px 12px; |
| | | } |
| | | |
| | | .carousel-track { |
| | | position: relative; |
| | | height: 500px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .brain-footer { |
| | | position: relative; |
| | | margin: 0 72px; |
| | | height: clamp(226px, 25vh,0); |
| | | border-radius: 18px; |
| | | border: 1px solid rgba(31, 122, 114, 0.28); |
| | | background: |
| | | linear-gradient(120deg, rgba(31, 122, 114, 0.14), rgba(30, 91, 255, 0.14)), |
| | | linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(236, 244, 249, 0.9)); |
| | | box-shadow: |
| | | 0 16px 34px rgba(31, 81, 131, 0.12), |
| | | inset 0 1px 0 rgba(255, 255, 255, 0.72); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .brain-footer::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: -22%; |
| | | bottom: -120%; |
| | | width: 52%; |
| | | height: 260%; |
| | | background: radial-gradient(ellipse at center, rgba(30, 91, 255, 0.2) 0%, rgba(30, 91, 255, 0) 72%); |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .brain-footer::after { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | background: linear-gradient(110deg, transparent 12%, rgba(255, 255, 255, 0.24) 38%, transparent 64%); |
| | | transform: translateX(-120%); |
| | | animation: footerSweep 5.8s linear infinite; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .footer-grid-overlay { |
| | | position: absolute; |
| | | inset: 0; |
| | | background: |
| | | repeating-linear-gradient( |
| | | 90deg, |
| | | rgba(31, 122, 114, 0.07) 0, |
| | | rgba(31, 122, 114, 0.07) 1px, |
| | | transparent 1px, |
| | | transparent 36px |
| | | ), |
| | | repeating-linear-gradient( |
| | | 0deg, |
| | | rgba(30, 91, 255, 0.06) 0, |
| | | rgba(30, 91, 255, 0.06) 1px, |
| | | transparent 1px, |
| | | transparent 28px |
| | | ); |
| | | opacity: 0.72; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .footer-metrics { |
| | | position: relative; |
| | | z-index: 2; |
| | | padding: 14px 20px 72px; |
| | | display: grid; |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | gap: 12px; |
| | | } |
| | | |
| | | .footer-metric { |
| | | min-height: 76px; |
| | | border-radius: 12px; |
| | | padding: 10px 14px; |
| | | border: 1px solid rgba(37, 124, 188, 0.2); |
| | | background: linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(245, 250, 255, 0.82)); |
| | | box-shadow: 0 10px 18px rgba(29, 83, 134, 0.08); |
| | | display: grid; |
| | | grid-template-rows: auto auto 1fr; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .footer-metric--focus { |
| | | border-color: rgba(38, 122, 194, 0.34); |
| | | box-shadow: |
| | | 0 12px 22px rgba(30, 91, 255, 0.12), |
| | | inset 0 0 0 1px rgba(85, 148, 232, 0.2); |
| | | } |
| | | |
| | | .footer-metric__label { |
| | | font-size: 14px; |
| | | color: rgba(38, 72, 108, 0.88); |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .footer-metric__value { |
| | | font-size: 30px; |
| | | line-height: 1; |
| | | font-style: italic; |
| | | font-weight: 700; |
| | | color: #1f5ddf; |
| | | text-shadow: 0 3px 10px rgba(30, 91, 255, 0.18); |
| | | } |
| | | |
| | | .footer-metric__hint { |
| | | margin-top: auto; |
| | | font-size: 13px; |
| | | color: rgba(52, 89, 128, 0.82); |
| | | } |
| | | |
| | | .footer-metric--period .footer-metric__hint { |
| | | margin-top: 0; |
| | | line-height: 1.25; |
| | | } |
| | | |
| | | .footer-metric--period { |
| | | min-height: 122px; |
| | | grid-template-rows: auto auto auto auto auto; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .footer-period-control { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .period-btn { |
| | | width: 22px; |
| | | height: 22px; |
| | | border-radius: 50%; |
| | | border: 1px solid rgba(38, 112, 183, 0.28); |
| | | background: rgba(255, 255, 255, 0.9); |
| | | color: #2054c9; |
| | | font-size: 14px; |
| | | font-weight: 700; |
| | | line-height: 1; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .period-input { |
| | | width: 56px; |
| | | height: 24px; |
| | | border-radius: 8px; |
| | | border: 1px solid rgba(38, 112, 183, 0.24); |
| | | background: rgba(255, 255, 255, 0.94); |
| | | color: #1f5ddf; |
| | | font-size: 13px; |
| | | font-weight: 600; |
| | | text-align: center; |
| | | padding: 0 4px; |
| | | } |
| | | |
| | | .period-unit { |
| | | font-size: 12px; |
| | | font-weight: 600; |
| | | color: rgba(40, 80, 117, 0.86); |
| | | } |
| | | |
| | | .footer-period-slider { |
| | | width: min(250px, 100%); |
| | | height: 3px; |
| | | accent-color: #2a6ded; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .footer-rail { |
| | | position: absolute; |
| | | left: 20px; |
| | | right: 20px; |
| | | bottom: 18px; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .footer-rail__line { |
| | | position: relative; |
| | | height: 2px; |
| | | border-radius: 999px; |
| | | background: linear-gradient(90deg, rgba(31, 122, 114, 0.12), rgba(30, 91, 255, 0.6), rgba(31, 122, 114, 0.12)); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .footer-rail__flow { |
| | | position: absolute; |
| | | top: -1px; |
| | | left: -18%; |
| | | width: 22%; |
| | | height: 4px; |
| | | border-radius: 999px; |
| | | background: linear-gradient(90deg, rgba(31, 122, 114, 0), rgba(59, 146, 244, 0.92), rgba(30, 91, 255, 0)); |
| | | filter: blur(0.2px); |
| | | animation: railFlow 3.1s ease-in-out infinite; |
| | | } |
| | | |
| | | .footer-rail__nodes { |
| | | margin-top: 12px; |
| | | display: grid; |
| | | grid-template-columns: repeat(5, minmax(0, 1fr)); |
| | | gap: 8px; |
| | | } |
| | | |
| | | .footer-node { |
| | | height: 34px; |
| | | border: 1px solid rgba(38, 112, 183, 0.18); |
| | | border-radius: 999px; |
| | | background: rgba(255, 255, 255, 0.76); |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | color: rgba(40, 80, 117, 0.92); |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | cursor: pointer; |
| | | transition: all 0.26s ease; |
| | | } |
| | | |
| | | .footer-node:hover { |
| | | transform: translateY(-1px); |
| | | border-color: rgba(31, 122, 114, 0.34); |
| | | box-shadow: 0 8px 14px rgba(31, 122, 114, 0.14); |
| | | } |
| | | |
| | | .footer-node--active { |
| | | color: #fff; |
| | | border-color: transparent; |
| | | background: linear-gradient(135deg, rgba(31, 122, 114, 0.94), rgba(30, 91, 255, 0.94)); |
| | | box-shadow: 0 10px 18px rgba(30, 91, 255, 0.28); |
| | | } |
| | | |
| | | .footer-node__dot { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | background: rgba(30, 91, 255, 0.72); |
| | | box-shadow: 0 0 10px rgba(30, 91, 255, 0.52); |
| | | } |
| | | |
| | | .footer-node--active .footer-node__dot { |
| | | background: #fff; |
| | | box-shadow: 0 0 12px rgba(255, 255, 255, 0.72); |
| | | animation: nodePulse 1.4s ease-in-out infinite; |
| | | } |
| | | |
| | | .agent-card { |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 0; |
| | | width: 460px; |
| | | margin-left: -230px; |
| | | cursor: pointer; |
| | | transform-origin: center bottom; |
| | | transition: transform 0.35s ease, opacity 0.35s ease; |
| | | } |
| | | |
| | | .agent-card__head { |
| | | height: 56px; |
| | | border-radius: 12px 12px 0 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 28px; |
| | | color: #fff; |
| | | font-weight: 700; |
| | | background: linear-gradient(135deg, #1f7a72 0%, #1e5bff 100%); |
| | | } |
| | | |
| | | .agent-card__head--active { |
| | | box-shadow: |
| | | 0 12px 22px rgba(30, 91, 255, 0.26), |
| | | inset 0 0 0 1px rgba(255, 255, 255, 0.28); |
| | | position: relative; |
| | | } |
| | | |
| | | .agent-card__head--active::after { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 12px; |
| | | right: 12px; |
| | | bottom: 6px; |
| | | height: 3px; |
| | | border-radius: 999px; |
| | | background: linear-gradient(90deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.22)); |
| | | } |
| | | |
| | | .agent-card__body { |
| | | position: relative; |
| | | height: 430px; |
| | | border: 1px solid var(--surface-border-strong); |
| | | border-top: none; |
| | | border-radius: 0 0 20px 20px; |
| | | background: rgba(255, 255, 255, 0.96); |
| | | overflow: hidden; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: flex-end; |
| | | isolation: isolate; |
| | | box-shadow: 0 12px 24px rgba(31, 49, 38, 0.1); |
| | | } |
| | | |
| | | .agent-card__body--active { |
| | | background: linear-gradient(180deg, rgba(248, 252, 251, 0.96), rgba(225, 241, 250, 0.9)); |
| | | border-color: rgba(31, 122, 114, 0.35); |
| | | } |
| | | |
| | | .agent-card__body--active::after { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | background: linear-gradient(108deg, transparent 28%, rgba(255, 255, 255, 0.34) 50%, transparent 72%); |
| | | transform: translateX(-125%); |
| | | animation: bodySweep 3.6s linear infinite; |
| | | pointer-events: none; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .avatar-shell { |
| | | position: relative; |
| | | width: 248px; |
| | | height: 430px; |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: center; |
| | | --base-core: rgba(53, 143, 222, 0.4); |
| | | --base-ring: rgba(39, 122, 201, 0.62); |
| | | --base-glow: rgba(46, 133, 214, 0.28); |
| | | } |
| | | |
| | | .avatar-shell::before { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: -10px; |
| | | width: 268px; |
| | | height: 58px; |
| | | transform: translateX(-50%); |
| | | border-radius: 50%; |
| | | background: radial-gradient( |
| | | ellipse at center, |
| | | rgba(55, 140, 219, 0.22) 0%, |
| | | rgba(55, 140, 219, 0.11) 46%, |
| | | rgba(55, 140, 219, 0) 74% |
| | | ); |
| | | filter: blur(2.4px); |
| | | animation: baseGlow 4.6s ease-in-out infinite; |
| | | z-index: 1; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .avatar-shell::after { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: 0; |
| | | width: 248px; |
| | | height: 46px; |
| | | transform: translateX(-50%); |
| | | border-radius: 50%; |
| | | border: 2px solid var(--base-ring); |
| | | box-shadow: |
| | | inset 0 0 0 1px rgba(255, 255, 255, 0.64), |
| | | 0 0 24px var(--base-glow); |
| | | animation: basePulse 3.1s ease-in-out infinite; |
| | | z-index: 4; |
| | | } |
| | | |
| | | .avatar-base { |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: 2px; |
| | | width: 224px; |
| | | height: 38px; |
| | | transform: translateX(-50%); |
| | | z-index: 2; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .avatar-base::before { |
| | | content: ""; |
| | | position: absolute; |
| | | inset: 0; |
| | | border-radius: 50%; |
| | | background: |
| | | radial-gradient( |
| | | ellipse at center, |
| | | rgba(255, 255, 255, 0.96) 0%, |
| | | rgba(255, 255, 255, 0.92) 36%, |
| | | var(--base-core) 68%, |
| | | rgba(38, 118, 195, 0.08) 100% |
| | | ); |
| | | box-shadow: |
| | | 0 0 30px var(--base-core), |
| | | 0 0 10px rgba(255, 255, 255, 0.34) inset; |
| | | z-index: 3; |
| | | } |
| | | |
| | | .avatar-base::after { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | width: 194px; |
| | | height: 194px; |
| | | transform: translate(-50%, -50%); |
| | | border-radius: 50%; |
| | | background: |
| | | conic-gradient( |
| | | from 180deg, |
| | | transparent 0deg, |
| | | var(--base-ring) 48deg, |
| | | transparent 112deg, |
| | | var(--base-ring) 208deg, |
| | | transparent 284deg, |
| | | rgba(33, 114, 191, 0.48) 332deg, |
| | | transparent 360deg |
| | | ); |
| | | -webkit-mask: radial-gradient(circle, transparent 61%, #000 62%, #000 68%, transparent 70%); |
| | | mask: radial-gradient(circle, transparent 61%, #000 62%, #000 68%, transparent 70%); |
| | | opacity: 0.62; |
| | | animation: baseRotate 10.5s linear infinite; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .avatar-shell--active { |
| | | --base-core: rgba(50, 141, 217, 0.52); |
| | | --base-ring: rgba(42, 127, 205, 0.76); |
| | | --base-glow: rgba(38, 130, 211, 0.38); |
| | | } |
| | | |
| | | .avatar-cut { |
| | | position: relative; |
| | | width: 220px; |
| | | height: 430px; |
| | | z-index: 6; |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: center; |
| | | filter: saturate(1.04) drop-shadow(0 14px 18px rgba(24, 44, 66, 0.14)); |
| | | transform-origin: center 82%; |
| | | animation: avatarFloat 3.2s ease-in-out infinite; |
| | | } |
| | | |
| | | .avatar-cut__img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; |
| | | object-position: center bottom; |
| | | display: block; |
| | | } |
| | | |
| | | .agent-card--active .avatar-cut { |
| | | animation-duration: 2.6s; |
| | | } |
| | | |
| | | .highlight-list { |
| | | position: absolute; |
| | | right: 10px; |
| | | top: 14px; |
| | | display: grid; |
| | | gap: 8px; |
| | | width: 220px; |
| | | z-index: 16; |
| | | } |
| | | |
| | | .highlight-item { |
| | | border-radius: 10px; |
| | | padding: 8px 10px; |
| | | font-size: 18px; |
| | | line-height: 1.4; |
| | | color: #fff; |
| | | background: rgba(33, 49, 63, 0.92); |
| | | box-shadow: 0 8px 16px rgba(21, 30, 40, 0.22); |
| | | } |
| | | |
| | | .agent-card--active .highlight-item { |
| | | background: rgba(31, 122, 114, 0.9); |
| | | } |
| | | |
| | | .nav-btn { |
| | | position: absolute; |
| | | top: 212px; |
| | | z-index: 80; |
| | | width: 50px; |
| | | height: 50px; |
| | | border-radius: 50%; |
| | | border: none; |
| | | font-size: 30px; |
| | | color: var(--colorPrimary); |
| | | background: var(--surface-base); |
| | | box-shadow: 0 10px 20px rgba(31, 49, 38, 0.16); |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .nav-btn--left { |
| | | left: 14px; |
| | | } |
| | | |
| | | .nav-btn--right { |
| | | right: 14px; |
| | | } |
| | | |
| | | .ai-fullscreen { |
| | | position: fixed; |
| | | inset: 0; |
| | | z-index: 2100; |
| | | padding: 12px; |
| | | background: rgba(33, 49, 63, 0.24); |
| | | backdrop-filter: blur(2px); |
| | | } |
| | | |
| | | .ai-panel { |
| | | height: 100%; |
| | | border-radius: 22px; |
| | | border: 1px solid var(--surface-border); |
| | | background: linear-gradient(180deg, #f9fcfb 0%, #f0f5f2 100%); |
| | | display: grid; |
| | | grid-template-rows: 62px minmax(0, 1fr) 110px; |
| | | box-shadow: var(--shadow-md); |
| | | } |
| | | |
| | | .ai-panel__top { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 0 24px; |
| | | } |
| | | |
| | | .ai-brand { |
| | | font-size: 34px; |
| | | color: var(--text-primary); |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .ai-close { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: none; |
| | | border-radius: 50%; |
| | | background: transparent; |
| | | font-size: 30px; |
| | | color: var(--text-secondary); |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .ai-panel__center { |
| | | padding: 8px 20px 10px; |
| | | display: grid; |
| | | grid-template-rows: 120px 290px minmax(0, 1fr); |
| | | gap: 10px; |
| | | min-height: 0; |
| | | } |
| | | |
| | | .welcome-card { |
| | | border-radius: 14px; |
| | | background: linear-gradient(135deg, rgba(232, 244, 242, 0.95), rgba(230, 237, 250, 0.9)); |
| | | padding: 16px 18px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | gap: 12px; |
| | | border: 1px solid var(--surface-border); |
| | | } |
| | | |
| | | .welcome-card__text h3 { |
| | | margin: 0; |
| | | font-size: 28px; |
| | | color: var(--text-primary); |
| | | } |
| | | |
| | | .welcome-card__text p { |
| | | margin: 8px 0 0; |
| | | font-size: 20px; |
| | | color: var(--text-secondary); |
| | | } |
| | | |
| | | .mini-avatar { |
| | | width: 120px; |
| | | height: 120px; |
| | | border-radius: 14px; |
| | | border: 1px solid var(--surface-border); |
| | | background-color: #fff; |
| | | background-clip: border-box; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .mini-avatar__img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; |
| | | object-position: center bottom; |
| | | display: block; |
| | | } |
| | | |
| | | .recommend-card { |
| | | border-radius: 14px; |
| | | border: 1px solid var(--surface-border); |
| | | background: rgba(255, 255, 255, 0.86); |
| | | padding: 12px 14px; |
| | | } |
| | | |
| | | .recommend-card__head { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | color: var(--text-primary); |
| | | font-size: 24px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .refresh-btn { |
| | | border: none; |
| | | background: transparent; |
| | | color: var(--text-secondary); |
| | | font-size: 18px; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .recommend-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 8px 18px; |
| | | } |
| | | |
| | | .recommend-item { |
| | | border: 1px solid var(--surface-border); |
| | | border-radius: 8px; |
| | | text-align: left; |
| | | padding: 8px 10px; |
| | | font-size: 18px; |
| | | color: var(--text-secondary); |
| | | background: #fff; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .recommend-item:hover { |
| | | background: rgba(31, 122, 114, 0.08); |
| | | color: var(--colorPrimary); |
| | | } |
| | | |
| | | .chat-card { |
| | | border-radius: 14px; |
| | | border: 1px solid var(--surface-border); |
| | | background: #fff; |
| | | min-height: 0; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .chat-empty { |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: var(--text-tertiary); |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .chat-messages { |
| | | height: 100%; |
| | | overflow-y: auto; |
| | | padding: 14px; |
| | | display: grid; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .chat-row { |
| | | display: flex; |
| | | } |
| | | |
| | | .chat-row--assistant { |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .chat-row--user { |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .chat-bubble { |
| | | max-width: 72%; |
| | | border-radius: 12px; |
| | | padding: 10px 12px; |
| | | font-size: 18px; |
| | | line-height: 1.5; |
| | | white-space: pre-wrap; |
| | | color: var(--text-primary); |
| | | background: var(--surface-soft); |
| | | border: 1px solid var(--surface-border); |
| | | } |
| | | |
| | | .chat-row--user .chat-bubble { |
| | | color: #fff; |
| | | background: linear-gradient(135deg, #1f7a72 0%, #1e5bff 100%); |
| | | border: none; |
| | | } |
| | | |
| | | .ai-panel__input { |
| | | display: grid; |
| | | grid-template-columns: minmax(0, 1fr) 130px; |
| | | gap: 12px; |
| | | padding: 14px 20px 18px; |
| | | } |
| | | |
| | | .ask-input :deep(.el-input__wrapper) { |
| | | height: 74px; |
| | | border-radius: 18px; |
| | | box-shadow: 0 0 0 1px var(--surface-border) inset; |
| | | background: #fff; |
| | | } |
| | | |
| | | .ask-input :deep(.el-input__inner) { |
| | | font-size: 20px; |
| | | } |
| | | |
| | | .send-btn { |
| | | align-self: center; |
| | | height: 56px; |
| | | font-size: 20px; |
| | | min-width: 98px; |
| | | } |
| | | |
| | | .fade-enter-active, |
| | | .fade-leave-active { |
| | | transition: opacity 0.2s ease; |
| | | } |
| | | |
| | | .fade-enter-from, |
| | | .fade-leave-to { |
| | | opacity: 0; |
| | | } |
| | | |
| | | @keyframes avatarFloat { |
| | | 0%, |
| | | 100% { |
| | | transform: translateY(0); |
| | | } |
| | | 50% { |
| | | transform: translateY(-8px); |
| | | } |
| | | } |
| | | |
| | | @keyframes basePulse { |
| | | 0%, |
| | | 100% { |
| | | transform: translateX(-50%) scale(1); |
| | | opacity: 0.88; |
| | | } |
| | | 50% { |
| | | transform: translateX(-50%) scale(1.045); |
| | | opacity: 0.95; |
| | | } |
| | | } |
| | | |
| | | @keyframes baseRotate { |
| | | from { |
| | | transform: translate(-50%, -50%) rotate(0deg); |
| | | } |
| | | to { |
| | | transform: translate(-50%, -50%) rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes baseGlow { |
| | | 0%, |
| | | 100% { |
| | | transform: translateX(-50%) scaleX(1); |
| | | opacity: 0.84; |
| | | } |
| | | 50% { |
| | | transform: translateX(-50%) scaleX(1.06); |
| | | opacity: 0.96; |
| | | } |
| | | } |
| | | |
| | | @keyframes bodySweep { |
| | | 0% { |
| | | transform: translateX(-125%); |
| | | } |
| | | 100% { |
| | | transform: translateX(135%); |
| | | } |
| | | } |
| | | |
| | | @keyframes footerSweep { |
| | | 0% { |
| | | transform: translateX(-120%); |
| | | } |
| | | 100% { |
| | | transform: translateX(140%); |
| | | } |
| | | } |
| | | |
| | | @keyframes railFlow { |
| | | 0% { |
| | | transform: translateX(0); |
| | | opacity: 0; |
| | | } |
| | | 20% { |
| | | opacity: 1; |
| | | } |
| | | 80% { |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | transform: translateX(520%); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | @keyframes nodePulse { |
| | | 0%, |
| | | 100% { |
| | | transform: scale(1); |
| | | } |
| | | 50% { |
| | | transform: scale(1.25); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 1600px) { |
| | | .head-title { |
| | | font-size: 34px; |
| | | height: 60px; |
| | | } |
| | | |
| | | .brain-intro h2 { |
| | | font-size: 36px; |
| | | } |
| | | |
| | | .brain-intro p { |
| | | font-size: 22px; |
| | | } |
| | | |
| | | .intro-sign { |
| | | font-size: 20px; |
| | | } |
| | | |
| | | .agent-card { |
| | | width: 380px; |
| | | margin-left: -190px; |
| | | } |
| | | |
| | | .agent-card__head { |
| | | font-size: 24px; |
| | | height: 54px; |
| | | } |
| | | |
| | | .agent-card__body { |
| | | height: 390px; |
| | | } |
| | | |
| | | .highlight-list { |
| | | width: 184px; |
| | | } |
| | | |
| | | .highlight-item { |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .avatar-shell { |
| | | width: 220px; |
| | | height: 390px; |
| | | } |
| | | |
| | | .avatar-cut { |
| | | width: 202px; |
| | | height: 390px; |
| | | } |
| | | |
| | | .avatar-base { |
| | | width: 194px; |
| | | height: 34px; |
| | | } |
| | | |
| | | .avatar-base::after { |
| | | width: 164px; |
| | | height: 164px; |
| | | } |
| | | |
| | | .avatar-shell::before { |
| | | width: 236px; |
| | | height: 48px; |
| | | bottom: -9px; |
| | | } |
| | | |
| | | .avatar-shell::after { |
| | | width: 220px; |
| | | height: 40px; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .brain-footer { |
| | | margin: 0 52px; |
| | | height: clamp(210px, 23vh, 264px); |
| | | } |
| | | |
| | | .footer-metrics { |
| | | padding: 12px 14px 66px; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .footer-metric { |
| | | min-height: 66px; |
| | | padding: 8px 10px; |
| | | } |
| | | |
| | | .footer-metric--period { |
| | | min-height: 108px; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .footer-metric__label { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .footer-metric__value { |
| | | font-size: 24px; |
| | | } |
| | | |
| | | .footer-metric__hint { |
| | | font-size: 11px; |
| | | } |
| | | |
| | | .footer-period-control { |
| | | gap: 6px; |
| | | } |
| | | |
| | | .period-btn { |
| | | width: 20px; |
| | | height: 20px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .period-input { |
| | | width: 50px; |
| | | height: 22px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .footer-period-slider { |
| | | width: 100%; |
| | | } |
| | | |
| | | .footer-rail { |
| | | left: 14px; |
| | | right: 14px; |
| | | bottom: 14px; |
| | | } |
| | | |
| | | .footer-rail__nodes { |
| | | margin-top: 10px; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .footer-node { |
| | | height: 30px; |
| | | font-size: 12px; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .footer-node__dot { |
| | | width: 7px; |
| | | height: 7px; |
| | | } |
| | | |
| | | .ai-brand { |
| | | font-size: 28px; |
| | | } |
| | | |
| | | .welcome-card__text h3, |
| | | .recommend-card__head { |
| | | font-size: 22px; |
| | | } |
| | | |
| | | .welcome-card__text p, |
| | | .recommend-item, |
| | | .chat-bubble, |
| | | .refresh-btn, |
| | | .chat-empty, |
| | | .ask-input :deep(.el-input__inner), |
| | | .send-btn { |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | </style> |