本项目是沙鲁智能体工作流平台前端应用,基于 Vue 3 + TypeScript + Vite 构建的企业级 SPA。
| 类别 | 技术选型 |
|---|---|
| 框架 | Vue 3.5 (Composition API, <script setup>) |
| 语言 | TypeScript 5.9 (strict mode) |
| 构建 | Vite 7 (rolldown-vite) |
| 路由 | Vue Router 4 (Hash 模式) |
| 状态管理 | Pinia 3 (Setup Store 语法) |
| UI 组件库 | Element Plus 2.x |
| CSS 方案 | UnoCSS + Less |
| 图标 | unplugin-icons (Iconify 生态) |
| 国际化 | 自研 i18n (zh-cn / en-us) |
| 代码编辑器 | Monaco Editor |
| 富文本 | TinyMCE + ByteMD (Markdown) |
| 图表 | ECharts 6 |
| 工作流编排 | @repo/workflow (内部 Monorepo 包) |
@repo/* workspace 引用共享包@repo/api-service — 由 OpenAPI schema 自动生成的 API 调用层@repo/api-client — 统一 HTTP 请求客户端 (基于 Axios)@repo/ui — 共享 UI 组件库@repo/workflow — 工作流编排引擎apps/web/
├── public/ # 静态资源
├── src/
│ ├── api/ # 通用 API 调用 (非 repo 生成的接口)
│ │ ├── index.ts # 文件上传等通用接口
│ │ └── interceptor.ts # 全局响应拦截器 (错误提示)
│ ├── assets/ # 静态资源 (图片、SVG 图标等)
│ │ └── icons/ # SVG 图标 (由 vite-plugin-svg-icons 收集)
│ ├── components/ # 全局共享组件
│ │ ├── Chart/ # 图表组件
│ │ ├── Chat/ # 聊天组件 (MessageList, ChatInput)
│ │ ├── CodeEditor/ # Monaco 代码编辑器封装
│ │ ├── MarkdownEditor/ # ByteMD Markdown 编辑器
│ │ ├── RichEditor/ # TinyMCE 富文本编辑器
│ │ ├── SearchDialog/ # 全局搜索弹窗
│ │ ├── Sidebar/ # 侧边栏
│ │ ├── SvgIcon/ # SVG 图标组件
│ │ └── ...
│ ├── composables/ # 组合式函数 (Hooks)
│ │ └── useI18n.ts # 国际化 Hook
│ ├── config/ # 应用配置
│ │ └── menu.ts # 侧边栏菜单配置
│ ├── constant/ # 常量定义
│ ├── features/ # 功能模块 (业务组件 + 逻辑)
│ │ ├── fileUpload/ # 文件上传
│ │ ├── RunWorkflow/ # 工作流运行
│ │ ├── editorFooter/ # 编辑器底部栏
│ │ ├── setter/ # 属性配置面板
│ │ └── ...
│ ├── i18n/ # 国际化
│ │ ├── index.ts # i18n 实例
│ │ └── locales/ # 语言包 (zh-cn.ts, en-us.ts)
│ ├── layouts/ # 布局组件
│ │ └── MainLayout.vue # 主布局 (侧边栏 + 内容区)
│ ├── nodes/ # 工作流节点定义
│ │ └── custom/ # 自定义节点注册
│ ├── router/ # 路由配置
│ │ └── index.ts # 路由表 (懒加载)
│ ├── store/ # Pinia 状态管理
│ │ ├── index.ts # Pinia 实例创建
│ │ └── modules/ # Store 模块 (按功能拆分)
│ ├── theme/ # 主题系统 (CSS 变量)
│ │ ├── light.ts # 亮色主题变量
│ │ └── dark.ts # 暗色主题变量
│ ├── types/ # 全局 TypeScript 类型定义
│ ├── utils/ # 工具函数
│ ├── views/ # 页面视图 (按功能模块分目录)
│ │ ├── agent/ # 智能体管理
│ │ ├── chat/ # 对话页面
│ │ ├── editor/ # 工作流编辑器
│ │ ├── flow/ # 工作流管理
│ │ ├── knowledge/ # 知识库管理
│ │ ├── model/ # 模型管理
│ │ ├── mcp/ # MCP 管理
│ │ ├── prompt/ # Prompt 管理
│ │ ├── skills/ # 技能管理
│ │ ├── storage/ # 存储管理
│ │ ├── workspace/ # 工作空间
│ │ └── ...
│ ├── App.vue # 根组件
│ ├── main.ts # 应用入口
│ └── style.css # 全局样式
├── AGENTS.md # 本文件
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── vite.config.ts
└── uno.config.ts
| 类型 | 规范 | 示例 |
|---|---|---|
| Vue 组件 | PascalCase | MessageList.vue, ChatInput.vue |
| 页面视图 | PascalCase (按业务模块分目录) | views/chat/index.vue, views/knowledge/WikiManage.vue |
| 组合式函数 | camelCase, use 前缀 |
useI18n.ts, useChatStream.ts |
| 工具函数 | camelCase | uuid.ts, formatDate.ts |
| Store 模块 | camelCase, store 后缀 |
dashboard.ts, runner.store.ts |
| 类型定义 | camelCase 或 PascalCase | types.ts |
| 常量文件 | camelCase | constant/index.ts |
| 类型 | 规范 | 示例 |
|---|---|---|
| 组合式函数 | use 前缀 |
useI18n(), useChatStream() |
| 响应式变量 | camelCase | isLoading, activeTab |
| 常量 | UPPER_SNAKE 或 camelCase | STORAGE_KEY, pageSize |
| 事件处理 | handle / on 前缀 |
handleSend, onMounted |
| 异步函数 | 描述性动词 | loadConversations(), fetchKnowledgeOptions() |
| 布尔变量 | is / has / should / can 前缀 |
isLoading, hasMore |
| Ref 引用 | Ref 后缀 |
messageListRef, chatInputRef |
// Props: 使用 defineProps 泛型 + withDefaults
const props = withDefaults(defineProps<{
messages: BubbleMessage[]
loading: boolean
copy?: boolean
}>(), {
copy: true
})
// Emits: 使用 defineEmits 泛型
const emit = defineEmits<{
retry: [message: BubbleMessage]
addToKb: [text: string]
}>()
每个功能模块视图目录下可包含:
views/chat/
├── index.vue # 主页面组件
├── types.ts # 模块类型定义
├── api/ # 模块 API 调用
│ └── chat.api.ts
├── components/ # 模块私有组件
│ ├── ChatHeader.vue
│ ├── ChatSidebar.vue
│ └── AddKbModal.vue
└── composables/ # 模块私有 Hooks
└── useChatStream.ts
<script setup> 结构顺序<script setup lang="ts">
// 1. 类型导入
import type { BubbleMessage } from './types'
// 2. 组合式函数
const { t } = useI18n()
const { isLoading, streamChat } = useChatStream()
// 3. Props / Emits
const props = withDefaults(defineProps<Props>(), { ... })
const emit = defineEmits<{ ... }>()
// 4. 响应式状态
const messages = ref<BubbleMessage[]>([])
const settingsDraft = reactive<ChatTargetConfig>(...)
// 5. 计算属性
const activeConversationTitle = computed(() => ...)
// 6. 工具函数
const createUserMessage = (content: string): BubbleMessage => { ... }
// 7. 生命周期
onMounted(async () => { ... })
onBeforeUnmount(() => { ... })
// 8. Watch
watch(() => props.messages.length, ...)
// 9. 暴露方法
defineExpose({ scrollToBottom })
</script>
composables/api/ 目录{
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true
}
interface 定义对象类型,type 用于联合/交叉类型types.tssrc/types/any,使用 unknown + 类型守卫,或具体类型defineProps<T>(),不推荐运行时声明import { useI18n } from '@/composables/useI18n'
import MessageList from '@/components/Chat/MessageList.vue'
// @ 映射到 src/
使用 Setup Store 语法 (组合式):
// store/modules/dashboard.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useDashboardStore = defineStore('dashboard', () => {
const activeTab = ref<string>('flows')
const setActiveTab = (tab: string) => {
activeTab.value = tab
}
return { activeTab, setActiveTab }
})
store/modules/ 下use[Xxx]Storestore/index.ts 中统一导出ref / reactive 而非 state 选项UI 组件 / Composable
↓
@repo/api-service (自动生成的类型安全 API)
↓
@repo/api-client (统一 HTTP 客户端)
↓
Axios → 后端服务
@repo/api-service 由 packages/api-service 的 openapi2ts 从 OpenAPI schema 自动生成。各模块的 API 函数按服务拆分:
import { knowledge, agentApplication, aiChat } from '@repo/api-service'
// 类型安全的 API 调用
const res = await knowledge.postAiFaqCreate({ ... })
const agents = await agentApplication.getAiAgentOptions()
src/api/ 下放置非自动生成的手写接口:
// api/index.ts
import request from '@repo/api-client'
export const uploadFile = (data: FormData) =>
request.post<UploadResponse>('/File/Upload', data)
src/api/interceptor.ts 注册全局响应拦截器:统一的 isSuccess === false 错误提示。
api/ 目录下fetch 或 axiosasync/await + try/catchisSuccess// router/index.ts
const routes = [
{
path: '/',
component: MainLayout, // 布局组件
children: [ // 嵌套子路由
{ path: '', name: 'Dashboard', component: Dashboard },
{ path: 'chat', name: 'Chat', component: Chat },
]
},
{
path: '/workflow/:id', // 独立路由 (无布局)
name: 'Editor',
component: Editor
}
]
createWebHashHistory)() => import('@/views/...')children 配置import { useI18n } from '@/composables/useI18n'
const { t } = useI18n()
// 模板中使用
{{ t('pages.chat.send') }}
// 带参数
t('pages.chat.messageCount', { count: 5 })
i18n/locales/{locale}.ts 采用层级嵌套结构:
export default {
common: {
confirm: '确认',
cancel: '取消'
},
pages: {
chat: {
send: '发送',
emptyTitle: '开始对话'
}
}
}
模块.页面.具体文案,使用 . 分隔zh-cn.ts 和 en-us.ts<style lang="less" scoped>
.component-name {
// 使用 CSS 变量,支持主题切换
color: var(--text-primary);
background: var(--bg-container);
// 穿透 Element Plus 子组件样式
:deep(.el-button) { ... }
}
</style>
<style scoped>style.cssvar(--xxx) 主题变量,不要硬编码色值cd apps/web
pnpm dev # 启动开发服务器 (默认 5174 端口)
pnpm build # 生产构建
pnpm preview # 预览构建产物
.env.development:
VITE_BASE_URL=localhost:8080
VITE_ 前缀import.meta.env.VITE_XXX 访问vue-tsc --noEmit)noUnusedLocals: true)@repo/api-service,不在组件中裸调el-image 懒加载watch 和 computed 嵌套packages/api-service/schema/*.openapi.json,通过 pnpm genapi 生成 TS 代码到 @repo/api-service@repo/api-client 基于 hook-fetch 封装,支持 SSE 流式响应vite-plugin-monaco-editor 按需加载,公共补全配置缓存在 src/components/CodeEditor/codeEditorCompletion.tssrc/assets/icons/,通过 SvgIcon 组件使用,ID 格式为 svg-icon-[dir]-[name]nodes/custom/ 中注册,通过 initializeCustomNodes() 统一加载