AGENTS.md 14 KB

AGENTS.md — 项目开发规范

项目概述

本项目是沙鲁智能体工作流平台前端应用,基于 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 包)

依赖约束

  • 包管理器: pnpm
  • 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 / Emits

// 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>

单向数据流

  • 父 → 子: Props 传递,只读
  • 子 → 父: Emits 事件
  • 跨层级: Pinia Store
  • 禁止直接修改 Props

组件拆分原则

  • 单个组件不超过 300 行 (模板 + 逻辑)
  • 可复用的 UI 片段抽取为独立组件
  • 业务逻辑复杂时抽取到 composables/
  • API 调用抽取到 api/ 目录

TypeScript 使用规范

编译配置 (tsconfig.app.json)

{
  "strict": true,
  "noUnusedLocals": true,
  "noUnusedParameters": true,
  "erasableSyntaxOnly": true
}

类型定义

  • 优先使用 interface 定义对象类型,type 用于联合/交叉类型
  • 模块级别类型定义放在 types.ts
  • 全局通用类型放在 src/types/
  • 避免 any,使用 unknown + 类型守卫,或具体类型
  • Props 使用泛型 defineProps<T>(),不推荐运行时声明

路径别名

import { useI18n } from '@/composables/useI18n'
import MessageList from '@/components/Chat/MessageList.vue'
// @ 映射到 src/

状态管理 (Pinia)

Store 定义模式

使用 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 使用规范

  • Store 文件放在 store/modules/
  • 按功能域拆分,避免单一巨型 store
  • Store 命名: use[Xxx]Store
  • store/index.ts 中统一导出
  • 优先使用 ref / reactive 而非 state 选项
  • 暴露方法而非直接暴露状态修改

API 调用规范

架构分层

UI 组件 / Composable
    ↓
@repo/api-service (自动生成的类型安全 API)
    ↓
@repo/api-client (统一 HTTP 客户端)
    ↓
Axios → 后端服务

自动生成的 API

@repo/api-servicepackages/api-serviceopenapi2ts 从 OpenAPI schema 自动生成。各模块的 API 函数按服务拆分:

import { knowledge, agentApplication, aiChat } from '@repo/api-service'

// 类型安全的 API 调用
const res = await knowledge.postAiFaqCreate({ ... })
const agents = await agentApplication.getAiAgentOptions()

通用 API 调用

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 调用原则

  • 页面级 API 调用放在模块 api/ 目录下
  • 不要在组件中直接写 fetchaxios
  • 异步调用统一使用 async/await + try/catch
  • 返回值始终检查 isSuccess

路由规范

路由配置

// 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
  }
]

路由规范

  • 使用 Hash 模式 (createWebHashHistory)
  • 页面组件使用懒加载: () => import('@/views/...')
  • 路由 name 使用 PascalCase
  • path 使用 kebab-case
  • 嵌套路由通过 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: '开始对话'
    }
  }
}

规范

  • Key 命名: 模块.页面.具体文案,使用 . 分隔
  • 禁止硬编码中文/英文到模板中
  • 新增文案同时更新 zh-cn.tsen-us.ts

样式规范

技术方案

  • CSS 框架: UnoCSS (原子化 CSS)
  • 预处理: Less (组件级样式)
  • UI 库: Element Plus (统一组件风格)
  • 主题: CSS 变量实现亮/暗色切换

组件样式规范

<style lang="less" scoped>
.component-name {
  // 使用 CSS 变量,支持主题切换
  color: var(--text-primary);
  background: var(--bg-container);

  // 穿透 Element Plus 子组件样式
  :deep(.el-button) { ... }
}
</style>

样式编写原则

  • 优先使用 UnoCSS 原子类 (class 属性)
  • 组件私有样式使用 <style scoped>
  • 避免全局样式污染,特殊全局样式放 style.css
  • 颜色使用 var(--xxx) 主题变量,不要硬编码色值
  • 布局优先使用 Flexbox / Grid

开发工作流

本地开发

cd apps/web
pnpm dev                  # 启动开发服务器 (默认 5174 端口)
pnpm build                # 生产构建
pnpm preview              # 预览构建产物

环境变量

.env.development:

VITE_BASE_URL=localhost:8080
  • 所有环境变量使用 VITE_ 前缀
  • 通过 import.meta.env.VITE_XXX 访问

Code Review 要点

  1. TypeScript strict 编译无错误 (vue-tsc --noEmit)
  2. 无未使用的变量/参数 (noUnusedLocals: true)
  3. 组件遵循单向数据流
  4. 新增文案同步更新中英文语言包
  5. API 调用通过 @repo/api-service,不在组件中裸调
  6. 样式使用主题变量,支持暗色模式

性能优化

  • 路由懒加载 (已配置)
  • 图片使用 el-image 懒加载
  • 大列表考虑虚拟滚动 (Element Plus 内置)
  • Monaco Editor 按需加载语言 worker
  • 避免不必要的 watchcomputed 嵌套

已知约定

  1. API 层: 接口定义在 packages/api-service/schema/*.openapi.json,通过 pnpm genapi 生成 TS 代码到 @repo/api-service
  2. 请求客户端: @repo/api-client 基于 hook-fetch 封装,支持 SSE 流式响应
  3. Monaco Editor: 通过 vite-plugin-monaco-editor 按需加载,公共补全配置缓存在 src/components/CodeEditor/codeEditorCompletion.ts
  4. SVG 图标: 放在 src/assets/icons/,通过 SvgIcon 组件使用,ID 格式为 svg-icon-[dir]-[name]
  5. 工作流节点: 自定义节点在 nodes/custom/ 中注册,通过 initializeCustomNodes() 统一加载