fix: 修复TypeScript配置错误并更新项目文档
详细说明: - 修复了@n8n/config包的TypeScript配置错误 - 移除了不存在的jest-expect-message类型引用 - 清理了所有TypeScript构建缓存 - 更新了可行性分析文档,添加了技术实施方案 - 更新了Agent prompt文档 - 添加了会展策划工作流文档 - 包含了n8n-chinese-translation子项目 - 添加了exhibition-demo展示系统框架
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# Role: 经验丰富且富有创意的会展设计师
|
||||
|
||||
## Profile
|
||||
- author: LangGPT
|
||||
- version: 1.0
|
||||
- language: 中文
|
||||
- description: 你是一位经验老道、创意新颖的会展设计师,擅长展会的整体策划,包括选址、展陈设计、预算编制、活动日期安排、营销宣传等。
|
||||
|
||||
## Skills
|
||||
1. 能快速理解并吸收外部资料与内容,整合成为实操性的策划要点。
|
||||
2. 擅长将抽象主题具体化,策划方案具有逻辑清晰、创意突出、实用可行的特点。
|
||||
3. 熟练掌握会展项目全过程,从前期调研、设计策划,到中期执行与后期评估。
|
||||
4. 能使用专业术语和严谨的结构表达完整策划内容。
|
||||
|
||||
## Background
|
||||
用户提供了三个节点文档:{{ $('When chat message received').item.json.chatInput }}代表整个会展的主题,{{ $json.text }}包含会展主题相关的资料和参考信息,{{ $json.text }}定义了文章的结构和写作范式。你的任务是理解这两个节点,并结合自身专业知识输出一份完整的策划书。
|
||||
|
||||
## Goals
|
||||
- 全文必须按照{{ $('When chat message received').item.json.chatInput }}的主题
|
||||
- 阅读并充分理解{{ $json.text }}中提供的信息和资料;
|
||||
- 遵循{{ $json.text }}所规定的文章结构与格式;
|
||||
- 撰写一份主题吸引人、语言专业、结构合理的会展策划方案书;
|
||||
- 策划书内容必须涵盖选址(中国长三角地区)、展陈设计、预算、时间安排、宣传推广等环节;
|
||||
- 正文长度不少于3000字。
|
||||
|
||||
## Rules
|
||||
1. 必须严格参考{{ $json.text }}内容进行创作,体现理解与重构能力;
|
||||
2. 主题需吸引眼球,具有创意性;
|
||||
3. 逻辑严密,段落结构清晰;
|
||||
4. 专业术语准确,语句通顺;
|
||||
5. 不允许出现“AI生成”、“我是AI”等措辞;
|
||||
6. 最终输出不得少于3000字。
|
||||
|
||||
## Workflows
|
||||
1. 阅读并吸收{{ $json.text }}内容,提炼核心信息;
|
||||
2. 确认所{{ $json.text }}规定的写作框架;
|
||||
3. 围绕会展策划关键点进行内容创作;
|
||||
4. 审核文字逻辑、术语准确性与字数;
|
||||
5. 以Json格式输出完整的策划方案书文本。
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Role: 会展营销宣传专家
|
||||
|
||||
## Profile
|
||||
- author: LangGPT
|
||||
- version: 1.0
|
||||
- language: 中文
|
||||
- description: 你是一位精通线上与线下整合传播的会展营销专家,擅长为展会项目量身定制多渠道宣传策略,提升展会曝光度、观众到场率及品牌影响力。
|
||||
|
||||
## Skills
|
||||
1. 能基于展会定位制定精准的营销目标与内容策略;
|
||||
2. 熟悉抖音、小红书、公众号等新媒体平台营销玩法;
|
||||
3. 精通线下地推、户外广告、合作联动等传统宣传方式;
|
||||
4. 善于制定分阶段、分人群的推广计划与内容排期;
|
||||
5. 输出结构标准化、逻辑清晰的整合营销方案(含推广时间表、渠道矩阵、内容主题等);
|
||||
|
||||
## Goals
|
||||
- 为展会制定一套完整的整合营销传播方案;
|
||||
- 输出方案需包含目标设定、策略框架、渠道矩阵、内容主题、执行时间表等;
|
||||
- 内容格式需支持结构化输出,方便项目管理或汇报使用;
|
||||
|
||||
## OutputFormat
|
||||
|
||||
```json
|
||||
{
|
||||
"展会名称": "2025数字经济博览会",
|
||||
"目标受众": ["B端科技企业", "C端泛科技爱好者"],
|
||||
"营销目标": "提升展会曝光率、提升参展报名转化率",
|
||||
"营销策略": {
|
||||
"总体策略": "线上线下联动,内容驱动,精准触达",
|
||||
"分阶段策略": {
|
||||
"预热期": "内容种草 + 社媒曝光",
|
||||
"引流期": "强内容发布 + 用户互动",
|
||||
"转化期": "落地活动 + 渠道转化"
|
||||
}
|
||||
},
|
||||
"渠道矩阵": {
|
||||
"线上渠道": ["抖音", "小红书", "微信公众号", "官网", "知乎"],
|
||||
"线下渠道": ["户外广告", "地铁灯箱", "异业联动门店", "校园推广"]
|
||||
},
|
||||
"内容主题规划": [
|
||||
{"主题": "行业趋势", "形式": "图文、视频", "平台": "知乎、公众号"},
|
||||
{"主题": "展会亮点", "形式": "短视频、直播", "平台": "抖音、小红书"},
|
||||
{"主题": "嘉宾预告", "形式": "人物专访", "平台": "视频号、公众号"}
|
||||
],
|
||||
"时间与执行表": [
|
||||
{
|
||||
"阶段": "预热期",
|
||||
"时间范围": "2025-02-01 ~ 2025-02-14",
|
||||
"重点动作": "达人种草、开设话题标签、内容预告"
|
||||
},
|
||||
{
|
||||
"阶段": "引流期",
|
||||
"时间范围": "2025-02-15 ~ 2025-02-28",
|
||||
"重点动作": "高频内容推送、互动抽奖、报名链接投放"
|
||||
},
|
||||
{
|
||||
"阶段": "转化期",
|
||||
"时间范围": "2025-03-01 ~ 2025-03-08",
|
||||
"重点动作": "地推执行、户外广告投放、现场报名引导"
|
||||
}
|
||||
]
|
||||
}
|
||||
145
web_frontend/doc/会展策划工作流.md
Normal file
145
web_frontend/doc/会展策划工作流.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# 会展策划专家系统工作流
|
||||
|
||||
## 工作流架构图
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
%% 定义节点样式
|
||||
classDef chatNode fill:#4a5568,stroke:#718096,color:#fff,stroke-width:2px
|
||||
classDef triggerNode fill:#d69e2e,stroke:#ecc94b,color:#fff,stroke-width:2px
|
||||
classDef splitNode fill:#805ad5,stroke:#9f7aea,color:#fff,stroke-width:2px
|
||||
classDef notionNode fill:#e53e3e,stroke:#fc8181,color:#fff,stroke-width:2px
|
||||
classDef summaryNode fill:#2b6cb0,stroke:#4299e1,color:#fff,stroke-width:3px
|
||||
|
||||
%% 节点定义
|
||||
Start[["🗨️ When chat message<br/>received"]]:::triggerNode
|
||||
|
||||
%% 第一阶段:专业分析
|
||||
subgraph Phase1 [" 第一阶段:专业分析 "]
|
||||
Expert1["🔗 设计专家<br/>Google Gemini Chat<br/>Model2"]:::chatNode
|
||||
Expert2["🔗 财务预算专家<br/>DeepSeek Chat<br/>Model2"]:::chatNode
|
||||
Expert3["🔗 信息检索专家<br/>DeepSeek Chat<br/>Model5"]:::chatNode
|
||||
end
|
||||
|
||||
%% 中心汇总节点
|
||||
Summary["📝 会展策划专家<br/>Chat Models + Memories<br/>(汇总与协调)"]:::summaryNode
|
||||
|
||||
%% 第二阶段:执行规划
|
||||
subgraph Phase2 [" 第二阶段:执行规划 "]
|
||||
Expert4["🔗 格式编辑专家<br/>DeepSeek Chat<br/>Model4"]:::chatNode
|
||||
Expert5["🔗 活动执行专家<br/>DeepSeek Chat<br/>Model1"]:::chatNode
|
||||
Expert6["🔗 营销宣传专家<br/>DeepSeek Chat<br/>Model3"]:::chatNode
|
||||
end
|
||||
|
||||
%% 输出节点
|
||||
Split["⚡ Split Out<br/>(输出分流)"]:::splitNode
|
||||
Notion["📕 存储<br/>Create Page<br/>(Notion)"]:::notionNode
|
||||
|
||||
%% 连接关系 - 第一阶段
|
||||
Start --> Expert1
|
||||
Start --> Expert2
|
||||
Start --> Expert3
|
||||
|
||||
Expert1 --> Summary
|
||||
Expert2 --> Summary
|
||||
Expert3 --> Summary
|
||||
|
||||
%% 连接关系 - 第二阶段
|
||||
Summary --> Expert4
|
||||
Summary --> Expert5
|
||||
Summary --> Expert6
|
||||
|
||||
Expert4 --> Summary
|
||||
Expert5 --> Summary
|
||||
Expert6 --> Summary
|
||||
|
||||
%% 输出连接
|
||||
Summary --> Split
|
||||
Split --> Notion
|
||||
```
|
||||
|
||||
## 工作流说明
|
||||
|
||||
### 整体架构
|
||||
这是一个**双向循环的专家系统工作流**,采用中心化协调模式,通过多个专业AI模型协同工作,完成复杂的会展策划任务。
|
||||
|
||||
### 核心组件
|
||||
|
||||
#### 1. 触发器 (Trigger)
|
||||
- **组件**: When chat message received
|
||||
- **功能**: 接收用户输入,启动整个工作流程
|
||||
|
||||
#### 2. 第一阶段专家组
|
||||
- **设计专家** (Google Gemini Chat Model2)
|
||||
- 负责展会视觉设计、空间布局
|
||||
- 品牌形象和创意方案
|
||||
|
||||
- **财务预算专家** (DeepSeek Chat Model2)
|
||||
- 成本核算与预算规划
|
||||
- 投资回报率分析
|
||||
|
||||
- **信息检索专家** (DeepSeek Chat Model5)
|
||||
- 市场调研和数据收集
|
||||
- 竞品分析和行业趋势
|
||||
|
||||
#### 3. 中央协调器
|
||||
- **会展策划专家** (Chat Models + Memories)
|
||||
- 汇总各专家意见
|
||||
- 协调不同领域的建议
|
||||
- 维护上下文记忆
|
||||
- 生成综合策划方案
|
||||
|
||||
#### 4. 第二阶段专家组
|
||||
- **格式编辑专家** (DeepSeek Chat Model4)
|
||||
- 文档格式化和排版
|
||||
- 确保输出规范性
|
||||
|
||||
- **活动执行专家** (DeepSeek Chat Model1)
|
||||
- 制定执行计划
|
||||
- 时间线和任务分配
|
||||
|
||||
- **营销宣传专家** (DeepSeek Chat Model3)
|
||||
- 推广策略制定
|
||||
- 媒体渠道规划
|
||||
|
||||
#### 5. 输出处理
|
||||
- **Split Out**: 将最终方案分流输出
|
||||
- **Notion存储**: 将策划方案保存到Notion页面
|
||||
|
||||
### 工作流特点
|
||||
|
||||
1. **双向循环机制**
|
||||
- 第一阶段:收集专业意见
|
||||
- 中央处理:汇总协调
|
||||
- 第二阶段:细化执行
|
||||
- 反馈优化:返回中央节点
|
||||
|
||||
2. **多模型协同**
|
||||
- 使用不同的AI模型处理不同专业领域
|
||||
- Google Gemini用于创意设计
|
||||
- DeepSeek用于逻辑分析和执行
|
||||
|
||||
3. **记忆保持**
|
||||
- 中央节点维护对话历史
|
||||
- 确保上下文连贯性
|
||||
|
||||
4. **结构化输出**
|
||||
- 通过格式编辑专家规范化输出
|
||||
- 自动存储到知识管理系统
|
||||
|
||||
## 使用场景
|
||||
|
||||
适用于:
|
||||
- 大型展会策划
|
||||
- 商业活动组织
|
||||
- 品牌发布会
|
||||
- 行业峰会论坛
|
||||
- 产品展示会
|
||||
|
||||
## 优势
|
||||
|
||||
1. **专业性**: 每个领域都有专门的AI专家处理
|
||||
2. **全面性**: 覆盖策划的各个方面
|
||||
3. **协调性**: 中央节点确保各部分协调一致
|
||||
4. **可追溯**: 所有决策过程都有记录
|
||||
5. **自动化**: 减少人工干预,提高效率
|
||||
@@ -1,10 +1,65 @@
|
||||
# 需求评估
|
||||
# 会展策划多Agent演示系统 - 技术实施方案
|
||||
|
||||
我现在想做一个演示的DEMO,
|
||||
## 项目定义
|
||||
构建一个基于React的Web演示系统,实时展示多Agent协同生成会展策划方案的完整过程。
|
||||
|
||||
## 技术决策
|
||||
- **框架**: React 18 + TypeScript
|
||||
- **动画**: Framer Motion + CSS Animations
|
||||
- **样式**: Tailwind CSS (参考字节跳动、Flowith、Raycast设计风格)
|
||||
- **状态管理**: Zustand
|
||||
- **构建工具**: Vite
|
||||
|
||||
## 演示参数
|
||||
- **总时长**: 3分钟
|
||||
- **文字生成速度**: 30-40字/秒
|
||||
- **Agent Prompt展示**: 完整展示,逐步显示
|
||||
- **交互控制**: 暂时不支持跳过,保证演示完整性
|
||||
|
||||
## 设计风格指南
|
||||
- **视觉风格**: 简洁实用、年轻化、高端感
|
||||
- **参考产品**: 火山引擎、Trea、Flowith、Raycast
|
||||
- **核心特征**:
|
||||
- 精致的微动画
|
||||
- 清晰的信息层级
|
||||
- 中性色调搭配品牌色
|
||||
- 优雅的过渡效果
|
||||
|
||||
|
||||
模拟一个Agent生成状态
|
||||
## 内容板块
|
||||
网页的Page形式按照一级标题进行独立输出,每一个一级标题都是一个独立页面
|
||||
|
||||
分为
|
||||
- 策划案
|
||||
- 展会介绍与预期效果
|
||||
- 营销方案
|
||||
- 现场运营方案
|
||||
- 预算与收益分析
|
||||
- 风险评估与应急预案
|
||||
|
||||
这个六个页面是必须要创建的
|
||||
|
||||
## 演示流程
|
||||
- 可以先出一个页面,上面有一个run的按钮
|
||||
- 当点击run按钮的时候,开始执行mermaid的流程,每个流程独立输出对应Agent的Prompt,加一些延迟和loading动画,让它像终端生成一样生成每个内容的文字内容和图片内容
|
||||
- 需要增加文字生成的动画
|
||||
- 图片加载的时候,需要有loading转圈的动画,模拟延迟
|
||||
|
||||
## 整体目标
|
||||
其实就是仿真模拟出一个多Agent生成各个方案的一个演示动画
|
||||
|
||||
## 页面内容完善
|
||||
|
||||
- 创建一个TODO清单,扩展每个一级标题中信息缺失和不完善的部分
|
||||
|
||||
- 让每个板块中的内容更加充实,符合现实业务场景,尽可能多
|
||||
|
||||
## 构建网页规范
|
||||
|
||||
- 在构建网页的时候需要满足参考claude.md中的superdesign规范
|
||||
- 现代化网页,符合UX设计规范的交互
|
||||
|
||||
## Agent角色
|
||||
**Agent的角色有**
|
||||
|
||||
1.信息检索专家:`web_frontend/data/Agent_prompt/信息检索专家.md`
|
||||
@@ -20,7 +75,3 @@
|
||||
6.活动执行专家:`web_frontend/data/Agent_prompt/活动执行专家.md`
|
||||
|
||||
7.营销宣传专家:`web_frontend/data/Agent_prompt/会展营销宣传专家.md`
|
||||
|
||||
|
||||
# 演示输出文档
|
||||
https://www.notion.so/266405c1652381f9b607ecd178ad93dd
|
||||
|
||||
385
web_frontend/doc/可行性分析_优化版.md
Normal file
385
web_frontend/doc/可行性分析_优化版.md
Normal file
@@ -0,0 +1,385 @@
|
||||
# 会展策划多Agent演示系统 - 需求与可行性分析文档
|
||||
|
||||
## 一、项目概述
|
||||
|
||||
### 1.1 项目背景
|
||||
基于n8n工作流平台构建的多Agent协作系统,需要创建一个Web演示界面,直观展示AI Agent协同生成会展策划方案的完整过程。
|
||||
|
||||
### 1.2 项目目标
|
||||
- **主要目标**:构建一个高保真的多Agent协作演示系统
|
||||
- **演示效果**:实时展示7个专业AI Agent协同工作,生成完整的汽车展会策划方案
|
||||
- **用户体验**:提供流畅的动画效果和交互体验,让观众理解Agent协作机制
|
||||
|
||||
### 1.3 项目范围
|
||||
- **核心功能**:Agent工作流可视化、内容生成动画、成果展示
|
||||
- **演示内容**:基于`web_frontend/data/会展策划/汽车展会展策划案_DEMO.md`
|
||||
- **技术边界**:前端演示系统,不涉及真实的AI调用
|
||||
|
||||
---
|
||||
|
||||
## 二、需求分析
|
||||
|
||||
### 2.1 功能需求
|
||||
|
||||
#### 2.1.1 核心功能模块
|
||||
|
||||
| 模块 | 功能描述 | 优先级 |
|
||||
|------|---------|--------|
|
||||
| **启动界面** | 展示系统介绍,提供"开始演示"按钮 | P0 |
|
||||
| **工作流可视化** | 基于Mermaid图动态展示Agent协作流程 | P0 |
|
||||
| **Agent执行动画** | 展示每个Agent的输入/输出,模拟思考过程 | P0 |
|
||||
| **内容生成展示** | 打字机效果展示文本,渐显效果展示图片 | P0 |
|
||||
| **成果展示页面** | 分页展示6个策划模块的完整内容 | P0 |
|
||||
| **进度指示器** | 显示当前执行阶段和整体进度 | P1 |
|
||||
| **交互控制** | 暂停/继续/重新开始功能 | P1 |
|
||||
|
||||
#### 2.1.2 Agent角色定义
|
||||
|
||||
```javascript
|
||||
const agents = {
|
||||
"信息检索专家": {
|
||||
icon: "🔍",
|
||||
model: "DeepSeek Chat Model5",
|
||||
role: "市场调研、数据收集、竞品分析",
|
||||
promptFile: "web_frontend/data/Agent_prompt/信息检索专家.md"
|
||||
},
|
||||
"设计专家": {
|
||||
icon: "🎨",
|
||||
model: "Google Gemini Chat Model2",
|
||||
role: "视觉设计、空间布局、品牌形象",
|
||||
promptFile: "web_frontend/data/Agent_prompt/设计师专家.md"
|
||||
},
|
||||
"财务预算专家": {
|
||||
icon: "💰",
|
||||
model: "DeepSeek Chat Model2",
|
||||
role: "成本核算、预算规划、ROI分析",
|
||||
promptFile: "web_frontend/data/Agent_prompt/财务预算专家.md"
|
||||
},
|
||||
"格式编辑专家": {
|
||||
icon: "📝",
|
||||
model: "DeepSeek Chat Model4",
|
||||
role: "文档格式化、内容结构优化",
|
||||
promptFile: "web_frontend/data/Agent_prompt/格式编辑专家.md"
|
||||
},
|
||||
"活动执行专家": {
|
||||
icon: "⚡",
|
||||
model: "DeepSeek Chat Model1",
|
||||
role: "执行计划、时间线管理、任务分配",
|
||||
promptFile: "web_frontend/data/Agent_prompt/活动执行专家.md"
|
||||
},
|
||||
"营销宣传专家": {
|
||||
icon: "📢",
|
||||
model: "DeepSeek Chat Model3",
|
||||
role: "推广策略、媒体规划、品牌传播",
|
||||
promptFile: "web_frontend/data/Agent_prompt/会展营销宣传专家.md"
|
||||
},
|
||||
"会展策划专家": {
|
||||
icon: "🎯",
|
||||
model: "Chat Models + Memories",
|
||||
role: "中央协调、方案整合、决策支持",
|
||||
promptFile: "web_frontend/data/Agent_prompt/会展策划专家.md"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 内容需求
|
||||
|
||||
#### 2.2.1 展示内容结构
|
||||
|
||||
```yaml
|
||||
展会策划方案:
|
||||
1. 策划案概述:
|
||||
- 封面信息
|
||||
- 策划背景
|
||||
- 策划目的
|
||||
- 策划依据
|
||||
|
||||
2. 展会介绍与预期效果:
|
||||
- 展会主题
|
||||
- 展会地址
|
||||
- 展品范围
|
||||
- 目标参展商/观众
|
||||
- 预计规模
|
||||
- 预期效果
|
||||
|
||||
3. 营销方案:
|
||||
- 整体推广策略
|
||||
- 线下推广方案
|
||||
- 线上推广方案
|
||||
- 精准邀请与对接
|
||||
|
||||
4. 现场运营方案:
|
||||
- 展区规划
|
||||
- 现场服务
|
||||
- 活动安排
|
||||
- 展陈设计
|
||||
|
||||
5. 预算与收益分析:
|
||||
- 支出预算明细
|
||||
- 收入预算明细
|
||||
- 收益预测
|
||||
- 投资回报率
|
||||
|
||||
6. 风险评估与应急预案:
|
||||
- 风险分析矩阵
|
||||
- 应对措施
|
||||
- 应急预案
|
||||
- 保障体系
|
||||
```
|
||||
|
||||
### 2.3 交互需求
|
||||
|
||||
#### 2.3.1 用户流程
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[访问页面] --> B[查看介绍]
|
||||
B --> C[点击开始演示]
|
||||
C --> D[观看Agent协作]
|
||||
D --> E[查看生成内容]
|
||||
E --> F[浏览完整方案]
|
||||
F --> G[重新演示/退出]
|
||||
```
|
||||
|
||||
#### 2.3.2 动画效果需求
|
||||
|
||||
| 动画类型 | 应用场景 | 实现方式 | 时长 |
|
||||
|---------|---------|---------|------|
|
||||
| **淡入淡出** | Agent切换 | CSS transition | 0.3s |
|
||||
| **打字机效果** | 文本生成 | JavaScript逐字显示 | 30-50字/秒 |
|
||||
| **流光效果** | 工作流连线 | SVG animation | 持续 |
|
||||
| **加载动画** | 图片加载 | CSS spinner | 1-2s |
|
||||
| **进度条** | 整体进度 | CSS/JS动态更新 | 实时 |
|
||||
| **脉冲效果** | 当前Active Agent | CSS animation | 持续 |
|
||||
|
||||
### 2.4 非功能需求
|
||||
|
||||
#### 2.4.1 性能要求
|
||||
- 页面加载时间 < 3秒
|
||||
- 动画帧率 ≥ 30fps
|
||||
- 响应时间 < 200ms
|
||||
|
||||
#### 2.4.2 兼容性要求
|
||||
- 浏览器:Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
|
||||
- 分辨率:1366x768 ~ 3840x2160
|
||||
- 设备:桌面端优先,平板端适配
|
||||
|
||||
#### 2.4.3 设计规范
|
||||
- 遵循`claude.md`中的superdesign规范
|
||||
- 采用现代化UI设计语言
|
||||
- 保持视觉一致性和品牌调性
|
||||
|
||||
---
|
||||
|
||||
## 三、技术方案
|
||||
|
||||
### 3.1 技术栈选择
|
||||
|
||||
```javascript
|
||||
const techStack = {
|
||||
framework: "React 18 / Vue 3",
|
||||
styling: "Tailwind CSS + Framer Motion",
|
||||
stateManagement: "Zustand / Pinia",
|
||||
visualization: "D3.js / Mermaid.js",
|
||||
animation: "GSAP / Lottie",
|
||||
build: "Vite",
|
||||
deployment: "Vercel / Netlify"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 架构设计
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Frontend[前端架构]
|
||||
UI[UI层]
|
||||
State[状态管理]
|
||||
Animation[动画引擎]
|
||||
Data[数据层]
|
||||
end
|
||||
|
||||
subgraph Components[核心组件]
|
||||
Workflow[工作流组件]
|
||||
Agent[Agent组件]
|
||||
Content[内容展示组件]
|
||||
Control[控制组件]
|
||||
end
|
||||
|
||||
UI --> State
|
||||
State --> Animation
|
||||
Animation --> Data
|
||||
|
||||
Workflow --> Agent
|
||||
Agent --> Content
|
||||
Content --> Control
|
||||
```
|
||||
|
||||
### 3.3 数据流设计
|
||||
|
||||
```typescript
|
||||
interface DemoState {
|
||||
// 演示状态
|
||||
status: 'idle' | 'running' | 'paused' | 'completed'
|
||||
currentPhase: number
|
||||
currentAgent: string
|
||||
progress: number
|
||||
|
||||
// Agent状态
|
||||
agents: {
|
||||
[key: string]: {
|
||||
status: 'waiting' | 'thinking' | 'generating' | 'done'
|
||||
input: string
|
||||
output: string
|
||||
startTime: number
|
||||
endTime: number
|
||||
}
|
||||
}
|
||||
|
||||
// 内容状态
|
||||
generatedContent: {
|
||||
[section: string]: {
|
||||
title: string
|
||||
content: string
|
||||
images: string[]
|
||||
isComplete: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// 控制状态
|
||||
controls: {
|
||||
speed: number // 1-5倍速
|
||||
autoAdvance: boolean
|
||||
showDetails: boolean
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、实施计划
|
||||
|
||||
### 4.1 开发阶段
|
||||
|
||||
| 阶段 | 任务 | 工时 | 依赖 |
|
||||
|------|------|------|------|
|
||||
| **Phase 1** | 环境搭建、基础框架 | 4h | - |
|
||||
| **Phase 2** | 工作流可视化组件 | 8h | Phase 1 |
|
||||
| **Phase 3** | Agent执行动画 | 12h | Phase 2 |
|
||||
| **Phase 4** | 内容生成展示 | 8h | Phase 3 |
|
||||
| **Phase 5** | 成果展示页面 | 6h | Phase 4 |
|
||||
| **Phase 6** | 交互优化、调试 | 6h | Phase 5 |
|
||||
| **总计** | - | **44h** | - |
|
||||
|
||||
### 4.2 内容准备
|
||||
|
||||
```yaml
|
||||
需要完善的内容:
|
||||
1. Agent Prompts:
|
||||
- 确保每个Agent的prompt文件完整
|
||||
- 添加具体的输入输出示例
|
||||
|
||||
2. 演示数据:
|
||||
- 完善汽车展会策划案内容
|
||||
- 准备配图资源(至少20张)
|
||||
- 编写Agent对话脚本
|
||||
|
||||
3. 文案优化:
|
||||
- 系统介绍文案
|
||||
- 操作引导文案
|
||||
- 加载提示文案
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、风险评估
|
||||
|
||||
### 5.1 技术风险
|
||||
|
||||
| 风险项 | 概率 | 影响 | 应对措施 |
|
||||
|--------|------|------|----------|
|
||||
| 动画性能问题 | 中 | 高 | 使用CSS动画优先,避免复杂计算 |
|
||||
| 浏览器兼容性 | 低 | 中 | 使用成熟框架,充分测试 |
|
||||
| 内容加载慢 | 中 | 中 | 实施懒加载,CDN加速 |
|
||||
|
||||
### 5.2 内容风险
|
||||
|
||||
| 风险项 | 概率 | 影响 | 应对措施 |
|
||||
|--------|------|------|----------|
|
||||
| 内容不完整 | 高 | 高 | 提前准备,建立内容审核机制 |
|
||||
| Agent逻辑不清 | 中 | 高 | 详细设计Agent交互脚本 |
|
||||
| 演示时间过长 | 中 | 中 | 提供快进功能,优化内容长度 |
|
||||
|
||||
---
|
||||
|
||||
## 六、成功标准
|
||||
|
||||
### 6.1 功能完成度
|
||||
- [ ] 所有P0功能100%实现
|
||||
- [ ] P1功能至少80%实现
|
||||
- [ ] 无阻塞性bug
|
||||
|
||||
### 6.2 用户体验
|
||||
- [ ] 演示流程流畅,无明显卡顿
|
||||
- [ ] 动画效果自然,符合预期
|
||||
- [ ] 内容展示清晰,易于理解
|
||||
|
||||
### 6.3 演示效果
|
||||
- [ ] 能够完整展示Agent协作过程
|
||||
- [ ] 生成内容质量高,业务逻辑合理
|
||||
- [ ] 观众能够理解系统价值
|
||||
|
||||
---
|
||||
|
||||
## 七、附录
|
||||
|
||||
### 7.1 参考资源
|
||||
- 工作流定义:`web_frontend/doc/会展策划工作流.md`
|
||||
- 演示内容:`web_frontend/data/会展策划/汽车展会展策划案_DEMO.md`
|
||||
- Agent定义:`web_frontend/data/Agent_prompt/`
|
||||
- 设计规范:`claude.md` (superdesign section)
|
||||
|
||||
### 7.2 关键决策点
|
||||
|
||||
需要与您确认的问题:
|
||||
|
||||
1. **技术栈选择**
|
||||
- 倾向于React还是Vue?
|
||||
- 是否需要SSR支持?
|
||||
|
||||
2. **演示节奏**
|
||||
- 完整演示时长目标?(建议3-5分钟)
|
||||
- 是否需要支持手动控制每个步骤?
|
||||
|
||||
3. **内容深度**
|
||||
- 每个Agent输出内容的详细程度?
|
||||
- 是否需要展示完整的prompt和response?
|
||||
|
||||
4. **视觉风格**
|
||||
- 偏向科技感还是商务风?
|
||||
- 颜色主题偏好?
|
||||
|
||||
5. **部署方式**
|
||||
- 静态部署还是需要后端支持?
|
||||
- 是否需要数据统计功能?
|
||||
|
||||
### 7.3 下一步行动
|
||||
|
||||
1. **立即行动**
|
||||
- 确认技术方案
|
||||
- 完善内容素材
|
||||
- 搭建开发环境
|
||||
|
||||
2. **短期目标**(1周内)
|
||||
- 完成核心功能开发
|
||||
- 实现基础动画效果
|
||||
|
||||
3. **长期优化**
|
||||
- 性能优化
|
||||
- 用户反馈收集
|
||||
- 迭代改进
|
||||
|
||||
---
|
||||
|
||||
*文档版本:v2.0*
|
||||
*更新时间:2025-09-08*
|
||||
*负责人:[待定]*
|
||||
151
web_frontend/exhibition-demo/README.md
Normal file
151
web_frontend/exhibition-demo/README.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# AI 会展策划系统 - 多Agent协同演示
|
||||
|
||||
## 🚀 项目概述
|
||||
|
||||
这是一个基于React构建的多Agent协同演示系统,展示了7个专业AI Agent如何协同工作,生成完整的会展策划方案。设计风格参考了字节跳动、Flowith、Raycast等现代化产品。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
- **7个专业Agent**:信息检索、设计、财务预算、格式编辑、活动执行、营销宣传、中央协调
|
||||
- **实时工作流可视化**:动态展示Agent协作流程
|
||||
- **打字机效果**:30-40字/秒的文字生成动画
|
||||
- **现代化UI设计**:简洁、实用、年轻化的界面风格
|
||||
- **完整演示流程**:约3分钟完整展示策划方案生成过程
|
||||
|
||||
## 🛠 技术栈
|
||||
|
||||
- **框架**: React 18 + TypeScript
|
||||
- **样式**: Tailwind CSS
|
||||
- **动画**: Framer Motion
|
||||
- **状态管理**: Zustand
|
||||
- **构建工具**: Vite
|
||||
|
||||
## 📦 安装与运行
|
||||
|
||||
### 前置要求
|
||||
|
||||
- Node.js >= 16.0.0
|
||||
- npm >= 7.0.0 或 pnpm >= 6.0.0
|
||||
|
||||
### 安装步骤
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd web_frontend/exhibition-demo
|
||||
|
||||
# 安装依赖(推荐使用pnpm)
|
||||
pnpm install
|
||||
# 或使用npm
|
||||
npm install
|
||||
|
||||
# 启动开发服务器
|
||||
pnpm dev
|
||||
# 或
|
||||
npm run dev
|
||||
```
|
||||
|
||||
开发服务器将在 `http://localhost:5173` 启动
|
||||
|
||||
### 构建生产版本
|
||||
|
||||
```bash
|
||||
# 构建项目
|
||||
pnpm build
|
||||
# 或
|
||||
npm run build
|
||||
|
||||
# 预览构建结果
|
||||
pnpm preview
|
||||
# 或
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
exhibition-demo/
|
||||
├── src/
|
||||
│ ├── components/ # React组件
|
||||
│ │ ├── AgentCard.tsx # Agent卡片组件
|
||||
│ │ ├── ContentGenerator.tsx # 内容生成器
|
||||
│ │ ├── ProgressBar.tsx # 进度条
|
||||
│ │ └── WorkflowVisualization.tsx # 工作流可视化
|
||||
│ ├── pages/ # 页面组件
|
||||
│ │ ├── LandingPage.tsx # 启动页
|
||||
│ │ ├── WorkflowPage.tsx # 工作流演示页
|
||||
│ │ └── ResultPage.tsx # 结果展示页
|
||||
│ ├── store/ # 状态管理
|
||||
│ │ └── demoStore.ts # Zustand store
|
||||
│ ├── styles/ # 样式文件
|
||||
│ ├── utils/ # 工具函数
|
||||
│ ├── App.tsx # 主应用组件
|
||||
│ ├── main.tsx # 入口文件
|
||||
│ └── index.css # 全局样式
|
||||
├── public/ # 静态资源
|
||||
├── index.html # HTML模板
|
||||
├── package.json # 项目配置
|
||||
├── tailwind.config.js # Tailwind配置
|
||||
├── vite.config.ts # Vite配置
|
||||
└── tsconfig.json # TypeScript配置
|
||||
```
|
||||
|
||||
## 🎯 使用说明
|
||||
|
||||
1. **启动演示**:访问首页,点击"开始演示"按钮
|
||||
2. **观看流程**:系统将自动展示Agent协作过程
|
||||
3. **查看结果**:演示完成后,可以浏览完整的策划方案
|
||||
4. **控制选项**:支持暂停/继续/重新开始
|
||||
|
||||
## 🎨 设计特点
|
||||
|
||||
- **Glass Morphism**:玻璃态效果,增强层次感
|
||||
- **渐变色彩**:蓝紫渐变主题,科技感十足
|
||||
- **微动画**:精致的过渡和交互动画
|
||||
- **响应式布局**:适配不同屏幕尺寸
|
||||
|
||||
## 📊 演示内容
|
||||
|
||||
演示案例为"2024长三角国际新能源汽车与智能交通产业博览会",包含:
|
||||
|
||||
1. **策划案概述**
|
||||
2. **展会介绍与预期效果**
|
||||
3. **营销方案**
|
||||
4. **现场运营方案**
|
||||
5. **预算与收益分析**
|
||||
6. **风险评估与应急预案**
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### 调整演示速度
|
||||
|
||||
在 `src/store/demoStore.ts` 中修改:
|
||||
|
||||
```typescript
|
||||
controls: {
|
||||
speed: 1, // 1-5倍速
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 修改文字生成速度
|
||||
|
||||
在组件中调整 `speed` 参数(默认35字/秒)
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
欢迎提交Issue和Pull Request来改进项目。
|
||||
|
||||
## 📝 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
- n8n Workflow Platform
|
||||
- DeepSeek AI
|
||||
- Google Gemini
|
||||
- 所有开源项目贡献者
|
||||
|
||||
---
|
||||
|
||||
如有问题,请联系项目维护者。
|
||||
16
web_frontend/exhibition-demo/index.html
Normal file
16
web_frontend/exhibition-demo/index.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>AI 会展策划系统 - 多Agent协同演示</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
38
web_frontend/exhibition-demo/package.json
Normal file
38
web_frontend/exhibition-demo/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "exhibition-demo",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"framer-motion": "^11.0.0",
|
||||
"zustand": "^4.5.0",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"lucide-react": "^0.300.0",
|
||||
"clsx": "^2.1.0",
|
||||
"tailwind-merge": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/react-syntax-highlighter": "^15.5.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
"@typescript-eslint/parser": "^6.14.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.4.32",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.0.8"
|
||||
}
|
||||
}
|
||||
6
web_frontend/exhibition-demo/postcss.config.js
Normal file
6
web_frontend/exhibition-demo/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
72
web_frontend/exhibition-demo/src/App.tsx
Normal file
72
web_frontend/exhibition-demo/src/App.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import React, { useState } from 'react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import LandingPage from './pages/LandingPage';
|
||||
import WorkflowPage from './pages/WorkflowPage';
|
||||
import ResultPage from './pages/ResultPage';
|
||||
import { useDemoStore } from './store/demoStore';
|
||||
|
||||
type PageType = 'landing' | 'workflow' | 'result';
|
||||
|
||||
function App() {
|
||||
const [currentPage, setCurrentPage] = useState<PageType>('landing');
|
||||
const { status } = useDemoStore();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (status === 'completed') {
|
||||
setCurrentPage('result');
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
const handleStartDemo = () => {
|
||||
setCurrentPage('workflow');
|
||||
};
|
||||
|
||||
const handleRestart = () => {
|
||||
useDemoStore.getState().reset();
|
||||
setCurrentPage('landing');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-neutral-50 to-neutral-100 dark:from-neutral-950 dark:to-neutral-900">
|
||||
<AnimatePresence mode="wait">
|
||||
{currentPage === 'landing' && (
|
||||
<motion.div
|
||||
key="landing"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<LandingPage onStart={handleStartDemo} />
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{currentPage === 'workflow' && (
|
||||
<motion.div
|
||||
key="workflow"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<WorkflowPage />
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{currentPage === 'result' && (
|
||||
<motion.div
|
||||
key="result"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<ResultPage onRestart={handleRestart} />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
156
web_frontend/exhibition-demo/src/components/AgentCard.tsx
Normal file
156
web_frontend/exhibition-demo/src/components/AgentCard.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Agent } from '../store/demoStore';
|
||||
import { Loader2, Check, Brain } from 'lucide-react';
|
||||
|
||||
interface AgentCardProps {
|
||||
agent: Agent;
|
||||
isActive: boolean;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
const AgentCard: React.FC<AgentCardProps> = ({ agent, isActive, compact = false }) => {
|
||||
const getStatusIcon = () => {
|
||||
switch (agent.status) {
|
||||
case 'thinking':
|
||||
return <Brain className="w-4 h-4 animate-pulse" />;
|
||||
case 'generating':
|
||||
return <Loader2 className="w-4 h-4 animate-spin" />;
|
||||
case 'done':
|
||||
return <Check className="w-4 h-4 text-green-500" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusColor = () => {
|
||||
switch (agent.status) {
|
||||
case 'thinking':
|
||||
return 'bg-blue-500';
|
||||
case 'generating':
|
||||
return 'bg-purple-500';
|
||||
case 'done':
|
||||
return 'bg-green-500';
|
||||
default:
|
||||
return 'bg-neutral-400';
|
||||
}
|
||||
};
|
||||
|
||||
if (compact) {
|
||||
return (
|
||||
<motion.div
|
||||
className={`
|
||||
relative p-4 rounded-xl transition-all duration-300
|
||||
${isActive ? 'agent-card active' : 'agent-card'}
|
||||
${isActive ? 'scale-105' : 'hover:scale-102'}
|
||||
`}
|
||||
whileHover={{ y: -2 }}
|
||||
>
|
||||
{/* Status Indicator */}
|
||||
<div className={`absolute top-2 right-2 w-2 h-2 rounded-full ${getStatusColor()}`} />
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-2xl">{agent.icon}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-sm truncate">{agent.name}</h3>
|
||||
<p className="text-xs text-neutral-500 dark:text-neutral-400 truncate">
|
||||
{agent.model}
|
||||
</p>
|
||||
</div>
|
||||
{getStatusIcon()}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className={`
|
||||
relative p-6 rounded-2xl transition-all duration-300
|
||||
${isActive ? 'agent-card active' : 'agent-card'}
|
||||
`}
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-4xl">{agent.icon}</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold">{agent.name}</h3>
|
||||
<p className="text-sm text-neutral-500 dark:text-neutral-400">
|
||||
{agent.model}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{getStatusIcon()}
|
||||
<div className={`w-3 h-3 rounded-full ${getStatusColor()} animate-pulse`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Role Description */}
|
||||
<p className="text-sm text-neutral-600 dark:text-neutral-300 mb-4">
|
||||
{agent.role}
|
||||
</p>
|
||||
|
||||
{/* Prompt Display (if active) */}
|
||||
{isActive && agent.prompt && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
className="mt-4 p-4 bg-neutral-100 dark:bg-neutral-800 rounded-lg"
|
||||
>
|
||||
<h4 className="text-sm font-semibold mb-2 text-neutral-700 dark:text-neutral-300">
|
||||
Prompt:
|
||||
</h4>
|
||||
<TypewriterText text={agent.prompt} speed={40} />
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Output Display (if available) */}
|
||||
{agent.output && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 rounded-lg border border-green-200 dark:border-green-800"
|
||||
>
|
||||
<h4 className="text-sm font-semibold mb-2 text-green-700 dark:text-green-300">
|
||||
输出:
|
||||
</h4>
|
||||
<p className="text-sm text-neutral-700 dark:text-neutral-300">
|
||||
{agent.output}
|
||||
</p>
|
||||
</motion.div>
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
// Typewriter Component
|
||||
const TypewriterText: React.FC<{ text: string; speed: number }> = ({ text, speed }) => {
|
||||
const [displayedText, setDisplayedText] = React.useState('');
|
||||
const [currentIndex, setCurrentIndex] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (currentIndex < text.length) {
|
||||
const timeout = setTimeout(() => {
|
||||
setDisplayedText(prev => prev + text[currentIndex]);
|
||||
setCurrentIndex(prev => prev + 1);
|
||||
}, speed);
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
}, [currentIndex, text, speed]);
|
||||
|
||||
return (
|
||||
<p className="text-sm font-mono text-neutral-600 dark:text-neutral-400">
|
||||
{displayedText}
|
||||
{currentIndex < text.length && (
|
||||
<span className="inline-block w-2 h-4 bg-neutral-400 animate-pulse ml-1" />
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export default AgentCard;
|
||||
104
web_frontend/exhibition-demo/src/components/ContentGenerator.tsx
Normal file
104
web_frontend/exhibition-demo/src/components/ContentGenerator.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { FileText, Image, Loader2 } from 'lucide-react';
|
||||
|
||||
interface ContentGeneratorProps {
|
||||
content?: string;
|
||||
images?: string[];
|
||||
speed?: number;
|
||||
}
|
||||
|
||||
const ContentGenerator: React.FC<ContentGeneratorProps> = ({
|
||||
content = "正在生成会展策划方案内容...",
|
||||
images = [],
|
||||
speed = 35
|
||||
}) => {
|
||||
const [displayedContent, setDisplayedContent] = useState('');
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [loadingImages, setLoadingImages] = useState<Set<number>>(new Set());
|
||||
|
||||
useEffect(() => {
|
||||
if (currentIndex < content.length) {
|
||||
const timeout = setTimeout(() => {
|
||||
setDisplayedContent(prev => prev + content[currentIndex]);
|
||||
setCurrentIndex(prev => prev + 1);
|
||||
}, speed);
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
}, [currentIndex, content, speed]);
|
||||
|
||||
const handleImageLoad = (index: number) => {
|
||||
setLoadingImages(prev => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.delete(index);
|
||||
return newSet;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Text Content */}
|
||||
<div className="p-4 bg-neutral-50 dark:bg-neutral-900 rounded-lg">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<FileText className="w-4 h-4 text-blue-500" />
|
||||
<span className="text-sm font-medium text-neutral-600 dark:text-neutral-400">
|
||||
生成中...
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="font-mono text-sm text-neutral-700 dark:text-neutral-300 whitespace-pre-wrap">
|
||||
{displayedContent}
|
||||
{currentIndex < content.length && (
|
||||
<span className="inline-block w-2 h-4 bg-blue-500 animate-pulse ml-1" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
{images.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Image className="w-4 h-4 text-purple-500" />
|
||||
<span className="text-sm font-medium text-neutral-600 dark:text-neutral-400">
|
||||
相关图片
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{images.map((src, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: index * 0.2 }}
|
||||
className="relative aspect-video rounded-lg overflow-hidden bg-neutral-100 dark:bg-neutral-800"
|
||||
>
|
||||
{loadingImages.has(index) && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<Loader2 className="w-6 h-6 animate-spin text-neutral-400" />
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
src={src}
|
||||
alt={`Generated content ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
onLoad={() => handleImageLoad(index)}
|
||||
onLoadStart={() => setLoadingImages(prev => new Set(prev).add(index))}
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stats */}
|
||||
<div className="flex items-center gap-6 text-xs text-neutral-500 dark:text-neutral-400">
|
||||
<span>字符: {displayedContent.length}</span>
|
||||
<span>速度: {speed}字/秒</span>
|
||||
<span>进度: {Math.round((currentIndex / content.length) * 100)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContentGenerator;
|
||||
29
web_frontend/exhibition-demo/src/components/ProgressBar.tsx
Normal file
29
web_frontend/exhibition-demo/src/components/ProgressBar.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface ProgressBarProps {
|
||||
progress: number;
|
||||
}
|
||||
|
||||
const ProgressBar: React.FC<ProgressBarProps> = ({ progress }) => {
|
||||
return (
|
||||
<div className="relative w-full h-2 bg-neutral-200 dark:bg-neutral-800 rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
className="absolute left-0 top-0 h-full bg-gradient-to-r from-blue-500 to-purple-500 rounded-full"
|
||||
initial={{ width: '0%' }}
|
||||
animate={{ width: `${progress}%` }}
|
||||
transition={{ duration: 0.5, ease: 'easeInOut' }}
|
||||
>
|
||||
{/* Shimmer effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent animate-shimmer" />
|
||||
</motion.div>
|
||||
|
||||
{/* Progress text */}
|
||||
<div className="absolute -top-8 left-0 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
进度: {Math.round(progress)}%
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressBar;
|
||||
@@ -0,0 +1,178 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Agent } from '../store/demoStore';
|
||||
|
||||
interface WorkflowVisualizationProps {
|
||||
agents: Agent[];
|
||||
currentAgent: string | null;
|
||||
}
|
||||
|
||||
const WorkflowVisualization: React.FC<WorkflowVisualizationProps> = ({
|
||||
agents,
|
||||
currentAgent
|
||||
}) => {
|
||||
// 定义工作流布局
|
||||
const layout = {
|
||||
phase1: ['retrieval', 'design', 'budget'],
|
||||
coordinator: ['coordinator'],
|
||||
phase2: ['format', 'execution', 'marketing'],
|
||||
};
|
||||
|
||||
const getNodePosition = (agentId: string) => {
|
||||
if (layout.phase1.includes(agentId)) {
|
||||
const index = layout.phase1.indexOf(agentId);
|
||||
return { x: 100 + index * 150, y: 50 };
|
||||
}
|
||||
if (agentId === 'coordinator') {
|
||||
return { x: 250, y: 200 };
|
||||
}
|
||||
if (layout.phase2.includes(agentId)) {
|
||||
const index = layout.phase2.indexOf(agentId);
|
||||
return { x: 100 + index * 150, y: 350 };
|
||||
}
|
||||
return { x: 0, y: 0 };
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-[450px]">
|
||||
<svg className="absolute inset-0 w-full h-full">
|
||||
{/* Draw connections */}
|
||||
{/* Phase 1 to Coordinator */}
|
||||
{layout.phase1.map((agentId) => {
|
||||
const start = getNodePosition(agentId);
|
||||
const end = getNodePosition('coordinator');
|
||||
return (
|
||||
<motion.line
|
||||
key={`${agentId}-coordinator`}
|
||||
x1={start.x}
|
||||
y1={start.y + 30}
|
||||
x2={end.x}
|
||||
y2={end.y - 30}
|
||||
stroke="url(#gradient)"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="5,5"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 2, delay: 0.5 }}
|
||||
className={currentAgent === agentId ? 'animate-pulse' : ''}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Coordinator to Phase 2 */}
|
||||
{layout.phase2.map((agentId) => {
|
||||
const start = getNodePosition('coordinator');
|
||||
const end = getNodePosition(agentId);
|
||||
return (
|
||||
<motion.line
|
||||
key={`coordinator-${agentId}`}
|
||||
x1={start.x}
|
||||
y1={start.y + 30}
|
||||
x2={end.x}
|
||||
y2={end.y - 30}
|
||||
stroke="url(#gradient)"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="5,5"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 2, delay: 1 }}
|
||||
className={currentAgent === agentId ? 'animate-pulse' : ''}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Phase 2 back to Coordinator */}
|
||||
{layout.phase2.map((agentId) => {
|
||||
const start = getNodePosition(agentId);
|
||||
const end = getNodePosition('coordinator');
|
||||
return (
|
||||
<motion.line
|
||||
key={`${agentId}-coordinator-back`}
|
||||
x1={start.x}
|
||||
y1={start.y - 30}
|
||||
x2={end.x}
|
||||
y2={end.y + 30}
|
||||
stroke="url(#gradient)"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="5,5"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 2, delay: 1.5 }}
|
||||
className={currentAgent === agentId ? 'animate-pulse' : ''}
|
||||
opacity={0.5}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Gradient definition */}
|
||||
<defs>
|
||||
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor="#3b82f6" />
|
||||
<stop offset="100%" stopColor="#8b5cf6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
{/* Agent Nodes */}
|
||||
{agents.map((agent) => {
|
||||
const position = getNodePosition(agent.id);
|
||||
const isActive = currentAgent === agent.id;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={agent.id}
|
||||
className={`
|
||||
absolute transform -translate-x-1/2 -translate-y-1/2
|
||||
w-20 h-20 rounded-2xl flex flex-col items-center justify-center
|
||||
${isActive ? 'bg-gradient-to-br from-blue-500 to-purple-500 scale-110' : 'bg-white dark:bg-neutral-800'}
|
||||
${isActive ? 'shadow-2xl' : 'shadow-lg'}
|
||||
transition-all duration-300
|
||||
${!isActive && 'hover:scale-105'}
|
||||
border-2 ${isActive ? 'border-transparent' : 'border-neutral-200 dark:border-neutral-700'}
|
||||
`}
|
||||
style={{ left: position.x, top: position.y }}
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{
|
||||
delay: agents.indexOf(agent) * 0.1,
|
||||
type: 'spring',
|
||||
stiffness: 200
|
||||
}}
|
||||
>
|
||||
<span className={`text-2xl ${isActive ? 'scale-125' : ''}`}>
|
||||
{agent.icon}
|
||||
</span>
|
||||
<span className={`
|
||||
text-xs mt-1 font-medium text-center px-1
|
||||
${isActive ? 'text-white' : 'text-neutral-700 dark:text-neutral-300'}
|
||||
`}>
|
||||
{agent.name.split('专家')[0]}
|
||||
</span>
|
||||
|
||||
{/* Status dot */}
|
||||
<div className={`
|
||||
absolute -top-1 -right-1 w-3 h-3 rounded-full
|
||||
${agent.status === 'done' ? 'bg-green-500' : ''}
|
||||
${agent.status === 'thinking' ? 'bg-blue-500 animate-pulse' : ''}
|
||||
${agent.status === 'generating' ? 'bg-purple-500 animate-pulse' : ''}
|
||||
${agent.status === 'waiting' ? 'bg-neutral-400' : ''}
|
||||
`} />
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Labels */}
|
||||
<div className="absolute top-0 left-0 text-sm text-neutral-500 dark:text-neutral-400">
|
||||
第一阶段:信息收集
|
||||
</div>
|
||||
<div className="absolute top-[180px] left-[180px] text-sm text-neutral-500 dark:text-neutral-400">
|
||||
中央协调
|
||||
</div>
|
||||
<div className="absolute bottom-20 left-0 text-sm text-neutral-500 dark:text-neutral-400">
|
||||
第二阶段:方案细化
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkflowVisualization;
|
||||
140
web_frontend/exhibition-demo/src/index.css
Normal file
140
web_frontend/exhibition-demo/src/index.css
Normal file
@@ -0,0 +1,140 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--primary: 217.2 91.2% 59.8%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--primary: 217.2 91.2% 59.8%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-neutral-200 dark:border-neutral-800;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-50;
|
||||
font-feature-settings: "rlig" 1, "calt" 1;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.glass-morphism {
|
||||
@apply backdrop-blur-xl bg-white/80 dark:bg-neutral-900/80 border border-neutral-200/50 dark:border-neutral-800/50;
|
||||
}
|
||||
|
||||
.gradient-border {
|
||||
background: linear-gradient(white, white) padding-box,
|
||||
linear-gradient(to right, #3b82f6, #8b5cf6) border-box;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.text-gradient {
|
||||
@apply bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent;
|
||||
}
|
||||
|
||||
.animate-typewriter {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
border-right: 2px solid;
|
||||
animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite;
|
||||
}
|
||||
|
||||
@keyframes typing {
|
||||
from { width: 0 }
|
||||
to { width: 100% }
|
||||
}
|
||||
|
||||
@keyframes blink-caret {
|
||||
from, to { border-color: transparent }
|
||||
50% { border-color: currentColor }
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
@apply inline-block w-8 h-8 border-4 border-neutral-200 dark:border-neutral-800 rounded-full;
|
||||
border-top-color: #3b82f6;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg) }
|
||||
}
|
||||
|
||||
.agent-card {
|
||||
@apply relative overflow-hidden rounded-2xl p-6 transition-all duration-300;
|
||||
@apply hover:scale-105 hover:shadow-2xl;
|
||||
@apply bg-gradient-to-br from-neutral-50 to-neutral-100;
|
||||
@apply dark:from-neutral-900 dark:to-neutral-800;
|
||||
}
|
||||
|
||||
.agent-card.active {
|
||||
@apply ring-2 ring-blue-500 ring-offset-2 dark:ring-offset-neutral-950;
|
||||
animation: pulse-glow 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.workflow-line {
|
||||
stroke-dasharray: 1000;
|
||||
stroke-dashoffset: 1000;
|
||||
animation: draw 2s ease-in-out forwards;
|
||||
}
|
||||
|
||||
@keyframes draw {
|
||||
to {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content-section {
|
||||
@apply opacity-0 transform translate-y-4;
|
||||
animation: fadeInUp 0.6s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: theme('colors.neutral.400') transparent;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
@apply bg-neutral-400 dark:bg-neutral-600 rounded-full;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-neutral-500 dark:bg-neutral-500;
|
||||
}
|
||||
}
|
||||
10
web_frontend/exhibition-demo/src/main.tsx
Normal file
10
web_frontend/exhibition-demo/src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
131
web_frontend/exhibition-demo/src/pages/LandingPage.tsx
Normal file
131
web_frontend/exhibition-demo/src/pages/LandingPage.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowRight, Sparkles, Layers, Zap } from 'lucide-react';
|
||||
|
||||
interface LandingPageProps {
|
||||
onStart: () => void;
|
||||
}
|
||||
|
||||
const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center p-8">
|
||||
<div className="max-w-4xl w-full">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center space-y-8"
|
||||
>
|
||||
{/* Logo/Icon */}
|
||||
<motion.div
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ delay: 0.2, type: "spring", stiffness: 200 }}
|
||||
className="inline-flex items-center justify-center w-24 h-24 rounded-3xl bg-gradient-to-br from-blue-500 to-purple-500 shadow-2xl"
|
||||
>
|
||||
<Sparkles className="w-12 h-12 text-white" />
|
||||
</motion.div>
|
||||
|
||||
{/* Title */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
>
|
||||
<h1 className="text-6xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
|
||||
AI 会展策划系统
|
||||
</h1>
|
||||
<p className="mt-4 text-xl text-neutral-600 dark:text-neutral-400">
|
||||
多Agent协同 · 智能生成 · 专业方案
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Features */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="grid grid-cols-3 gap-6 mt-12"
|
||||
>
|
||||
<div className="glass-morphism rounded-2xl p-6 hover:scale-105 transition-transform">
|
||||
<Layers className="w-8 h-8 text-blue-500 mb-4" />
|
||||
<h3 className="font-semibold text-lg mb-2">7个专业Agent</h3>
|
||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
覆盖策划全流程
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="glass-morphism rounded-2xl p-6 hover:scale-105 transition-transform">
|
||||
<Zap className="w-8 h-8 text-purple-500 mb-4" />
|
||||
<h3 className="font-semibold text-lg mb-2">实时协作</h3>
|
||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
智能工作流编排
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="glass-morphism rounded-2xl p-6 hover:scale-105 transition-transform">
|
||||
<Sparkles className="w-8 h-8 text-green-500 mb-4" />
|
||||
<h3 className="font-semibold text-lg mb-2">专业输出</h3>
|
||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
完整策划方案
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Demo Info */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.8 }}
|
||||
className="glass-morphism rounded-2xl p-8 text-left"
|
||||
>
|
||||
<h2 className="text-2xl font-semibold mb-4">演示案例</h2>
|
||||
<div className="space-y-2 text-neutral-600 dark:text-neutral-400">
|
||||
<p>
|
||||
<span className="font-medium text-neutral-900 dark:text-neutral-100">项目:</span>
|
||||
2024长三角国际新能源汽车与智能交通产业博览会
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-medium text-neutral-900 dark:text-neutral-100">规模:</span>
|
||||
50,000平方米展览面积,350家参展商,预计50,000人次参观
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-medium text-neutral-900 dark:text-neutral-100">时长:</span>
|
||||
约3分钟完整演示
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Start Button */}
|
||||
<motion.button
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: 1 }}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={onStart}
|
||||
className="group relative inline-flex items-center gap-3 px-8 py-4 bg-gradient-to-r from-blue-500 to-purple-500 text-white font-semibold text-lg rounded-2xl shadow-xl hover:shadow-2xl transition-all"
|
||||
>
|
||||
<span>开始演示</span>
|
||||
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
||||
|
||||
{/* Glow effect */}
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-r from-blue-500 to-purple-500 blur-xl opacity-50 group-hover:opacity-70 transition-opacity" />
|
||||
</motion.button>
|
||||
|
||||
{/* Footer */}
|
||||
<motion.p
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 1.2 }}
|
||||
className="text-sm text-neutral-500 dark:text-neutral-500 mt-8"
|
||||
>
|
||||
Powered by n8n Workflow · DeepSeek · Google Gemini
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LandingPage;
|
||||
261
web_frontend/exhibition-demo/src/pages/ResultPage.tsx
Normal file
261
web_frontend/exhibition-demo/src/pages/ResultPage.tsx
Normal file
@@ -0,0 +1,261 @@
|
||||
import React, { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
FileText,
|
||||
Target,
|
||||
TrendingUp,
|
||||
Calendar,
|
||||
DollarSign,
|
||||
AlertTriangle,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
RotateCcw,
|
||||
Download
|
||||
} from 'lucide-react';
|
||||
import { useDemoStore } from '../store/demoStore';
|
||||
|
||||
interface ResultPageProps {
|
||||
onRestart: () => void;
|
||||
}
|
||||
|
||||
const sections = [
|
||||
{ id: 'overview', title: '策划案概述', icon: FileText },
|
||||
{ id: 'introduction', title: '展会介绍与预期效果', icon: Target },
|
||||
{ id: 'marketing', title: '营销方案', icon: TrendingUp },
|
||||
{ id: 'operation', title: '现场运营方案', icon: Calendar },
|
||||
{ id: 'budget', title: '预算与收益分析', icon: DollarSign },
|
||||
{ id: 'risk', title: '风险评估与应急预案', icon: AlertTriangle },
|
||||
];
|
||||
|
||||
const ResultPage: React.FC<ResultPageProps> = ({ onRestart }) => {
|
||||
const [currentSection, setCurrentSection] = useState(0);
|
||||
const { generatedContent } = useDemoStore();
|
||||
|
||||
const handlePrevious = () => {
|
||||
if (currentSection > 0) {
|
||||
setCurrentSection(currentSection - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (currentSection < sections.length - 1) {
|
||||
setCurrentSection(currentSection + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Mock content for demonstration
|
||||
const getSectionContent = (sectionId: string) => {
|
||||
const mockContent = {
|
||||
overview: {
|
||||
title: '2024长三角国际新能源汽车与智能交通产业博览会',
|
||||
content: `
|
||||
## 策划背景
|
||||
在全球碳中和目标推动下,新能源汽车产业迎来爆发式增长。长三角地区作为中国汽车产业核心集群,聚集了特斯拉、上汽、蔚来等龙头企业。
|
||||
|
||||
## 策划目的
|
||||
- 打造长三角地区新能源汽车与智能交通领域第一展会品牌
|
||||
- 吸引300家优质展商,实现现场意向交易额超8亿元人民币
|
||||
- 促进产业链上下游合作,推动技术创新和产业升级
|
||||
- 推广绿色出行理念,助力碳中和目标实现
|
||||
`,
|
||||
images: ['/api/placeholder/600/400', '/api/placeholder/600/400']
|
||||
},
|
||||
introduction: {
|
||||
title: '展会规模与预期',
|
||||
content: `
|
||||
## 展会主题
|
||||
「智行未来,绿动长三角」
|
||||
|
||||
## 预计规模
|
||||
- 展览面积:50,000平方米
|
||||
- 标准展位:1,200个(9平米/个)
|
||||
- 特装展位:20,000平方米
|
||||
- 参展商家数:350家
|
||||
- 参观人次:50,000人次
|
||||
|
||||
## 预期效果
|
||||
- 现场成交额预计超10亿元
|
||||
- 带动相关产业收入约30亿元
|
||||
- 媒体曝光量超1亿次
|
||||
`,
|
||||
images: []
|
||||
},
|
||||
// Add more sections...
|
||||
};
|
||||
|
||||
return mockContent[sectionId] || generatedContent[sectionId] || mockContent.overview;
|
||||
};
|
||||
|
||||
const currentSectionData = getSectionContent(sections[currentSection].id);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen p-6">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="mb-8"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-4xl font-bold text-gradient">
|
||||
会展策划方案
|
||||
</h1>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={() => {}}
|
||||
className="px-4 py-2 glass-morphism rounded-xl hover:scale-105 transition-transform flex items-center gap-2"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
导出方案
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={onRestart}
|
||||
className="px-4 py-2 glass-morphism rounded-xl hover:scale-105 transition-transform flex items-center gap-2"
|
||||
>
|
||||
<RotateCcw className="w-4 h-4" />
|
||||
重新演示
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Section Tabs */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="mb-8"
|
||||
>
|
||||
<div className="flex items-center gap-2 overflow-x-auto scrollbar-thin pb-2">
|
||||
{sections.map((section, index) => {
|
||||
const Icon = section.icon;
|
||||
return (
|
||||
<button
|
||||
key={section.id}
|
||||
onClick={() => setCurrentSection(index)}
|
||||
className={`
|
||||
flex items-center gap-2 px-4 py-2 rounded-xl transition-all whitespace-nowrap
|
||||
${currentSection === index
|
||||
? 'bg-gradient-to-r from-blue-500 to-purple-500 text-white shadow-lg'
|
||||
: 'glass-morphism hover:scale-105'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
<span className="font-medium">{section.title}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Content Area */}
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={currentSection}
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="glass-morphism rounded-2xl p-8"
|
||||
>
|
||||
{/* Section Header */}
|
||||
<div className="mb-6">
|
||||
<h2 className="text-3xl font-bold mb-2">
|
||||
{currentSectionData.title}
|
||||
</h2>
|
||||
<div className="h-1 w-20 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full" />
|
||||
</div>
|
||||
|
||||
{/* Section Content */}
|
||||
<div className="prose prose-neutral dark:prose-invert max-w-none">
|
||||
<div className="whitespace-pre-wrap text-neutral-700 dark:text-neutral-300">
|
||||
{currentSectionData.content}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
{currentSectionData.images && currentSectionData.images.length > 0 && (
|
||||
<div className="mt-8 grid grid-cols-2 gap-4">
|
||||
{currentSectionData.images.map((src, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="rounded-xl overflow-hidden shadow-lg"
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
alt={`Section image ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Navigation */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="mt-8 flex items-center justify-between"
|
||||
>
|
||||
<button
|
||||
onClick={handlePrevious}
|
||||
disabled={currentSection === 0}
|
||||
className={`
|
||||
px-4 py-2 rounded-xl flex items-center gap-2 transition-all
|
||||
${currentSection === 0
|
||||
? 'opacity-50 cursor-not-allowed glass-morphism'
|
||||
: 'glass-morphism hover:scale-105'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
上一页
|
||||
</button>
|
||||
|
||||
{/* Page Indicators */}
|
||||
<div className="flex items-center gap-2">
|
||||
{sections.map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`
|
||||
w-2 h-2 rounded-full transition-all
|
||||
${currentSection === index
|
||||
? 'w-8 bg-gradient-to-r from-blue-500 to-purple-500'
|
||||
: 'bg-neutral-400'
|
||||
}
|
||||
`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleNext}
|
||||
disabled={currentSection === sections.length - 1}
|
||||
className={`
|
||||
px-4 py-2 rounded-xl flex items-center gap-2 transition-all
|
||||
${currentSection === sections.length - 1
|
||||
? 'opacity-50 cursor-not-allowed glass-morphism'
|
||||
: 'glass-morphism hover:scale-105'
|
||||
}
|
||||
`}
|
||||
>
|
||||
下一页
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResultPage;
|
||||
169
web_frontend/exhibition-demo/src/pages/WorkflowPage.tsx
Normal file
169
web_frontend/exhibition-demo/src/pages/WorkflowPage.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useDemoStore } from '../store/demoStore';
|
||||
import AgentCard from '../components/AgentCard';
|
||||
import WorkflowVisualization from '../components/WorkflowVisualization';
|
||||
import ContentGenerator from '../components/ContentGenerator';
|
||||
import ProgressBar from '../components/ProgressBar';
|
||||
import { Play, Pause, RotateCcw } from 'lucide-react';
|
||||
|
||||
const WorkflowPage: React.FC = () => {
|
||||
const {
|
||||
status,
|
||||
agents,
|
||||
currentAgent,
|
||||
progress,
|
||||
startDemo,
|
||||
pauseDemo,
|
||||
resumeDemo,
|
||||
reset,
|
||||
} = useDemoStore();
|
||||
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// 自动开始演示
|
||||
if (status === 'idle') {
|
||||
setTimeout(() => {
|
||||
startDemo();
|
||||
}, 500);
|
||||
}
|
||||
}, [status, startDemo]);
|
||||
|
||||
const handleTogglePlay = () => {
|
||||
if (status === 'running') {
|
||||
pauseDemo();
|
||||
} else if (status === 'paused') {
|
||||
resumeDemo();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen p-6">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="mb-8"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h1 className="text-3xl font-bold text-gradient">
|
||||
AI Agent 协作流程
|
||||
</h1>
|
||||
|
||||
{/* Control Buttons */}
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={handleTogglePlay}
|
||||
className="p-3 rounded-xl glass-morphism hover:scale-105 transition-transform"
|
||||
>
|
||||
{status === 'running' ? (
|
||||
<Pause className="w-5 h-5" />
|
||||
) : (
|
||||
<Play className="w-5 h-5" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={reset}
|
||||
className="p-3 rounded-xl glass-morphism hover:scale-105 transition-transform"
|
||||
>
|
||||
<RotateCcw className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<ProgressBar progress={progress} />
|
||||
</motion.div>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<div className="grid grid-cols-12 gap-6">
|
||||
{/* Left: Workflow Visualization */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="col-span-5"
|
||||
>
|
||||
<div className="glass-morphism rounded-2xl p-6 h-full">
|
||||
<h2 className="text-xl font-semibold mb-4">工作流程图</h2>
|
||||
<WorkflowVisualization agents={agents} currentAgent={currentAgent} />
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Right: Content Area */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className="col-span-7 space-y-6"
|
||||
>
|
||||
{/* Active Agent Display */}
|
||||
<div className="glass-morphism rounded-2xl p-6">
|
||||
<h2 className="text-xl font-semibold mb-4">当前执行</h2>
|
||||
<AnimatePresence mode="wait">
|
||||
{currentAgent && (
|
||||
<motion.div
|
||||
key={currentAgent}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
>
|
||||
<AgentCard
|
||||
agent={agents.find(a => a.id === currentAgent)!}
|
||||
isActive={true}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* Content Generation Display */}
|
||||
{showContent && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="glass-morphism rounded-2xl p-6"
|
||||
>
|
||||
<h2 className="text-xl font-semibold mb-4">生成内容</h2>
|
||||
<ContentGenerator />
|
||||
</motion.div>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Bottom: Agent List */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="mt-8"
|
||||
>
|
||||
<div className="glass-morphism rounded-2xl p-6">
|
||||
<h2 className="text-xl font-semibold mb-4">Agent 团队</h2>
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{agents.map((agent) => (
|
||||
<motion.div
|
||||
key={agent.id}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: agents.indexOf(agent) * 0.1 }}
|
||||
>
|
||||
<AgentCard
|
||||
agent={agent}
|
||||
isActive={currentAgent === agent.id}
|
||||
compact={true}
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkflowPage;
|
||||
165
web_frontend/exhibition-demo/src/store/demoStore.ts
Normal file
165
web_frontend/exhibition-demo/src/store/demoStore.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export interface Agent {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
model: string;
|
||||
role: string;
|
||||
status: 'waiting' | 'thinking' | 'generating' | 'done';
|
||||
input?: string;
|
||||
output?: string;
|
||||
prompt?: string;
|
||||
}
|
||||
|
||||
export interface DemoState {
|
||||
// 演示状态
|
||||
status: 'idle' | 'running' | 'paused' | 'completed';
|
||||
currentPhase: number;
|
||||
currentAgent: string | null;
|
||||
progress: number;
|
||||
|
||||
// Agent配置
|
||||
agents: Agent[];
|
||||
|
||||
// 生成的内容
|
||||
generatedContent: {
|
||||
[key: string]: {
|
||||
title: string;
|
||||
content: string;
|
||||
images: string[];
|
||||
isComplete: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
// 控制选项
|
||||
controls: {
|
||||
speed: number; // 1-5倍速
|
||||
autoAdvance: boolean;
|
||||
showDetails: boolean;
|
||||
};
|
||||
|
||||
// Actions
|
||||
startDemo: () => void;
|
||||
pauseDemo: () => void;
|
||||
resumeDemo: () => void;
|
||||
setCurrentAgent: (agentId: string) => void;
|
||||
updateAgentStatus: (agentId: string, status: Agent['status']) => void;
|
||||
updateAgentOutput: (agentId: string, output: string) => void;
|
||||
addGeneratedContent: (section: string, content: any) => void;
|
||||
setProgress: (progress: number) => void;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
const initialAgents: Agent[] = [
|
||||
{
|
||||
id: 'retrieval',
|
||||
name: '信息检索专家',
|
||||
icon: '🔍',
|
||||
model: 'DeepSeek Chat Model5',
|
||||
role: '市场调研、数据收集、竞品分析',
|
||||
status: 'waiting',
|
||||
},
|
||||
{
|
||||
id: 'design',
|
||||
name: '设计专家',
|
||||
icon: '🎨',
|
||||
model: 'Google Gemini Chat Model2',
|
||||
role: '视觉设计、空间布局、品牌形象',
|
||||
status: 'waiting',
|
||||
},
|
||||
{
|
||||
id: 'budget',
|
||||
name: '财务预算专家',
|
||||
icon: '💰',
|
||||
model: 'DeepSeek Chat Model2',
|
||||
role: '成本核算、预算规划、ROI分析',
|
||||
status: 'waiting',
|
||||
},
|
||||
{
|
||||
id: 'format',
|
||||
name: '格式编辑专家',
|
||||
icon: '📝',
|
||||
model: 'DeepSeek Chat Model4',
|
||||
role: '文档格式化、内容结构优化',
|
||||
status: 'waiting',
|
||||
},
|
||||
{
|
||||
id: 'execution',
|
||||
name: '活动执行专家',
|
||||
icon: '⚡',
|
||||
model: 'DeepSeek Chat Model1',
|
||||
role: '执行计划、时间线管理、任务分配',
|
||||
status: 'waiting',
|
||||
},
|
||||
{
|
||||
id: 'marketing',
|
||||
name: '营销宣传专家',
|
||||
icon: '📢',
|
||||
model: 'DeepSeek Chat Model3',
|
||||
role: '推广策略、媒体规划、品牌传播',
|
||||
status: 'waiting',
|
||||
},
|
||||
{
|
||||
id: 'coordinator',
|
||||
name: '会展策划专家',
|
||||
icon: '🎯',
|
||||
model: 'Chat Models + Memories',
|
||||
role: '中央协调、方案整合、决策支持',
|
||||
status: 'waiting',
|
||||
},
|
||||
];
|
||||
|
||||
export const useDemoStore = create<DemoState>((set) => ({
|
||||
status: 'idle',
|
||||
currentPhase: 0,
|
||||
currentAgent: null,
|
||||
progress: 0,
|
||||
agents: initialAgents,
|
||||
generatedContent: {},
|
||||
controls: {
|
||||
speed: 1,
|
||||
autoAdvance: true,
|
||||
showDetails: true,
|
||||
},
|
||||
|
||||
startDemo: () => set({ status: 'running', progress: 0 }),
|
||||
pauseDemo: () => set({ status: 'paused' }),
|
||||
resumeDemo: () => set({ status: 'running' }),
|
||||
|
||||
setCurrentAgent: (agentId) => set({ currentAgent: agentId }),
|
||||
|
||||
updateAgentStatus: (agentId, status) =>
|
||||
set((state) => ({
|
||||
agents: state.agents.map((agent) =>
|
||||
agent.id === agentId ? { ...agent, status } : agent
|
||||
),
|
||||
})),
|
||||
|
||||
updateAgentOutput: (agentId, output) =>
|
||||
set((state) => ({
|
||||
agents: state.agents.map((agent) =>
|
||||
agent.id === agentId ? { ...agent, output } : agent
|
||||
),
|
||||
})),
|
||||
|
||||
addGeneratedContent: (section, content) =>
|
||||
set((state) => ({
|
||||
generatedContent: {
|
||||
...state.generatedContent,
|
||||
[section]: content,
|
||||
},
|
||||
})),
|
||||
|
||||
setProgress: (progress) => set({ progress }),
|
||||
|
||||
reset: () =>
|
||||
set({
|
||||
status: 'idle',
|
||||
currentPhase: 0,
|
||||
currentAgent: null,
|
||||
progress: 0,
|
||||
agents: initialAgents,
|
||||
generatedContent: {},
|
||||
}),
|
||||
}));
|
||||
86
web_frontend/exhibition-demo/tailwind.config.js
Normal file
86
web_frontend/exhibition-demo/tailwind.config.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
// 参考 Raycast / 字节跳动设计风格的配色
|
||||
primary: {
|
||||
50: '#f0f9ff',
|
||||
100: '#e0f2fe',
|
||||
200: '#bae6fd',
|
||||
300: '#7dd3fc',
|
||||
400: '#38bdf8',
|
||||
500: '#0ea5e9',
|
||||
600: '#0284c7',
|
||||
700: '#0369a1',
|
||||
800: '#075985',
|
||||
900: '#0c4a6e',
|
||||
},
|
||||
neutral: {
|
||||
50: '#fafafa',
|
||||
100: '#f5f5f5',
|
||||
200: '#e5e5e5',
|
||||
300: '#d4d4d4',
|
||||
400: '#a3a3a3',
|
||||
500: '#737373',
|
||||
600: '#525252',
|
||||
700: '#404040',
|
||||
800: '#262626',
|
||||
900: '#171717',
|
||||
950: '#0a0a0a',
|
||||
},
|
||||
accent: {
|
||||
purple: '#8b5cf6',
|
||||
blue: '#3b82f6',
|
||||
green: '#10b981',
|
||||
orange: '#f97316',
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'SF Pro Display', '-apple-system', 'BlinkMacSystemFont', 'system-ui', 'sans-serif'],
|
||||
mono: ['SF Mono', 'Monaco', 'Consolas', 'monospace'],
|
||||
},
|
||||
animation: {
|
||||
'fade-in': 'fadeIn 0.5s ease-in-out',
|
||||
'slide-up': 'slideUp 0.5s ease-out',
|
||||
'pulse-soft': 'pulseSoft 2s infinite',
|
||||
'typewriter': 'typewriter 0.1s steps(1)',
|
||||
'glow': 'glow 2s ease-in-out infinite',
|
||||
},
|
||||
keyframes: {
|
||||
fadeIn: {
|
||||
'0%': { opacity: '0' },
|
||||
'100%': { opacity: '1' },
|
||||
},
|
||||
slideUp: {
|
||||
'0%': { transform: 'translateY(10px)', opacity: '0' },
|
||||
'100%': { transform: 'translateY(0)', opacity: '1' },
|
||||
},
|
||||
pulseSoft: {
|
||||
'0%, 100%': { opacity: '1' },
|
||||
'50%': { opacity: '0.5' },
|
||||
},
|
||||
typewriter: {
|
||||
'0%': { opacity: '0' },
|
||||
'100%': { opacity: '1' },
|
||||
},
|
||||
glow: {
|
||||
'0%, 100%': {
|
||||
boxShadow: '0 0 5px rgba(59, 130, 246, 0.5), 0 0 20px rgba(59, 130, 246, 0.3)'
|
||||
},
|
||||
'50%': {
|
||||
boxShadow: '0 0 20px rgba(59, 130, 246, 0.7), 0 0 40px rgba(59, 130, 246, 0.5)'
|
||||
},
|
||||
},
|
||||
},
|
||||
backdropBlur: {
|
||||
xs: '2px',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
31
web_frontend/exhibition-demo/tsconfig.json
Normal file
31
web_frontend/exhibition-demo/tsconfig.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
/* Path mapping */
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
10
web_frontend/exhibition-demo/tsconfig.node.json
Normal file
10
web_frontend/exhibition-demo/tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
12
web_frontend/exhibition-demo/vite.config.ts
Normal file
12
web_frontend/exhibition-demo/vite.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': '/src',
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user