update pay function

This commit is contained in:
2025-11-22 12:07:55 +08:00
parent 47be4584f9
commit 244968a1cb
3 changed files with 174 additions and 124 deletions

View File

@@ -35,7 +35,7 @@
"echarts": "^5.6.0",
"echarts-for-react": "^3.0.2",
"echarts-wordcloud": "^2.1.0",
"framer-motion": "^4.1.17",
"framer-motion": "^12.23.24",
"fullcalendar": "^5.9.0",
"globalize": "^1.7.0",
"history": "^5.3.0",

View File

@@ -7,10 +7,10 @@
### 🚀 技术栈
- **Hero UI** - 现代化 React UI 组件库NextUI 的继任者)
- **Framer Motion** - 物理动画引擎
- **Framer Motion 12** - 物理动画引擎(已升级!)
- **Tailwind CSS** - 原子化 CSS 框架Hero UI 内置)
- **Lucide Icons** - 现代化图标库
- **Chakra UI Toast** - 通知提示(保留
- **Chakra UI Toast** - 通知提示(待迁移到 Hero UI Toast
### 🎨 视觉特性
@@ -19,11 +19,12 @@
- `backdrop-blur-xl` + 渐变背景
- 深色模式完美支持
2. **流畅动画**
- 侧边栏 Spring 动画滑入/滑出
- 消息依次淡入(错开延迟
- 按钮悬停/点击缩放效果
- AI 头像持续旋转动画
2. **流畅动画Framer Motion 12 物理引擎)**
- 侧边栏 **Spring 弹性动画**滑入/滑出stiffness: 300, damping: 30
- ✨ 消息 **淡入上移** + **错开延迟**staggerChildren: 0.05
- ✨ 按钮 **悬停缩放**1.05x + **点击压缩**0.95x
- AI 头像 **360度持续旋转**duration: 3s, linear
- ✨ 快捷问题卡片 **退出动画**AnimatePresence
3. **渐变色设计**
- 标题:蓝到紫渐变

View File

@@ -1,8 +1,9 @@
// src/views/AgentChat/index.js
// 超炫酷的 AI 投研助手 - Hero UI 版本
// 使用 CSS 动画替代 Framer Motion(避免版本冲突)
// 使用 Framer Motion 物理动画引擎
import React, { useState, useEffect, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import {
Button,
Card,
@@ -67,6 +68,78 @@ import {
Rocket,
} from 'lucide-react';
/**
* Framer Motion 动画变体配置
*/
const animations = {
// 侧边栏滑入动画(带弹性效果)
slideInLeft: {
initial: { x: -320, opacity: 0 },
animate: {
x: 0,
opacity: 1,
transition: {
type: 'spring',
stiffness: 300,
damping: 30,
},
},
exit: {
x: -320,
opacity: 0,
transition: { duration: 0.2 },
},
},
slideInRight: {
initial: { x: 320, opacity: 0 },
animate: {
x: 0,
opacity: 1,
transition: {
type: 'spring',
stiffness: 300,
damping: 30,
},
},
exit: {
x: 320,
opacity: 0,
transition: { duration: 0.2 },
},
},
// 消息淡入动画(带上移效果)
fadeInUp: {
initial: { opacity: 0, y: 20 },
animate: {
opacity: 1,
y: 0,
transition: {
type: 'spring',
stiffness: 400,
damping: 25,
},
},
},
// 列表项淡入(错开延迟)
staggerItem: {
initial: { opacity: 0, y: 10 },
animate: { opacity: 1, y: 0 },
},
// 父容器错开动画
staggerContainer: {
animate: {
transition: {
staggerChildren: 0.05,
},
},
},
// 按钮点击动画
pressScale: {
whileTap: { scale: 0.95 },
whileHover: { scale: 1.05 },
},
};
/**
* 消息类型
*/
@@ -355,7 +428,13 @@ const AgentChat = () => {
<div className="flex h-screen bg-gradient-to-br from-gray-50 to-blue-50 dark:from-gray-900 dark:to-gray-800 overflow-hidden">
{/* 左侧栏 */}
{isLeftSidebarOpen && (
<div className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-r border-gray-200 dark:border-gray-700 shadow-xl animate-slide-in-left">
<motion.div
className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-r border-gray-200 dark:border-gray-700 shadow-xl"
initial="initial"
animate="animate"
exit="exit"
variants={animations.slideInLeft}
>
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
@@ -457,7 +536,7 @@ const AgentChat = () => {
</div>
</div>
</div>
</div>
</motion.div>
)}
{/* 中间主聊天区域 */}
@@ -476,12 +555,17 @@ const AgentChat = () => {
</Button>
)}
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 3, repeat: Infinity, ease: 'linear' }}
>
<Avatar
icon={<Cpu className="w-6 h-6" />}
classNames={{
base: 'bg-gradient-to-br from-purple-500 to-pink-500 animate-spin-slow',
base: 'bg-gradient-to-br from-purple-500 to-pink-500',
}}
/>
</motion.div>
<div>
<h1 className="text-xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
@@ -524,18 +608,39 @@ const AgentChat = () => {
</div>
<ScrollShadow className="flex-1 p-6">
<div className="max-w-4xl mx-auto space-y-4">
{messages.map((message) => (
<div key={message.id} className="animate-fade-in">
<motion.div
className="max-w-4xl mx-auto space-y-4"
variants={animations.staggerContainer}
initial="initial"
animate="animate"
>
<AnimatePresence mode="popLayout">
{messages.map((message, index) => (
<motion.div
key={message.id}
variants={animations.fadeInUp}
initial="initial"
animate="animate"
exit={{ opacity: 0, y: -20 }}
layout
>
<MessageRenderer message={message} userAvatar={user?.avatar} />
</div>
</motion.div>
))}
</AnimatePresence>
<div ref={messagesEndRef} />
</div>
</motion.div>
</ScrollShadow>
<AnimatePresence>
{messages.length <= 2 && !isProcessing && (
<div className="px-6 py-3 animate-fade-in">
<motion.div
className="px-6 py-3"
variants={animations.fadeInUp}
initial="initial"
animate="animate"
exit={{ opacity: 0, y: 20 }}
>
<div className="max-w-4xl mx-auto">
<p className="text-xs text-gray-500 mb-2 font-medium flex items-center gap-1">
<Sparkles className="w-3 h-3" />
@@ -543,11 +648,11 @@ const AgentChat = () => {
</p>
<div className="grid grid-cols-2 gap-2">
{quickQuestions.map((question, idx) => (
<motion.div key={idx} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<Button
key={idx}
variant="bordered"
color="primary"
className="w-full justify-start h-auto py-3 hover:scale-105 transition-transform"
className="w-full justify-start h-auto py-3"
onPress={() => {
setInputValue(question.text);
inputRef.current?.focus();
@@ -556,11 +661,13 @@ const AgentChat = () => {
<span className="mr-2">{question.emoji}</span>
{question.text}
</Button>
</motion.div>
))}
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
<div className="bg-white/70 dark:bg-gray-800/70 backdrop-blur-xl border-t border-gray-200 dark:border-gray-700 px-6 py-4">
<div className="max-w-4xl mx-auto">
@@ -579,6 +686,7 @@ const AgentChat = () => {
inputWrapper: 'border-2 hover:border-primary-500 transition-colors',
}}
/>
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<Button
isIconOnly
color="primary"
@@ -587,10 +695,11 @@ const AgentChat = () => {
onPress={handleSendMessage}
isLoading={isProcessing}
isDisabled={!inputValue.trim() || isProcessing}
className="bg-gradient-to-r from-blue-500 to-purple-500 hover:scale-105 transition-transform"
className="bg-gradient-to-r from-blue-500 to-purple-500"
>
{!isProcessing && <Send className="w-5 h-5" />}
</Button>
</motion.div>
</div>
<div className="flex items-center gap-4 mt-2 text-xs text-gray-500">
@@ -611,7 +720,13 @@ const AgentChat = () => {
{/* 右侧栏 */}
{isRightSidebarOpen && (
<div className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-l border-gray-200 dark:border-gray-700 shadow-xl animate-slide-in-right">
<motion.div
className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-l border-gray-200 dark:border-gray-700 shadow-xl"
initial="initial"
animate="animate"
exit="exit"
variants={animations.slideInRight}
>
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
@@ -709,80 +824,14 @@ const AgentChat = () => {
</Tab>
</Tabs>
</ScrollShadow>
</div>
</motion.div>
)}
<style>{`
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slide-in-left {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-in-right {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes spin-slow {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.animate-fade-in {
animation: fade-in 0.3s ease-out;
}
.animate-slide-in-left {
animation: slide-in-left 0.3s ease-out;
}
.animate-slide-in-right {
animation: slide-in-right 0.3s ease-out;
}
.animate-spin-slow {
animation: spin-slow 3s linear infinite;
}
.hover\\:scale-102:hover {
transform: scale(1.02);
}
.hover\\:scale-105:hover {
transform: scale(1.05);
}
`}</style>
</div>
);
};
export default AgentChat;
/**
* 消息渲染器
*/