# VTJ Prompts 完整文档

> 本文档由 /docs/llms/ 目录下的所有提示词文档合并整理而成，供 AI 大模型深度参考。

---

# VTJ Prompts 索引

> 本文档为 `/docs/llms/` 目录下的所有提示词文档提供标签索引,用于 AI 意图匹配度查询。

---

## 文档标签索引

### [api.md](./api.md) - VTJ API 管理指南

**简介:** 详细介绍 VTJ 平台中 API 接口的完整生命周期管理，包括如何通过设计器配置 API、API 的完整字段结构、请求配置选项、Mock 数据设置，以及在组件中如何通过 `__apis` 调用接口。涵盖路径参数、查询参数的正确使用方式，以及 JSONP、请求拦截等特殊场景。

**核心主题:** API 配置、调用、管理工具方法

**标签:**

- `API管理`
- `HTTP请求`
- `setApi工具`
- `路径参数`
- `查询参数`
- `Mock数据`
- `接口调用`
- `请求配置`
- `数据源`
- `CRUD接口`
- `ApiSchema`
- `JSONP请求`
- `请求拦截`
- `响应校验`
- `opts.params`
- `opts.query`
- `__apis调用`
- `模拟数据模板`
- `接口测试`
- `category分组`

---

### [block.md](./block.md) - 区块管理指南

**简介:** 说明 VTJ 中区块（Block）的三种来源类型（Schema、UrlSchema、Plugin），区块的创建、编辑、删除操作流程，以及区块在页面中的引用方式。同时介绍区块的代码生成规则和与页面的区别，帮助理解如何实现组件的跨页面复用。

**核心主题:** 区块创建、来源类型、复用机制

**标签:**

- `区块管理`
- `Schema区块`
- `UrlSchema`
- `Plugin插件`
- `组件复用`
- `区块工具方法`
- `from引用`
- `代码生成`
- `BlockFile`
- `嵌套引用`
- `createBlock`
- `updateBlock`
- `removeBlock`
- `getBlocks`
- `预设区块`
- `市场安装`
- `资源URL`
- `library库名`
- `双向复用`
- `页面保存为区块`

---

### [chart.md](./chart.md) - 图表库使用指南

**简介:** 介绍 `@vtj/charts` 图表库的完整使用方法，包括基于 ECharts 封装的 `XChart` 通用图表组件和 `XMapChart` 地图图表组件。涵盖组件 Props、事件监听、响应式 Option 更新、组合式 API（useChart、useMapChart），以及柱状图、折线图、饼图、地图等常用图表类型的配置示例。

**核心主题:** ECharts 图表组件、地图图表、组合式 API

**标签:**

- `图表库`
- `ECharts`
- `XChart组件`
- `XMapChart`
- `地图图表`
- `GeoJSON`
- `响应式Option`
- `useChart`
- `数据可视化`
- `图表事件`
- `组合式API`
- `useMapChart`
- `自动resize`
- `图表类型`
- `柱状图`
- `折线图`
- `饼图`
- `雷达图`
- `散点图`
- `层级地图`

---

### [coder.md](./coder.md) - Vue 代码编写约定

**简介:** 定义 AI 生成 Vue 组件代码时必须遵循的严格规范，确保输出内容能被 `@vtj/parser` 无损解析为 DSL。包括强制使用 Composition API、响应式状态命名（`__state`、`__props`、`__emit`）、全局 API 映射表、模板指令使用规范、Import 导入约定，以及明确的禁止事项清单。

**核心主题:** parser 可解析的 Vue 代码规范、Composition API 强制要求

**标签:**

- `代码规范`
- `Composition API`
- `script setup`
- `parser解析`
- `变量命名`
- `全局API映射`
- `模板约定`
- `Import规范`
- `DSL生成`
- `禁止事项`
- `__state命名`
- `__props声明`
- `__emit调用`
- `生命周期钩子`
- `computed计算`
- `watch监听`
- `指令使用`
- `Slot插槽`
- `动态class`
- `PascalCase命名`

---

### [env.md](./env.md) - 环境变量指南

**简介:** 讲解 VTJ 平台中环境变量的配置和使用方式，支持开发（development）和生产（production）双环境值存储。介绍如何通过 `createEnv` 工具创建环境变量、`getEnv` 查询配置，以及在组件中通过 `__provider.env` 运行时访问环境变量，适用于 API 地址、功能开关、第三方 Key 等场景。

**核心主题:** 环境变量配置、双环境值存储、运行时访问

**标签:**

- `环境变量`
- `双环境配置`
- `development`
- `production`
- `createEnv工具`
- `运行时访问`
- `EnvConfig`
- `功能开关`
- `API地址配置`
- `全局只读配置`
- `getEnv`
- `removeEnv`
- `nodeEnv切换`
- `平铺对象`
- `UPPER_SNAKE_CASE`
- `__provider.env`
- `环境值类型`
- `同名不覆盖`
- `自动选取`
- `第三方Key配置`

---

### [globals.md](./globals.md) - 全局 API 使用指南

**简介:** 详细说明 `@vtj/renderer` 提供的全局变量及其在 Vue 组件中的使用方式。这些变量由框架自动注入，包括 `__store`（Pinia 状态）、`__request`（HTTP 请求）、`__libs`（第三方库集合）、`__apis`（接口集合）、`__access`（权限控制）等，涵盖权限判断、登录管理、动态请求、跨 UI 库调用等典型场景。

**核心主题:** renderer 提供的全局变量、自动注入机制

**标签:**

- `全局API`
- `自动注入`
- `__store`
- `__request`
- `__libs`
- `__apis`
- `__access`
- `权限控制`
- `Pinia实例`
- `HTTP请求`
- `__pinia`
- `第三方库集合`
- `ElementPlus`
- `AntDesignVue`
- `VueUse`
- `登录管理`
- `权限判断`
- `Token管理`
- `请求封装`
- `拦截器注册`

---

### [i18n.md](./i18n.md) - 国际化指南

**简介:** 介绍 VTJ 平台的国际化（i18n）配置和使用方法，基于 vue-i18n 的 Composition API 模式。包括如何通过 `createI18nMessage` 创建中英文对照词条、词条 Key 的命名规范（点号分层）、在模板和 script 中使用 `__i18n.t()` 进行翻译，以及插值参数、数字格式化、日期格式化等高级功能。

**核心主题:** 多语言配置、词条管理、翻译函数使用

**标签:**

- `国际化`
- `i18n`
- `多语言`
- `词条管理`
- `vue-i18n`
- `翻译函数`
- `createI18nMessage`
- `插值参数`
- `Key命名规范`
- `locale设置`
- `getI18nMessage`
- `removeI18nMessage`
- `fallbackLocale`
- `zh-CN`
- `英文翻译`
- `点号分层`
- `模块页面元素`
- `__i18n.t()`
- `数字格式化`
- `日期格式化`

---

### [icons.md](./icons.md) - 图标库使用指南

**简介:** 全面介绍 `@vtj/icons` 图标库的四种图标来源（SVG 组件、Iconfont 图标、Element Plus 图标、Assets 图标），以及全局注册和按需导入两种使用方式。详细说明 `XIcon` 组件的用法，包括 `icon` 和 `src` 属性的区别、size/color 控制、动态图标切换，并提供完整的图标分类列表。

**核心主题:** 图标组件、按需导入、XIcon 渲染

**标签:**

- `图标库`
- `XIcon组件`
- `SVG图标`
- `Iconfont`
- `Element Plus图标`
- `Assets图标`
- `按需导入`
- `全局注册`
- `图标命名`
- `动态图标`
- `IconsPlugin`
- `VtjIcon前缀`
- `colors属性`
- `size属性`
- `color属性`
- `src属性`
- `CSS类实现`
- `组件引用`
- `图标分类`
- `业务操作图标`

---

### [page.md](./page.md) - 页面管理指南

**简介:** 讲解 VTJ 中页面的三种类型（page、dir、layout）及其路由行为，页面树的创建、编辑、删除操作流程，以及路由体系和菜单生成规则。涵盖特殊属性如 KeepAlive 缓存、mask 内嵌母版、hidden 隐藏菜单、raw 源码模式，以及 UniApp 平台的专属字段（needLogin、style）。

**核心主题:** 页面类型、路由体系、菜单生成

**标签:**

- `页面管理`
- `路由体系`
- `页面类型`
- `目录节点`
- `布局页面`
- `createPage工具`
- `菜单生成`
- `RouterView`
- `KeepAlive`
- `源码模式`
- `PageFile`
- `getPages`
- `updatePage`
- `removePage`
- `setHomepage`
- `movePage`
- `路由元信息`
- `mask内嵌母版`
- `hidden隐藏菜单`
- `needLogin登录校验`

---

### [settings.md](./settings.md) - 应用设置指南

**简介:** 详细介绍 VTJ 应用级别的全局配置项，覆盖三大类：全局配置（CSS、Pinia Store、Access 权限控制、Enhance 增强）、请求配置（Axios 基础配置、请求拦截器、响应拦截器）、路由守卫（beforeEach 前置守卫、afterEach 后置守卫）。说明渲染引擎的严格初始化顺序及各配置项的依赖关系。

**核心主题:** 全局配置、请求配置、路由守卫

**标签:**

- `应用设置`
- `全局配置`
- `Pinia Store`
- `权限控制`
- `Axios配置`
- `请求拦截器`
- `响应拦截器`
- `路由守卫`
- `beforeEach`
- `初始化顺序`
- `setGlobalCss`
- `setGlobalStore`
- `setGlobalAccess`
- `setGlobalAxios`
- `afterEach后置守卫`
- `enhance增强`
- `GlobalConfig`
- `设计模式不生效`
- `函数代码字符串`
- `依赖关系`

---

### [tools.md](./tools.md) - 设计器工具方法说明

**简介:** 汇总 AI 在 VTJ 设计器中可调用的所有工具方法（Tools），包括页面管理（createPage、updatePage、removePage）、区块管理（createBlock、getBlocks）、API 管理（setApi、getApis）、全局配置（setGlobalCss、setGlobalStore 等）、环境变量、国际化、UniApp 配置等。提供标准的 JSON 调用格式和典型工作流程建议。

**核心主题:** 所有可调用的工具方法、调用格式、工作流程

**标签:**

- `工具方法`
- `action调用`
- `页面管理工具`
- `区块管理工具`
- `API管理工具`
- `全局配置工具`
- `环境变量工具`
- `国际化配置`
- `UniApp配置`
- `工作流程`
- `getSkills`
- `refresh刷新`
- `getCurrentFile`
- `getNodeSelected`
- `active打开`
- `movePage移动`
- `parameters格式`
- `JSON调用`
- `技能文档`
- `运行时检测`

---

### [ui.md](./ui.md) - UI 组件库使用指南

**简介:** 全面介绍 `@vtj/ui` 企业级 UI 组件库的核心组件和使用规范，包括 XGrid 数据表格（基于 VXE Table）、XForm 表单、XField 字段、XDialog 对话框、XActionBar 操作栏、XQueryForm 查询表单、XContainer 布局容器等。同时说明 Hooks 工具（useIcon、useLoader）、Adapter 适配器系统，以及完整的 CRUD 页面示例。

**核心主题:** 业务组件、Hooks、Adapter 适配器系统

**标签:**

- `UI组件库`
- `XGrid表格`
- `XForm表单`
- `XField字段`
- `XDialog对话框`
- `XActionBar`
- `XQueryForm`
- `Hooks工具`
- `Adapter适配器`
- `CRUD页面`
- `VXE Table`
- `列渲染器`
- `单元格编辑`
- `行拖拽排序`
- `inline表单`
- `sticky按钮`
- `编辑器类型`
- `级联刷新`
- `useIcon`
- `useLoader`

---

### [uni.md](./uni.md) - UniApp 全局配置指南

**简介:** 专门针对 UniApp 平台的全局配置文档，介绍如何通过 `setUniConfig` 工具配置 manifest.json（应用配置）、pages.json（页面路由配置）、全局 CSS，以及应用生命周期函数（onLaunch、onShow、onHide、onError 等）。说明配置在运行时的注入流程和仅 UniApp 平台生效的限制条件。

**核心主题:** UniApp 平台配置、manifest.json、pages.json、生命周期

**标签:**

- `UniApp配置`
- `manifestJson`
- `pagesJson`
- `应用生命周期`
- `onLaunch`
- `全局样式`
- `tabBar配置`
- `setUniConfig`
- `平台专属`
- `运行时注入`
- `getUniConfig`
- `onShow`
- `onHide`
- `onError`
- `onPageNotFound`
- `onThemeChange`
- `onExit`
- `window.__uniConfig`
- `adoptedStyleSheets`
- `easycom`

---

### [utils.md](./utils.md) - 工具库使用指南

**简介:** 介绍 `@vtj/utils` 通用工具库的完整功能模块，包括 HTTP 请求封装（request、createApi、useApi）、本地存储（storage 支持过期机制）、Cookie 操作、URL 解析与拼接、文件下载、日志模块、动态脚本加载、JSONP 跨域请求、客户端环境检测、requestAnimationFrame 封装等。提供典型工作流程示例，如数据获取与展示、文件上传与下载。

**核心主题:** HTTP 请求、本地存储、URL 处理、文件下载等通用工具

**标签:**

- `工具库`
- `HTTP请求`
- `本地存储`
- `storage`
- `cookie操作`
- `URL处理`
- `文件下载`
- `日志模块`
- `动态脚本加载`
- `JSONP请求`
- `request封装`
- `createApi`
- `createApis`
- `useApi钩子`
- `缓存过期`
- `localStorage`
- `sessionStorage`
- `url.stringify`
- `downloadBlob`
- `loadScript`

---

## 标签分类汇总

### 按功能模块分类

**数据与接口:**

- API管理、HTTP请求、setApi工具、路径参数、查询参数、Mock数据、接口调用、请求配置、数据源、CRUD接口、**request、**apis、权限控制

**组件与UI:**

- 图表库、ECharts、XChart组件、XMapChart、UI组件库、XGrid表格、XForm表单、XField字段、XDialog对话框、XActionBar、XQueryForm、Hooks工具、Adapter适配器、图标库、XIcon组件

**配置与管理:**

- 环境变量、双环境配置、应用设置、全局配置、Pinia Store、Axios配置、请求拦截器、响应拦截器、路由守卫、UniApp配置、manifestJson、pagesJson

**国际化与本地化:**

- 国际化、i18n、多语言、词条管理、vue-i18n、翻译函数

**页面与路由:**

- 页面管理、路由体系、页面类型、目录节点、布局页面、RouterView、KeepAlive

**代码规范:**

- 代码规范、Composition API、script setup、parser解析、变量命名、全局API映射、模板约定

**工具与实用:**

- 工具方法、本地存储、storage、cookie操作、URL处理、文件下载、日志模块、动态脚本加载、JSONP请求

**区块与复用:**

- 区块管理、Schema区块、UrlSchema、Plugin插件、组件复用

---

## 使用建议

### AI 意图匹配查询示例

**场景 1:用户问"如何调用接口"**

- 匹配标签:`API管理`、`HTTP请求`、`接口调用`
- 推荐文档:[api.md](./api.md)、[globals.md](./globals.md)、[utils.md](./utils.md)

**场景 2:用户问"如何使用表格组件"**

- 匹配标签:`XGrid表格`、`UI组件库`
- 推荐文档:[ui.md](./ui.md)

**场景 3:用户问"如何配置多语言"**

- 匹配标签:`国际化`、`词条管理`
- 推荐文档:[i18n.md](./i18n.md)

**场景 4:用户问"如何设置环境变量"**

- 匹配标签:`环境变量`、`双环境配置`
- 推荐文档:[env.md](./env.md)

**场景 5:用户问"如何生成 Vue 代码"**

- 匹配标签:`代码规范`、`Composition API`、`parser解析`
- 推荐文档:[coder.md](./coder.md)

**场景 6:用户问"如何使用图表"**

- 匹配标签:`图表库`、`ECharts`、`XChart组件`
- 推荐文档:[chart.md](./chart.md)

**场景 7:用户问"如何配置路由守卫"**

- 匹配标签:`路由守卫`、`beforeEach`、`应用设置`
- 推荐文档:[settings.md](./settings.md)

**场景 8:用户问"如何使用图标"**

- 匹配标签:`图标库`、`XIcon组件`
- 推荐文档:[icons.md](./icons.md)

---

> **说明:** 每个文档的 20 个标签基于其核心内容、功能模块、使用场景、API方法、配置项等精准提取,可用于 AI 意图识别和文档推荐系统的匹配查询。

<!-- ============================================ -->
<!-- 文档: quick-ref.md -->
<!-- ============================================ -->

# VTJ 工具速查清单

> AI 意图 → 工具/文档 快速映射。按任务场景查找，直达对应工具或文档。

---

## 一、页面管理

| 意图                | 工具方法                                              | 参考文档             |
| ------------------- | ----------------------------------------------------- | -------------------- |
| 查看页面树/菜单结构 | [`getMenus`](./tools.md#getmenus--获取页面菜单树)     | [page.md](./page.md) |
| 获取所有页面列表    | [`getPages`](./tools.md#getpages--获取所有页面)       | [page.md](./page.md) |
| 新建页面            | [`createPage`](./tools.md#createpage--创建页面)       | [page.md](./page.md) |
| 修改页面信息        | [`updatePage`](./tools.md#updatepage--更新页面信息)   | [page.md](./page.md) |
| 调整页面层级/父级   | [`movePage`](./tools.md#movepage--移动页面层级)       | [page.md](./page.md) |
| 删除页面            | [`removePage`](./tools.md#removepage--删除页面)       | [page.md](./page.md) |
| 设为首页            | [`setHomepage`](./tools.md#sethomepage--设置应用主页) | [page.md](./page.md) |

## 二、区块管理

| 意图         | 工具方法                                              | 参考文档               |
| ------------ | ----------------------------------------------------- | ---------------------- |
| 查看所有区块 | [`getBlocks`](./tools.md#getblocks--获取所有区块)     | [block.md](./block.md) |
| 创建区块     | [`createBlock`](./tools.md#createblock--创建区块)     | [block.md](./block.md) |
| 修改区块信息 | [`updateBlock`](./tools.md#updateblock--更新区块信息) | [block.md](./block.md) |
| 删除区块     | [`removeBlock`](./tools.md#removeblock--删除区块)     | [block.md](./block.md) |

## 三、API 接口管理

| 意图           | 工具方法                                            | 参考文档                                       |
| -------------- | --------------------------------------------------- | ---------------------------------------------- |
| 查看所有接口   | [`getApis`](./tools.md#getapis--获取接口列表)       | [api.md](./api.md)                             |
| 新建/更新接口  | [`setApi`](./tools.md#setapi--新增或更新接口)       | [api.md](./api.md)                             |
| 删除单个接口   | [`removeApi`](./tools.md#removeapi--删除接口)       | [api.md](./api.md)                             |
| 批量删除接口   | [`removeApis`](./tools.md#removeapis--批量删除接口) | [api.md](./api.md)                             |
| 组件中调用接口 | → 用 `__apis.xxx()`                                 | [api.md](./api.md)、[globals.md](./globals.md) |

## 四、文件与运行时操作

| 意图                  | 工具方法                                                                      |
| --------------------- | ----------------------------------------------------------------------------- |
| 打开（激活）页面/区块 | [`active`](./tools.md#active--打开文件)                                       |
| 获取当前打开文件信息  | [`getCurrentFile`](./tools.md#getcurrentfile--获取当前文件信息)               |
| 获取当前文件 Vue 源码 | [`getCurrentFileContent`](./tools.md#getcurrentfilecontent--获取当前文件源码) |
| 刷新运行时、检测错误  | [`refresh`](./tools.md#refresh--刷新运行时)                                   |
| 获取选中节点路径      | [`getNodeSelected`](./tools.md#getnodeselected--获取当前选中节点路径)         |

## 五、全局配置

| 意图                         | 工具方法                                                        | 参考文档                     |
| ---------------------------- | --------------------------------------------------------------- | ---------------------------- |
| 全局 CSS                     | `getGlobalCss` / `setGlobalCss`                                 | [settings.md](./settings.md) |
| Pinia Store（vue3 状态管理） | `getGlobalStore` / `setGlobalStore`                             | [settings.md](./settings.md) |
| 权限控制配置                 | `getGlobalAccess` / `setGlobalAccess`                           | [settings.md](./settings.md) |
| Axios 请求基础配置           | `getGlobalAxios` / `setGlobalAxios`                             | [settings.md](./settings.md) |
| 请求拦截器                   | `getGlobalRequestInterceptor` / `setGlobalRequestInterceptor`   | [settings.md](./settings.md) |
| 响应拦截器                   | `getGlobalResponseInterceptor` / `setGlobalResponseInterceptor` | [settings.md](./settings.md) |
| 路由前置守卫                 | `getGlobalBeforeEach` / `setGlobalBeforeEach`                   | [settings.md](./settings.md) |
| 路由后置守卫                 | `getGlobalAfterEach` / `setGlobalAfterEach`                     | [settings.md](./settings.md) |

> 所有 set 类配置传入 **JS 函数代码字符串**，详见 [settings.md](./settings.md)。

## 六、环境变量管理

| 意图             | 工具方法                                        | 参考文档                                       |
| ---------------- | ----------------------------------------------- | ---------------------------------------------- |
| 查看所有环境变量 | [`getEnv`](./env.md#getenv--获取所有环境变量)   | [env.md](./env.md)                             |
| 新增环境变量     | [`createEnv`](./env.md#createenv--新增环境变量) | [env.md](./env.md)                             |
| 删除环境变量     | [`removeEnv`](./env.md#removeenv--删除环境变量) | [env.md](./env.md)                             |
| 组件中访问       | → `__provider.env.变量名`                       | [env.md](./env.md)、[globals.md](./globals.md) |

## 七、国际化 i18n

| 意图           | 工具方法                                     | 参考文档                                         |
| -------------- | -------------------------------------------- | ------------------------------------------------ |
| 查看所有词条   | `getI18nMessage`                             | [i18n.md](./i18n.md)                             |
| 新增词条       | `createI18nMessage`                          | [i18n.md](./i18n.md)                             |
| 删除词条       | `removeI18nMessage`                          | [i18n.md](./i18n.md)                             |
| 组件中使用翻译 | → `__i18n.t('key')` | [i18n.md](./i18n.md)、[globals.md](./globals.md) |

## 八、UniApp 专属

| 意图                       | 工具方法/参数                           | 参考文档           |
| -------------------------- | --------------------------------------- | ------------------ |
| 查看 UniApp 配置           | `getUniConfig(key)`                     | [uni.md](./uni.md) |
| 配置 manifest.json         | `setUniConfig("manifestJson", jsonStr)` | [uni.md](./uni.md) |
| 配置 pages.json            | `setUniConfig("pagesJson", jsonStr)`    | [uni.md](./uni.md) |
| 全局 CSS                   | `setUniConfig("css", cssStr)`           | [uni.md](./uni.md) |
| 生命周期：启动/显示/隐藏等 | `setUniConfig("onLaunch", fnStr)` 等    | [uni.md](./uni.md) |
| 配置 tabBar                | → 编辑 pagesJson 中的 tabBar 字段       | [uni.md](./uni.md) |

## 九、技能文档查询

| 意图                   | 操作                                   |
| ---------------------- | -------------------------------------- |
| 查阅特定功能的详细用法 | `getSkills([技能ID])` — 技能 ID 见下表 |

**可用技能 ID 与文档映射：**

| 技能 ID    | 文档                         | 速查场景                                                          |
| ---------- | ---------------------------- | ----------------------------------------------------------------- |
| `api`      | [api.md](./api.md)           | API 配置、路径参数、Mock 数据、接口调用规范                       |
| `block`    | [block.md](./block.md)       | 区块概念、来源类型、复用机制                                      |
| `chart`    | [chart.md](./chart.md)       | XChart、XMapChart、ECharts 图表配置                               |
| `coder`    | [coder.md](./coder.md)       | **Vue Composition API 代码规范**（AI 生成代码前必读）             |
| `env`      | [env.md](./env.md)           | 环境变量双环境配置、UPPER_SNAKE_CASE 命名                         |
| `globals`  | [globals.md](./globals.md)   | 全局注入变量详解：`__store`、`__request`、`__apis`、`__access` 等 |
| `i18n`     | [i18n.md](./i18n.md)         | 多语言词条管理、`__i18n.t()` 翻译、参数插值                       |
| `icons`    | [icons.md](./icons.md)       | 图标库 4 种来源、XIcon 组件、按需导入                             |
| `page`     | [page.md](./page.md)         | 页面类型（page/dir/layout）、路由体系、KeepAlive                  |
| `settings` | [settings.md](./settings.md) | 全局配置（Store/Access/Axios/路由守卫）详细说明                   |
| `tools`    | [tools.md](./tools.md)       | 所有设计器工具方法完整文档                                        |
| `ui`       | [ui.md](./ui.md)             | @vtj/ui 组件：XGrid、XForm、XField、XDialog、Hooks                |
| `uni`      | [uni.md](./uni.md)           | UniApp 平台配置（仅 UniApp 生效）                                 |
| `utils`    | [utils.md](./utils.md)       | @vtj/utils 工具库：HTTP 请求、storage、URL 处理等                 |

## 十、编程库 / 全局 API 速查

| 模块            | 用途           | 组件中访问方式                                                                            | 参考文档                   |
| --------------- | -------------- | ----------------------------------------------------------------------------------------- | -------------------------- |
| `@vtj/renderer` | 全局注入能力   | `__store`、`__request`、`__apis`、`__access`、`__libs`、`__pinia`、`__i18n`、`__provider` | [globals.md](./globals.md) |
| `@vtj/ui`       | 企业级 UI 组件 | `<XGrid>`, `<XForm>`, `<XDialog>`, `<XField>` 等                                          | [ui.md](./ui.md)           |
| `@vtj/charts`   | ECharts 图表   | `<XChart>`, `<XMapChart>`, `useChart()`                                                   | [chart.md](./chart.md)     |
| `@vtj/icons`    | 图标系统       | `<XIcon icon="xxx">`                                                                      | [icons.md](./icons.md)     |
| `@vtj/utils`    | 通用工具函数   | `request()`, `storage`, `url`, `downloadBlob` 等                                          | [utils.md](./utils.md)     |

## 十一、代码生成速查

| 场景                       | 核心约束                                                                                                                                                            | 参考文档                                                         |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| **生成 Vue 页面/区块代码** | 强制 Composition API + `script setup`；状态命名 `__state`、props `__props`、emit `__emit`；用 `__apis.xxx()` 调接口；全局变量用 `__store`/`__request`/`__access` 等 | **[coder.md](./coder.md)**（必读）                               |
| 使用 UI 组件               | 组件 PascalCase 引用；`@vtj/ui` 组件用 `<XGrid>` 等；图表用 `<XChart>`；图标用 `<XIcon>`                                                                            | [ui.md](./ui.md)、[chart.md](./chart.md)、[icons.md](./icons.md) |
| 调用 API                   | `const { data } = await __apis.getUserList({ ...opts })`                                                                                                            | [api.md](./api.md)                                               |
| 使用全局 Store             | `const user = __store.user; __store.setUser(data)`                                                                                                            | [settings.md](./settings.md)、[globals.md](./globals.md)         |

## 十二、典型工作流

```
1. 了解项目 → getMenus / getPages                 (了解页面结构)
2. 创建页面 → createPage(page, parentId?)           (先父级再子级)
3. 创建区块 → createBlock(block)                    (跨页面复用的组件)
4. 注册接口 → setApi(apiSchema)                     (后端 API 对接)
5. 生成代码 → 遵循 coder.md 规范 + 调用 getSkills    (AI 生成 Vue 源码)
6. 验证结果 → refresh                               (检测运行时错误)
7. 全局配置 → setGlobalStore / setGlobalAccess 等    (Store/权限/路由守卫)
8. i18n 配置 → createI18nMessage                    (多语言词条)
9. 环境变量 → createEnv                             (开发/生产双环境值)
```

---

## 速查索引（按关键词）

| 关键词                                | 动作                                                       |
| ------------------------------------- | ---------------------------------------------------------- |
| 创建页面/删除页面/移动页面            | → 见[页面管理](#一页面管理)                                |
| 创建区块/删除区块                     | → 见[区块管理](#二区块管理)                                |
| 注册接口/调接口/\_\_apis              | → 见[API 管理](#三-api-接口管理) + [api.md](./api.md)      |
| 获取源码/刷新/选中节点                | → 见[文件与运行时](#四文件与运行时操作)                    |
| 全局CSS/Store/Pinia                   | → 见[全局配置](#五全局配置) + [settings.md](./settings.md) |
| 权限/登录/access                      | → `setGlobalAccess` + [settings.md](./settings.md)         |
| Axios/baseURL/请求拦截                | → `setGlobalAxios` + [settings.md](./settings.md)          |
| 路由守卫/beforeEach                   | → `setGlobalBeforeEach` + [settings.md](./settings.md)     |
| 环境变量/env/\_\_provider             | → 见[环境变量](#六环境变量管理) + [env.md](./env.md)       |
| i18n/多语言/翻译/$t                   | → 见[i18n](#七国际化-i18n) + [i18n.md](./i18n.md)          |
| UniApp/manifest/pages.json            | → 见[UniApp](#八uniapp-专属) + [uni.md](./uni.md)          |
| 代码规范/Composition API/script setup | → [coder.md](./coder.md)                                   |
| 全局变量/**store/**request/\_\_libs   | → [globals.md](./globals.md)                               |
| XGrid 表格/XForm 表单/XDialog         | → [ui.md](./ui.md)                                         |
| XChart 图表/useChart/ECharts          | → [chart.md](./chart.md)                                   |
| XIcon 图标/SVG/Iconfont               | → [icons.md](./icons.md)                                   |
| request/storage/工具函数              | → [utils.md](./utils.md)                                   |
| 生成 Vue 代码/parser 解析             | → [coder.md](./coder.md)                                   |
| 查看详细工具文档                      | → [tools.md](./tools.md)                                   |

<!-- ============================================ -->
<!-- 文档: page.md -->
<!-- ============================================ -->

# VTJ 页面管理 — AI 配置与使用指南

> 本文档帮助 AI 理解页面的三种类型、路由体系、以及如何通过工具方法管理页面。

---

## 一、页面数据流全貌

```
设计器页面树                   项目数据                         渲染引擎
pages/index.vue             project.pages                    routes.ts + page.ts
  ├── 新增 → createPage() ──→ PageFile[] ──→ createStaticRoutes() → RouteRecordRaw[]
  ├── 编辑 → updatePage()                     │
  ├── 删除 → removePage()                     ├──→ createMenus() → 侧边菜单
  ├── 拖拽 → update()                          │
  ├── 主页 → setHomepage()                     └──→ getHomepage() / getPage()
  └── 克隆 → clonePage()
```

**核心概念：** 页面是项目结构的基础单元，页面树即路由树。三种类型决定了路由行为和嵌套关系。

---

## 二、页面类型

| 类型              | `dir`   | `layout` | 说明                                                     |
| ----------------- | ------- | -------- | -------------------------------------------------------- |
| **页面** (page)   | `false` | `false`  | 标准页面，拥有 DSL 及独立路由                            |
| **目录** (dir)    | `true`  | —        | 纯容器节点，无 DSL 无路由，只用于组织子页面              |
| **布局** (layout) | —       | `true`   | 布局壳页面，拥有 DSL 及路由，子页面嵌入其 `<RouterView>` |

**类型决策：**

- 需要嵌套路由时使用**目录**：用户看到的 URL 是子页面路径
- 需要共享外壳（侧边栏、顶栏等）时使用**布局**：子页面路由挂载在布局路由的 `children` 下
- UniApp 平台**不支持**目录和布局类型

---

## 三、PageFile 完整字段

```typescript
interface PageFile extends BlockFile {
  dir?: boolean; // 是否目录
  layout?: boolean; // 是否布局页面
  icon?: string; // 菜单图标（ElementPlus 图标 或 @vtj/icons 名称）
  children?: PageFile[]; // 子页面（目录/布局的子级）
  mask?: boolean; // 内嵌母版，仅 Web 平台
  hidden?: boolean; // 隐藏菜单，仅 Web 平台
  raw?: boolean; // 源码模式（非低代码页面，不可在线编辑）
  pure?: boolean; // 纯净页面（默认 true）
  cache?: boolean; // 开启 KeepAlive 缓存，仅 Web 平台
  meta?: Record<string, any>; // 路由元信息（JSON 对象）
  needLogin?: boolean; // 需要登录，UniApp 专用
  style?: Record<string, any>; // 页面窗口表现，UniApp 专用
}

// 继承自 BlockFile
interface BlockFile {
  type: 'page' | 'block'; // 文件类型
  id: string; // 唯一标识（自动生成）
  name: string; // 文件名（驼峰格式，如 Dashboard）
  title: string; // 页面标题（显示名）
  category?: string; // 分组
  dsl?: BlockSchema; // 页面 DSL 内容（目录类型无此字段）
}
```

---

## 四、页面工具方法

### 4.1 `getPages` — 获取全部页面

**参数：** 无

**返回：** 简化的页面列表，每项含 `id`、`name`、`title`、`layout`、`dir`、`icon`

---

### 4.2 `createPage` — 新建页面

**参数：**

| 参数          | 类型    | 必填 | 说明                              |
| ------------- | ------- | ---- | --------------------------------- |
| `page.name`   | string  | ✅   | 页面名称，驼峰格式，如 `UserList` |
| `page.title`  | string  | ✅   | 页面标题，如 `用户列表`           |
| `page.icon`   | string  | ❌   | 图标名，如 `User`（ElementPlus）  |
| `page.dir`    | boolean | ❌   | 是否目录（默认 false）            |
| `page.layout` | boolean | ❌   | 是否布局（默认 false）            |
| `parentId`    | string  | ❌   | 父页面 ID，用于嵌套               |

**注意事项：**

- 创建嵌套页面时，必须先创建父级（目录或布局），再以 `parentId` 创建子页面
- UniApp 平台强制 `dir = false`、`layout = false`
- 创建非目录页面后自动激活该页面
- 返回 `{ id, name, title, layout, dir }`

**示例：**

```json
{
  "action": "createPage",
  "parameters": [
    { "name": "Dashboard", "title": "仪表盘", "icon": "DataAnalysis" }
  ]
}
```

创建目录及子页面：

```json
// 先创建目录
{ "action": "createPage", "parameters": [{ "name": "System", "title": "系统管理", "dir": true }] }
// 再创建子页面（需用返回的 parentId）
{ "action": "createPage", "parameters": [{ "name": "UserList", "title": "用户列表" }, "DIR_ID"] }
```

---

### 4.3 `updatePage` — 编辑页面元信息

**参数：**

| 参数         | 类型   | 必填 | 说明     |
| ------------ | ------ | ---- | -------- |
| `page.id`    | string | ✅   | 页面 ID  |
| `page.name`  | string | ✅   | 页面名称 |
| `page.title` | string | ✅   | 页面标题 |
| `page.icon`  | string | ❌   | 图标名   |

**限制：** 仅支持修改 `id`、`name`、`title`、`icon` 四个字段，不支持修改 `dir`/`layout` 等类型字段。

---

### 4.4 `removePage` — 删除页面

**参数：** `id: string` — 页面 ID

**限制：** 目录或布局类型的页面需先删除所有子级页面。

---

### 4.5 `setHomepage` — 设置主页

**参数：** `id: string` — 页面 ID

主页即应用的根路由（`/`），访问应用时首先展示的页面。

---

## 五、路由体系

### 5.1 路由生成规则

| 页面类型 | 路由 path                 | 路由 name      | 说明                               |
| -------- | ------------------------- | -------------- | ---------------------------------- |
| 页面     | `/${pageRouteName}/${id}` | `id`           | 标准页面路由                       |
| 目录     | 无路由                    | —              | 仅作为 children 的容器             |
| 布局     | `${prefix}`               | `layout_${id}` | 布局路由的 children 包含子页面路由 |
| 主页     | `/`                       | `home_${id}`   | 根路径路由                         |

**路由元信息合并：**

```typescript
route.meta = {
  title, // 页面标题
  ...routeMeta, // 全局路由元信息配置
  ...page.meta, // 页面自定义 meta（JSON）
  __vtj__: id // 内部标识，用于 getPage(id)
};
```

### 5.2 页面渲染流程

```
URL 访问 /page/userList
  → vue-router 匹配 RouteRecordRaw
  → PageContainer.setup()
  → route.meta.__vtj__ 或 route.params.id 获取 id
  → provider.getPage(id) 获取 PageFile
  → provider.getRenderComponent(file.id)
      ├── 普通模式: getDsl(id) → createDslRenderer(dsl)
      └── 源码模式: modules[`${vtjRawDir}/${id}.vue`]
  → h(component, { ...query, key: sid }) 渲染
```

**关键点：**

- 路由 `params.id` 即页面的 `id` 字段
- 源码模式（`raw = true`）直接导入 `.vue` 文件，跳过 DSL 渲染

### 5.3 菜单生成

菜单从 `project.pages` 树直接生成：

- `layout` 类型：不生成菜单项，递归处理其 children
- 非 `layout` 类型：生成菜单项，`url = ${menuPathPrefix}/${pageRouteName}/${id}`
- `hidden = true` 的菜单项仍生成但标记为隐藏
- 菜单可配合 `access` 进行权限过滤

---

## 六、特殊页面属性

### 6.1 `mask`（内嵌母版）

仅 Web 平台有效。页面显示在父级布局的内部，而不是作为独立路由页面。

**与路由的关系：** mask 页面不影响路由结构，由前端框架的母版机制处理。

### 6.2 `cache`（KeepAlive）

仅 Web 平台有效。开启后页面组件被 `<keep-alive>` 缓存，切换时不销毁。

**代码影响：** 无，由框架自动处理。

### 6.3 `hidden`（隐藏菜单）

仅 Web 平台有效，菜单中不显示该项，但路由仍然存在可访问。

### 6.4 `raw`（源码模式）

页面使用原生 Vue 单文件组件，存储为 `.vue` 文件而非 DSL。**不可在设计器中在线编辑。**

**代码影响：** 自己编写完整 `.vue` 文件，通过 `__provider` 等全局实例访问环境变量、API 等。

### 6.5 `meta`（路由元信息）

JSON 对象，合并到 `route.meta`，用于携带自定义路由数据：

```json
{
  "roles": ["admin"],
  "keepAlive": true,
  "order": 1
}
```

组件中通过 `useRoute().meta` 读取。

### 6.6 UniApp 专属字段

| 字段        | 说明                                                                                                         |
| ----------- | ------------------------------------------------------------------------------------------------------------ |
| `needLogin` | 页面是否需要登录才能访问                                                                                     |
| `style`     | 页面窗口表现配置，参考 [uni-app pages.json style](https://uniapp.dcloud.net.cn/collocation/pages.html#style) |

---

## 七、典型操作场景

### 7.1 创建标准页面结构

```
Dashboard (page)                        — 仪表盘主页
System (dir)                            — 系统管理目录
  ├── UserList (page)                   — 用户列表
  └── RoleManage (page)                 — 角色管理
```

操作顺序：

1. `createPage({ name: "Dashboard", title: "仪表盘", icon: "Odometer" })`
2. `createPage({ name: "System", title: "系统管理", dir: true, icon: "Setting" })`
3. `createPage({ name: "UserList", title: "用户列表" }, "System的ID")`
4. `createPage({ name: "RoleManage", title: "角色管理" }, "System的ID")`
5. `setHomepage("Dashboard的ID")`

### 7.2 创建布局套子页面

```
AdminLayout (layout)                    — 管理布局（侧边栏+顶栏外壳）
  ├── Profile (page)                    — 个人中心
  └── Settings (page)                   — 系统设置
```

**前提：** AI 需要在 `AdminLayout` 的 DSL 中使用 `<RouterView>` 才能显示子页面。

---

## 八、注意事项

1. **创建顺序：** 必须先创建父级（目录/布局），再创建子页面。子页面的 `parentId` 必须为已存在的目录或布局 ID
2. **删除顺序：** 删除目录/布局前，必须先删除所有子级页面，否则操作失败
3. **名称唯一：** 页面 `name` 在同级中不可重复
4. **名称自动格式化：** `name` 自动转为大驼峰（`UpperCamelCase`）
5. **UniApp 限制：** UniApp 平台不支持 `dir` 和 `layout` 类型，所有页面均为扁平 page 类型
6. **布局 + RouterView：** 布局页面的 DSL 中必须包含 `<RouterView>` 组件，否则子页面不会显示
7. **`updatePage` 限制：** 只能修改 `name`、`title`、`icon`，无法修改类型（dir/layout）等结构字段
8. **源码模式路径：** `raw = true` 的页面，Vue 文件路径为 `{vtjRawDir}/{id}.vue`，其中 `vtjRawDir` 默认为 `.vtj/vue`

<!-- ============================================ -->
<!-- 文档: block.md -->
<!-- ============================================ -->

# VTJ 区块管理 — AI 配置与使用指南

> 本文档帮助 AI 理解区块（Block）的三种来源类型、在页面中的使用方式、以及如何通过工具方法管理区块。

---

## 一、区块数据流全貌

```
设计器区块面板                   项目数据                        渲染引擎      →    代码生成
blocks/index.vue              project.blocks                   loader.ts         coder
  ├── 新增 Schema ──→ BlockFile ──→ getDsl(id) ──→ AsyncComponent ──→ import BlockName from './id.vue'
  ├── 新增 UrlSchema ──→           ──→ getDslByUrl(url) ──→ AsyncComponent ──→ provider.defineUrlSchemaComponent()
  ├── 新增 Plugin ──→              ──→ loadScriptUrl(scripts) ──→ AsyncComponent ──→ provider.definePluginComponent()
  ├── 编辑 → updateBlock()
  ├── 删除 → removeBlock()
  └── 克隆 → cloneBlock()

页面使用区块：
  DSL node.from = { type: 'Schema', id: 'blockId' }
                         │
                         ▼
              BlockLoader(id, name, from) → 加载并渲染区块
```

**核心概念：** 区块是可复用的独立组件单元，有三种来源类型。在页面 DSL 中，通过 `node.from` 引用区块。区块可以独立编辑，也可以被拖拽到任意页面使用。

---

## 二、区块三种来源类型

| 类型                 | `fromType`    | 说明                                         | 存储方式     |
| -------------------- | ------------- | -------------------------------------------- | ------------ |
| **设计** (Schema)    | `'Schema'`    | 在设计器中用低代码方式编辑的区块，有独立 DSL | 项目内置 DSL |
| **引用** (UrlSchema) | `'UrlSchema'` | 引用远程 JSON 文件作为 DSL                   | 远程 URL     |
| **插件** (Plugin)    | `'Plugin'`    | 动态加载 JS/CSS 插件作为组件                 | 远程资源 URL |

**类型决策：**

- 需要就地编辑的 → **Schema**
- 多个项目共享同一组件 → **UrlSchema**（中央管理 JSON）
- 需要动态加载第三方或自定义组件 → **Plugin**（上传 JS/CSS 文件）

---

## 三、BlockFile 完整字段

```typescript
interface BlockFile {
  type: 'block'; // 固定为 'block'
  id: string; // 唯一标识（自动生成）
  name: string; // 文件名，驼峰格式，如 UserCard
  title: string; // 显示名称，如 "用户卡片"
  category?: string; // 分组，如 "数据展示"、"表单"
  fromType?: 'Schema' | 'UrlSchema' | 'Plugin'; // 来源类型，默认 'Schema'
  urls?: string; // 资源文件路径（Plugin/UrlSchema 使用，多个用逗号分隔）
  library?: string; // 插件库名（Plugin 类型使用）
  dsl?: BlockSchema; // 区块 DSL 内容（Schema 类型使用）
  preset?: boolean; // 是否为预设区块（不可编辑删除）
  market?: MarketInstallInfo; // 物料市场安装信息
}
```

---

## 四、区块工具方法

### 4.1 `getBlocks` — 获取全部区块

**参数：** 无

**返回：** 简化的区块列表，每项含 `id`、`name`、`title`、`category`

---

### 4.2 `createBlock` — 新建区块

**参数：**

| 参数             | 类型   | 必填 | 说明                                  |
| ---------------- | ------ | ---- | ------------------------------------- |
| `block.name`     | string | ✅   | 区块名称，驼峰格式，如 `UserCard`     |
| `block.title`    | string | ✅   | 区块标题，如 `用户卡片`               |
| `block.category` | string | ❌   | 分组名称，如 `数据展示`。可为新分组名 |

**创建后自动激活** 该区块进入编辑模式。

**示例：**

```json
{
  "action": "createBlock",
  "parameters": [
    {
      "name": "UserCard",
      "title": "用户卡片",
      "category": "数据展示"
    }
  ]
}
```

---

### 4.3 `updateBlock` — 编辑区块元信息

**参数：**

| 参数             | 类型   | 必填 | 说明     |
| ---------------- | ------ | ---- | -------- |
| `block.id`       | string | ✅   | 区块 ID  |
| `block.name`     | string | ❌   | 区块名称 |
| `block.title`    | string | ❌   | 区块标题 |
| `block.category` | string | ❌   | 分组名称 |

---

### 4.4 `removeBlock` — 删除区块

**参数：** `id: string` — 区块 ID

---

## 五、区块在页面中的使用

### 5.1 使用方式

区块被拖拽到页面后，在 DSL 中表现为一个带有 `from` 引用的节点：

```json
{
  "name": "UserCard",
  "from": {
    "type": "Schema",
    "id": "abc123def456"
  },
  "props": {
    "title": "团队成员"
  }
}
```

三种 `from` 格式：

| 类型      | `from` 格式                                           | 加载方式                    |
| --------- | ----------------------------------------------------- | --------------------------- |
| Schema    | `{ type: 'Schema', id: 'blockId' }`                   | 按 id 获取 DSL → 创建渲染器 |
| UrlSchema | `{ type: 'UrlSchema', url: 'https://...' }`           | 按 URL 获取 JSON DSL        |
| Plugin    | `{ type: 'Plugin', urls: [...], library: 'libName' }` | 动态加载 JS/CSS 文件        |

### 5.2 页面保存为区块

将某个页面保存为区块，供其他页面复用：

```
pages/index.vue → 右键 "保存到区块" → project.saveToBlock(page)
```

---

## 六、代码生成规则

### 6.1 Schema 区块

生成独立 `.vue` 文件 + `import` 语句：

```javascript
// 生成在 script setup 顶部
import UserCard from './abc123def456.vue';
```

### 6.2 UrlSchema 区块

生成异步组件定义：

```javascript
const UserCard = __provider.defineUrlSchemaComponent(
  'https://example.com/block.json'
);
```

### 6.3 Plugin 区块

生成插件组件定义：

```javascript
const UserCard = __provider.definePluginComponent({
  type: 'Plugin',
  urls: ['https://example.com/plugin.js', 'https://example.com/plugin.css'],
  library: 'UserCard'
});
```

### 6.4 模板中的使用

三种类型的区块在模板中使用方式相同：

```html
<template>
  <div>
    <UserCard title="团队成员" />
  </div>
</template>
```

---

## 七、区块 vs 页面

| 维度     | 区块 (Block)                | 页面 (Page)                     |
| -------- | --------------------------- | ------------------------------- |
| 用途     | 可复用组件单元              | 独立路由页面                    |
| 路由     | 无独立路由                  | 有独立路由                      |
| 复用     | 可被多个页面引用            | 作为路由终点                    |
| 类型     | Schema / UrlSchema / Plugin | page / dir / layout             |
| 文件类型 | `type: 'block'`             | `type: 'page'`                  |
| 编辑     | 独立编辑区                  | 独立编辑区                      |
| 嵌套     | 通过 `from` 引用            | 通过 children + RouterView 嵌套 |

**典型关系：**

- 页面内部可以嵌入多个区块（通过拖拽）
- 一个区块可以被多个页面同时引用
- 页面可以保存为区块供其他地方复用
- 区块也可以引用其他区块（通过 `from.type = 'Schema'`）

---

## 八、注意事项

1. **Schema 区块可编辑：** 只有 `fromType: 'Schema'` 的区块可以在设计器中打开编辑，UrlSchema 和 Plugin 类型不能在线编辑
2. **预设区块不可删改：** `preset: true` 的区块只能使用，不能编辑和删除
3. **名称自动格式化：** `name` 自动转为大驼峰（`UpperCamelCase`）
4. **名称不可重复：** 区块和页面共享同一个命名空间，`name` 全局不可重复
5. **资源文件格式限制：**
   - UrlSchema 的 `urls` 只接受一个 `.json` 文件
   - Plugin 的 `urls` 可接受多个 `.js`、`.css`、`.json` 文件
6. **Plugin 必须指定 library：** `fromType: 'Plugin'` 时必须填写 `library` 字段，用于查找插件组件
7. **Schema 区块缓存：** 运行时按 `blockId + instanceId` 缓存组件实例，相同区块多实例共享 DSL 但各自独立渲染
8. **双向复用：** 页面可以保存为区块，区块也可以通过拖拽加入页面，两者可互相转换

<!-- ============================================ -->
<!-- 文档: api.md -->
<!-- ============================================ -->

# VTJ API 管理 — AI 配置指南

> 本文档帮助 AI 理解如何通过工具方法管理项目 API，以及在组件中调用 API 发送请求。

---

## 一、数据流全貌

```
设计器 API 管理面板                  项目数据                       渲染引擎
apis/index.vue ──setApi()────▶  project.apis  ──createSchemaApis()──▶  provider.apis
apis/form.vue   (增/删/改)        (ApiSchema[])   (register by id+name)  (callable functions)
                                                                    │
                                                    组件中调用: __apis['apiName'](data, opts)
```

**关键流程：**

1. **创建阶段：** 通过 `setApi` 工具将 API 配置存入 `project.apis`
2. **初始化阶段：** 应用启动时 `Provider.load()` → `createSchemaApis(apis, meta, adapter)` 将每条 API 编译为可调用的函数
3. **注册方式：** 每条 API 同时以 `id` 和 `name` 作为键注册到 `provider.apis`，组件中通过 `__apis['name']` 调用
4. **调用阶段：** `__apis['apiName'](data, opts)` → 底层调用 `request.send(config)` 发送请求

---

## 二、API 工具方法

### `setApi` — 新增或更新 API

新增一条 API 配置。若 `name` 已存在则更新，否则新增。

**参数：**

| 参数               | 类型    | 必填 | 说明                                                                        |
| ------------------ | ------- | ---- | --------------------------------------------------------------------------- |
| `api.name`         | string  | ✅   | 接口名称，camelCase，如 `getUserList`                                       |
| `api.url`          | string  | ✅   | 请求 URL，支持路径参数如 `/api/user/:id`                                    |
| `api.label`        | string  | ❌   | 接口描述说明                                                                |
| `api.method`       | string  | ❌   | 请求方法：`get` / `post` / `put` / `delete` / `patch` / `jsonp`，默认 `get` |
| `api.category`     | string  | ❌   | 分组名称，用于 UI 分类展示                                                  |
| `api.settings`     | object  | ❌   | 请求配置（下表）                                                            |
| `api.headers`      | string  | ❌   | 请求头，JS 表达式字符串，如 `"({'Content-Type': 'application/json'})"`      |
| `api.mock`         | boolean | ❌   | 是否开启模拟数据，默认 `false`                                              |
| `api.mockTemplate` | string  | ❌   | 模拟数据模板函数（JS 函数字符串），签名 `(req) => template`                 |
| `api.jsonpOptions` | object  | ❌   | JSONP 配置（仅 method 为 `jsonp` 时有效）                                   |

**`api.settings` 子字段：**

| 字段             | 类型    | 默认值  | 说明                                                                  |
| ---------------- | ------- | ------- | --------------------------------------------------------------------- |
| `type`           | string  | `form`  | 发送数据类型：`form`（表单）/ `json`（JSON）/ `data`（文件/FormData） |
| `loading`        | boolean | `true`  | 请求时是否显示全局 loading                                            |
| `failMessage`    | boolean | `true`  | 请求失败时是否弹出错误提示                                            |
| `validSuccess`   | boolean | `false` | 是否校验响应成功                                                      |
| `originResponse` | boolean | `true`  | 是否返回原始 Axios 响应对象                                           |
| `injectHeaders`  | boolean | `false` | 是否注入自定义请求头                                                  |
| `proxy`          | boolean | `false` | 是否开启请求代理                                                      |

**`api.jsonpOptions` 子字段（仅 method 为 `jsonp`）：**

| 字段                    | 类型    | 说明             |
| ----------------------- | ------- | ---------------- |
| `jsonpCallback`         | string  | 回调函数名       |
| `jsonpCallbackFunction` | string  | 回调函数         |
| `timeout`               | number  | 超时时间（毫秒） |
| `crossorigin`           | boolean | 是否跨域         |

**示例（简单配置）：**

```json
{
  "action": "setApi",
  "parameters": [
    {
      "name": "getUserList",
      "label": "获取用户列表",
      "url": "/api/users",
      "method": "get",
      "category": "用户管理"
    }
  ]
}
```

**示例（完整配置）：**

```json
{
  "action": "setApi",
  "parameters": [
    {
      "name": "getUserList",
      "label": "获取用户列表",
      "url": "/api/users",
      "method": "get",
      "category": "用户管理",
      "settings": {
        "type": "json",
        "loading": true,
        "failMessage": true,
        "validSuccess": false,
        "originResponse": true,
        "injectHeaders": false,
        "proxy": false
      },
      "headers": {
        "type": "JSExpression",
        "value": "({'Content-Type': 'application/json'})"
      },
      "mock": false,
      "mockTemplate": {
        "type": "JSFunction",
        "value": "(req) => {\n  return {\n    code: 0,\n    message: 'ok',\n    data: {\n      'list|10': [{\n        id: '@guid',\n        name: '@cname',\n        'age|18-60': 1\n      }]\n    }\n  };\n}"
      }
    }
  ]
}
```

---

### `getApis` — 获取所有 API

获取当前项目中已配置的全部 API 列表。

**参数：** 无

**返回：** `ApiSchema[]` 数组，每项包含 `id`、`name`、`url`、`method`、`label`、`category` 等字段

---

### `removeApi` — 删除单个 API

通过名称或 ID 删除一条 API。

**参数：**

- `name: string` — API 名称或 ID

---

### `removeApis` — 批量删除 API

批量删除多条 API。

**参数：**

- `apis: string[]` — API 名称或 ID 数组

---

## 三、API 完整配置（ApiSchema）

设计器表单分为 4 个 Tab：**基础信息**、**请求配置**、**模拟数据**、**接口测试**。以下是 `ApiSchema` 的完整字段：

### 3.1 基础信息（必填）

| 字段       | 类型   | 必填     | 说明                                                            |
| ---------- | ------ | -------- | --------------------------------------------------------------- |
| `id`       | string | 自动生成 | 唯一标识（UUID），系统自动分配                                  |
| `name`     | string | ✅       | 接口名称，camelCase，如 `getUserList`，该名称也是组件中调用的键 |
| `label`    | string | ❌       | 接口描述说明                                                    |
| `url`      | string | ✅       | 请求 URL，支持路径参数占位符如 `/api/users/:id`                 |
| `method`   | string | ❌       | 请求方法：`get` / `post` / `put` / `delete` / `patch` / `jsonp` |
| `category` | string | ❌       | 分组名称，用于 UI 分类展示                                      |

### 3.2 请求配置（settings + headers）

| 字段                      | 类型         | 默认值  | 说明                                                                  |
| ------------------------- | ------------ | ------- | --------------------------------------------------------------------- |
| `settings.type`           | string       | `form`  | 发送数据类型：`form`（表单）、`json`（JSON）、`data`（文件/FormData） |
| `settings.loading`        | boolean      | `true`  | 请求时是否显示全局 loading 动画                                       |
| `settings.failMessage`    | boolean      | `true`  | 请求失败时是否弹出错误提示                                            |
| `settings.validSuccess`   | boolean      | `false` | 是否校验响应是否成功（调用全局 validate 函数）                        |
| `settings.originResponse` | boolean      | `true`  | 是否返回原始 Axios 响应对象（而非 data 部分）                         |
| `settings.injectHeaders`  | boolean      | `false` | 是否注入请求拦截器中自定义的请求头                                    |
| `settings.proxy`          | boolean      | `false` | 是否开启请求代理                                                      |
| `headers`                 | JSExpression | `({})`  | 请求头配置，值为 JS 表达式字符串                                      |

**headers 示例：**

```javascript
// headers.value 的内容是一个 JS 表达式
({
  'Content-Type': 'application/json',
  'X-Custom-Header': 'value'
});
```

### 3.3 模拟数据（mock）

| 字段           | 类型       | 默认值  | 说明             |
| -------------- | ---------- | ------- | ---------------- |
| `mock`         | boolean    | `false` | 是否开启模拟数据 |
| `mockTemplate` | JSFunction | —       | 模拟数据模板函数 |

**mockTemplate 函数签名：** `(req) => template`

`req` 参数提供的信息：

| 属性         | 说明         |
| ------------ | ------------ |
| `req.url`    | 请求 URL     |
| `req.type`   | 请求方法类型 |
| `req.data`   | 请求体数据   |
| `req.params` | URL 路径参数 |
| `req.query`  | URL 查询参数 |

函数返回的数据会被 Mock.js 处理生成随机模拟数据：

```javascript
(req) => {
  return {
    code: 0,
    message: 'ok',
    data: {
      id: '@guid',
      name: '@cname',
      'age|18-60': 1
    }
  };
};
```

> 模板使用 Mock.js 语法规则，参考：https://vtj.pro/help/mock.html

### 3.4 jsonp 专用配置

当 `method` 为 `jsonp` 时，额外提供以下字段：

| 字段                                 | 类型    | 说明             |
| ------------------------------------ | ------- | ---------------- |
| `jsonpOptions.jsonpCallback`         | string  | 回调函数名       |
| `jsonpOptions.jsonpCallbackFunction` | string  | 回调函数         |
| `jsonpOptions.timeout`               | number  | 超时时间（毫秒） |
| `jsonpOptions.crossorigin`           | boolean | 是否跨域         |

### 3.5 常见问题

#### 返回数据异常或 `undefined`

如果调用 API 后发现返回数据不符合预期（如为 `undefined`、缺少字段或结构不对），请检查以下两处 `originResponse` 配置是否一致：

1. **应用设置中的全局请求配置**（`setGlobalAxios`）
2. **单个 API 的请求配置**（`setApi` 的 `settings.originResponse`）

**原理说明：**

- 当 `originResponse: true`（默认）时，返回完整的 Axios 响应对象（`AxiosResponse`），需通过 `res.data` 获取后端响应体，再通过 `res.data.data` 获取业务数据
- 当 `originResponse: false` 时，VTJ 自动提取并返回 `res.data?.data`，即后端响应体中的业务数据，省去手动取值步骤
- 单个 API 的 `settings.originResponse` 会覆盖全局配置

**典型错误场景：**

```javascript
// 错误示例 1：全局 originResponse: true（默认），但代码按业务数据直接取值
const res = await __apis['getUserList']();
this.list = res.list; // ❌ res 是完整 Axios 响应对象，不是业务数据

// 正确写法
const res = await __apis['getUserList']();
this.list = res.data.data.list; // ✅ originResponse: true 时，res.data.data 才是业务数据
// 或在 setApi 中设置 settings.originResponse: false，则可直接 res.list
```

```javascript
// 错误示例 2：全局 originResponse: false，但代码按原始响应对象处理
const res = await __apis['getUserList']();
console.log(res.status); // ❌ res 是业务数据（res.data?.data），没有 status 属性

// 正确写法：临时开启 originResponse
const res = await __apis['getUserList'](
  {},
  { settings: { originResponse: true } }
);
console.log(res.status); // ✅ 200
```

**排查步骤：**

1. 确认应用设置中 `setGlobalAxios` 的 `settings.originResponse` 值
2. 确认该 API 在 `setApi` 中是否单独设置了 `settings.originResponse`
3. 确保组件中处理响应数据的方式与当前生效的 `originResponse` 值匹配

#### 请求成功但进入 `catch` 或弹出错误提示

如果接口实际已返回 200 且数据正常，但代码进入了 `catch` 分支，或页面弹出了错误提示，请检查 `validSuccess` 配置：

1. **应用设置中的全局请求配置**（`setGlobalAxios` 的 `settings.validSuccess`）
2. **单个 API 的请求配置**（`setApi` 的 `settings.validSuccess`）
3. **全局请求配置中的 `validate` 函数**是否与后端响应结构匹配

**原理说明：**

- 当 `validSuccess: false`（默认）时，VTJ 不校验后端业务响应码，只要 HTTP 状态码为 2xx 即视为成功
- 当 `validSuccess: true` 时，VTJ 会调用 `validate` 函数对响应体进行业务成功校验；若 `validate` 返回 `false`，请求会被视为失败，Promise 被 `reject` 并弹出错误提示
- 全局默认 `validate` 函数逻辑为：`res.data?.code === 0 || !!res.data?.success`，即要求后端响应体中包含 `code: 0` 或 `success: true`
- 单个 API 的 `settings.validSuccess` 会覆盖全局配置

**典型错误场景：**

```javascript
// 错误示例 1：后端返回结构不匹配默认 validate
// 后端响应：{ status: 'ok', data: [...] }
const res = await __apis['getUserList']();
// ❌ 若 validSuccess: true，默认 validate 找不到 code/success，判定失败，进入 catch

// 解决方案：自定义 validate 函数匹配后端结构
{
  "action": "setGlobalAxios",
  "parameters": [
    "(app) => {\n  return {\n    settings: {\n      validSuccess: true,\n      validate: (res) => {\n        return res.data?.status === 'ok';\n      }\n    }\n  };\n}"
  ]
}
```

```javascript
// 错误示例 2：未处理 validSuccess 导致的 reject
// 全局 validSuccess: true，但代码未加 try/catch
const res = await __apis['getUserList'](); // ❌ 校验失败时会抛出异常，导致后续代码不执行

// 正确写法
try {
  const res = await __apis['getUserList']();
  this.list = res;
} catch (e) {
  // 校验失败或请求异常时进入此处
  console.error('请求失败', e);
}
```

```javascript
// 错误示例 3：某个 API 不需要校验但全局 validSuccess: true
// 该 API 后端返回无固定 code/success 结构
const res = await __apis['getRawData'](); // ❌ 被全局 validate 误判为失败

// 解决方案：该 API 单独关闭 validSuccess
{
  "action": "setApi",
  "parameters": [
    {
      "name": "getRawData",
      "url": "/api/raw",
      "settings": {
        "validSuccess": false
      }
    }
  ]
}
```

**排查步骤：**

1. 确认应用设置中 `setGlobalAxios` 的 `settings.validSuccess` 值
2. 确认全局 `validate` 函数逻辑是否与后端实际响应结构匹配（如 `code`、`success`、`status` 等字段）
3. 确认该 API 在 `setApi` 中是否单独设置了 `settings.validSuccess`
4. 若 `validSuccess: true`，确保调用处已处理 `reject` 场景（`try/catch` 或 `.catch()`）

---

## 四、组件中调用 API

### 4.1 基本调用

API 在运行时通过 `__apis` 全局变量访问，键名为 API 的 `name` 或 `id`：

```javascript
// 无参数 GET 请求
const res = await __apis['getUserList']();

// POST 请求，带请求体
const res = await __apis['createUser']({
  name: '张三',
  age: 28
});
```

**调用签名：**

```
(data?, opts?) => Promise<any>
```

- `data` — 请求体数据（GET/JSONP 时通常为空）
- `opts` — 额外配置，可覆盖 API 的默认 settings，也可传递路径参数（`opts.params`）和查询参数（`opts.query`）

### 4.2 路径参数（URL 中的 `:id` 占位符）

如果 API 的 URL 包含路径参数（如 `/api/users/:id`），通过第二个参数 `opts.params` 传入：

```javascript
// URL: /api/users/:id
const user = await __apis['getUserById'](
  {}, // 无请求体
  { params: { id: 'abc123' } } // 路径参数
);

// URL: /api/orders/:orderId/items/:itemId
const item = await __apis['getOrderItem'](
  {},
  { params: { orderId: '100', itemId: '5' } }
);
```

### 4.3 查询参数

查询参数（即 `?key=value`）通过 `opts.query` 传入：

```javascript
// 最终请求: /api/users?page=1&size=20&keyword=张三
const res = await __apis['getUserList'](
  {},
  { query: { page: 1, size: 20, keyword: '张三' } }
);
```

> **区分：** `opts.params` 用于替换 URL 中的路径占位符（如 `/api/users/:id`），`opts.query` 用于拼接 URL 查询字符串（`?key=value`）。两者不可混用。

### 4.4 覆盖请求配置

`opts` 参数支持临时覆盖 API 的 settings：

```javascript
// 本次请求不显示 loading，不显示错误提示
const res = await __apis['getUserList'](
  {},
  { settings: { loading: false, failMessage: false } }
);

// 自定义请求头
const res = await __apis['getUserList'](
  {},
  {
    settings: { injectHeaders: true },
    headers: { 'X-Device': 'mobile' }
  }
);
```

### 4.5 响应数据提取与 `originResponse`

VTJ 的请求底层基于 Axios，后端接口通常返回如下格式的响应体：

```json
{
  "code": 0,
  "msg": "ok",
  "data": { ... }
}
```

**默认行为（`originResponse: true`）：** 请求成功后，返回完整的 Axios 响应对象（`AxiosResponse`），需通过 `res.data` 获取后端响应体，再通过 `res.data.data` 获取业务数据：

```javascript
// 默认 originResponse: true
const res = await __apis['getUserList']();
// res 是 Axios 响应对象，res.data 是 { code: 0, msg: 'ok', data: { list: [...] } }
// res.data.data 才是业务数据 { list: [...], total: 100 }
```

**设置 `originResponse: false`：** VTJ 自动提取 `res.data?.data`，即只返回响应体中 `data` 字段的值，省去手动 `.data.data` 的取值步骤：

```javascript
// 设置 originResponse: false
const res = await __apis['getUserList']();
// res 直接是 { list: [...], total: 100 }，而非完整的响应包装
```

**开启 `originResponse: true`（默认）：** 返回完整的 Axios 响应对象（`AxiosResponse`），包含以下属性：

| 属性          | 说明                            |
| ------------- | ------------------------------- |
| `res.data`    | 响应体（即后端返回的完整 JSON） |
| `res.status`  | HTTP 状态码（如 200）           |
| `res.headers` | 响应头对象                      |
| `res.config`  | 请求配置对象                    |

适用场景：

- 需要读取 HTTP 响应头（如分页总数 `X-Total-Count`、下载文件的 `Content-Disposition`）
- 需要判断 HTTP 状态码做差异化处理
- 需要访问后端响应体的顶层字段（如 `code`、`msg`），而不仅是 `data` 部分

**设置方式：**

1. **在 API 配置中全局设置：**

```json
{
  "action": "setApi",
  "parameters": [
    {
      "name": "getFileList",
      "url": "/api/files",
      "method": "get",
      "settings": {
        "originResponse": true
      }
    }
  ]
}
```

2. **在调用时临时覆盖：**

```javascript
// 临时获取原始响应对象
const res = await __apis['getUserList'](
  {},
  {
    settings: { originResponse: true }
  }
);
console.log(res.status); // 200
console.log(res.headers); // { 'x-total-count': '50', ... }
console.log(res.data); // { code: 0, msg: 'ok', data: { list: [...] } }
```

**示例：读取分页响应头**

```javascript
const __state = reactive({
  list: [],
  total: 0,
  async fetchPage(page = 1) {
    // 后端通过响应头返回总数
    const res = await __apis['getUserList'](
      {},
      {
        query: { page, size: 20 },
        settings: { originResponse: true }
      }
    );
    this.list = res.data.data || []; // res.data 是响应体，res.data.data 是业务数据
    this.total = Number(res.headers['x-total-count']) || 0;
  }
});
```

**示例：读取下载文件响应**

```javascript
const __state = reactive({
  async downloadFile(id) {
    const res = await __apis['downloadFile'](
      {},
      {
        params: { id },
        settings: { originResponse: true }
      }
    );
    // 从响应头获取文件名
    const disposition = res.headers['content-disposition'];
    const filename = disposition?.match(/filename=(.+)/)?.[1] || 'unknown.bin';
    // res.data 为文件二进制内容（需配合 responseType: 'blob'）
    return { content: res.data, filename };
  }
});
```

> **注意：** 设置 `originResponse: false` 后，返回值从 Axios 响应对象变为业务数据（`res.data?.data`），取值路径需相应调整。默认返回完整 Axios 响应对象，需通过 `res.data.data` 或 `res.data`（取决于后端响应结构）获取数据。

### 4.6 JSONP 请求

JSONP 类型 API 的调用签名特殊：

```javascript
(query?) => Promise<any>
```

```javascript
// 设置 API: method = 'jsonp', url = 'https://api.example.com/data'
const res = await __apis['getExternalData']({
  userId: '123',
  callback: 'jsonpCallback'
});
```

### 4.7 Vue SFC 中使用

在 `<script setup>` 中，通过 `__state` 中定义方法调用 API：

```javascript
const __state = reactive({
  list: [],
  loading: false,
  async fetchData() {
    this.loading = true;
    const res = await __apis['getUserList']({}, { query: { page: 1 } });
    this.list = res.data?.data || [];
    this.loading = false;
  }
});

// 在生命周期中调用
onMounted(() => {
  __state.fetchData();
});
```

在模板中绑定：

```html
<el-button :loading="__state.loading" @click="__state.fetchData">
  加载数据
</el-button>
```

---

## 五、渲染器底层 API（@vtj/renderer）

### 5.1 `__provider.apis` — 直接调用已注册的 API 函数

`__provider.apis` 是渲染器 Provider 实例上的 API 函数注册表，所有通过 `setApi` 配置的 API 在运行时会被编译为可调用的函数并注册到此对象中。

**访问方式：**

```javascript
// 通过 API 的 name 或 id 访问
const apiFunction = __provider.apis['getUserList'];

// 直接调用
const res = await __provider.apis['getUserList'](data, opts);
```

**与 `__apis` 的关系：**

- `__apis` 是 `__provider.apis` 的快捷引用，两者指向同一对象
- 组件中推荐使用 `__apis`，代码更简洁
- `__provider.apis` 主要用于底层框架代码或需要显式访问 Provider 实例的场景

**示例：**

```javascript
// 在数据源方法中使用
const fetchData = async (...args) => {
  return await __provider.apis['getUserList']
    .apply(null, args)
    .then((res) => res.data?.data);
};
```

---

### 5.2 `__provider.createMock` — 创建模拟数据函数

`__provider.createMock` 用于根据 Mock.js 模板函数创建模拟数据 API。它会解析模板函数，并在调用时使用 Mock.js 生成随机数据。

**函数签名：**

```typescript
createMock(mockTemplate: (req?: any) => object): (...args: any[]) => Promise<any>
```

**参数：**

- `mockTemplate` — Mock.js 模板函数，签名为 `(req) => template`，返回 Mock.js 模板对象
  - `req` — 请求信息对象，包含 `url`、`type`、`data`、`params`、`query` 等属性
  - 返回值 — Mock.js 模板对象，使用 Mock.js 占位符语法（如 `@guid`、`@cname`、`@integer(10, 100)`）

**返回：**

- 一个异步函数，调用后返回 Mock.js 生成的模拟数据

**示例 1：基础用法**

```javascript
// 创建模拟数据函数
const mockApi = __provider.createMock((req) => {
  return {
    code: 0,
    message: 'ok',
    data: {
      'list|10': [
        {
          id: '@guid',
          name: '@cname',
          'age|18-60': 1
        }
      ]
    }
  };
});

// 调用模拟 API
const res = await mockApi();
console.log(res);
// 输出：{ code: 0, message: 'ok', data: { list: [...] } }
```

**示例 2：在数据源方法中使用**

```javascript
// Composition 模式数据源方法
const getMockData = async (...args) => {
  const mock = __provider.createMock((req) => {
    return {
      code: 0,
      data: {
        totalUsers: '@integer(100, 10000)',
        todayOrders: '@integer(10, 500)',
        revenue: '@float(1000, 50000, 2, 2)'
      }
    };
  });
  return await mock.apply(null, args);
};
```

**示例 3：根据请求参数动态生成模拟数据**

```javascript
const mockUserApi = __provider.createMock((req) => {
  const count = req.query?.size || 5;
  return {
    code: 0,
    data: {
      [`list|${count}`]: [
        {
          id: '@guid',
          name: '@cname',
          email: '@email'
        }
      ]
    }
  };
});

// 调用时传入查询参数
const res = await mockUserApi({}, { query: { size: 3 } });
// 返回 3 条模拟数据
```

**Mock.js 常用占位符：**

| 占位符                         | 说明             | 示例                                     |
| ------------------------------ | ---------------- | ---------------------------------------- | ------ | ----------------------- |
| `@guid`                        | 生成 GUID        | `'f3c8b5a7-1d2e-4f6a-8b9c-0d1e2f3a4b5c'` |
| `@cname`                       | 生成中文姓名     | `'张三'`                                 |
| `@email`                       | 生成邮箱         | `'example@email.com'`                    |
| `@integer(min, max)`           | 生成整数         | `@integer(10, 100)`                      |
| `@float(min, max, dmin, dmax)` | 生成浮点数       | `@float(1000, 50000, 2, 2)`              |
| `'key                          | min-max': value` | 生成数组                                 | `'list | 10': [{ id: '@guid' }]` |

> Mock.js 完整语法规则参考：https://vtj.pro/help/mock.html

**异常处理：**

`createMock` 内部会捕获模板函数执行异常，并输出警告日志，不会阻断调用流程：

```javascript
const mockApi = __provider.createMock((req) => {
  throw new Error('模板错误');
});

// 不会抛出异常，返回空对象 {}
const res = await mockApi();
```

---

## 六、典型配置示例

### 示例 1：配置一个完整的 CRUD 接口组

```json
[
  {
    "action": "setApi",
    "parameters": [
      {
        "name": "getUserList",
        "label": "获取用户列表",
        "url": "/api/users",
        "method": "get",
        "category": "用户管理"
      }
    ]
  },
  {
    "action": "setApi",
    "parameters": [
      {
        "name": "getUserById",
        "label": "获取用户详情",
        "url": "/api/users/:id",
        "method": "get",
        "category": "用户管理"
      }
    ]
  },
  {
    "action": "setApi",
    "parameters": [
      {
        "name": "createUser",
        "label": "创建用户",
        "url": "/api/users",
        "method": "post",
        "category": "用户管理"
      }
    ]
  },
  {
    "action": "setApi",
    "parameters": [
      {
        "name": "updateUser",
        "label": "更新用户",
        "url": "/api/users/:id",
        "method": "put",
        "category": "用户管理"
      }
    ]
  },
  {
    "action": "setApi",
    "parameters": [
      {
        "name": "deleteUser",
        "label": "删除用户",
        "url": "/api/users/:id",
        "method": "delete",
        "category": "用户管理"
      }
    ]
  }
]
```

### 示例 2：带完整模拟数据的 API

```json
{
  "action": "setApi",
  "parameters": [
    {
      "name": "getDashboard",
      "label": "获取仪表盘数据",
      "url": "/api/dashboard",
      "method": "get",
      "category": "仪表盘",
      "mock": true,
      "mockTemplate": {
        "type": "JSFunction",
        "value": "(req) => {\n  return {\n    code: 0,\n    data: {\n      totalUsers: '@integer(100, 10000)',\n      todayOrders: '@integer(10, 500)',\n      revenue: '@float(1000, 50000, 2, 2)'\n    }\n  };\n}"
      }
    }
  ]
}
```

> `mockTemplate` 使用 Mock.js 语法规则，`@integer`、`@float`、`@cname` 等为 Mock.js 占位符。

### 示例 3：带路径参数的 POST 接口

```json
{
  "action": "setApi",
  "parameters": [
    {
      "name": "updateUser",
      "label": "更新用户信息",
      "url": "/api/users/:id",
      "method": "put",
      "category": "用户管理",
      "settings": {
        "type": "json",
        "loading": true,
        "failMessage": true
      }
    }
  ]
}
```

---

## 七、常用请求场景速查

| 场景                        | 调用方式                                                                        |
| --------------------------- | ------------------------------------------------------------------------------- |
| 简单查询（无参数）          | `__apis['apiName']()`                                                           |
| GET 查询（带查询参数）      | `__apis['apiName']({}, { query: { page: 1 } })`                                 |
| POST 提交表单               | `__apis['apiName']({ name: '张三', age: 28 })`                                  |
| 带路径参数                  | `__apis['apiName']({}, { params: { id: 'xxx' } })`                              |
| 上传文件                    | `__apis['upload'](formData)`，设置 `settings.type = 'data'`                     |
| 静默请求（无 loading/提示） | `__apis['apiName'](data, { settings: { loading: false, failMessage: false } })` |
| JSONP 跨域                  | `__apis['externalApi']({ userId: '123' })`                                      |
| 获取原始响应对象            | `__apis['apiName'](data, { settings: { originResponse: true } })`               |

---

## 八、注意事项

1. **name 唯一性：** API 的 `name` 在同一项目中必须唯一，创建前建议先调用 `getApis` 检查是否已存在同名 API
2. **路径参数放 `opts.params`：** URL 中的 `:id` 占位符替换依赖 `opts.params` 字段，不要将路径参数放在 `data` 中
3. **查询参数放 `opts.query`：** URL 查询字符串 `?key=value` 通过 `opts.query` 传入，不要与路径参数 `opts.params` 混用
4. **请求体仅用于 POST/PUT/PATCH：** GET 和 DELETE 请求通常不需要 `data` 参数
5. **settings 覆盖规则：** 调用时传入的 `opts.settings` 会与 API 配置的 `settings` 合并，调用时的值优先级更高
6. **$provider.apis vs \_\_apis：** 两者指向同一对象，组件中推荐使用 `__apis`
7. **API 需刷新后生效：** 通过 `setApi` 新增的 API 需要通过 `refresh` 刷新运行时才能生效

<!-- ============================================ -->
<!-- 文档: env.md -->
<!-- ============================================ -->

# VTJ 环境变量 — AI 配置与使用指南

> 本文档帮助 AI 理解如何通过工具方法配置环境变量，以及在组件中读取环境变量。

---

## 一、数据流全貌

```
设计器 env 面板                   项目数据                       渲染引擎
env/index.vue  ──setEnv()──▶  project.env  ──initEnv()──▶  provider.env
  (表格编辑)                     (EnvConfig[])  (按当前环境平铺)   (Record<string, string>)
                                                                  │
                                                  组件中: __provider.env.VAR_NAME
```

**关键机制：**

1. **双环境值存储：** 每条环境变量同时配置 `development`（开发）和 `production`（生产）两个值
2. **运行时切换：** `initEnv()` 根据当前 `nodeEnv` 自动选取对应的值，平铺为 `{ VAR_NAME: value }` 对象
3. **全局可访问：** `provider.env` 挂载在 Provider 实例上，组件通过 `__provider.env.VAR_NAME` 读取

---

## 二、EnvConfig 数据结构

| 字段          | 类型   | 必填 | 说明                                              |
| ------------- | ------ | ---- | ------------------------------------------------- |
| `name`        | string | ✅   | 变量名，通常大写+下划线，如 `BASE_URL`、`API_KEY` |
| `development` | string | ✅   | 开发环境下的值                                    |
| `production`  | string | ✅   | 生产环境下的值                                    |

**示例数据：**

```json
[
  {
    "name": "BASE_URL",
    "development": "http://localhost:8080",
    "production": "https://api.example.com"
  },
  { "name": "APP_TITLE", "development": "VTJ 开发版", "production": "VTJ" },
  {
    "name": "API_KEY",
    "development": "dev-key-123",
    "production": "prod-key-456"
  }
]
```

运行时（开发环境）`__provider.env` 的实际值：

```javascript
{
  BASE_URL: 'http://localhost:8080',
  APP_TITLE: 'VTJ 开发版',
  API_KEY: 'dev-key-123'
}
```

---

## 三、环境变量工具方法

### `getEnv` — 获取所有环境变量

获取当前项目配置的全部环境变量列表（包含两套值）。

**参数：** 无

**返回：** `EnvConfig[]` 数组，每项包含 `name`、`development`、`production`

---

### `createEnv` — 新增环境变量

新增一条环境变量。如果 `name` 已存在则追加（同名可重复）。

**参数：**

| 参数                 | 类型   | 必填 | 说明                                   |
| -------------------- | ------ | ---- | -------------------------------------- |
| `config.name`        | string | ✅   | 变量名，推荐大写+下划线，如 `BASE_URL` |
| `config.development` | string | ✅   | 开发环境的值                           |
| `config.production`  | string | ✅   | 生产环境的值                           |

**示例：**

```json
{
  "action": "createEnv",
  "parameters": [
    {
      "name": "BASE_URL",
      "development": "http://localhost:8080/api",
      "production": "https://api.example.com"
    }
  ]
}
```

---

### `removeEnv` — 删除环境变量

根据变量名 `name` 删除环境变量。如果存在多个同名变量，同时全部删除。

**参数：**

- `name: string` — 变量名称

**示例：**

```json
{
  "action": "removeEnv",
  "parameters": ["BASE_URL"]
}
```

---

## 四、组件中读取环境变量

### 4.1 在 `<script setup>` 中使用

VTJ 使用 Composition API 模式，环境变量通过 `__provider.env` 全局实例访问：

```javascript
// 读取单个变量
const baseUrl = __provider.env.BASE_URL;

// 带默认值的安全读取
const apiKey = __provider.env.API_KEY || 'default-key';

// 用于 API 请求前缀
const fullUrl = `${__provider.env.BASE_URL}/api/users`;
```

### 4.2 在模板中使用

```html
<template>
  <div>
    <p>应用标题: {{ __provider.env.APP_TITLE }}</p>
    <p>API 地址: {{ __provider.env.BASE_URL }}</p>
  </div>
</template>
```

### 4.3 完整 SFC 示例

```html
<template>
  <div class="page">
    <h1>{{ __provider.env.APP_TITLE || 'VTJ' }}</h1>
    <el-button @click="__state.loadData">加载数据</el-button>
  </div>
</template>

<script setup>
  import { reactive } from 'vue';

  const __state = reactive({
    data: null,
    loading: false,
    async loadData() {
      this.loading = true;
      const res = await __apis['getDashboard']();
      this.data = res.data?.data || null;
      this.loading = false;
    }
  });

  onMounted(() => {
    // 开发环境下打印日志
    if (__provider.env.DEBUG === 'true') {
      console.log('当前 BASE_URL:', __provider.env.BASE_URL);
    }
  });
</script>
```

---

## 五、典型使用场景

### 5.1 API 基础地址

```json
{
  "action": "createEnv",
  "parameters": [
    {
      "name": "API_BASE_URL",
      "development": "http://localhost:3000/api",
      "production": "https://api.myapp.com"
    }
  ]
}
```

组件中使用环境变量拼接 API URL：

```javascript
// 注意：API 的 url 字段可直接使用相对路径 /api/users，
// 实际请求地址由全局 axios 的 baseURL 决定。
// 环境变量更适用于非请求场景的地址引用。
const uploadUrl = `${__provider.env.API_BASE_URL}/upload`;
```

### 5.2 功能开关

```json
{
  "action": "createEnv",
  "parameters": [
    {
      "name": "ENABLE_DEBUG_PANEL",
      "development": "true",
      "production": "false"
    }
  ]
}
```

组件中：

```html
<debug-panel v-if="__provider.env.ENABLE_DEBUG_PANEL === 'true'" />
```

### 5.3 第三方 Key / 配置

```json
{
  "action": "createEnv",
  "parameters": [
    {
      "name": "MAP_KEY",
      "development": "dev-abc123",
      "production": "prod-xyz789"
    }
  ]
}
```

---

## 六、注意事项

1. **值类型为字符串：** `EnvConfig` 的 `development` 和 `production` 字段类型均为 `string`，布尔值需使用 `'true'` / `'false'` 字符串
2. **同名不覆盖：** `createEnv` 不检查同名变量是否已存在，直接追加。如需更新，建议先 `removeEnv` 再 `createEnv`
3. **运行时自动选取：** `initEnv()` 根据 `provider.nodeEnv`（`development` 或 `production`）自动选择对应值，无需手动判断环境
4. **变量名约定：** 推荐使用大写字母 + 下划线命名（`UPPER_SNAKE_CASE`），如 `BASE_URL`、`APP_TITLE`
5. **脚本层访问：** 环境变量通过 `__provider.env` 实例属性访问，不是 `$provider.env` 或 `this.$provider.env`
6. **不在 `__state` 中存储：** 环境变量是全局只读配置，不要将 `__provider.env` 的值复制到 `__state` 中，直接在需要的地方读取即可
7. **不需要手动 import：** `__provider` 由框架在组件模板中通过 `useProvider()` 自动声明，组件内直接使用

<!-- ============================================ -->
<!-- 文档: globals.md -->
<!-- ============================================ -->

# @vtj/renderer 全局 API 使用指南

> 本文档描述 `@vtj/renderer` 提供的全局变量在 Vue 组件中的使用方式，供 AI 大模型生成代码时参考。

---

## 重要约定

这些全局变量由 `@vtj/renderer` 提供，AI 生成代码时可选择以下任一方式使用：

- **直接使用变量名**（推荐）：在方法体内直接使用 `__store`、`__apis` 等变量，无需手动声明或导入，框架会在生成的 `<script setup>` 中自动添加对应的 import 和声明语句
- **从 `@vtj/renderer` 导入**：也可通过 `import { useStore, useApis, ... } from '@vtj/renderer'` 显式导入，再调用对应的 composable 函数获取实例

---

## 一、`__store` — 全局 Pinia Store

### 功能说明

`__store` 是通过 `useStore()` 获取的全局 Pinia store 实例，对应 `this.$store`。用于访问全局共享状态（如用户信息、应用配置等）。

> **前置条件：** `__store` 仅在 VTJ 应用设置中配置了「应用状态 Pinia」后才会生成，未配置时该变量不可用。AI 在生成代码前需先确认项目是否已启用此功能。

### 使用方式

```js
// 读取 store 中的状态
const user = __store.user;
const token = __store.token;

// 调用 store 中的 action
__store.logout();
__store.setUser({ name: '张三', role: 'admin' });
```

### 典型场景

```js
// 获取当前登录用户信息
const getUser = () => {
  return __store.user;
};

// 在 onMounted 中读取用户权限
onMounted(() => {
  const permissions = __store.permissions;
  __state.canEdit = permissions.includes('edit');
});

// 登录后存储用户信息（调用 action）
const handleLogin = async () => {
  const res = await __apis['login']({
    username: __state.username,
    password: __state.password
  });
  __store.setUser(res.data?.data);
};
```

---

## 二、`__pinia` — Pinia 实例

### 功能说明

`__pinia` 是通过 `usePinia()` 获取的 Pinia 实例本身，对应 `this.$pinia`。通常用于在组件内动态注册或访问其他 store 模块。

### 使用方式

```js
import { defineStore } from 'pinia';

// 使用 pinia 实例动态访问其他 store
const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++;
    }
  }
});

const counterStore = useCounterStore(__pinia);
counterStore.increment();
```

### 典型场景

```js
// 在方法中动态获取某个 store 的状态
const getCounterValue = () => {
  const useCounterStore = defineStore('counter', {
    state: () => ({ count: 0 })
  });
  const store = useCounterStore(__pinia);
  return store.count;
};
```

> 注意：`__pinia` 主要用于需要 pinia 实例参数的高级场景，普通 store 访问请优先使用 `__store`。

---

## 三、`__request` — HTTP 请求实例

### 功能说明

`__request` 是通过 `useRequest()` 获取的 HTTP 请求实例，对应 `this.$request`。用于直接发送 HTTP 请求，或注册请求/响应拦截器。

### 使用方式

#### 发送请求

```js
// GET 请求
const fetchData = async () => {
  const res = await __request.send({
    url: '/api/users',
    method: 'get'
  });
  __state.list = res.data?.data;
};

// POST 请求（带参数）
const submitForm = async () => {
  const res = await __request.send({
    url: '/api/users',
    method: 'post',
    data: {
      name: __state.name,
      email: __state.email
    }
  });
  return res.data?.data;
};

// 带查询参数的 GET
const searchUsers = async (keyword) => {
  const res = await __request.send({
    url: '/api/users/search',
    method: 'get',
    data: { keyword, page: __state.page }
  });
  return res.data?.data;
};
```

#### 注册请求拦截器

```js
// 添加请求头
__request.useRequest((config) => {
  config.headers['X-Custom-Header'] = 'value';
  return config;
});

// 注册响应拦截器
__request.useResponse((res) => {
  return res;
});
```

### 典型场景

```js
// 直接调用接口（不走数据源）
const loadUserDetail = async (id) => {
  __state.loading = true;
  try {
    const res = await __request.send({
      url: `/api/users/${id}`,
      method: 'get'
    });
    __state.user = res.data;
  } finally {
    __state.loading = false;
  }
};
```

> 注意：通常推荐使用 `__apis` 调用在项目中预配置的接口，`__request` 适用于临时或动态构建 URL 的场景。

---

## 四、`__libs` — 第三方库集合

### 功能说明

`__libs` 是通过 `useLibs()` 获取的第三方库对象集合，对应 `this.$libs`。包含项目所有注册的 UI 库和工具库（如 `ElementPlus`、`AntDesignVue`、`VueUse`、`Vant` 等）。

### 结构说明

```
__libs
├── ElementPlus      ← Element Plus 库（如果项目使用）
├── AntDesignVue     ← Ant Design Vue 库（如果项目使用）
├── VueUse           ← VueUse 工具库（如果项目使用）
├── Vant             ← Vant 移动端组件库（如果项目使用）
└── ...              ← 其他注册的第三方库
```

### 使用方式

```js
// 调用 Element Plus 的弹窗
__libs.ElementPlus.ElMessage.success('操作成功');
__libs.ElementPlus.ElNotification({
  title: '提示',
  message: '内容',
  type: 'info'
});

// 调用 Ant Design Vue 的消息提示
__libs.AntDesignVue.message.success('保存成功');
__libs.AntDesignVue.Modal.confirm({
  title: '确认删除？',
  onOk: () => handleDelete()
});

// 使用 VueUse 工具函数
const { useDark } = __libs.VueUse;
const isDark = useDark();

// 调用 Vant 的 Toast
__libs.Vant.showToast('加载中...');
```

### 典型场景

```js
// 动态获取组件或工具（当不确定库名时）
const showMessage = (msg) => {
  if (__libs.ElementPlus) {
    __libs.ElementPlus.ElMessage.success(msg);
  } else if (__libs.AntDesignVue) {
    __libs.AntDesignVue.message.success(msg);
  }
};

// 在事件处理中使用
const handleExport = () => {
  __libs.ElementPlus.ElMessage({ message: '导出中，请稍候...', type: 'info' });
  // ... 导出逻辑
};
```

> 注意：库名（如 `ElementPlus`）是项目配置时指定的 library 名称，需与项目依赖配置中的 `library` 字段一致。

---

## 五、`__apis` — 项目接口集合

### 功能说明

`__apis` 是通过 `useApis()` 获取的项目接口函数集合，对应 `this.$apis`。包含所有在 VTJ 项目中配置的接口（API），每个接口可通过接口 ID 或接口名称调用。

### 使用方式

```js
// 通过接口 ID 调用（推荐，ID 不会随改名变化）
const res = await __apis['api_id_001']({ page: 1, size: 20 });

// 通过接口名称调用
const res = await __apis['getUserList']({ page: 1, size: 20 });

// 带路径参数的接口（URL 形如 /api/users/:id）
// 路径参数通过第二个参数的 params 字段传入
const res = await __apis['getUserById']({}, { params: { id: userId } });

// POST 接口
const res = await __apis['createUser']({
  name: __state.name,
  email: __state.email,
  role: __state.role
});
```

### 典型场景

```js
// 列表查询
const loadList = async () => {
  __state.loading = true;
  try {
    const res = await __apis['getUserList']({
      page: __state.page,
      size: __state.pageSize,
      keyword: __state.keyword
    });
    __state.list = res.data.list;
    __state.total = res.data.total;
  } finally {
    __state.loading = false;
  }
};

// 表单提交
const handleSubmit = async () => {
  const res = await __apis['saveUser'](__state.form);
  if (res.code === 200) {
    __libs.ElementPlus.ElMessage.success('保存成功');
    loadList();
  }
};

// 删除操作（带确认）
const handleDelete = async (id) => {
  await __libs.ElementPlus.ElMessageBox.confirm('确认删除？', '提示');
  await __apis['deleteUser']({ id });
  __libs.ElementPlus.ElMessage.success('删除成功');
  loadList();
};
```

> 注意：`__apis` 中的接口由项目 API 配置决定，接口 ID 和名称需与项目中定义的一致。

---

## 六、`__access` — 权限控制实例

### 功能说明

`__access` 是通过 `useAccess()` 获取的权限控制实例，对应 `this.$access`。提供登录状态管理、权限判断、登录/登出等功能。

> **前置条件：** `__access` 仅在 VTJ 应用设置中配置了「权限控制 Access」后才会生成，未配置时该变量不可用。AI 在生成代码前需先确认项目是否已启用此功能。

### 主要方法

| 方法                   | 说明                         | 返回值                |
| ---------------------- | ---------------------------- | --------------------- |
| `__access.isLogined()` | 判断是否已登录               | `boolean`             |
| `__access.getToken()`  | 获取当前 token               | `string \| undefined` |
| `__access.getData()`   | 获取完整的登录数据（含权限） | `AccessData \| null`  |
| `__access.can(code)`   | 判断是否拥有某权限码（所有） | `boolean`             |
| `__access.some(code)`  | 判断是否拥有某权限码（任一） | `boolean`             |
| `__access.login(data)` | 登录（存储 token 和权限）    | `void`                |
| `__access.logout()`    | 登出（清除数据并跳转登录页） | `void`                |
| `__access.clear()`     | 仅清除本地存储（不跳转）     | `void`                |
| `__access.toLogin()`   | 跳转到登录页                 | `void`                |

### 使用方式

```js
// 判断是否登录
const checkLogin = () => {
  if (!__access.isLogined()) {
    __access.toLogin();
    return;
  }
  loadData();
};

// 获取 token（用于自定义请求头）
const getAuthHeader = () => {
  return `Bearer ${__access.getToken()}`;
};

// 权限判断
const canEdit = __access.can('user:edit');
const canView = __access.can(['user:view', 'admin']); // 需要所有权限
const hasAnyPerm = __access.some(['user:edit', 'admin']); // 任一权限即可

// 登录成功后存储数据
const handleLoginSuccess = (res) => {
  __access.login({
    token: res.data.token,
    permissions: res.data.permissions,
    userInfo: res.data.userInfo
  });
  __router.push('/');
};

// 登出
const handleLogout = () => {
  __access.logout(); // 清除数据并跳转登录页
};
```

### 典型场景

```js
// 页面加载时校验登录态
onMounted(() => {
  if (!__access.isLogined()) {
    __access.toLogin();
    return;
  }
  loadPageData();
});

// 根据权限显示/隐藏按钮（配合响应式状态）
onMounted(() => {
  __state.canDelete = __access.can('record:delete');
  __state.canExport = __access.some(['data:export', 'admin']);
});

// 登录页面提交
const submitLogin = async () => {
  const res = await __apis['login']({
    username: __state.username,
    password: __state.password
  });
  __access.login({
    token: res.data.token,
    permissions: res.data.permissions || {}
  });
  __router.replace('/');
};
```

### 路由页面权限控制

VTJ 低代码平台的动态页面访问受路由权限控制，页面跳转前路由守卫会检查当前用户是否有权访问目标页面。权限检查规则如下：

| 场景                        | 检查的权限码         | 说明                                                                   |
| --------------------------- | -------------------- | ---------------------------------------------------------------------- |
| VTJ 动态页面                | `to.params.id`       | 路由名为 `VtjPage` 的页面，需将页面 ID 记录到 `permissions` 中才能访问 |
| 带 `__vtj__` meta 的路由    | `to.meta.__vtj__`    | 路由 meta 中声明的 `__vtj__` 字段值                                    |
| 带 `permission` meta 的路由 | `to.meta.permission` | 路由 meta 中声明的 `permission` 字段值                                 |

**重要：** 若使用 VTJ 低代码动态页面，登录时必须将用户有权限访问的页面 ID 写入 `permissions`，否则路由守卫会拦截跳转。`permissions` 支持对象键或数组元素两种形式：

```js
// permissions 为对象形式（键为页面 ID，值为 true）
__access.login({
  token: res.data.token,
  permissions: {
    page_123: true,
    page_456: true,
    'user:edit': true
  }
});

// permissions 为数组形式（元素为页面 ID 或权限码）
__access.login({
  token: res.data.token,
  permissions: ['page_123', 'page_456', 'user:edit']
});
```

---

## 七、综合使用示例

以下示例展示多个全局 API 协同工作的场景：

```vue
<template>
  <div class="user-manage">
    <div class="header">
      <span>欢迎，{{ __state.userName }}</span>
      <ElButton @click="handleLogout">退出登录</ElButton>
    </div>
    <ElTable :data="__state.list" v-loading="__state.loading">
      <ElTableColumn prop="name" label="姓名" />
      <ElTableColumn prop="email" label="邮箱" />
      <ElTableColumn label="操作">
        <template #default="{ row }">
          <ElButton v-if="__state.canEdit" size="small" @click="handleEdit(row)"
            >编辑</ElButton
          >
          <ElButton
            v-if="__state.canDelete"
            size="small"
            type="danger"
            @click="handleDelete(row.id)"
            >删除</ElButton
          >
        </template>
      </ElTableColumn>
    </ElTable>
  </div>
</template>

<script setup>
  import { reactive, onMounted } from 'vue';
  import { ElButton, ElTable, ElTableColumn } from 'element-plus';

  const __state = reactive({
    loading: false,
    list: [],
    total: 0,
    userName: '',
    canEdit: false,
    canDelete: false
  });

  const loadList = async () => {
    __state.loading = true;
    try {
      const res = await __apis['getUserList']({ page: 1, size: 20 });
      __state.list = res.data.list || [];
      __state.total = res.data.total || 0;
    } finally {
      __state.loading = false;
    }
  };

  const handleEdit = (row) => {
    __router.push(`/user/edit/${row.id}`);
  };

  const handleDelete = async (id) => {
    await __libs.ElementPlus.ElMessageBox.confirm('确认删除该用户？', '提示', {
      type: 'warning'
    });
    await __apis['deleteUser']({ id });
    __libs.ElementPlus.ElMessage.success('删除成功');
    loadList();
  };

  const handleLogout = () => {
    __access.logout();
  };

  onMounted(() => {
    // 从全局 store 获取用户信息
    __state.userName = __store.user?.name || '未知用户';

    // 权限控制
    __state.canEdit = __access.can('user:edit');
    __state.canDelete = __access.can('user:delete');

    loadList();
  });
</script>
```

<!-- ============================================ -->
<!-- 文档: i18n.md -->
<!-- ============================================ -->

# VTJ 国际化（i18n）— AI 配置与使用指南

> 本文档帮助 AI 理解如何通过工具方法配置国际化词条，以及在组件中实现多语言文本。

---

## 一、数据流全貌

```
设计器 i18n 面板                    项目数据                         渲染引擎
i18n/index.vue  ──setI18n()──▶  project.i18n  ──initI18n()──▶  VueI18n 插件注册
  (表格编辑)                      (I18nConfig)   (消息格式转换)    (app.use)
                                                    │
                                [ { key, 'zh-CN', en } ] →  { 'zh-CN': { key: value }, en: { key: value } }
```

**关键流程：**

1. **词条存储：** 设计器以扁平数组形式存储 `[{ key, 'zh-CN', en }]`
2. **渲染器转换：** `initI18n()` 将扁平数组转为 locale 分组的嵌套对象 `{ 'zh-CN': { key: value }, en: { key: value } }`
3. **插件注册：** 通过 `VueI18n.createI18n()` 创建 vue-i18n 实例，以 Composable API 模式（`legacy: false`）注册到 Vue 应用
4. **全局可用：** 组件中通过 `__i18n` 访问翻译功能，模板和 script 均使用 `__i18n.t('key')`

---

## 二、I18n 工具方法

### `getI18nMessage` — 获取所有词条

获取当前项目已配置的全部中英对照词条。

**参数：** 无

**返回：** `I18nMessage[]` 数组，每项包含 `key`、`zh-CN`、`en`

---

### `createI18nMessage` — 新增词条

新增一条中英文对照词条。`key` 必须唯一，重复的 `key` 会覆盖。

**参数：**

| 参数            | 类型   | 必填 | 说明                                                                 |
| --------------- | ------ | ---- | -------------------------------------------------------------------- |
| `message.key`   | string | ✅   | 词条标识，推荐点号分层命名，如 `common.confirm`、`user.profile.name` |
| `message.zh-CN` | string | ✅   | 简体中文内容                                                         |
| `message.en`    | string | ✅   | 英文内容                                                             |

**示例：**

```json
{
  "action": "createI18nMessage",
  "parameters": [
    {
      "key": "common.submit",
      "zh-CN": "提交",
      "en": "Submit"
    }
  ]
}
```

**批量创建词条：** 需要多次调用此工具，每次传入一条。

---

### `removeI18nMessage` — 删除词条

通过 `key` 删除指定词条。

**参数：**

- `key: string` — 词条标识

**示例：**

```json
{
  "action": "removeI18nMessage",
  "parameters": ["common.submit"]
}
```

---

## 三、语言设置（locale / fallbackLocale）

国际化有两个全局设置：

| 设置项   | 键名             | 可选值         | 说明                   |
| -------- | ---------------- | -------------- | ---------------------- |
| 默认语言 | `locale`         | `zh-CN` / `en` | 应用启动时使用的语言   |
| 回退语言 | `fallbackLocale` | `zh-CN` / `en` | 找不到翻译时的兜底语言 |

> ⚠️ **暂无对应工具方法。** 当前 `locale` 和 `fallbackLocale` 只能通过设计器 i18n 面板的「设置」按钮手动配置，无对应的 AI 工具调用。如未配置，默认均为 `zh-CN`。

---

## 四、组件中使用国际化

### 4.1 模板中使用（推荐方式）

> VTJ 使用 vue-i18n Composition API 模式（`legacy: false`），模板中必须使用 `__i18n.t()`，不支持 Options API 的 `$t()`。

```html
<template>
  <div>
    <h1>{{ __i18n.t('page.dashboard.title') }}</h1>
    <el-button>{{ __i18n.t('common.submit') }}</el-button>
    <el-table :empty-text="__i18n.t('table.empty')">
      <!-- ... -->
    </el-table>
  </div>
</template>
```

### 4.2 script 中使用

在 `<script setup>` 中通过 `__i18n` 全局变量访问：

```javascript
// 文本翻译
const title = __i18n.t('page.dashboard.title');
const confirmText = __i18n.t('common.confirm');

// 带插值参数
const message = __i18n.t('user.welcome', { name: '张三' });

// 数字格式化
const formatted = __i18n.n(1234567, 'currency');
// 等价于模板中: {{ __i18n.n(1234567, 'currency') }}

// 日期格式化
const dateStr = __i18n.d(new Date(), 'short');
// 等价于模板中: {{ __i18n.d(new Date(), 'short') }}
```

### 4.3 Vue SFC 完整示例

```html
<template>
  <div class="user-profile">
    <h1>{{ __i18n.t('user.profile.title') }}</h1>

    <el-descriptions :column="2" border>
      <el-descriptions-item :label="__i18n.t('user.profile.name')">
        {{ __state.user?.name }}
      </el-descriptions-item>
      <el-descriptions-item :label="__i18n.t('user.profile.email')">
        {{ __state.user?.email }}
      </el-descriptions-item>
      <el-descriptions-item :label="__i18n.t('user.profile.phone')">
        {{ __state.user?.phone }}
      </el-descriptions-item>
    </el-descriptions>

    <div class="footer">
      <el-button @click="__state.onBack">
        {{ __i18n.t('common.back') }}
      </el-button>
      <el-button type="primary" @click="__state.onEdit">
        {{ __i18n.t('user.profile.edit') }}
      </el-button>
    </div>
  </div>
</template>

<script setup>
  import { reactive } from 'vue';

  const __state = reactive({
    user: {
      name: '',
      email: '',
      phone: ''
    },
    onBack() {
      __router.back();
    },
    onEdit() {
      // 编辑逻辑
    }
  });

  onMounted(async () => {
    const res = await __apis['getUserInfo']();
    __state.user = res.data?.data || {};
  });
</script>
```

---

## 五、词条 Key 命名规范

推荐使用点号分层的命名方式，按「模块.页面.元素」的结构组织：

| 规范     | 示例 Key              | 中文值           | 说明                 |
| -------- | --------------------- | ---------------- | -------------------- |
| 通用类   | `common.submit`       | 提交             | 跨页面复用的通用文案 |
|          | `common.cancel`       | 取消             |                      |
|          | `common.confirm`      | 确认             |                      |
|          | `common.delete`       | 删除             |                      |
|          | `common.back`         | 返回             |                      |
| 表格类   | `table.empty`         | 暂无数据         | 表格空状态           |
|          | `table.search`        | 查询             |                      |
|          | `table.reset`         | 重置             |                      |
| 业务页面 | `user.list.title`     | 用户列表         | 特定页面的标题       |
|          | `user.list.create`    | 新增用户         |                      |
|          | `user.profile.name`   | 姓名             | 用户详情字段         |
|          | `order.detail.status` | 状态             | 订单状态标签         |
| 表单校验 | `validation.required` | 此项必填         | 表单校验提示         |
|          | `validation.email`    | 邮箱格式不正确   |                      |
| 操作提示 | `tips.deleteConfirm`  | 确定删除该项？   | 操作前的确认提示     |
|          | `tips.saveSuccess`    | 保存成功         |                      |
|          | `tips.saveFailed`     | 保存失败，请重试 |                      |

---

## 六、典型工作流程

### 6.1 新建项目时的 i18n 配置流程

```
1. 分析页面中的静态文案，整理成词条清单
2. 逐条调用 createI18nMessage 创建词条
3. 在 Vue 模板中使用 __i18n.t('key') 替代硬编码文本
```

### 6.2 新增页面时

```
1. 查看现有词条：getI18nMessage
2. 复用已有词条（如 common.*）
3. 创建新词条：createI18nMessage
4. 在生成的 Vue 代码中使用 __i18n.t('key')
```

---

## 七、带插值参数的翻译

词条中可以包含 `{name}` 占位符，调用时传入参数：

```json
{
  "action": "createI18nMessage",
  "parameters": [
    {
      "key": "user.welcome",
      "zh-CN": "欢迎回来，{name}！",
      "en": "Welcome back, {name}!"
    }
  ]
}
```

模板中使用：

```html
<span>{{ __i18n.t('user.welcome', { name: __state.userName }) }}</span>
```

Script 中使用：

```javascript
const msg = __i18n.t('user.welcome', { name: __state.userName });
```

---

## 八、注意事项

1. **key 必须唯一：** 重复的 key 会导致词条覆盖，创建前可通过 `getI18nMessage` 确认
2. **`zh-CN` 和 `en` 均必须填写：** 每条词条的中英文内容均不可为空
3. **统一使用 `__i18n.t()`：** VTJ 使用 Composition API 模式（`legacy: false`），模板和 script 中均使用 `__i18n.t('key')`，不支持 Options API 的 `$t()` 写法
4. **不需要手动 import：** `__i18n` 是由 `vue-i18n` 的 `useI18n()` 提供的全局变量，框架已自动注入，组件中无需手动导入
5. **设置后需 `refresh` 生效：** 通过工具新增词条后，需要调用 `refresh` 刷新运行时才能看到效果
6. **locale/fallbackLocale 无工具：** 默认语言和回退语言需通过设计器面板手动配置

<!-- ============================================ -->
<!-- 文档: icons.md -->
<!-- ============================================ -->

# @vtj/icons 图标库 — AI 使用指南

> 本文档帮助 AI 理解 `@vtj/icons` 图标库的构成、导入方式和在 Vue 组件中的使用规范。

---

## 一、图标库构成

`@vtj/icons` 包含四类图标来源：

| 来源                                                 | 说明                                   | 组件类型                     |
| ---------------------------------------------------- | -------------------------------------- | ---------------------------- |
| **SVG 组件**（`./components`）                       | 内置 SVG 图标，Vue 组件形式            | Vue 组件，支持 `colors` 属性 |
| **Iconfont 图标**（`./icons`）                       | 自定义 CSS 图标字体，`VtjIconXxx` 命名 | Vue 组件（CSS 类实现）       |
| **Element Plus 图标**（re-export from `@vtj/icons`） | 全量 re-exported Element Plus 图标     | Vue 组件                     |
| **Assets 图标**（`./assets`）                        | SVG 资源文件导入，`IconXxx` 命名       | SVG 字符串/路径资源          |

所有图标均可作为 Vue 组件使用。全局注册后可在模板中直接使用标签名；按需导入时需配合 `XIcon` 组件（来自 `@vtj/ui`）使用。

---

## 二、安装与注册

### 2.1 安装依赖

```json
{
  "dependencies": {
    "@vtj/icons": "workspace:~"
  }
}
```

### 2.2 全局注册（推荐）

```typescript
import { createApp } from 'vue';
import { IconsPlugin } from '@vtj/icons';
import '@vtj/icons/dist/style.css';

const app = createApp(App);
app.use(IconsPlugin); // 注册所有图标为全局组件
```

或使用独立 `install` 方法：

```typescript
import { install } from '@vtj/icons/install';
import '@vtj/icons/dist/style.css';

install(app);
```

### 2.3 按需导入

按需导入时，图标需通过 `XIcon` 组件渲染。`XIcon` 的 `icon` 属性接收图标组件引用，通过 `:icon="IconName"` 传递：

```vue
<template>
  <XIcon :icon="Edit" :size="20" color="#666666" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { Edit } from '@vtj/icons';
</script>
```

---

## 三、SVG 组件图标

### 3.1 图标列表

以下图标位于 `@vtj/icons` 的 SVG 组件目录，直接按名称导入：

| 组件名           | 说明       |
| ---------------- | ---------- |
| `Fullscreen`     | 全屏       |
| `ExitFullscreen` | 退出全屏   |
| `Fixed`          | 固定       |
| `UnFixed`        | 取消固定   |
| `Visible`        | 可见       |
| `Invisible`      | 不可见     |
| `Maximize`       | 最大化     |
| `Minimize`       | 最小化     |
| `Popup`          | 弹出       |
| `LineHeight`     | 行高       |
| `Rows`           | 行布局     |
| `Skin`           | 皮肤       |
| `QrCodeLogin`    | 二维码登录 |
| `RawClose`       | 原始关闭   |
| `RawLock`        | 原始锁定   |
| `RawUnLock`      | 原始解锁   |
| `UserLogin`      | 用户登录   |

### 3.2 用法

SVG 组件支持 `colors` 属性传递颜色数组，按 path 顺序着色。按需导入时需通过 `XIcon` 渲染：

```vue
<template>
  <!-- 按需导入：通过 XIcon 渲染 -->
  <XIcon :icon="Fullscreen" />
  <XIcon :icon="Fullscreen" :size="24" color="#409EFF" />
  <XIcon :icon="UserLogin" :size="30" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { Fullscreen, UserLogin } from '@vtj/icons';
</script>
```

> **全局注册后**也可直接使用标签名：`<Fullscreen />`、`<UserLogin :colors="['#333', '#409EFF']" />`。

---

## 四、Iconfont 图标

### 4.1 命名规则

所有 iconfont 图标以 `VtjIcon` 为前缀，驼峰命名。相互对应 CSS class 以 `vtj-icon-` 为前缀，短横线命名：

| 组件名              | CSS Class              |
| ------------------- | ---------------------- |
| `VtjIconSetting`    | `vtj-icon-setting`     |
| `VtjIconArrowRight` | `vtj-icon-arrow-right` |
| `VtjIconAi`         | `vtj-icon-ai`          |
| `VtjIconUser`       | `vtj-icon-user`        |
| `VtjIconSave`       | `vtj-icon-save`        |

### 4.2 完整图标列表

#### 通用操作类

| 组件名             | 说明     |
| ------------------ | -------- |
| `VtjIconAdd`       | 添加     |
| `VtjIconRemove`    | 移除     |
| `VtjIconDelete`    | 删除     |
| `VtjIconEdit`      | 编辑     |
| `VtjIconCopy`      | 复制     |
| `VtjIconSave`      | 保存     |
| `VtjIconSearch`    | 搜索     |
| `VtjIconRefresh`   | 刷新     |
| `VtjIconClose`     | 关闭     |
| `VtjIconCheck`     | 勾选     |
| `VtjIconHelp`      | 帮助     |
| `VtjIconInfo`      | 信息     |
| `VtjIconMore`      | 更多     |
| `VtjIconShare`     | 分享     |
| `VtjIconLock`      | 锁定     |
| `VtjIconUnlock`    | 解锁     |
| `VtjIconExport`    | 导出     |
| `VtjIconImport`    | 导入     |
| `VtjIconUpload`    | 上传     |
| `VtjIconDownload`  | 下载     |
| `VtjIconPreview`   | 预览     |
| `VtjIconPublish`   | 发布     |
| `VtjIconSwitch`    | 切换     |
| `VtjIconFixed`     | 固定     |
| `VtjIconUnfixed`   | 取消固定 |
| `VtjIconVisible`   | 可见     |
| `VtjIconInvisible` | 不可见   |

#### 方向箭头类

| 组件名              | 说明   |
| ------------------- | ------ |
| `VtjIconArrowRight` | 箭头右 |
| `VtjIconArrowLeft`  | 箭头左 |
| `VtjIconArrowDown`  | 箭头下 |
| `VtjIconArrowUp`    | 箭头上 |
| `VtjIconGreater`    | 大于号 |
| `VtjIconSmaller`    | 小于号 |
| `VtjIconBack`       | 返回   |
| `VtjIconExpand`     | 展开   |
| `VtjIconCollapsed`  | 收起   |
| `VtjIconRedo`       | 重做   |
| `VtjIconUndo`       | 撤销   |

#### 导航与布局类

| 组件名               | 说明     |
| -------------------- | -------- |
| `VtjIconHome`        | 首页     |
| `VtjIconMenu`        | 菜单     |
| `VtjIconCategory`    | 分类     |
| `VtjIconProject`     | 项目     |
| `VtjIconTemplate`    | 模板     |
| `VtjIconComponents`  | 组件     |
| `VtjIconBlock`       | 区块     |
| `VtjIconPageSetting` | 页面设置 |
| `VtjIconLayers`      | 图层     |
| `VtjIconOutline`     | 大纲     |
| `VtjIconDeps`        | 依赖     |
| `VtjIconData`        | 数据     |
| `VtjIconFile`        | 文件     |
| `VtjIconFolder`      | 文件夹   |
| `VtjIconDocument`    | 文档     |
| `VtjIconHistory`     | 历史     |
| `VtjIconNotice`      | 通知     |
| `VtjIconFav`         | 收藏     |

#### 设备与窗口类

| 组件名                  | 说明       |
| ----------------------- | ---------- |
| `VtjIconPc`             | PC 设备    |
| `VtjIconPhone`          | 手机设备   |
| `VtjIconPad`            | 平板设备   |
| `VtjIconUniapp`         | UniApp     |
| `VtjIconFullscreen`     | 全屏       |
| `VtjIconExitFullscreen` | 退出全屏   |
| `VtjIconWindowMax`      | 窗口最大化 |
| `VtjIconWindowMin`      | 窗口最小化 |
| `VtjIconWindowClose`    | 窗口关闭   |
| `VtjIconWindowNormal`   | 窗口还原   |
| `VtjIconWindowDown`     | 窗口下移   |
| `VtjIconWindowUp`       | 窗口上移   |

#### AI 与工具类

| 组件名              | 说明       |
| ------------------- | ---------- |
| `VtjIconAi`         | AI         |
| `VtjIconNewChat`    | 新建对话   |
| `VtjIconChatRecord` | 对话记录   |
| `VtjIconEnv`        | 环境变量   |
| `VtjIconConfig`     | 配置       |
| `VtjIconLang`       | 语言       |
| `VtjIconGlobal`     | 全局       |
| `VtjIconApi`        | API        |
| `VtjIconOpenapi`    | OpenAPI    |
| `VtjIconSwagger`    | Swagger    |
| `VtjIconDevTools`   | 开发者工具 |
| `VtjIconDiff`       | 差异对比   |
| `VtjIconMaster`     | 主控       |
| `VtjIconConsole`    | 控制台     |
| `VtjIconTeam`       | 团队       |
| `VtjIconBug`        | Bug        |
| `VtjIconVars`       | 变量       |
| `VtjIconJs`         | JS         |
| `VtjIconDatabase`   | 数据库     |

#### 面板定位类

| 组件名               | 说明     |
| -------------------- | -------- |
| `VtjIconTopPanel`    | 顶部面板 |
| `VtjIconBottomPanel` | 底部面板 |
| `VtjIconLeftPanel`   | 左侧面板 |
| `VtjIconRightPanel`  | 右侧面板 |

#### 业务操作类（Np 前缀）

| 组件名               | 说明     |
| -------------------- | -------- |
| `VtjIconNpAdd`       | 新增     |
| `VtjIconNpAddRow`    | 新增行   |
| `VtjIconNpRemove`    | 移除     |
| `VtjIconNpRemoveRow` | 移除行   |
| `VtjIconNpDelete`    | 删除     |
| `VtjIconNpEdit`      | 编辑     |
| `VtjIconNpSave`      | 保存     |
| `VtjIconNpCancel`    | 取消     |
| `VtjIconNpConfirm`   | 确认     |
| `VtjIconNpSubmit`    | 提交     |
| `VtjIconNpReset`     | 重置     |
| `VtjIconNpSearch`    | 搜索     |
| `VtjIconNpSelect`    | 选择     |
| `VtjIconNpExport`    | 导出     |
| `VtjIconNpImport`    | 导入     |
| `VtjIconNpList`      | 列表     |
| `VtjIconNpPrint`     | 打印     |
| `VtjIconNpShare`     | 分享     |
| `VtjIconNpFile`      | 文件     |
| `VtjIconNpRefresh`   | 刷新     |
| `VtjIconNpReturn`    | 返回     |
| `VtjIconNpReturnAll` | 全部返回 |
| `VtjIconNpExit`      | 退出     |
| `VtjIconNpClose`     | 关闭     |
| `VtjIconNpExtend`    | 扩展     |

### 4.3 用法

按需导入时通过 `XIcon` 渲染 Iconfont 图标：

```vue
<template>
  <XIcon :icon="VtjIconSetting" :size="20" />
  <XIcon :icon="VtjIconArrowRight" :size="18" color="#409EFF" />
  <XIcon :icon="VtjIconUser" :size="24" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { VtjIconSetting, VtjIconArrowRight, VtjIconUser } from '@vtj/icons';
</script>
```

> **全局注册后**也可直接使用标签名：`<VtjIconSetting />`。

Iconfont 图标本质是 `<i>` 元素，可通过 CSS 控制大小和颜色：

```css
.vtj-icon-setting {
  font-size: 18px;
  color: var(--el-text-color-primary);
}
```

```css
.vtj-icon-setting {
  font-size: 18px;
  color: var(--el-text-color-primary);
}
```

---

## 五、Element Plus 图标

`@vtj/icons` 全量 re-export Element Plus 的所有图标，直接按 Element Plus 官方名称导入使用。

按需导入时通过 `XIcon` 渲染：

```vue
<template>
  <XIcon :icon="Edit" :size="18" />
  <XIcon :icon="Delete" :size="18" color="#F56C6C" />
  <XIcon :icon="Plus" :size="18" />
  <XIcon :icon="Search" :size="18" />
  <XIcon :icon="ArrowDown" :size="14" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { Edit, Delete, Plus, Search, ArrowDown } from '@vtj/icons';
</script>
```

全量图标列表参见 [Element Plus Icons 文档](https://element-plus.org/zh-CN/component/icon.html)。

---

## 六、Assets 图标

Assets 图标是 SVG 资源导入，以 `Icon` 为前缀命名。它们本质是 SVG 文件路径，适用于非组件场景（如设计器面板预览图、占位图等）。

```typescript
import {
  IconButton,
  IconTable,
  IconForm,
  IconInput,
  IconImage,
  IconMenu,
  IconPage,
  IconSetting
} from '@vtj/icons';
```

Assets 图标数量约 300+，涵盖以下类别：

- **组件类：** `IconButton`、`IconInput`、`IconTable`、`IconForm`、`IconSelect`、`IconRadio`、`IconCheckbox`、`IconDatepick`、`IconTabs`、`IconTag`、`IconCarousel`、`IconSwiper` 等
- **布局类：** `IconRow`、`IconCol`、`IconGrid`、`IconDisplayFlex`、`IconDisplayBlock`、`IconHr`、`IconDivider`（对应 `IconLine`）等
- **样式类：** `IconBorderAll`、`IconMargin`、`IconPadding`、`IconBackgroundColor`、`IconBackgroundImage`、`IconTextAlignCenter`、`IconFontStyleItalic` 等
- **光标类：** `IconCursorPointer`、`IconCursorMove`、`IconCursorGrab`、`IconCursorText`、`IconCursorZoomIn` 等
- **状态类：** `IconEmpty`、`IconEmptyData`、`IconEmptyAction`、`IconLoading`、`IconSuccess`（对应 `IconFlowSuccess`）、`IconError`、`IconWarning`、`IconAlert` 等
- **操作类：** `IconAdd`、`IconDelete`、`IconEdit`、`IconCopy`、`IconSave`、`IconSearch`、`IconRefresh`、`IconPreview`、`IconReturn`、`IconClose`、`IconClear` 等
- **Flow 类：** `IconFlowAdd`、`IconFlowDelete`、`IconFlowEdit`、`IconFlowCheckmark`、`IconFlowCross`、`IconFlowSuccess`、`IconFlowFailure` 等
- **插件类：** `IconPluginIconData`、`IconPluginIconJs`、`IconPluginIconPage`、`IconPluginIconTree`、`IconPluginIconRobot` 等
- **其他：** `IconPage`、`IconHome`、`IconUserLocked`、`IconLocation`、`IconLanguage`、`IconTinyLogo` 等

---

## 七、模板中使用规范

### 7.1 全局注册后使用（推荐）

全局注册后，所有图标均为全局组件，模板中直接使用标签名：

```html
<template>
  <div class="page">
    <!-- SVG 组件图标 -->
    <Fullscreen @click="onFullscreen" />

    <!-- Iconfont 图标 -->
    <VtjIconSetting class="icon" />
    <VtjIconArrowRight style="font-size: 14px;" />

    <!-- Element Plus 图标 -->
    <el-icon :size="18">
      <Edit />
    </el-icon>
  </div>
</template>
```

### 7.2 按需导入使用（推荐）

按需导入时，通过 `XIcon` 组件渲染图标，并可自由控制大小和颜色：

```vue
<template>
  <XIcon :icon="Edit" :size="18" />
  <XIcon :icon="Delete" :size="20" color="#F56C6C" />
  <XIcon
    :icon="VtjIconSetting"
    :size="22"
    color="var(--el-text-color-primary)" />

  <!-- 带点击事件 -->
  <XIcon :icon="Refresh" :size="18" class="action-icon" @click="onRefresh" />

  <!-- SVG 组件传递 colors 属性需先处理 -->
  <XIcon :icon="UserLogin" :size="24" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { Edit, Delete, VtjIconSetting, Refresh } from '@vtj/icons';

  const onRefresh = () => {};
</script>
```

### 7.3 动态图标

```vue
<template>
  <XIcon :icon="currentIcon" :size="24" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { VtjIconSetting, VtjIconUser } from '@vtj/icons';

  let currentIcon = VtjIconSetting;
  const toggleIcon = () => {
    currentIcon = currentIcon === VtjIconSetting ? VtjIconUser : VtjIconSetting;
  };
</script>
```

> 注意：动态切换时，`XIcon` 的 `:icon="variable"` 中 `variable` 应始终持有图标的 Vue 组件引用（即 `import` 后的变量名），而不是字符串。

### 7.4 Assets 图标通过 `src` 属性使用

Assets 图标本质是 SVG 文件路径，通过 `XIcon` 的 `src` 属性渲染：

```vue
<template>
  <XIcon :src="IconButton" :size="40" />
  <XIcon :src="IconTable" :size="40" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { IconButton, IconTable } from '@vtj/icons';
</script>
```

---

## 八、VTJ Composition API 完整示例

以下示例展示按需导入模式，使用 `XIcon` 渲染各类图标：

```vue
<template>
  <div class="page">
    <div class="toolbar">
      <!-- Element Plus 图标 -->
      <XIcon :icon="Plus" :size="18" />
      <XIcon :icon="Edit" :size="18" />
      <XIcon :icon="Delete" :size="18" color="#F56C6C" />

      <!-- VTJ Iconfont 图标 -->
      <XIcon :icon="VtjIconSetting" :size="18" @click="onSetting" />
      <XIcon :icon="VtjIconRefresh" :size="18" @click="onRefresh" />
      <XIcon :icon="VtjIconSearch" :size="18" />

      <!-- SVG 内置图标 -->
      <XIcon :icon="Fullscreen" :size="20" />
      <XIcon :icon="Popup" :size="20" />

      <!-- Assets 图标（通过 src 属性） -->
      <XIcon :src="IconPage" :size="40" />
      <XIcon :src="IconTable" :size="40" />
    </div>

    <el-button type="primary" @click="onSubmit">
      <template #icon>
        <XIcon :icon="Plus" :size="14" />
      </template>
      {{ __i18n.t('common.submit') }}
    </el-button>

    <el-input v-model="keyword" :placeholder="__i18n.t('common.search')">
      <template #prefix>
        <XIcon :icon="Search" :size="14" />
      </template>
    </el-input>
  </div>
</template>

<script setup>
  import { ref } from 'vue';
  import { XIcon } from '@vtj/ui';
  import {
    Plus,
    Edit,
    Delete,
    Search,
    VtjIconSetting,
    VtjIconRefresh,
    VtjIconSearch,
    Fullscreen,
    Popup
  } from '@vtj/icons';
  import { IconPage, IconTable } from '@vtj/icons';

  const keyword = ref('');
  const onSetting = () => {};
  const onRefresh = () => {};
  const onSubmit = () => {};
</script>
```

> 全局注册后则无需 `import { XIcon }` 和图标组件，直接在模板中使用标签名即可。

---

## 九、注意事项

1. **全局注册后可直接使用：** 使用 `IconsPlugin` 或 `install` 全局注册后，所有图标在模板中直接使用标签名，无需手动 import
2. **按需导入需配合 XIcon：** 未全局注册时，需从 `@vtj/ui` 导入 `XIcon`，通过 `:icon="IconName"` 传递图标组件引用
3. **`icon` 属性传递组件引用：** `XIcon` 的 `:icon` 接收 Vue 组件对象（即 `import` 后的变量名），并非字符串路径
4. **Assets 图标用 `src` 属性：** Assets 图标（`IconXxx`）通过 `XIcon` 的 `:src="IconName"` 属性渲染，不是 `:icon`
5. **XIcon 支持 `size` 和 `color`：** `:size` 可传数字或 `'small'`/`'default'`/`'large'`/`'inherit'`，`color` 传 CSS 色值字符串
6. **Iconfont 图标默认颜色：** `VtjIconXxx` 通过 `XIcon` 渲染时，`color` 属性控制颜色；全局注册直接使用时通过 CSS `color` 控制
7. **SVG 组件 `colors` 属性：** SVG 组件支持 `colors` 数组按 path 顺序着色，但需通过 `<component :is="IconName">` 或全局注册后直接标签使用
8. **样式文件：** 需要导入 `@vtj/icons/dist/style.css` 以确保 iconfont 字体和 SVG 组件样式生效
9. **无需耗时搜索图标名称：** 上述列表已覆盖所有图标，直接从列表中选取即可

<!-- ============================================ -->
<!-- 文档: settings.md -->
<!-- ============================================ -->

# VTJ 应用设置 — AI 配置指南

> 本文档帮助 AI 理解如何通过工具方法配置应用级别的全局设置。
> 设计器的"应用设置"面板提供 9 个配置项，覆盖 3 大类：全局配置、请求配置、路由守卫。
> 这些配置最终存储在 `project.globals` 中，由渲染引擎在初始化时按顺序执行。

---

## 一、数据流全貌

```
设计器面板                          项目数据                         渲染引擎
globals/index.vue  ──setGloblas()──▶  project.globals  ──initRuntimeGlobals()──▶  运行时生效
    (UI)                              (GlobalConfig)                              (globals.ts)
```

**关键流程：**

1. 通过工具方法设置 → 存入 `project.globals`（`GlobalConfig` 类型）
2. 应用启动时 `Provider.install()` → 调用 `initGlobals()` → 调用 `initRuntimeGlobals()`
3. 渲染引擎按**固定顺序**初始化各项配置

> **注意：** 设计模式（Design Mode）下 `initGlobals` 不会被调用，仅在预览/运行时生效。

---

## 二、配置项与工具方法对照表

| 设计器面板项                | 配置键名     | 设置工具                       | 查询工具                       | 值类型     |
| --------------------------- | ------------ | ------------------------------ | ------------------------------ | ---------- |
| 应用全局「CSS」             | `css`        | `setGlobalCss`                 | `getGlobalCss`                 | CSS 字符串 |
| 应用状态「Pinia」           | `store`      | `setGlobalStore`               | `getGlobalStore`               | JS 函数    |
| 权限控制「Access」          | `access`     | `setGlobalAccess`              | `getGlobalAccess`              | JS 函数    |
| 应用增强「JS」              | `enhance`    | ⚠️ 暂无工具                    | ⚠️ 暂无工具                    | JS 函数    |
| 请求配置「IRequestOptions」 | `axios`      | `setGlobalAxios`               | `getGlobalAxios`               | JS 函数    |
| 请求拦截器                  | `request`    | `setGlobalRequestInterceptor`  | `getGlobalRequestInterceptor`  | JS 函数    |
| 响应拦截器                  | `response`   | `setGlobalResponseInterceptor` | `getGlobalResponseInterceptor` | JS 函数    |
| 前置守卫「beforeEach」      | `beforeEach` | `setGlobalBeforeEach`          | `getGlobalBeforeEach`          | JS 函数    |
| 后置守卫「afterEach」       | `afterEach`  | `setGlobalAfterEach`           | `getGlobalAfterEach`           | JS 函数    |

> ⚠️ **`enhance`（应用增强 JS）** 目前没有对应的工具方法，暂无法通过 AI 工具配置。

---

## 三、渲染引擎初始化顺序

`initRuntimeGlobals()` 严格按以下顺序执行，**顺序不可更改**，配置时需注意依赖关系：

```
1. CSS（全局样式）         ── adoptedStyleSheets 注入
    │
2. Store（Pinia 状态）     ── 创建 Pinia 实例，注册 $store
    │
3. Axios + 拦截器          ── 先设基础配置，再设请求拦截器，最后设响应拦截器
    │
4. Access（权限控制）      ── 创建 Access 实例，连接 router/request
    │
5. 路由守卫                ── 先 beforeEach，后 afterEach
    │
6. Enhance（应用增强）     ── 最后执行，可访问全部已初始化的 libs 和 app
```

**含义：**

- `store` 中的状态可在 `access`、路由守卫、拦截器中通过 `app.config.globalProperties.$store` 访问
- `access` 实例挂载后，路由守卫可通过 `app.config.globalProperties.$access` 访问
- `enhance` 最后执行，可访问所有已初始化的资源（store、access、router 等）

---

## 四、各配置项详细说明

### 4.1 应用全局 CSS — `css`

**对应工具：** `setGlobalCss` / `getGlobalCss`

直接传入 CSS 代码字符串，会在应用启动时注入为全局样式表。

**示例：**

```json
{
  "action": "setGlobalCss",
  "parameters": [
    "body { margin: 0; padding: 0; font-family: 'Helvetica Neue', sans-serif; }\n.page-container { min-height: 100vh; }"
  ]
}
```

**设计器默认值：** 空字符串（不注入额外 CSS）

---

### 4.2 应用状态 Pinia — `store`

**对应工具：** `setGlobalStore` / `getGlobalStore`

定义应用的全局 Pinia Store，函数必须返回一个标准 Pinia Store 配置对象。

**函数签名：** `(app) => ({ state, getters, actions })`

| 参数  | 说明                                                             |
| ----- | ---------------------------------------------------------------- |
| `app` | Vue 应用实例，可用于获取全局属性如 `app.config.globalProperties` |

**设计器默认模板：**

```javascript
(app) => {
  return {
    state: () => {
      return {
        // 定义状态
      };
    },
    getters: {
      // 计算属性
    },
    actions: {
      // 方法
    }
  };
};
```

**完整示例：**

```json
{
  "action": "setGlobalStore",
  "parameters": [
    "(app) => {\n  return {\n    state: () => ({\n      user: null,\n      token: '',\n      permissions: []\n    }),\n    getters: {\n      isLogined: (state) => !!state.token,\n      userName: (state) => state.user?.name || '游客'\n    },\n    actions: {\n      login(data) {\n        this.token = data.token\n        this.user = data.user\n        this.permissions = data.permissions || []\n      },\n      logout() {\n        this.token = ''\n        this.user = null\n        this.permissions = []\n      },\n      hasPermission(code) {\n        return this.permissions.includes(code)\n      }\n    }\n  }\n}"
  ]
}
```

**运行时访问：** 组件中通过 `__store.xxx` 访问：

```javascript
const user = __store.user;
__store.login({ token: 'xxx', user: { name: '张三' } });
```

---

### 4.3 权限控制 Access — `access`

**对应工具：** `setGlobalAccess` / `getGlobalAccess`

配置应用的权限控制插件，函数返回 `AccessOptions` 配置对象。

**函数签名：** `(app) => AccessOptions`

**设计器默认模板：**

```javascript
(app) => {
  return {
    // session: false,
    // authKey: 'Authorization',
    // storageKey: 'ACCESS_STORAGE',
    // auth: '/#/login',
    // whiteList: (to) => true,
    // redirectParam: 'r',
    // unauthorizedCode: 401,
    // unauthorizedMessage: '登录已经失效，请重新登录！',
    // noPermissionMessage: '无权限访问该页面',
    // statusKey: 'code'
  };
};
```

**`AccessOptions` 配置项说明：**

| 配置项                | 类型     | 默认值         | 说明                                                               |
| --------------------- | -------- | -------------- | ------------------------------------------------------------------ |
| `session`             | boolean  | false          | 是否开启 session 存储（关闭浏览器即失效），false 使用 localStorage |
| `authKey`             | string   | Authorization  | 请求头和 cookie 中 token 的键名                                    |
| `storageKey`          | string   | ACCESS_STORAGE | 本地缓存的键名                                                     |
| `auth`                | string   | /#/login       | 登录页面的 hash 路径                                               |
| `whiteList`           | function | (to) => false  | 路由白名单，返回 true 跳过权限校验，接收 to 路由对象               |
| `redirectParam`       | string   | r              | 登录后重定向的参数名                                               |
| `unauthorizedCode`    | number   | 401            | 未登录时后端返回的状态码                                           |
| `unauthorizedMessage` | string   | 登录已失效...  | 未登录时的提示消息                                                 |
| `noPermissionMessage` | string   | 无权限...      | 无权限时的提示消息                                                 |
| `statusKey`           | string   | code           | 响应数据中状态码的字段名                                           |

**运行时访问：** 组件中通过 `__access` 访问：

```javascript
// 检查是否已登录
__access.isLogined();

// 检查是否拥有指定权限
__access.can('user:edit');

// 检查是否至少有一个权限
__access.some(['user:edit', 'user:view']);

// 获取当前 token
__access.getToken();

// 获取登录用户数据
__access.getData();

// 登录
__access.login({
  token: 'xxx',
  user: { name: '张三' },
  permissions: ['user:view']
});

// 登出
__access.logout();
```

---

### 4.4 应用增强 JS — `enhance`

⚠️ **尚无对应工具方法。**

该配置允许在应用初始化的最后阶段注入自定义增强逻辑，函数接收 `app`（Vue 应用实例）和 `libs`（所有注册的第三方库集合）。

**设计器默认模板：**

```javascript
(app) => {
  // 在此添加增强代码
};
```

**执行时机：** 在所有其他全局配置（CSS、Store、Axios、Access、路由守卫）初始化完成后执行。

---

### 4.5 请求配置 IRequestOptions — `axios`

**对应工具：** `setGlobalAxios` / `getGlobalAxios`  
UI 标签中也称 `getGlobalAxios`，设置时使用 `setGlobalAxios`。

配置 Axios 请求工具的基础选项（baseURL、超时、响应校验等）。

**函数签名：** `(app) => IRequestOptions`

**设计器默认模板：**

```javascript
(app) => {
  return {
    // baseURL: '/',
    // timeout: 60000,
    // settings: {
    //   type: 'form',
    //   validSuccess: false,
    //   originResponse: true,
    //   loading: true,
    //   failMessage: true,
    //   validate: (res) => {
    //     return res.data?.code === 0 || !!res.data?.success;
    //   }
    // }
  };
};
```

**`IRequestOptions` 常见配置项：**

| 配置项                    | 类型     | 说明                                                             |
| ------------------------- | -------- | ---------------------------------------------------------------- |
| `baseURL`                 | string   | 请求的基础 URL 前缀                                              |
| `timeout`                 | number   | 请求超时时间（毫秒）                                             |
| `settings.type`           | string   | 请求数据类型：`form`（表单）、`json`（JSON）、`data`（FormData） |
| `settings.validSuccess`   | boolean  | 是否校验响应是否成功                                             |
| `settings.originResponse` | boolean  | 是否返回原始 Axios 响应对象                                      |
| `settings.loading`        | boolean  | 请求时是否显示全局 loading                                       |
| `settings.failMessage`    | boolean  | 请求失败时是否显示错误提示                                       |
| `settings.validate`       | function | 自定义响应成功校验函数，接收 `res` 返回 boolean                  |

**完整示例：**

```json
{
  "action": "setGlobalAxios",
  "parameters": [
    "(app) => {\n  return {\n    baseURL: 'https://api.example.com',\n    timeout: 30000,\n    settings: {\n      type: 'json',\n      validSuccess: true,\n      originResponse: false,\n      loading: true,\n      failMessage: true,\n      validate: (res) => {\n        return res.data?.code === 0\n      }\n    }\n  }\n}"
  ]
}
```

---

### 4.6 请求拦截器 — `request`

**对应工具：** `setGlobalRequestInterceptor` / `getGlobalRequestInterceptor`

在每次请求发送前执行，常用于注入 token、设置请求头等。

**函数签名：** `(config, app) => config`

| 参数     | 说明               |
| -------- | ------------------ |
| `config` | Axios 请求配置对象 |
| `app`    | Vue 应用实例       |

**必须返回**请求配置对象 `config`。

**设计器默认模板：**

```javascript
(config, app) => {
  return config;
};
```

**典型示例（自动注入 token）：**

```javascript
(config, app) => {
  const access = app.config.globalProperties.$access;
  if (access?.getToken()) {
    config.headers['Authorization'] = `Bearer ${access.getToken()}`;
  }
  return config;
};
```

---

### 4.7 响应拦截器 — `response`

**对应工具：** `setGlobalResponseInterceptor` / `getGlobalResponseInterceptor`

在每次请求收到响应后执行，常用于统一处理响应数据、错误处理等。

**函数签名：** `(res, app) => res`

| 参数  | 说明               |
| ----- | ------------------ |
| `res` | Axios 原始响应对象 |
| `app` | Vue 应用实例       |

**必须返回**响应对象 `res`。

**设计器默认模板：**

```javascript
(res, app) => {
  return res;
};
```

**典型示例（统一错误处理）：**

```javascript
(res, app) => {
  if (res.data?.code === 401) {
    const access = app.config.globalProperties.$access;
    if (access) {
      access.logout();
    }
  }
  return res;
};
```

---

### 4.8 前置路由守卫 beforeEach — `beforeEach`

**对应工具：** `setGlobalBeforeEach` / `getGlobalBeforeEach`

在路由跳转前执行，常用于权限校验、登录拦截等。

**函数签名：** `(to, from, next, app) => void`

| 参数   | 说明                                            |
| ------ | ----------------------------------------------- |
| `to`   | 即将进入的目标路由对象                          |
| `from` | 当前正要离开的路由对象                          |
| `next` | 执行跳转的函数（与 vue-router beforeEach 一致） |
| `app`  | Vue 应用实例                                    |

**设计器默认模板：**

```javascript
(to, from, next, app) => {
  next();
};
```

**典型示例（登录校验）：**

```javascript
(to, from, next, app) => {
  const access = app.config.globalProperties.$access;
  const isPublic = access?.options?.whiteList?.(to);
  if (!isPublic && !access?.isLogined()) {
    next('/login');
  } else {
    next();
  }
};
```

---

### 4.9 后置路由守卫 afterEach — `afterEach`

**对应工具：** `setGlobalAfterEach` / `getGlobalAfterEach`

在路由跳转完成后执行，常用于页面标题更新、埋点上报等。

**函数签名：** `(to, from, failure, app) => void`

| 参数      | 说明                               |
| --------- | ---------------------------------- |
| `to`      | 进入的目标路由对象                 |
| `from`    | 离开的路由对象                     |
| `failure` | 导航失败信息，成功时为 `undefined` |
| `app`     | Vue 应用实例                       |

**设计器默认模板：**

```javascript
(to, from, failure, app) => {};
```

**典型示例（埋点上报）：**

```javascript
(to, from, failure, app) => {
  if (!failure) {
    // 页面浏览埋点
    console.log('Page view:', to.path);
  }
};
```

---

## 五、配置建议与注意事项

### 5.1 典型配置流程

当 AI 需要配置应用级别功能时，建议按以下顺序调用工具：

```
1. setGlobalCss        → 设置全局样式
2. setGlobalStore      → 定义全局 Pinia 状态
3. setGlobalAxios      → 配置请求基础选项
4. setGlobalRequestInterceptor  → 设置请求拦截（如注入 token）
5. setGlobalResponseInterceptor → 设置响应拦截（如统一错误处理）
6. setGlobalAccess     → 配置权限控制插件
7. setGlobalBeforeEach → 设置前置路由守卫（登录校验等）
8. setGlobalAfterEach  → 设置后置路由守卫（埋点等）
```

这个顺序与渲染引擎内部初始化顺序一致，可以保证依赖关系正确。

### 5.2 重要约定

1. **值类型：** `css` 为普通字符串，其余 7 项（store、access、enhance、axios、request、response、beforeEach、afterEach）均为 **JS 函数代码字符串**，不是 JSON 对象

2. **工具参数格式：** 所有 set 类工具的配置内容均作为**单个字符串参数**传入（即 `parameters` 数组的第一个元素），不要在字符串外额外包裹对象

3. **`enhance` 暂无工具：** 如需初始化后执行自定义逻辑，目前只能通过设计器面板手动编辑，无对应 AI 工具方法

4. **运行时不直接调用：** 工具方法只负责**存储配置**到项目中，配置的实际生效发生在应用启动时，不影响当前打开的预览窗口（可调用 `refresh` 刷新查看效果）

5. **设计模式不生效：** 设计器内编辑页面时，全局配置不会立即在页面渲染中生效（`initGlobals` 仅在非设计模式下执行）

6. **函数内可访问全局属性：**
   - `app.config.globalProperties.$store` — Pinia store 实例
   - `app.config.globalProperties.$access` — 权限控制实例
   - `app.config.globalProperties.$router` — 路由实例
   - `app.config.globalProperties.$provider` — Provider 实例

### 5.3 查询已有配置

在修改配置前，建议先调用对应的 `get` 工具查看已有内容，避免覆盖：

```
getGlobalCss      → 查看当前全局 CSS
getGlobalStore    → 查看当前 Pinia store 代码
getGlobalAccess   → 查看当前权限配置
getGlobalAxios    → 查看当前请求基础配置
getGlobalRequestInterceptor  → 查看当前请求拦截器
getGlobalResponseInterceptor → 查看当前响应拦截器
getGlobalBeforeEach → 查看当前前置路由守卫
getGlobalAfterEach  → 查看当前后置路由守卫
```

<!-- ============================================ -->
<!-- 文档: tools.md -->
<!-- ============================================ -->

# VTJ 设计器工具方法说明

> 本文档描述 AI 可调用的所有工具方法（Tools），每个工具均通过 `action` + `parameters` 的 JSON 格式调用。

---

## 调用格式

```json
{
  "action": "工具名称",
  "parameters": [参数1, 参数2, ...]
}
```

---

## 一、技能与辅助

### `getSkills` — 获取技能文档

获取指定 ID 的技能文档内容，用于查阅特定功能的详细用法说明。

**参数：**

- `...ids: string[]` — 一个或多个技能 ID

**示例：**

```json
{
  "action": "getSkills",
  "parameters": ["pinia", "axios"]
}
```

---

## 二、页面管理

### `getMenus` — 获取页面菜单树

获取当前项目的页面菜单树结构（含层级关系）。

**参数：** 无

**返回：** 树形结构数组，每项包含 `id`、`name`、`title`、`layout`、`dir`、`icon`、`children`

**示例：**

```json
{
  "action": "getMenus",
  "parameters": []
}
```

---

### `getPages` — 获取所有页面

获取当前项目的全部页面列表（扁平结构）。

**参数：** 无

**返回：** 数组，每项包含 `id`、`name`、`title`、`layout`、`dir`、`icon`

**示例：**

```json
{
  "action": "getPages",
  "parameters": []
}
```

---

### `createPage` — 创建页面

在当前项目中新建页面。有层级关系时，需先创建父级（目录或布局页面），再创建子页面。

**参数：**

| 参数          | 类型    | 必填 | 说明                                                                          |
| ------------- | ------- | ---- | ----------------------------------------------------------------------------- |
| `page`        | object  | ✅   | 页面对象                                                                      |
| `page.name`   | string  | ✅   | 页面名称，PascalCase，如 `UserList`                                           |
| `page.title`  | string  | ✅   | 页面标题，如 `用户列表`                                                       |
| `page.icon`   | string  | ❌   | 图标名称，可选 ElementPlus 图标或 `@vtj/icons` 图标                           |
| `page.dir`    | boolean | ❌   | 是否目录，目录可包含子目录或子页面                                            |
| `page.layout` | boolean | ❌   | 是否布局页面，布局页面的子级为子页面（需配合 RouterView 使用），UniApp 不支持 |
| `parentId`    | string  | ❌   | 父页面 ID，不传则创建在根目录                                                 |

**注意：**

- UniApp 平台不支持 `dir` 和 `layout` 类型，会自动忽略
- 创建成功后会自动打开（激活）该页面（目录类型除外）

---

### `updatePage` — 更新页面信息

修改已有页面的元信息（名称、标题、图标等）。

**参数：**

| 参数         | 类型   | 必填 | 说明     |
| ------------ | ------ | ---- | -------- |
| `page.id`    | string | ✅   | 页面 ID  |
| `page.name`  | string | ✅   | 页面名称 |
| `page.title` | string | ✅   | 页面标题 |
| `page.icon`  | string | ❌   | 图标名称 |

**示例：**

```json
{
  "action": "updatePage",
  "parameters": [
    {
      "id": "abc123",
      "name": "Dashboard",
      "title": "仪表盘",
      "icon": "DataAnalysis"
    }
  ]
}
```

---

### `movePage` — 移动页面层级

将页面移动到指定父级下（改变层级关系）。

**参数：**

| 参数       | 类型   | 必填 | 说明                                  |
| ---------- | ------ | ---- | ------------------------------------- |
| `id`       | string | ✅   | 要移动的页面 ID                       |
| `parentId` | string | ❌   | 目标父页面 ID，传 `null` 则移到根目录 |

**示例：**

```json
{
  "action": "movePage",
  "parameters": ["abc123", "xyz789"]
}
```

---

### `removePage` — 删除页面

删除指定页面或目录。

**注意：** 删除目录或布局类型页面前，需先删除其所有子页面。

**参数：**

- `id: string` — 页面文件 ID

**示例：**

```json
{
  "action": "removePage",
  "parameters": ["abc123"]
}
```

---

### `setHomepage` — 设置应用主页

将指定页面设为应用首页。

**参数：**

- `id: string` — 页面 ID

**示例：**

```json
{
  "action": "setHomepage",
  "parameters": ["abc123"]
}
```

---

## 三、区块组件管理

### `getBlocks` — 获取所有区块

获取当前项目的全部区块组件列表。

**参数：** 无

**返回：** 数组，每项包含 `id`、`name`、`title`、`category`

**示例：**

```json
{
  "action": "getBlocks",
  "parameters": []
}
```

---

### `createBlock` — 创建区块

在当前项目中新建区块组件，创建后自动打开（激活）。

**参数：**

| 参数             | 类型   | 必填 | 说明                                |
| ---------------- | ------ | ---- | ----------------------------------- |
| `block.name`     | string | ✅   | 区块名称，PascalCase，如 `UserCard` |
| `block.title`    | string | ✅   | 区块标题                            |
| `block.category` | string | ❌   | 区块分类/分组名称                   |

**示例：**

```json
{
  "action": "createBlock",
  "parameters": [
    {
      "name": "UserCard",
      "title": "用户卡片",
      "category": "通用"
    }
  ]
}
```

---

### `updateBlock` — 更新区块信息

修改已有区块的元信息。

**参数：**

| 参数             | 类型   | 必填 | 说明     |
| ---------------- | ------ | ---- | -------- |
| `block.id`       | string | ✅   | 区块 ID  |
| `block.name`     | string | ❌   | 区块名称 |
| `block.title`    | string | ❌   | 区块标题 |
| `block.category` | string | ❌   | 分类     |

**示例：**

```json
{
  "action": "updateBlock",
  "parameters": [
    {
      "id": "def456",
      "name": "UserProfile",
      "title": "用户资料",
      "category": "业务组件"
    }
  ]
}
```

---

### `removeBlock` — 删除区块

删除指定区块文件。

**参数：**

- `id: string` — 区块文件 ID

**示例：**

```json
{
  "action": "removeBlock",
  "parameters": ["def456"]
}
```

---

## 四、文件操作

### `active` — 打开文件

在设计器中打开（激活）指定的页面或区块。

**参数：**

- `id: string` — 文件（页面或区块）ID

**示例：**

```json
{
  "action": "active",
  "parameters": ["abc123"]
}
```

---

### `getCurrentFile` — 获取当前文件信息

获取当前设计器中打开的文件元信息（名称、标题、ID）。

**参数：** 无

**返回：** `{ id, name, title }`，无打开文件时抛出错误

**示例：**

```json
{
  "action": "getCurrentFile",
  "parameters": []
}
```

---

### `getCurrentFileContent` — 获取当前文件源码

获取当前打开文件的 Vue 组件源码内容。

**参数：** 无

**返回：** Vue 组件源码字符串

**示例：**

```json
{
  "action": "getCurrentFileContent",
  "parameters": []
}
```

---

### `refresh` — 刷新运行时

刷新当前页面/区块的运行时渲染，并检测是否存在运行时错误。

**参数：** 无

**返回：**

- `true` — 无运行时错误
- 错误信息字符串 — 检测到运行时报错，需修复代码

**用途：** 代码生成完毕后调用，确认是否有运行时错误。

**示例：**

```json
{
  "action": "refresh",
  "parameters": []
}
```

---

### `getNodeSelected` — 获取当前选中节点路径

获取当前页面中选中元素的节点路径，路径最后一项为选中元素名称。

**参数：** 无

**返回：** 路径字符串，如 `ElTable[0]>ElTableColumn[2]`，无选中时返回 `null`

**示例：**

```json
{
  "action": "getNodeSelected",
  "parameters": []
}
```

---

## 五、接口 API 管理

### `getApis` — 获取接口列表

获取当前项目中已配置的所有接口。

**参数：** 无

**返回：** `ApiSchema[]` 数组

**示例：**

```json
{
  "action": "getApis",
  "parameters": []
}
```

---

### `setApi` — 新增或更新接口

新增或更新一个接口配置。若接口名称已存在则更新，不存在则新增。

**参数：**

| 参数                                    | 类型    | 必填 | 说明                                                                                  |
| --------------------------------------- | ------- | ---- | ------------------------------------------------------------------------------------- |
| `api.name`                              | string  | ✅   | 接口名称，camelCase，如 `getUserList`                                                 |
| `api.url`                               | string  | ✅   | 请求 URL，支持路径参数如 `/api/user/:id`                                              |
| `api.label`                             | string  | ❌   | 接口描述说明                                                                          |
| `api.method`                            | string  | ❌   | `get` \| `post` \| `put` \| `delete` \| `patch` \| `jsonp`，默认 `get`                |
| `api.category`                          | string  | ❌   | 接口分组名称，用于 UI 分类展示                                                         |
| `api.settings.type`                     | string  | ❌   | 发送数据类型：`form`（表单）\| `json`（JSON）\| `data`（文件/FormData），默认 `form`  |
| `api.settings.loading`                  | boolean | ❌   | 请求时是否显示全局 loading 动画，默认 `true`                                           |
| `api.settings.failMessage`              | boolean | ❌   | 请求失败时是否弹出错误提示，默认 `true`                                                |
| `api.settings.validSuccess`             | boolean | ❌   | 是否校验响应是否成功（调用全局 validate 函数），默认 `false`                           |
| `api.settings.originResponse`           | boolean | ❌   | 是否返回原始 Axios 响应对象（而非 data 部分），默认 `true`                             |
| `api.settings.injectHeaders`            | boolean | ❌   | 是否注入请求拦截器中自定义的请求头，默认 `false`                                        |
| `api.settings.proxy`                    | boolean | ❌   | 是否开启请求代理，默认 `false`                                                          |
| `api.headers`                           | object  | ❌   | 请求头配置，JSExpression 对象 `{ type: 'JSExpression', value: '...' }`                 |
| `api.mock`                              | boolean | ❌   | 是否开启模拟数据，默认 `false`                                                          |
| `api.mockTemplate`                      | object  | ❌   | 模拟数据模板函数，JSFunction 对象 `{ type: 'JSFunction', value: '(req) => {...}' }`    |
| `api.jsonpOptions.jsonpCallback`        | string  | ❌   | jsonp 回调函数名（仅 `method: 'jsonp'` 时有效）                                        |
| `api.jsonpOptions.jsonpCallbackFunction` | string  | ❌   | jsonp 回调函数（仅 `method: 'jsonp'` 时有效）                                          |
| `api.jsonpOptions.timeout`              | number  | ❌   | jsonp 超时时间，毫秒（仅 `method: 'jsonp'` 时有效）                                    |
| `api.jsonpOptions.crossorigin`          | boolean | ❌   | jsonp 是否跨域（仅 `method: 'jsonp'` 时有效）                                          |

**示例：**

```json
{
  "action": "setApi",
  "parameters": [
    {
      "name": "getUserList",
      "label": "获取用户列表",
      "url": "/api/users",
      "method": "get"
    }
  ]
}
```

---

### `setApis` — 批量新增或更新接口

批量新增或更新多个接口配置。遍历数组中的每个 ApiSchema，逐个执行新增或更新逻辑。

**参数：**

| 参数   | 类型    | 必填 | 说明                 |
| ------ | ------- | ---- | -------------------- |
| `apis` | array   | ✅   | ApiSchema 对象数组   |
| `apis[].name`   | string | ✅   | 接口名称，camelCase |
| `apis[].url`    | string | ✅   | 请求 URL             |
| `apis[].label`  | string | ❌   | 接口描述说明         |
| `apis[].method` | string | ❌   | 请求方法，默认 `get` |

每个 ApiSchema 对象的完整字段与 `setApi` 相同（含 `settings`、`headers`、`mock`、`mockTemplate`、`jsonpOptions` 等）。

**示例：**

```json
{
  "action": "setApis",
  "parameters": [
    [
      {
        "name": "getUserList",
        "label": "获取用户列表",
        "url": "/api/users",
        "method": "get"
      },
      {
        "name": "createUser",
        "label": "创建用户",
        "url": "/api/users",
        "method": "post"
      }
    ]
  ]
}
```

---

### `removeApi` — 删除接口

删除指定接口。

**参数：**

- `name: string` — 接口名称或 ID

**示例：**

```json
{
  "action": "removeApi",
  "parameters": ["getUserList"]
}
```

---

### `removeApis` — 批量删除接口

批量删除多个接口。

**参数：**

- `apis: string[]` — 接口名称或 ID 数组

**示例：**

```json
{
  "action": "removeApis",
  "parameters": [["getUserList", "createUser"]]
}
```

---

## 六、依赖管理

### `getDeps` — 获取依赖列表

获取当前项目配置的所有依赖包。

**参数：** 无

**返回：** `Dependencie[]` 数组，每项包含 `package`、`version`、`library`、`urls`、`platform`、`official` 等

**示例：**

```json
{
  "action": "getDeps",
  "parameters": []
}
```

---

### `setDeps` — 批量新增或更新依赖

批量新增或更新项目的依赖包配置。**不会操作 `official: true` 的内置依赖**（如 Vue、Element Plus 等平台内置依赖会跳过）。

**参数：**

| 参数                  | 类型    | 必填 | 说明                                                                 |
| --------------------- | ------- | ---- | -------------------------------------------------------------------- |
| `deps[].package`      | string  | ✅   | 包名，如 `vue`、`element-plus`、`echarts`                            |
| `deps[].version`      | string  | ✅   | 版本号，如 `latest`、`^3.4.0`                                        |
| `deps[].library`      | string  | ✅   | 库导出名称，如 `Vue`、`ElementPlus`、`echarts`                       |
| `deps[].urls`         | string[] | ✅  | 加载资源 URL 数组，如 `@vtj/materials/deps/vue/index.umd.js`         |
| `deps[].platform`     | string[] | ❌  | 支持平台 `web` \| `h5` \| `uniapp`，不填则所有平台通用               |
| `deps[].required`     | boolean  | ❌  | 是否必须依赖，默认 `false`                                           |
| `deps[].enabled`      | boolean  | ❌  | 是否启用，默认 `true`                                                |
| `deps[].localeLibrary` | string  | ❌   | 语言包库导出名称                                                     |
| `deps[].assetsUrl`    | string  | ❌   | 资产配置 URL                                                         |
| `deps[].assetsLibrary` | string  | ❌  | 资产配置导出名称                                                     |
| `deps[].easycom`      | object  | ❌   | UniApp easycom 自动导入配置，`{ key: '^El[A-Z]', value: '...' }`     |

**注意：** 当操作包含 `official: true` 的内置依赖时，工具会跳过并返回跳过的包名。

**示例：**

```json
{
  "action": "setDeps",
  "parameters": [
    [
      {
        "package": "echarts",
        "version": "latest",
        "library": "echarts",
        "urls": ["@vtj/materials/deps/echarts/index.umd.js"]
      }
    ]
  ]
}
```

---

### `removeDeps` — 批量删除依赖

批量删除项目的依赖包。**不会删除 `official: true` 的内置依赖**。

**参数：**

- `packages: string[]` — 依赖包名数组

**返回：** 成功时返回 `true`；若跳过了内置依赖，返回 `{ success: true, skipped: string[], message: '...' }`

**示例：**

```json
{
  "action": "removeDeps",
  "parameters": [["lodash", "moment"]]
}
```

---

## 七、全局配置

### `getGlobalCss` / `setGlobalCss` — 全局 CSS

获取或设置应用的全局 CSS 样式代码。

**`setGlobalCss` 参数：**

- `css: string` — CSS 代码字符串

**注意：** UniApp 平台会自动将全局 CSS 存入 UniApp 配置的 `css` 项中。

**调用示例：**

```json
{
  "action": "getGlobalCss",
  "parameters": []
}
```

```json
{
  "action": "setGlobalCss",
  "parameters": ["body { background: #f5f5f5; }"]
}
```

---

### `getGlobalStore` / `setGlobalStore` — 全局 Pinia Store

获取或设置应用的全局 Pinia 状态。

**`setGlobalStore` 参数：**

- `store: string` — JS 函数代码字符串

函数接收 `app`（Vue 应用实例），返回标准 Pinia Store 配置对象：

```javascript
(app) => {
  return {
    state: () => ({
      user: null,
      token: ''
    }),
    getters: {
      isLogined: (state) => !!state.token
    },
    actions: {
      setUser(user) {
        this.user = user;
      },
      logout() {
        this.user = null;
        this.token = '';
      }
    }
  };
};
```

组件中通过 `__store.xxx` 访问（如 `__store.user`、`__store.setUser(data)`）。

**调用示例：**

```json
{
  "action": "getGlobalStore",
  "parameters": []
}
```

```json
{
  "action": "setGlobalStore",
  "parameters": ["(app) => { return { state: () => ({ user: null, token: '' }), getters: { isLogined: (state) => !!state.token }, actions: { setUser(user) { this.user = user }, logout() { this.user = null; this.token = '' } } } }"]
}
```

---

### `getGlobalAccess` / `setGlobalAccess` — 权限控制配置

获取或设置权限控制插件配置。

**`setGlobalAccess` 参数：**

- `access: string` — JS 函数代码字符串

函数接收 `app`，返回 `AccessOptions` 配置对象：

```javascript
(app) => {
  return {
    session: false,
    authKey: 'Authorization',
    storageKey: 'ACCESS_STORAGE',
    auth: '/#/login',
    whiteList: (to) => false,
    redirectParam: 'r',
    unauthorizedCode: 401,
    unauthorizedMessage: '登录已经失效，请重新登录！',
    noPermissionMessage: '无权限访问该页面',
    statusKey: 'code'
  };
};
```

组件中通过 `__access` 访问（如 `__access.isLogined()`、`__access.can('user:edit')`）。

**调用示例：**

```json
{
  "action": "getGlobalAccess",
  "parameters": []
}
```

```json
{
  "action": "setGlobalAccess",
  "parameters": ["(app) => { return { session: false, authKey: 'Authorization', storageKey: 'ACCESS_STORAGE', auth: '/#/login', whiteList: (to) => false, redirectParam: 'r', unauthorizedCode: 401, unauthorizedMessage: '登录已经失效，请重新登录！', noPermissionMessage: '无权限访问该页面', statusKey: 'code' } }"]
}
```

---

### `getGlobalAxios` / `setGlobalAxios` — Axios 基础配置

获取或设置全局 Axios 请求工具的基础配置（baseURL、超时、响应校验等）。

**`setGlobalAxios` 参数：**

- `axios: string` — JS 函数代码字符串

函数接收 `app`，返回 `IRequestOptions` 配置对象：

```javascript
(app) => {
  return {
    baseURL: '/',
    timeout: 60000,
    settings: {
      type: 'json',
      validSuccess: false,
      originResponse: true,
      loading: true,
      failMessage: true,
      validate: (res) => {
        return res.data?.code === 0 || !!res.data?.success;
      }
    }
  };
```

**调用示例：**

```json
{
  "action": "getGlobalAxios",
  "parameters": []
}
```

```json
{
  "action": "setGlobalAxios",
  "parameters": ["(app) => { return { baseURL: '/', timeout: 60000, settings: { type: 'json', validSuccess: false, originResponse: true, loading: true, failMessage: true, validate: (res) => res.data?.code === 0 || !!res.data?.success } } }"]
}
```

---

### `getGlobalRequestInterceptor` / `setGlobalRequestInterceptor` — 请求拦截器

获取或设置全局 Axios 请求拦截器，用于在发请求前修改请求配置（如注入 token）。

**`setGlobalRequestInterceptor` 参数：**

- `request: string` — JS 函数代码字符串

函数接收两个参数：

- `config` — 请求配置对象
- `app` — Vue 应用实例

函数**必须返回** `config`：

```javascript
(config, app) => {
  const token = app.config.globalProperties.$access?.getToken();
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  return config;
};
```

**调用示例：**

```json
{
  "action": "getGlobalRequestInterceptor",
  "parameters": []
}
```

```json
{
  "action": "setGlobalRequestInterceptor",
  "parameters": ["(config, app) => { const token = app.config.globalProperties.$access?.getToken(); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }"]
}
```

---

### `getGlobalResponseInterceptor` / `setGlobalResponseInterceptor` — 响应拦截器

获取或设置全局 Axios 响应拦截器，用于统一处理响应数据。

**`setGlobalResponseInterceptor` 参数：**

- `response: string` — JS 函数代码字符串

函数接收两个参数：

- `res` — Axios 原始响应对象
- `app` — Vue 应用实例

函数**必须返回** `res`：

```javascript
(res, app) => {
  return res;
};
```

**调用示例：**

```json
{
  "action": "getGlobalResponseInterceptor",
  "parameters": []
}
```

```json
{
  "action": "setGlobalResponseInterceptor",
  "parameters": ["(res, app) => { return res; }"]
}
```

---

### `getGlobalBeforeEach` / `setGlobalBeforeEach` — 前置路由守卫

获取或设置全局前置路由守卫（`router.beforeEach`）。

**`setGlobalBeforeEach` 参数：**

- `value: string` — JS 函数代码字符串

函数接收 4 个参数：

- `to` — 即将进入的目标路由
- `from` — 当前正要离开的路由
- `next` — 执行跳转的函数
- `app` — Vue 应用实例

```javascript
(to, from, next, app) => {
  const access = app.config.globalProperties.$access;
  if (!access.isLogined() && to.path !== '/login') {
    next('/login');
  } else {
    next();
  }
};
```

**调用示例：**

```json
{
  "action": "getGlobalBeforeEach",
  "parameters": []
}
```

```json
{
  "action": "setGlobalBeforeEach",
  "parameters": ["(to, from, next, app) => { const access = app.config.globalProperties.$access; if (!access.isLogined() && to.path !== '/login') { next('/login'); } else { next(); } }"]
}
```

---

### `getGlobalAfterEach` / `setGlobalAfterEach` — 后置路由守卫

获取或设置全局后置路由守卫（`router.afterEach`）。

**`setGlobalAfterEach` 参数：**

- `value: string` — JS 函数代码字符串

函数接收 4 个参数：

- `to` — 即将进入的目标路由
- `from` — 当前正要离开的路由
- `failure` — 导航失败信息（无失败则为 `undefined`）
- `app` — Vue 应用实例

```javascript
(to, from, failure, app) => {
  if (!failure) {
    console.log('navigated to', to.path);
  }
};
```

**调用示例：**

```json
{
  "action": "getGlobalAfterEach",
  "parameters": []
}
```

```json
{
  "action": "setGlobalAfterEach",
  "parameters": ["(to, from, failure, app) => { if (!failure) { console.log('navigated to', to.path); } }"]
}
```

---

## 八、环境变量管理

### `getEnv` — 获取环境变量列表

获取项目中配置的所有环境变量。

**参数：** 无

**返回：** `EnvConfig[]` 数组，每项包含 `name`、`development`、`production`

**组件中访问环境变量：** `__provider.env.变量名`

**示例：**

```json
{
  "action": "getEnv",
  "parameters": []
}
```

---

### `createEnv` — 新增环境变量

新增一个环境变量，区分开发/生产两套值。

**参数：**

| 参数                 | 类型   | 必填 | 说明                  |
| -------------------- | ------ | ---- | --------------------- |
| `config.name`        | string | ✅   | 变量名，如 `BASE_URL` |
| `config.development` | string | ✅   | 开发环境的值          |
| `config.production`  | string | ✅   | 生产环境的值          |

**示例：**

```json
{
  "action": "createEnv",
  "parameters": [
    {
      "name": "BASE_URL",
      "development": "http://dev.api.com",
      "production": "https://api.com"
    }
  ]
}
```

---

### `removeEnv` — 删除环境变量

删除指定名称的环境变量。

**参数：**

- `name: string` — 环境变量名称

**示例：**

```json
{
  "action": "removeEnv",
  "parameters": ["BASE_URL"]
}
```

---

## 九、国际化（i18n）管理

### `getI18nMessage` — 获取词条列表

获取 vue-i18n 的所有中英文对照词条。

**参数：** 无

**返回：** `I18nMessage[]` 数组，每项包含 `key`、`zh-CN`、`en`

**组件中使用词条：** `__i18n.t('key')`

**示例：**

```json
{
  "action": "getI18nMessage",
  "parameters": []
}
```

---

### `createI18nMessage` — 新增词条（批量）

批量新增一条或多条中英文对照词条。**支持批量创建**，参数为数组而非单个对象。

**参数：**

| 参数                    | 类型   | 必填 | 说明                          |
| ----------------------- | ------ | ---- | ----------------------------- |
| `messages[].key`        | string | ✅   | 词条标识，如 `common.confirm` |
| `messages[].zh-CN`      | string | ✅   | 中文内容                      |
| `messages[].en`         | string | ✅   | 英文内容                      |

**示例：**

```json
{
  "action": "createI18nMessage",
  "parameters": [
    [
      { "key": "common.confirm", "zh-CN": "确认", "en": "Confirm" },
      { "key": "common.cancel", "zh-CN": "取消", "en": "Cancel" }
    ]
  ]
}
```

---

### `removeI18nMessage` — 删除词条（批量）

批量删除指定 key 的词条。**支持批量删除**，参数为数组而非单个 key。

**参数：**

- `keys: string[]` — 词条标识 key 数组

**示例：**

```json
{
  "action": "removeI18nMessage",
  "parameters": [["common.confirm", "common.cancel"]]
}
```

---

## 十、UniApp 专属配置

> 以下工具仅适用于 UniApp 平台项目。

### `getUniConfig` / `setUniConfig` — UniApp 全局配置

获取或设置 UniApp 全局配置，包括 pages.json、manifest.json、全局 CSS、App.vue 生命周期等。

**参数：**

| 参数    | 类型   | 必填      | 说明                       |
| ------- | ------ | --------- | -------------------------- |
| `key`   | string | ✅        | 配置类型名称（见下方枚举） |
| `value` | string | ✅（set） | 配置内容（见下方规则）     |

**`key` 枚举值：**

| key                    | 说明                    |
| ---------------------- | ----------------------- |
| `manifestJson`         | manifest.json 应用配置  |
| `pagesJson`            | pages.json 页面路由配置 |
| `css`                  | App.vue 中的全局 CSS    |
| `onLaunch`             | App 启动时              |
| `onShow`               | App 显示时              |
| `onHide`               | App 隐藏时              |
| `onError`              | App 报错时              |
| `onLastPageBackPress`  | 最后一个页面返回事件    |
| `onPageNotFound`       | 页面不存在时            |
| `onUnhandledRejection` | 未处理 Promise 拒绝时   |
| `onThemeChange`        | 主题变化时              |
| `onUniNViewMessage`    | nvue 发送消息时         |
| `onExit`               | App 退出时              |

**`value` 格式规则：**

1. `key` 为 `manifestJson` 或 `pagesJson` 时，`value` 为 **JSON 字符串**
2. `key` 为 `css` 时，`value` 为 **CSS 代码字符串**
3. `key` 为生命周期名称时，`value` 为 **JS 函数代码块**：
   ```javascript
   () => {
     // 生命周期逻辑
   };
   ```
4. `value` 为 `null` 时，表示清除该配置

**示例（设置 pages.json）：**

```json
{
  "action": "setUniConfig",
  "parameters": [
    "pagesJson",
    "{\"pages\":[{\"path\":\"pages/index/index\",\"style\":{\"navigationBarTitleText\":\"首页\"}}],\"globalStyle\":{\"navigationBarTextStyle\":\"black\",\"navigationBarTitleText\":\"uni-app\",\"navigationBarBackgroundColor\":\"#F8F8F8\",\"backgroundColor\":\"#F8F8F8\"}}"
  ]
}
```

**示例（设置 onLaunch 生命周期）：**

```json
{
  "action": "setUniConfig",
  "parameters": ["onLaunch", "() => { console.log('App launched') }"]
}
```

**调用示例（获取配置）：**

```json
{
  "action": "getUniConfig",
  "parameters": ["css"]
}
```

---

## 十一、工具调用建议

### 典型工作流程

1. **了解项目结构：** 先调用 `getMenus` 或 `getPages` 了解页面结构
2. **创建页面：** 用 `createPage` 创建，层级页面先建父级
3. **生成内容：** 结合 Vue 代码规范生成页面源码
4. **验证结果：** 调用 `refresh` 检测是否有运行时错误
5. **配置接口：** 用 `setApi` 注册后端接口
6. **管理依赖：** 用 `getDeps` 查看当前依赖，`setDeps` 添加第三方库
7. **全局配置：** 按需配置 store、access、axios 等全局选项

### 注意事项

- 调用 `createPage`/`createBlock` 后，目标文件会自动激活打开
- `refresh` 会等待渲染完成（约 1 秒），建议生成完整代码后再调用
- UniApp 平台不支持 `dir`（目录）和 `layout`（布局）类型页面
- 函数类型配置（store、access、axios、拦截器、路由守卫）均传入 **JS 函数代码字符串**，不是 JSON
- `setGlobalStore` 的函数必须返回 Pinia store 配置对象，可用 `getSkills` 查询 pinia 用法
- `setDeps` 和 `removeDeps` 均会跳过 `official: true` 的内置依赖（如 Vue、Element Plus）

<!-- ============================================ -->
<!-- 文档: ui.md -->
<!-- ============================================ -->

# @vtj/ui 组件库 — AI 使用指南

> 本文档帮助 AI 理解 `@vtj/ui` 组件库的构成、导入方式和在 Vue 组件中的使用规范。

---

## 一、组件库概览

`@vtj/ui` 是 VTJ 平台的企业级 UI 组件库，基于 Vue 3 + TypeScript 构建，底层依赖 Element Plus 和 VXE Table。提供以下核心模块：

| 模块         | 功能说明                                     | 导入方式                                              |
| ------------ | -------------------------------------------- | ----------------------------------------------------- |
| **组件**     | 30+ 业务组件（Grid、Form、Dialog、Field 等） | `import { XGrid, XForm } from '@vtj/ui'`              |
| **Hooks**    | 可复用的 Composition API 钩子                | `import { useIcon, useLoader } from '@vtj/ui'`        |
| **指令**     | 拖拽、缩放等自定义指令                       | `import { vDraggable, vResizable } from '@vtj/ui'`    |
| **Adapter**  | 适配器系统（上传、字段编辑器、VXE 配置等）   | `import { useAdapter, AdapterPlugin } from '@vtj/ui'` |
| **工具方法** | 通用工具函数                                 | `import { parseSize } from '@vtj/ui/utils'`           |
| **常量**     | 全局常量定义                                 | `import { ... } from '@vtj/ui/constants'`             |

### 1.1 技术栈依赖

```json
{
  "dependencies": {
    "@vtj/icons": "latest",
    "@vtj/utils": "latest",
    "@vueuse/core": "~14.1.0",
    "element-plus": "~2.13.0",
    "sortablejs": "~1.15.6",
    "vxe-table": "~4.6.17",
    "vxe-table-plugin-menus": "~4.0.3"
  }
}
```

### 1.2 组件命名规范

- 所有组件均以 `X` 前缀命名，如 `XGrid`、`XForm`、`XDialog`
- 组件类型定义以 `Props`、`Emits`、`Instance` 结尾，如 `GridProps`、`GridEmits`、`GridInstance`

---

## 二、安装与注册

### 2.1 安装依赖

```json
{
  "dependencies": {
    "@vtj/ui": "latest"
  }
}
```

### 2.2 全局注册

```typescript
import { createApp } from 'vue';
import { AdapterPlugin } from '@vtj/ui';
import '@vtj/ui/dist/style.css';

const app = createApp(App);

// 注册适配器插件（会自动注册 Element Plus 的消息组件）
app.use(AdapterPlugin, {
  uploader: async (file: File) => {
    // 自定义上传逻辑
    return { url: '...', name: file.name };
  },
  fieldEditors: {
    // 自定义字段编辑器
  }
});
```

---

## 三、核心组件使用

### 3.1 XGrid 数据表格

`XGrid` 是基于 VXE Table 封装的高级数据表格组件，支持数据加载、排序、过滤、行/列拖拽排序、单元格编辑等功能。

#### 基本用法

```vue
<template>
  <XGrid
    :columns="columns"
    :loader="loadData"
    :pager="true"
    :row-sortable="true"
    @rowSort="onRowSort"
    @loaded="onLoaded">
    <template #action="{ row }">
      <XAction label="编辑" mode="text" type="primary" @click="onEdit(row)" />
      <XAction label="删除" mode="text" type="danger" @click="onDelete(row)" />
    </template>
  </XGrid>
</template>

<script setup>
  import { ref } from 'vue';
  import { XGrid, XAction } from '@vtj/ui';

  const columns = ref([
    { field: 'name', title: '名称', width: 200 },
    { field: 'status', title: '状态', width: 120 },
    { field: 'action', title: '操作', width: 150, slots: { default: 'action' } }
  ]);

  const loadData = async (state) => {
    const { page, pageSize } = state;
    const res = await request({
      url: '/api/data',
      method: 'get',
      query: { page, pageSize }
    });
    return {
      list: res.list,
      total: res.total
    };
  };

  const onRowSort = (e) => {
    console.log('行排序:', e.oldIndex, '->', e.newIndex);
  };

  const onLoaded = (rows) => {
    console.log('数据加载完成:', rows.length);
  };
</script>
```

#### 列渲染器

```vue
<script setup>
  import { ref } from 'vue';

  const columns = ref([
    // 文本渲染
    { field: 'name', title: '名称' },

    // 日期渲染
    { field: 'date', title: '日期', cellRender: { name: 'Date' } },

    // 数字渲染
    { field: 'amount', title: '金额', cellRender: { name: 'Number' } },

    // 图片渲染
    { field: 'avatar', title: '头像', cellRender: { name: 'Image' } },

    // 链接渲染
    { field: 'url', title: '链接', cellRender: { name: 'Link' } },

    // 标签渲染
    {
      field: 'status',
      title: '状态',
      cellRender: {
        name: 'Tag',
        props: {
          options: [
            { label: '启用', value: 1, type: 'success' },
            { label: '禁用', value: 0, type: 'danger' }
          ]
        }
      }
    },

    // 单元格编辑
    {
      field: 'name',
      title: '名称',
      editRender: { name: 'Input' }
    },

    // 下拉编辑
    {
      field: 'status',
      title: '状态',
      editRender: {
        name: 'Select',
        props: {
          options: [
            { label: '启用', value: 1 },
            { label: '禁用', value: 0 }
          ]
        }
      }
    }
  ]);
</script>
```

#### 高级特性

```vue
<template>
  <XGrid
    ref="gridRef"
    :columns="columns"
    :loader="loadData"
    :customable="true"
    :filter="true"
    :sort="true"
    :edit-config="{ trigger: 'click', mode: 'row' }"
    @editChange="onEditChange"
    @cellSelected="onCellSelected">
  </XGrid>
</template>

<script setup>
  import { ref } from 'vue';
  import { XGrid } from '@vtj/ui';

  const gridRef = ref(null);

  // 获取选中行
  const getSelectedRows = () => {
    return gridRef.value?.getSelectedRows() || [];
  };

  // 刷新数据
  const refresh = () => {
    gridRef.value?.reload();
  };

  const onEditChange = (data) => {
    console.log('编辑数据变化:', data);
  };

  const onCellSelected = (params) => {
    console.log('单元格选中:', params);
  };
</script>
```

---

### 3.2 XForm 表单

`XForm` 是基于 Element Plus Form 封装的表单组件，支持 inline 模式、自动提交/重置、sticky 底部按钮等特性。

#### 基本用法

```vue
<template>
  <XForm
    ref="formRef"
    v-model="formModel"
    :submit-method="onSubmit"
    submit-text="提交"
    reset-text="重置"
    :enter-submit="true"
    :sticky="false"
    footer-align="left"
    @submit="onSubmit"
    @reset="onReset"
    @change="onChange">
    <XField
      name="username"
      label="用户名"
      editor="text"
      :rules="[{ required: true, message: '请输入用户名' }]" />
    <XField name="email" label="邮箱" editor="text" />
    <XField
      name="status"
      label="状态"
      editor="select"
      :options="statusOptions" />
  </XForm>
</template>

<script setup>
  import { ref } from 'vue';
  import { XForm, XField } from '@vtj/ui';

  const formRef = ref(null);
  const formModel = ref({
    username: '',
    email: '',
    status: 1
  });

  const statusOptions = [
    { label: '启用', value: 1 },
    { label: '禁用', value: 0 }
  ];

  const onSubmit = async (model) => {
    console.log('提交表单:', model);
    await request({
      url: '/api/user',
      method: 'post',
      data: model
    });
  };

  const onReset = () => {
    console.log('表单重置');
  };

  const onChange = (model) => {
    console.log('表单数据变化:', model);
  };
</script>
```

#### Inline 模式

```vue
<template>
  <XForm inline :inline-columns="3" v-model="queryModel" :footer="false">
    <XField name="keyword" label="关键词" editor="text" />
    <XField
      name="status"
      label="状态"
      editor="select"
      :options="statusOptions" />
    <XField name="date" label="日期" editor="date" />
  </XForm>
</template>
```

---

### 3.3 XField 字段组件

`XField` 是统一的表单字段组件，支持多种编辑器类型、动态选项加载、级联刷新、可见性控制等。

#### 内置编辑器

| 编辑器类型 | 说明       | 适用场景     |
| ---------- | ---------- | ------------ |
| `text`     | 文本输入框 | 单行文本     |
| `textarea` | 多行文本框 | 多行文本     |
| `number`   | 数字输入框 | 数值输入     |
| `select`   | 下拉选择   | 单选/多选    |
| `radio`    | 单选框     | 少量选项单选 |
| `checkbox` | 复选框     | 少量选项多选 |
| `date`     | 日期选择   | 日期/时间    |
| `switch`   | 开关       | 布尔值       |

#### 基本用法

```vue
<template>
  <!-- 文本字段 -->
  <XField name="name" label="姓名" editor="text" placeholder="请输入姓名" />

  <!-- 下拉选择 -->
  <XField
    name="status"
    label="状态"
    editor="select"
    :options="statusOptions"
    placeholder="请选择状态" />

  <!-- 动态选项加载 -->
  <XField
    name="department"
    label="部门"
    editor="select"
    :options="loadDepartments" />

  <!-- 级联字段 -->
  <XField
    name="city"
    label="城市"
    editor="select"
    :options="loadCities"
    cascader="province" />

  <!-- 可见性控制 -->
  <XField
    name="detail"
    label="详情"
    editor="textarea"
    :visible="formModel.showDetail" />

  <!-- 禁用/只读 -->
  <XField name="code" label="编码" editor="text" disabled />
  <XField name="createTime" label="创建时间" editor="text" readonly />
</template>

<script setup>
  import { ref } from 'vue';
  import { XField } from '@vtj/ui';

  const formModel = ref({
    showDetail: false
  });

  const statusOptions = [
    { label: '启用', value: 1 },
    { label: '禁用', value: 0 }
  ];

  const loadDepartments = async () => {
    const res = await request({ url: '/api/departments' });
    return res.map((item) => ({ label: item.name, value: item.id }));
  };

  const loadCities = (model) => {
    // model 包含表单数据，可根据 province 字段值过滤城市
    return request({
      url: '/api/cities',
      query: { province: model.province }
    });
  };
</script>
```

---

### 3.4 XDialog 对话框

`XDialog` 是增强型对话框组件，支持拖拽、缩放、最大化/最小化、多实例管理等。

#### 基本用法

```vue
<template>
  <XDialog
    v-model="visible"
    title="编辑用户"
    size="default"
    :width="'60%'"
    :height="'70%'"
    :draggable="true"
    :resizable="true"
    :maximizable="true"
    :minimizable="false"
    :modal="true"
    :closable="true"
    :body-padding="true"
    @open="onOpen"
    @close="onClose"
    @submit="onSubmit"
    @cancel="onCancel">
    <XForm v-model="userForm" :footer="false">
      <XField name="name" label="姓名" editor="text" />
      <XField name="email" label="邮箱" editor="text" />
    </XForm>
  </XDialog>
</template>

<script setup>
  import { ref } from 'vue';
  import { XDialog, XForm, XField } from '@vtj/ui';

  const visible = ref(false);
  const userForm = ref({});

  const open = () => {
    visible.value = true;
  };

  const onOpen = (instance) => {
    console.log('对话框打开:', instance);
  };

  const onClose = () => {
    console.log('对话框关闭');
  };

  const onSubmit = () => {
    console.log('提交:', userForm.value);
    visible.value = false;
  };

  const onCancel = () => {
    visible.value = false;
  };

  defineExpose({ open });
</script>
```

---

### 3.5 XActionBar 操作栏

`XActionBar` 用于统一的操作按钮区域，支持按钮、文本、图标模式，下拉菜单等。

```vue
<template>
  <XActionBar
    :items="actions"
    mode="button"
    type="primary"
    size="default"
    @click="onActionClick"
    @command="onActionCommand" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XActionBar, Add, Edit, Delete } from '@vtj/ui';

  const actions = ref([
    { name: 'add', label: '新增', icon: Add },
    {
      name: 'edit',
      label: '编辑',
      icon: Edit,
      disabled: () => !selectedRow.value
    },
    '|', // 分隔符
    {
      name: 'more',
      label: '更多',
      menus: [
        { command: 'export', label: '导出' },
        { command: 'import', label: '导入' },
        { command: 'delete', label: '批量删除', icon: Delete }
      ]
    }
  ]);

  const onActionClick = (action) => {
    console.log('点击操作:', action.name);
    if (action.name === 'add') {
      // 新增逻辑
    }
  };

  const onActionCommand = (item, command) => {
    console.log('菜单命令:', command.command);
  };
</script>
```

---

### 3.6 XQueryForm 查询表单

`XQueryForm` 是专门用于数据查询的表单组件，支持折叠展开、自动布局等。

```vue
<template>
  <XQueryForm
    :items="queryItems"
    v-model="queryModel"
    :inline-columns="4"
    :disabled="loading"
    @submit="onQuery"
    @reset="onReset">
  </XQueryForm>
</template>

<script setup>
  import { ref } from 'vue';
  import { XQueryForm } from '@vtj/ui';

  const queryModel = ref({});
  const loading = ref(false);

  const queryItems = ref([
    { name: 'keyword', label: '关键词', editor: 'text' },
    { name: 'status', label: '状态', editor: 'select', options: statusOptions },
    { name: 'date', label: '日期范围', editor: 'date' }
  ]);

  const onQuery = () => {
    console.log('查询条件:', queryModel.value);
    // 触发数据加载
  };

  const onReset = () => {
    queryModel.value = {};
  };
</script>
```

---

### 3.7 XContainer 布局容器

`XContainer` 是基于 Flex 布局的容器组件，提供丰富的布局控制属性。

```vue
<template>
  <XContainer direction="column" :gap="true" :padding="true">
    <XContainer direction="row" justify="space-between" align="center">
      <span>标题</span>
      <XActionBar :items="actions" />
    </XContainer>

    <XContainer :grow="true" :overflow="'auto'">
      <XGrid :columns="columns" :loader="loadData" />
    </XContainer>
  </XContainer>
</template>

<script setup>
  import { XContainer, XActionBar, XGrid } from '@vtj/ui';
</script>
```

---

### 3.8 其他常用组件

#### XAction 操作按钮

```vue
<template>
  <XAction
    label="新增"
    mode="button"
    type="primary"
    :icon="Add"
    @click="onAdd" />
  <XAction
    label="编辑"
    mode="text"
    type="primary"
    :icon="Edit"
    @click="onEdit" />
  <XAction
    label="删除"
    mode="icon"
    type="danger"
    :icon="Delete"
    @click="onDelete" />
</template>
```

#### XIcon 图标组件

```vue
<template>
  <XIcon :icon="Add" :size="20" color="#409EFF" />
</template>

<script setup>
  import { XIcon } from '@vtj/ui';
  import { Add } from '@vtj/icons';
</script>
```

#### XTabs 标签页

```vue
<template>
  <XTabs v-model="activeTab" :tabs="tabList">
    <template #tab1>
      <p>标签页1内容</p>
    </template>
    <template #tab2>
      <p>标签页2内容</p>
    </template>
  </XTabs>
</template>

<script setup>
  import { ref } from 'vue';
  import { XTabs } from '@vtj/ui';

  const activeTab = ref('tab1');
  const tabList = [
    { name: 'tab1', label: '基本信息' },
    { name: 'tab2', label: '高级设置' }
  ];
</script>
```

#### XAttachment 附件上传

```vue
<template>
  <XAttachment
    v-model="fileList"
    :limit="5"
    :accept="'.jpg,.png,.pdf'"
    :multiple="true"
    @change="onChange"
    @remove="onRemove" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XAttachment } from '@vtj/ui';

  const fileList = ref([]);

  const onChange = (files) => {
    console.log('文件列表变化:', files);
  };

  const onRemove = (file) => {
    console.log('移除文件:', file);
  };
</script>
```

#### XHeader 页头

```vue
<template>
  <XHeader
    title="用户管理"
    subtitle="管理系统用户信息"
    :breadcrumb="breadcrumb">
    <template #extra>
      <XActionBar :items="actions" />
    </template>
  </XHeader>
</template>

<script setup>
  import { XHeader, XActionBar } from '@vtj/ui';

  const breadcrumb = [
    { label: '首页', path: '/' },
    { label: '系统管理' },
    { label: '用户管理' }
  ];
</script>
```

#### XMask 遮罩布局

```vue
<template>
  <XMask>
    <template #menu>
      <XMenu :items="menuItems" />
    </template>

    <template #header>
      <XHeader title="后台管理系统" />
    </template>

    <template #tabs>
      <XTabs v-model="activeTab" :tabs="tabList" />
    </template>

    <router-view />
  </XMask>
</template>
```

---

## 四、Hooks 工具

### 4.1 useIcon 图标钩子

```typescript
import { useIcon } from '@vtj/ui';

const { IconComponent } = useIcon('Add');
```

### 4.2 useLoader 加载器钩子

```typescript
import { useLoader } from '@vtj/ui';

const { data, loading, error, reload } = useLoader(async () => {
  return await request({ url: '/api/data' });
});
```

### 4.3 useDisabled 禁用状态钩子

```typescript
import { useDisabled } from '@vtj/ui';

const disabled = useDisabled(props.disabled, actionItem);
```

### 4.4 useDefer 延迟渲染钩子

```typescript
import { useDefer } from '@vtj/ui';

const defer = useDefer(100); // 延迟 100ms
```

---

## 五、Adapter 适配器系统

### 5.1 使用适配器

```vue
<script setup>
  import { useAdapter } from '@vtj/ui';

  const adapter = useAdapter();

  // 使用上传功能
  const uploadFile = async (file) => {
    if (adapter.uploader) {
      const result = await adapter.uploader(file);
      console.log('上传结果:', result);
    }
  };
</script>
```

### 5.2 配置适配器

```typescript
import { AdapterPlugin } from '@vtj/ui';

app.use(AdapterPlugin, {
  // 自定义上传
  uploader: async (file) => {
    const formData = new FormData();
    formData.append('file', file);
    const res = await request({
      url: '/api/upload',
      method: 'post',
      data: formData
    });
    return { url: res.url, name: file.name };
  },

  // 自定义字段编辑器
  fieldEditors: {
    customEditor: {
      component: CustomEditorComponent,
      defaultValue: ''
    }
  },

  // VXE Table 配置
  vxeConfig: {
    // ...
  },

  // Grid 自定义配置持久化
  getCustom: async (id) => {
    return await request({ url: `/api/grid-config/${id}` });
  },
  saveCustom: async (info) => {
    await request({
      url: '/api/grid-config',
      method: 'post',
      data: info
    });
  }
});
```

---

## 六、VTJ 代码规范

### 6.1 Composition API 强制规范

所有组件必须使用 `<script setup>` 语法，禁止使用 Options API：

```vue
<!-- ✅ 正确 -->
<script setup>
  import { ref } from 'vue';
  import { XGrid } from '@vtj/ui';

  const columns = ref([]);
</script>

<!-- ❌ 错误 -->
<script>
  export default {
    data() {
      return { columns: [] };
    }
  };
</script>
```

### 6.2 组件导入规范

从 `@vtj/ui` 按需导入组件，不要全局导入：

```vue
<script setup>
  // ✅ 正确：按需导入
  import { XGrid, XForm, XField, XDialog } from '@vtj/ui';

  // ❌ 错误：不要使用全局导入
  import Vue from 'vue';
  Vue.use(UiLibrary);
</script>
```

### 6.3 变量命名规范

- 使用 `ref` 或 `reactive` 声明响应式变量
- 变量名使用 camelCase
- 组件 ref 使用 `xxRef` 命名

```vue
<script setup>
  import { ref } from 'vue';

  // ✅ 正确
  const formData = ref({});
  const gridRef = ref(null);
  const dialogVisible = ref(false);

  // ❌ 错误
  const FormData = ref({});
  const grid_ref = ref(null);
</script>
```

### 6.4 类型定义规范

使用 TypeScript 类型定义：

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import type { GridProps, FormProps } from '@vtj/ui';

  const gridProps = ref<Partial<GridProps>>({
    pager: true,
    custom: true
  });
</script>
```

### 6.5 事件处理规范

事件处理函数使用 `on` 前缀：

```vue
<script setup>
  const onSubmit = () => {
    /* ... */
  };
  const onRowClick = (row) => {
    /* ... */
  };
  const onDialogClose = () => {
    /* ... */
  };
</script>
```

---

## 七、完整示例

### 7.1 标准 CRUD 页面

```vue
<template>
  <XContainer direction="column" :gap="true" :padding="true" :fit="true">
    <!-- 页头 -->
    <XHeader title="用户管理">
      <template #extra>
        <XActionBar :items="actions" @click="onActionClick" />
      </template>
    </XHeader>

    <!-- 查询表单 -->
    <XQueryForm
      :items="queryItems"
      v-model="queryModel"
      :inline-columns="4"
      @submit="loadData"
      @reset="onResetQuery" />

    <!-- 数据表格 -->
    <XContainer :grow="true" :overflow="'auto'">
      <XGrid
        ref="gridRef"
        :columns="columns"
        :loader="loadData"
        :pager="true"
        :row-sortable="false"
        @cellSelected="onCellSelected">
        <template #status="{ row }">
          <ElTag :type="row.status === 1 ? 'success' : 'danger'">
            {{ row.status === 1 ? '启用' : '禁用' }}
          </ElTag>
        </template>
      </XGrid>
    </XContainer>

    <!-- 编辑对话框 -->
    <XDialog
      v-model="dialogVisible"
      :title="dialogTitle"
      size="default"
      @submit="onSubmitForm">
      <XForm ref="formRef" v-model="formModel" :footer="false">
        <XField
          name="name"
          label="姓名"
          editor="text"
          :rules="[{ required: true, message: '请输入姓名' }]" />
        <XField name="email" label="邮箱" editor="text" />
        <XField
          name="status"
          label="状态"
          editor="select"
          :options="statusOptions" />
      </XForm>
    </XDialog>
  </XContainer>
</template>

<script setup lang="ts">
  import { ref } from 'vue';
  import { ElMessage, ElTag } from 'element-plus';
  import {
    XContainer,
    XHeader,
    XActionBar,
    XQueryForm,
    XGrid,
    XDialog,
    XForm,
    XField,
    type GridInstance
  } from '@vtj/ui';
  import { Add, Edit, Delete } from '@vtj/icons';
  import { request } from '@vtj/utils';

  // 状态定义
  const gridRef = ref<GridInstance>(null);
  const formRef = ref(null);
  const dialogVisible = ref(false);
  const dialogTitle = ref('');
  const queryModel = ref({});
  const formModel = ref({});
  const selectedRow = ref(null);

  // 操作栏配置
  const actions = ref([
    { name: 'add', label: '新增', icon: Add, type: 'primary' },
    {
      name: 'edit',
      label: '编辑',
      icon: Edit,
      disabled: () => !selectedRow.value
    },
    {
      name: 'delete',
      label: '删除',
      icon: Delete,
      disabled: () => !selectedRow.value
    }
  ]);

  // 查询配置
  const queryItems = ref([
    { name: 'keyword', label: '关键词', editor: 'text' },
    {
      name: 'status',
      label: '状态',
      editor: 'select',
      options: [
        { label: '启用', value: 1 },
        { label: '禁用', value: 0 }
      ]
    }
  ]);

  // 表格列配置
  const columns = ref([
    { field: 'name', title: '姓名', width: 150 },
    { field: 'email', title: '邮箱', width: 200 },
    { field: 'status', title: '状态', width: 100, slots: { default: 'status' } }
  ]);

  // 选项数据
  const statusOptions = [
    { label: '启用', value: 1 },
    { label: '禁用', value: 0 }
  ];

  // 数据加载
  const loadData = async (state) => {
    const res = await request({
      url: '/api/users',
      method: 'get',
      query: { ...queryModel.value, ...state }
    });
    return { list: res.list, total: res.total };
  };

  // 操作处理
  const onActionClick = (action) => {
    if (action.name === 'add') {
      dialogTitle.value = '新增用户';
      formModel.value = {};
      dialogVisible.value = true;
    } else if (action.name === 'edit') {
      dialogTitle.value = '编辑用户';
      formModel.value = { ...selectedRow.value };
      dialogVisible.value = true;
    } else if (action.name === 'delete') {
      ElMessage.success('删除成功');
    }
  };

  // 表单提交
  const onSubmitForm = async () => {
    await request({
      url: '/api/user',
      method: 'post',
      data: formModel.value
    });
    ElMessage.success('保存成功');
    dialogVisible.value = false;
    gridRef.value?.reload();
  };

  // 查询重置
  const onResetQuery = () => {
    queryModel.value = {};
    loadData();
  };

  // 单元格选中
  const onCellSelected = (params) => {
    selectedRow.value = params.row;
  };
</script>
```

---

## 八、常见问题

### 8.1 如何自定义 Grid 列渲染？

使用 `slots` 配置：

```vue
<XGrid :columns="columns" :loader="loadData">
  <template #customRender="{ row }">
    <span>{{ row.name }} - {{ row.status }}</span>
  </template>
</XGrid>

<script setup>
  const columns = [
    { field: 'name', title: '名称', slots: { default: 'customRender' } }
  ];
</script>
```

### 8.2 如何在 Field 中使用自定义编辑器？

通过 Adapter 注册：

```typescript
app.use(AdapterPlugin, {
  fieldEditors: {
    customEditor: {
      component: CustomEditor,
      defaultValue: ''
    }
  }
});
```

```vue
<XField name="custom" label="自定义" editor="customEditor" />
```

### 8.3 Dialog 如何获取内部组件实例？

通过 `componentInstance` 属性：

```vue
<XDialog
  v-model="visible"
  :content="DialogContent"
  :component-instance="dialogInstance" />

<script setup>
  const dialogInstance = ref(null);
</script>
```

---

## 九、总结

`@vtj/ui` 提供了一套完整的企业级业务组件库，核心特点：

1. **组件前缀统一**：所有组件以 `X` 开头，避免命名冲突
2. **基于成熟生态**：底层使用 Element Plus 和 VXE Table
3. **TypeScript 友好**：完整的类型定义支持
4. **适配器模式**：灵活的扩展机制
5. **Composition API**：全面支持 Vue 3 组合式 API

在编写代码时，请遵循 VTJ 代码规范，使用 Composition API 模式，按需导入组件，合理使用 Hooks 和 Adapter 系统。

<!-- ============================================ -->
<!-- 文档: chart.md -->
<!-- ============================================ -->

# @vtj/charts 图表库 — AI 使用指南

> 本文档帮助 AI 理解 `@vtj/charts` 图表库的构成、组件属性和在 Vue 组件中的使用规范。

---

## 一、库概述

`@vtj/charts` 是基于 ECharts 5.x 封装的 Vue 3 图表组件库，提供开箱即用的图表组件和组合式 API。

| 内容       | 说明                                          |
| ---------- | --------------------------------------------- |
| 基础依赖   | `echarts` `~5.6.0`                            |
| 核心组件   | `XChart`（通用图表）、`XMapChart`（地图图表） |
| 组合式 API | `useChart`、`useMapChart`                     |
| 自动特性   | 响应式 option、自动 resize、事件代理          |

---

## 二、安装与注册

### 2.1 安装依赖

```json
{
  "dependencies": {
    "@vtj/charts": "latest"
  }
}
```

### 2.2 全局注册（推荐）

```typescript
import { createApp } from 'vue';
import Charts from '@vtj/charts';

const app = createApp(App);
app.use(Charts); // 注册 XChart、XMapChart 为全局组件
```

全局注册后，模板中可直接使用 `<XChart>` 和 `<XMapChart>` 标签。

### 2.3 按需导入

```vue
<template>
  <XChart :option="chartOption" height="300px" />
</template>

<script setup>
  import { XChart } from '@vtj/charts';
  import type { ChartOption } from '@vtj/charts';

  const chartOption: ChartOption = {
    xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
    yAxis: { type: 'value' },
    series: [{ type: 'bar', data: [10, 20, 30] }]
  };
</script>
```

---

## 三、XChart 通用图表组件

### 3.1 Props

| 属性     | 类型            | 默认值      | 说明                       |
| -------- | --------------- | ----------- | -------------------------- |
| `width`  | `string`        | `'100%'`    | 图表容器宽度               |
| `height` | `string`        | `'400px'`   | 图表容器高度               |
| `option` | `EChartsOption` | `undefined` | ECharts 配置项，响应式更新 |

### 3.2 Exposed 属性

通过模板 ref 可访问以下属性：

| 名称        | 类型                         | 说明                  |
| ----------- | ---------------------------- | --------------------- |
| `elRef`     | `HTMLElement`                | 图表容器 DOM 引用     |
| `option`    | `EChartsOption`              | 当前 option           |
| `instance`  | `Ref<ECharts \| undefined>`  | ECharts 实例引用      |
| `getEChart` | `() => ECharts \| undefined` | 获取 ECharts 实例方法 |

### 3.3 事件

组件接收 ECharts 事件监听器，通过 Vue 的 `v-on` 或属性传递，支持以下格式：

- **kebab-case 事件名：** `:on-click="handler"`、`:on-dblclick="handler"`
- 引擎自动转换 kebab-case 事件名为 camelCase 后注册到 ECharts 实例

### 3.4 基础用法

```vue
<template>
  <XChart :option="chartOption" height="400px" :on-click="handleClick" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XChart } from '@vtj/charts';

  const chartOption = ref({
    tooltip: { trigger: 'axis' },
    legend: { data: ['销量'] },
    xAxis: {
      type: 'category',
      data: ['一月', '二月', '三月', '四月', '五月']
    },
    yAxis: { type: 'value' },
    series: [
      {
        name: '销量',
        type: 'bar',
        data: [120, 200, 150, 80, 70]
      }
    ]
  });

  const handleClick = (params) => {
    console.log('点击了', params.name, params.value);
  };
</script>
```

### 3.5 动态更新 Option

`option` 是响应式属性，变更后图表自动更新：

```vue
<template>
  <div>
    <el-button @click="switchData">切换数据</el-button>
    <XChart :option="chartOption" height="400px" />
  </div>
</template>

<script setup>
  import { ref } from 'vue';
  import { XChart } from '@vtj/charts';

  const dataA = [120, 200, 150, 80, 70];
  const dataB = [80, 150, 200, 120, 100];

  const chartOption = ref({
    xAxis: { type: 'category', data: ['一月', '二月', '三月', '四月', '五月'] },
    yAxis: { type: 'value' },
    series: [{ type: 'bar', data: dataA }]
  });

  const switchData = () => {
    chartOption.value.series[0].data =
      chartOption.value.series[0].data === dataA ? dataB : dataA;
  };
</script>
```

### 3.6 通过 ref 访问 ECharts 实例

```vue
<template>
  <XChart ref="chartRef" :option="chartOption" height="400px" />
  <el-button @click="showLoading">显示加载中</el-button>
</template>

<script setup>
  import { ref } from 'vue';
  import { XChart } from '@vtj/charts';

  const chartRef = ref();
  const chartOption = ref({
    xAxis: { type: 'category', data: ['A', 'B', 'C'] },
    yAxis: { type: 'value' },
    series: [{ type: 'line', data: [10, 20, 15] }]
  });

  const showLoading = async () => {
    const chart = chartRef.value?.getEChart();
    if (chart) {
      chart.showLoading();
      await new Promise((r) => setTimeout(r, 2000));
      chart.hideLoading();
    }
  };
</script>
```

---

## 四、XMapChart 地图图表组件

### 4.1 Props

| 属性      | 类型               | 默认值                                                            | 说明                                 |
| --------- | ------------------ | ----------------------------------------------------------------- | ------------------------------------ |
| `geoJson` | `string \| object` | `'https://unpkg.com/vtj-geojson@0.1.3/geo/100000/100000.geoJson'` | GeoJSON 数据或 URL，默认中国省级地图 |
| `name`    | `string`           | `'china'`                                                         | 地图注册名称                         |

其余 ECharts option 通过 `v-bind="$attrs"` 透传，需在 option 中配置地图相关的 series 或 geo。

### 4.2 Emits

| 事件名  | 参数      | 说明                   |
| ------- | --------- | ---------------------- |
| `ready` | `geoJSON` | 地图数据加载完成后触发 |

### 4.3 Exposed 属性

| 名称          | 类型                                | 说明                     |
| ------------- | ----------------------------------- | ------------------------ |
| `chartRef`    | `XChart 实例`                       | 内部 XChart 组件引用     |
| `instance`    | `ComputedRef<ECharts \| undefined>` | 计算属性，ECharts 实例   |
| `option`      | `Ref<any>`                          | 当前 option              |
| `getEChart`   | `() => ECharts \| undefined`        | 获取 ECharts 实例方法    |
| `getChartRef` | `() => XChart 实例`                 | 获取内部 XChart 组件引用 |
| `geoJSON`     | `ShallowRef<any>`                   | 已加载的 GeoJSON 数据    |

### 4.4 基础用法

提供 `geoJson` 数据源后，组件自动注册地图并通过 `v-bind="$attrs"` 透传的 option 渲染：

```vue
<template>
  <XMapChart
    :option="mapOption"
    name="china"
    height="500px"
    @ready="onMapReady" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XMapChart } from '@vtj/charts';

  const mapOption = ref({
    tooltip: {
      trigger: 'item',
      formatter: '{b}: {c}'
    },
    visualMap: {
      min: 0,
      max: 1000,
      left: 'left',
      top: 'bottom',
      text: ['高', '低'],
      inRange: { color: ['#e0ffff', '#006edd'] }
    },
    series: [
      {
        type: 'map',
        map: 'china',
        roam: true,
        label: { show: true },
        data: [
          { name: '广东省', value: 800 },
          { name: '浙江省', value: 600 },
          { name: '北京市', value: 900 },
          { name: '上海市', value: 950 },
          { name: '江苏省', value: 700 }
        ]
      }
    ]
  });

  const onMapReady = (geoJSON) => {
    console.log('地图数据加载完成', geoJSON);
  };
</script>
```

### 4.5 使用自定义 GeoJSON

```vue
<template>
  <XMapChart
    :geo-json="myGeoJson"
    name="myCity"
    :option="mapOption"
    height="500px" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XMapChart } from '@vtj/charts';

  const myGeoJson = ref({
    // 自定义 GeoJSON 对象
  });

  const mapOption = ref({
    series: [
      {
        type: 'map',
        map: 'myCity',
        data: [
          { name: '区域A', value: 100 },
          { name: '区域B', value: 200 }
        ]
      }
    ]
  });
</script>
```

### 4.6 使用 geo 组件而非 series-map

若配置 `option.geo` 而非 `series-map`，组件会自动将 `option.geo.map` 设为当前 `name`：

```vue
<template>
  <XMapChart :option="geoOption" name="china" height="500px" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XMapChart } from '@vtj/charts';

  const geoOption = ref({
    geo: {
      map: 'china',
      roam: true,
      label: { show: true },
      itemStyle: {
        areaColor: '#f3f3f3',
        borderColor: '#999'
      }
    },
    series: []
  });
</script>
```

---

## 五、组合式 API

### 5.1 useChart

用于在非组件场景下创建 ECharts 图表实例：

```typescript
import { ref, onMounted } from 'vue';
import { useChart } from '@vtj/charts';
import type { ChartOption } from '@vtj/charts';

// 模板中：<div ref="chartContainer" style="width:100%;height:400px;"></div>
const chartContainer = ref<HTMLElement>();
const chartOption = ref<ChartOption>({
  xAxis: { type: 'category', data: ['A', 'B', 'C'] },
  yAxis: { type: 'value' },
  series: [{ type: 'line', data: [10, 20, 15] }]
});

const { instance, getEChart } = useChart(chartContainer, chartOption, {});

onMounted(() => {
  console.log('ECharts 实例:', getEChart());
});
```

**参数说明：**

| 参数     | 类型                              | 说明                               |
| -------- | --------------------------------- | ---------------------------------- |
| `el`     | `MaybeRef<HTMLElement>`           | 图表容器元素引用                   |
| `option` | `Ref<EChartsOption \| undefined>` | ECharts option 响应式引用          |
| `attrs`  | `Record<string, any>`             | 事件监听器（如 `{ onClick: fn }`） |

**返回值：**

| 名称        | 类型                         | 说明                    |
| ----------- | ---------------------------- | ----------------------- |
| `instance`  | `Ref<ECharts \| undefined>`  | ECharts 实例            |
| `getEChart` | `() => ECharts \| undefined` | 获取 ECharts 实例的方法 |

**自动能力：**

- `onMounted` 时初始化 ECharts 实例并注册事件和 option
- `option` 变化时自动 `setOption`
- `useResizeObserver` 自动 resize（防抖 150ms）
- `onUnmounted` 时自动 dispose 实例

### 5.2 useMapChart

用于自定义地图场景：

```typescript
import { ref } from 'vue';
import { useMapChart } from '@vtj/charts';

const mapName = ref('china');
const geoJson = ref('https://unpkg.com/vtj-geojson@0.1.3/geo/100000/100000.geoJson');

const { chartRef, geoJSON, option } = useMapChart(mapName, geoJson, {
  option: {
    series: [{ type: 'map', map: 'china', data: [...] }]
  }
});
```

**参数说明：**

| 参数      | 类型                  | 说明                     |
| --------- | --------------------- | ------------------------ |
| `name`    | `Ref<string>`         | 地图注册名称             |
| `geoJson` | `Ref<any>`            | GeoJSON 数据或 URL       |
| `attrs`   | `Record<string, any>` | 透传属性（含 option 等） |

**返回值：**

| 名称       | 类型               | 说明                  |
| ---------- | ------------------ | --------------------- |
| `chartRef` | `Ref<XChart 实例>` | 内部 XChart 组件引用  |
| `geoJSON`  | `ShallowRef<any>`  | 已加载的 GeoJSON 数据 |
| `option`   | `Ref<any>`         | 合并后的 option       |

**自动能力：**

- name / geoJson 变化时自动重新加载和注册地图
- 加载过程中自动显示 / 隐藏 ECharts loading 状态
- 自动将 option 中的 series-map 和 geo.map 设为当前 name

---

## 六、VTJ Composition API 完整示例

### 6.1 通用图表 - 柱状图 + 折线图混合

```vue
<template>
  <div class="page">
    <div class="chart-header">
      <h3>{{ __i18n.t('chart.salesTitle') }}</h3>
      <el-radio-group v-model="chartType" @change="onTypeChange">
        <el-radio value="bar">柱状图</el-radio>
        <el-radio value="line">折线图</el-radio>
      </el-radio-group>
    </div>
    <XChart
      ref="chartRef"
      :option="chartOption"
      height="350px"
      :on-click="onChartClick" />
  </div>
</template>

<script setup>
  import { ref, computed } from 'vue';
  import { XChart } from '@vtj/charts';

  const chartRef = ref();
  const chartType = ref('bar');
  const categories = ['一月', '二月', '三月', '四月', '五月'];
  const chartData = [120, 200, 150, 80, 70];

  const chartOption = computed(() => ({
    tooltip: { trigger: 'axis' },
    legend: { data: ['销量'] },
    xAxis: { type: 'category', data: categories },
    yAxis: { type: 'value' },
    series: [
      {
        name: '销量',
        type: chartType.value,
        data: chartData,
        smooth: chartType.value === 'line',
        barWidth: '40%'
      }
    ]
  }));

  const onTypeChange = () => {
    // computed 自动响应，无需手动触发
  };

  const onChartClick = (params) => {
    console.log('点击图表:', params.name, params.value);
  };
</script>
```

### 6.2 饼图

```vue
<template>
  <XChart :option="pieOption" height="400px" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XChart } from '@vtj/charts';

  const pieOption = ref({
    tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
    legend: { orient: 'vertical', left: 'left' },
    series: [
      {
        type: 'pie',
        radius: ['40%', '70%'],
        avoidLabelOverlap: false,
        label: { show: false },
        emphasis: {
          label: { show: true, fontSize: 16, fontWeight: 'bold' }
        },
        data: [
          { value: 1048, name: '搜索引擎' },
          { value: 735, name: '直接访问' },
          { value: 580, name: '邮件营销' },
          { value: 484, name: '联盟广告' },
          { value: 300, name: '视频广告' }
        ]
      }
    ]
  });
</script>
```

### 6.3 地图图表

```vue
<template>
  <XMapChart :option="mapOption" name="china" height="500px" @ready="onReady" />
</template>

<script setup>
  import { ref } from 'vue';
  import { XMapChart } from '@vtj/charts';

  const mapOption = ref({
    tooltip: { trigger: 'item', formatter: '{b}: {c}' },
    visualMap: {
      min: 0,
      max: 1000,
      text: ['高', '低'],
      inRange: { color: ['#f5f5f5', '#006edd'] }
    },
    series: [
      {
        type: 'map',
        map: 'china',
        roam: true,
        selectedMode: 'single',
        label: { show: true },
        data: [
          { name: '广东省', value: 800, selected: true },
          { name: '浙江省', value: 600 },
          { name: '北京市', value: 900 }
        ]
      }
    ]
  });

  const onReady = (geoJSON) => {
    console.log('地图已加载');
  };
</script>
```

### 6.4 异步数据加载

```vue
<template>
  <XChart :option="chartOption" height="400px" v-loading="loading" />
</template>

<script setup>
  import { ref, onMounted } from 'vue';
  import { XChart } from '@vtj/charts';

  const loading = ref(true);
  const chartOption = ref({});

  const fetchData = async () => {
    loading.value = true;
    // 模拟异步请求
    const data = await new Promise((resolve) =>
      setTimeout(
        () =>
          resolve({
            categories: ['产品A', '产品B', '产品C', '产品D'],
            values: [320, 480, 200, 560]
          }),
        1000
      )
    );
    chartOption.value = {
      xAxis: { type: 'category', data: data.categories },
      yAxis: { type: 'value' },
      series: [{ type: 'bar', data: data.values }]
    };
    loading.value = false;
  };

  onMounted(() => {
    fetchData();
  });
</script>
```

---

## 七、图表类型快速参考

以下列出 ECharts 支持的常用图表配置模式，可直接在 `option.series` 中使用：

### 7.1 折线图

```typescript
{ type: 'line', data: [10, 20, 15], smooth: true, areaStyle: {} }
```

### 7.2 柱状图

```typescript
{ type: 'bar', data: [10, 20, 15], barWidth: '40%' }
```

### 7.3 饼图

```typescript
{ type: 'pie', data: [{ value: 100, name: 'A' }], radius: '50%' }
```

### 7.4 散点图

```typescript
{ type: 'scatter', data: [[10, 20], [15, 25], [20, 15]] }
```

### 7.5 雷达图

```typescript
{
  type: 'radar',
  data: [{ value: [80, 90, 70, 85], name: '预算分配' }]
}
// 需配合 radar 组件：
// radar: { indicator: [{ name: '销售额', max: 100 }, ...] }
```

### 7.6 地图

```typescript
{
  type: 'map',
  map: 'china',
  data: [{ name: '广东省', value: 800 }]
}
```

---

## 八、注意事项

1. **option 必须为响应式：** 推荐使用 `ref` 包裹 option 对象，使其变更时自动触发图表更新
2. **option 深度监听：** 引擎内部对 option 进行了 `{ deep: true }` 的 watch，修改深层属性也会触发更新
3. **高度设默认值：** `XChart` 默认高度 `400px`，务必根据需要显式设置 `height`
4. **地图需 GeoJSON：** `XMapChart` 内置默认中国省级地图 GeoJSON URL，使用其他地图需提供自定义 GeoJSON
5. **事件名 kebab-case：** 事件属性以 `:on-` 开头、kebab-case 命名，如 `:on-click="handler"`、`:on-dblclick="handler"`
6. **自动 resize：** 组件内部已集成 `ResizeObserver`，容器尺寸变化时图表自动 resize
7. **生命周期管理：** 组件卸载时自动 dispose ECharts 实例，无需手动清理
8. **类型导入：** 需要 ECharts 类型时可从 `@vtj/charts` 导入 `ChartOption` 和 `EChartsInstance`
9. **无需直接操作 echarts 实例：** 大多数场景通过 `:option` 响应式更新即可，避免直接调用 `setOption`
10. **无需耗时搜索 ECharts 配置：** ECharts 的 option 配置语法遵循官方的 ECharts Option 规范，上述示例覆盖了最常用的图表类型和模式

---

## 九、VTJ 设计器 Composition API 规范示例

> 以下示例展示在 VTJ 设计器中使用 Composition API（`<script setup>`）集成图表组件的完整写法。

### 9.1 通用图表 + 地图图表完整示例

- `XChart` 组件可实现折线图、柱状图、饼图等除地图之外的所有 ECharts 图表
- `XMapChart` 组件实现 GeoJSON 地图图表
- `echarts` 原始对象可直接从 `@vtj/charts` 导入使用
- 通过 `useProvider` 接入 VTJ 渲染引擎 Provider 上下文

```vue
<template>
  <div class="charts-page">
    <XChart
      width="100%"
      height="400px"
      :option="{
        xAxis: {
          type: 'category',
          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        },
        yAxis: { type: 'value' },
        series: [{ data: [150, 230, 224, 218, 135, 147, 260], type: 'line' }]
      }" />
    <XMapChart
      name="100000"
      geo-json="https://unpkg.com/vtj-geojson@0.1.3/geo/100000.geoJson"
      width="100%"
      height="400px"
      :option="{
        series: [{ data: [{ name: '广东省', value: 100 }], type: 'map' }],
        visualMap: {}
      }" />
  </div>
</template>

<script setup>
  import { reactive } from 'vue';
  import { useProvider } from '@vtj/renderer';
  import { XChart, XMapChart, echarts } from '@vtj/charts';

  const provider = useProvider({ id: 'charts', version: '' });
  const state = reactive({});

  // echarts 原始对象可从 @vtj/charts 导入，用于高级自定义操作
  console.log(echarts);
</script>
```

### 9.2 GeoJSON 地图层级路径说明

`XMapChart` 的 `geo-json` 属性支持按行政区划编码逐级获取 GeoJSON 数据，当更换 `geo-json` 路径时需同步更新 `name` 属性：

| 层级     | 示例 GeoJSON URL                                                          | `name` 值  | 说明             |
| -------- | ------------------------------------------------------------------------- | ---------- | ---------------- |
| 全国     | `https://unpkg.com/vtj-geojson@0.1.3/geo/100000.geoJson`                  | `'100000'` | 中国全国地图     |
| 省级     | `https://unpkg.com/vtj-geojson@0.1.3/geo/100000/440000.geoJson`           | `'440000'` | 广东省地图       |
| 市级     | `https://unpkg.com/vtj-geojson@0.1.3/geo/100000/440000/440100.geoJson`    | `'440100'` | 广州市地图       |
| 省级含子 | `https://unpkg.com/vtj-geojson@0.1.3/geo/100000/100000.geoJson`（默认值） | `'china'`  | 中国省级区划地图 |

```vue
<template>
  <div class="map-page">
    <!-- 全国地图 -->
    <XMapChart
      name="100000"
      geo-json="https://unpkg.com/vtj-geojson@0.1.3/geo/100000.geoJson"
      height="500px"
      :option="mapOption" />

    <!-- 广东省地图 -->
    <XMapChart
      name="440000"
      geo-json="https://unpkg.com/vtj-geojson@0.1.3/geo/100000/440000.geoJson"
      height="500px"
      :option="mapOption" />

    <!-- 广州市地图 -->
    <XMapChart
      name="440100"
      geo-json="https://unpkg.com/vtj-geojson@0.1.3/geo/100000/440000/440100.geoJson"
      height="500px"
      :option="mapOption" />
  </div>
</template>

<script setup>
  import { reactive } from 'vue';
  import { useProvider } from '@vtj/renderer';
  import { XMapChart } from '@vtj/charts';

  const provider = useProvider({ id: 'mapDemo', version: '' });

  const mapOption = {
    series: [
      {
        type: 'map',
        data: [
          { name: '广东省', value: 100 },
          { name: '浙江省', value: 200 },
          { name: '北京市', value: 300 }
        ]
      }
    ],
    visualMap: {
      min: 0,
      max: 300,
      text: ['高', '低'],
      inRange: { color: ['#e0ffff', '#006edd'] }
    }
  };
</script>
```

> **注意：** 以上示例中的 `state` 和 `provider` 为 VTJ 设计器组件的标准模板代码，请保留。echarts 原始对象按需导入使用。

<!-- ============================================ -->
<!-- 文档: coder.md -->
<!-- ============================================ -->

# VTJ Vue 代码编写约定

> 本文档描述 AI 大模型生成 Vue 组件代码时必须遵循的约定，确保输出内容能被 `@vtj/parser` 无损解析为 DSL。

---

## 一、基础结构约定

### 1.1 强制使用 `<script setup>`

所有组件**必须**使用 `<script setup>` 语法，**禁止**使用 Options API（`export default defineComponent({...})`）。

```vue
<!-- ✅ 正确 -->
<script setup>
  // 组件逻辑
</script>

<!-- ❌ 错误 -->
<script>
  export default defineComponent({
    setup() {}
  });
</script>
```

### 1.2 SFC 文件结构顺序

```vue
<template>
  <!-- 模板内容 -->
</template>

<script setup>
  // 脚本逻辑
</script>

<style scoped>
  /* 样式 */
</style>
```

---

## 二、`<script setup>` 内部约定

### 2.1 响应式状态声明

**主状态对象**必须命名为 `__state`，使用 `reactive()` 声明：

```js
// ✅ 正确：主状态对象固定命名为 __state
const __state = reactive({
  count: 0,
  name: '',
  list: []
});

// ✅ 正确：独立 ref 变量（非 __state 的补充）
const visible = ref(false);
const inputValue = ref('');

// ✅ 正确：其他 reactive 对象（非 __state）
const formData = reactive({ title: '', desc: '' });

// ❌ 错误：不得将主状态命名为其他名称
const state = reactive({ count: 0 }); // 错误，必须用 __state
const data = reactive({ count: 0 }); // 错误
```

### 2.2 Props 声明

必须使用 `defineProps()` 声明，**接收变量必须命名为 `__props`**：

```js
// ✅ 正确（运行时声明）
const __props = defineProps({
  title: { type: String, required: true },
  count: { type: Number, default: 0 }
});

// ✅ 正确（数组声明）
const __props = defineProps(['title', 'count']);

// ❌ 错误：变量名不得使用 props
const props = defineProps({ title: String });
```

### 2.3 Emits 声明

必须使用 `defineEmits()`，**接收变量必须命名为 `__emit`**：

```js
// ✅ 正确
const __emit = defineEmits(['update', 'close']);

// 调用示例
__emit('update', newValue);

// ❌ 错误：变量名不得使用 emit
const emit = defineEmits(['update']);
```

### 2.4 Expose 声明

使用 `defineExpose()` 暴露组件方法或属性：

```js
// ✅ 正确
defineExpose({
  open,
  close,
  getData
});
```

### 2.5 Computed 声明

使用顶层 `const xxx = computed(...)` 声明：

```js
// ✅ 正确
const fullName = computed(() => `${__state.firstName} ${__state.lastName}`);
const isValid = computed(() => __state.list.length > 0);
```

### 2.6 方法声明

使用箭头函数表达式或 `function` 关键字声明，**必须放在顶层**：

```js
// ✅ 正确（箭头函数）
const handleClick = () => {
  __state.count++;
};

const fetchData = async () => {
  const res = await __apis['getList']();
  __state.list = res.data?.data;
};

// ✅ 正确（function 声明）
function handleSubmit() {
  __emit('submit', __state.formData);
}

// ❌ 错误：不得在方法内嵌套定义其他核心方法
```

### 2.7 Watch 声明

使用顶层 `watch(...)` 调用：

```js
// ✅ 正确
watch(
  () => __state.count,
  (newVal, oldVal) => {
    console.log('count changed', newVal);
  }
);

watch(
  () => __props.title,
  (newVal) => {
    __state.localTitle = newVal;
  },
  { immediate: true }
);
```

### 2.8 生命周期钩子

使用标准 Composition API 生命周期钩子，**直接顶层调用**：

```js
// ✅ 正确
onMounted(() => {
  fetchData();
});

onBeforeUnmount(() => {
  cleanup();
});

onUpdated(() => {
  // 更新后逻辑
});
```

**支持的生命周期钩子列表：**

| 钩子名称          | 说明            |
| ----------------- | --------------- |
| `onBeforeMount`   | 挂载前          |
| `onMounted`       | 挂载后          |
| `onBeforeUpdate`  | 更新前          |
| `onUpdated`       | 更新后          |
| `onBeforeUnmount` | 卸载前          |
| `onUnmounted`     | 卸载后          |
| `onErrorCaptured` | 错误捕获        |
| `onActivated`     | keep-alive 激活 |
| `onDeactivated`   | keep-alive 停用 |

### 2.9 Inject / Provide

```js
// inject
const theme = inject('theme', 'light');
const config = inject('config');

// provide
provide('myValue', __state.count);
provide('getList', () => __state.list);
```

### 2.10 自定义 Composables

自定义 composable 函数必须满足 `/^use[A-Z]/` 命名规范（以 `use` 开头，后跟大写字母）：

```js
// ✅ 正确：从第三方库引入
import { useDark, useToggle } from '@vueuse/core';
const isDark = useDark();

// ✅ 正确：从本地文件引入
import { useTable } from '../composables/useTable';
const { tableData, loading, fetchTable } = useTable();

// ❌ 错误：命名不符合规范
const myHook = myCustomHook(); // 不以 use 开头
```

---

## 三、全局 API 使用约定

`@vtj/parser` 内置了一套全局 API 映射机制，以下 API 在 Composition 模式下**有固定的写法**，不得随意命名。

### 3.1 全局 API 变量命名表

以下全局变量为**系统保留变量**，固定由框架自动生成，**AI 生成代码时不得自行声明**，可以直接在方法和表达式中使用：

| DSL 中的引用        | Composition 代码中的变量        | 来源                                |
| ------------------- | ------------------------------- | ----------------------------------- |
| `this.state`        | `__state`                       | 系统保留（reactive对象）            |
| `this.$props`       | `__props`                       | `defineProps()` 接收变量            |
| `this.$emit`        | `__emit`                        | `defineEmits()` 接收变量            |
| `this.$attrs`       | `__attrs`                       | `const __attrs = useAttrs()`        |
| `this.$slots`       | `__slots`                       | `const __slots = useSlots()`        |
| `this.$nextTick`    | `nextTick`                      | 从 `vue` 导入                       |
| `this.$watch`       | `watch`                         | 从 `vue` 导入                       |
| `this.$refs`        | `__instance.proxy.$refs`        | `getCurrentInstance()`              |
| `this.$el`          | `__instance.proxy.$el`          | `getCurrentInstance()`              |
| `this.$parent`      | `__instance.proxy.$parent`      | `getCurrentInstance()`              |
| `this.$root`        | `__instance.proxy.$root`        | `getCurrentInstance()`              |
| `this.$options`     | `__instance.proxy.$options`     | `getCurrentInstance()`              |
| `this.$forceUpdate` | `__instance.proxy.$forceUpdate` | `getCurrentInstance()`              |
| `this.$router`      | `__router`                      | `const __router = useRouter()`      |
| `this.$route`       | `__route`                       | `const __route = useRoute()`        |
| `this.$i18n`        | `__i18n`                        | `const __i18n = useI18n()`          |
| `this.$t`           | `__i18n.t`                      | `const __i18n = useI18n()`          |
| `this.$n`           | `__i18n.n`                      | `const __i18n = useI18n()`          |
| `this.$d`           | `__i18n.d`                      | `const __i18n = useI18n()`          |
| `this.$store`       | `__store`                       | `useStore()` from `@vtj/renderer`   |
| `this.$pinia`       | `__pinia`                       | `usePinia()` from `@vtj/renderer`   |
| `this.$request`     | `__request`                     | `useRequest()` from `@vtj/renderer` |
| `this.$libs`        | `__libs`                        | `useLibs()` from `@vtj/renderer`    |
| `this.$access`      | `__access`                      | `useAccess()` from `@vtj/renderer`  |
| `this.$apis`        | `__apis`                        | `useApis()` from `@vtj/renderer`    |

### 3.2 Element Plus UI 库 API

使用 Element Plus 时，以下 API 有固定写法：

| DSL 中的引用       | Composition 代码写法        |
| ------------------ | --------------------------- |
| `this.$message`    | `ElMessage(...)`            |
| `this.$notify`     | `ElNotification(...)`       |
| `this.$messageBox` | `ElMessageBox(...)`         |
| `this.$confirm`    | `ElMessageBox.confirm(...)` |
| `this.$prompt`     | `ElMessageBox.prompt(...)`  |
| `this.$loading`    | `ElLoading.service(...)`    |

### 3.3 Ant Design Vue UI 库 API

使用 Ant Design Vue 时：

| DSL 中的引用         | Composition 代码写法   |
| -------------------- | ---------------------- |
| `this.$message`      | `message(...)`         |
| `this.$confirm`      | `Modal.confirm(...)`   |
| `this.$notification` | `notification(...)`    |
| `this.$info`         | `message.info(...)`    |
| `this.$success`      | `message.success(...)` |
| `this.$warning`      | `message.warning(...)` |
| `this.$error`        | `message.error(...)`   |

### 3.4 禁止使用的系统内部变量

以下以 `__` 开头的变量为系统内部保留变量，**AI 生成的代码不得声明或使用**（框架会自动处理）：

- `__instance`（`getCurrentInstance()` 结果）
- `__provider`（内部数据源 provider）
- 任何以 `__` 开头且不在上述全局 API 表中列出的变量

---

## 四、数据源约定

数据源方法（API 调用）必须通过 `__provider.apis` 或 `__provider.createMock` 调用，parser 会识别这种模式并提取为 DSL 的 `dataSources` 字段：

### 4.1 API 数据源

通过 `__provider.apis` 调用已注册的 API 接口：

```ts
// ✅ 正确：API 数据源
const getUserList = async () => {
  return __provider.apis['getUserList']().then((res: any) => {
    return res.data?.data;
  });
};

// ✅ 正确：带参数的 API 调用
const getUserById = async (id: string) => {
  return __provider.apis['getUserById']({ id }).then((res: any) => {
    return res.data?.data;
  });
};
```

### 4.2 Mock 数据源

通过 `__provider.createMock` 创建模拟数据源，支持传入 mockTemplate 函数：

#### 方法签名

```ts
__provider.createMock(mockTemplate?: (params?: any) => Promise<any> | any): Promise<any>
```

**参数说明：**

- `mockTemplate`（可选）：mock 数据模板函数，返回 mock.js 格式的模板对象
- 返回值：返回 Promise，resolve 后得到 mock 生成的数据

#### 使用示例

```ts
// ✅ 正确：无参数的 Mock 数据源
const getMockData = async () => {
  return __provider.createMock().then((res: any) => {
    return res;
  });
};

// ✅ 正确：带 mockTemplate 的 Mock 数据源
const getUserMockData = async () => {
  return __provider
    .createMock((params) => ({
      'id|1-100': 1,
      name: '@cname',
      email: '@email',
      'age|18-60': 18
    }))
    .then((res: any) => {
      return res;
    });
};

// ✅ 正确：根据参数动态生成 mock 数据
const getListMockData = async (count: number = 10) => {
  return __provider
    .createMock((params) => ({
      'list|10': [
        {
          'id|+1': 1,
          title: '@ctitle(5, 10)',
          'status|1': ['pending', 'active', 'completed']
        }
      ]
    }))
    .then((res: any) => {
      return res.list;
    });
};

// ✅ 正确：异步 mockTemplate
const getAsyncMockData = async () => {
  return __provider
    .createMock(async (params) => {
      // 可以在这里执行异步逻辑
      const template = {
        timestamp: () => Date.now(),
        'data|5': [
          {
            id: '@id',
            value: '@integer(100, 1000)'
          }
        ]
      };
      return template;
    })
    .then((res: any) => {
      return res;
    });
};
```

#### Mock 模板语法

`mockTemplate` 函数返回的对象遵循 [mock.js](https://github.com/nuysoft/Mock) 语法规范：

- `'key|min-max': value` - 生成 min 到 max 个重复项
- `'key|count': value` - 生成 count 个重复项
- `'key|+1': value` - 自动递增
- `'@cname'` - 生成中文姓名
- `'@email'` - 生成邮箱
- `'@ctitle(5, 10)'` - 生成 5-10 个中文字符的标题
- `'@integer(100, 1000)'` - 生成 100-1000 的整数
- 更多语法参考 [mock.js 文档](https://github.com/nuysoft/Mock/wiki)

#### 注意事项

1. **必须使用 `__provider.createMock`**，不得直接调用 `Mock.mock()`
2. **mockTemplate 必须返回对象**，不得返回其他类型
3. **异步处理**：如果 mockTemplate 是异步函数，必须使用 `async/await` 或返回 Promise
4. **错误处理**：mockTemplate 执行异常会被捕获并输出警告，不会影响主流程
5. **数据转换**：建议在 `.then()` 中对 mock 数据进行二次处理（如提取特定字段）

---

## 五、模板约定

### 5.1 组件标签命名

- **自定义组件**必须使用 **PascalCase**（大驼峰）：`<MyComponent />`、`<ElButton />`
- **HTML 原生标签**必须使用**小写**：`<div>`、`<span>`、`<input />`
- **不得**将 HTML 标签写成大驼峰或小驼峰

```vue
<!-- ✅ 正确 -->
<template>
  <div class="container">
    <ElButton @click="handleClick">点击</ElButton>
    <MyForm :data="__state.formData" />
  </div>
</template>

<!-- ❌ 错误 -->
<template>
  <Div class="container">
    <!-- HTML 标签不能大写 -->
    <el-button @click="handleClick"> <!-- 组件建议用 PascalCase --> </el-button>
  </Div>
</template>
```

### 5.2 指令使用规范

**v-if / v-else-if / v-else：**

- `v-if` 与 `v-else`/`v-else-if` 的兄弟节点必须保持连续，中间不得插入其他节点
- `v-if` 与 `v-for` 不建议用在同一个节点上，优先使用 `v-for` 的外层包裹

**v-for：**

- 必须提供 `:key` 属性
- 当 `v-for` 的直接子节点只有一个 ELEMENT 时，直接写在该元素上；多子节点则需要用 `<span>` 或 `<div>` 包裹

```vue
<!-- ✅ 正确 -->
<ul>
  <li v-for="item in __state.list" :key="item.id">{{ item.name }}</li>
</ul>

<!-- ✅ 正确（多子节点使用包裹元素） -->
<template>
  <span v-for="item in __state.list" :key="item.id">
    <span>{{ item.name }}</span>
    <span>{{ item.value }}</span>
  </span>
</template>
```

### 5.3 事件绑定

事件处理函数直接引用方法名或内联箭头函数：

```vue
<!-- ✅ 正确 -->
<button @click="handleClick">点击</button>
<input @input="(e) => (__state.value = e.target.value)" />

<!-- ✅ 正确（带参数） -->
<li
  v-for="item in __state.list"
  :key="item.id"
  @click="handleItemClick(item)"></li>
```

### 5.4 动态绑定

```vue
<!-- ✅ 正确：绑定响应式数据 -->
<div :class="__state.className">
<input :value="__state.inputVal" />
<MyComp :title="__props.title" :count="fullName" />

<!-- ✅ 正确：绑定 ref -->
<div :style="{ color: isDark.value ? 'white' : 'black' }">
```

### 5.5 Slot 插槽

```vue
<!-- 父组件：传入插槽 -->
<template>
  <MyDialog>
    <template #header>
      <h2>标题</h2>
    </template>
    <template #default="{ row }">
      <span>{{ row.name }}</span>
    </template>
  </MyDialog>
</template>

<!-- 子组件：定义插槽 -->
<template>
  <div>
    <slot name="header" />
    <slot :row="currentRow" />
  </div>
</template>
```

---

## 六、Import 导入约定

### 6.1 Vue 核心 API 导入

```js
import {
  ref,
  reactive,
  computed,
  watch,
  onMounted,
  inject,
  provide,
  nextTick
} from 'vue';
```

### 6.2 Vue Router 导入

```js
import { useRouter, useRoute } from 'vue-router';
const __router = useRouter();
const __route = useRoute();
```

### 6.3 vue-i18n 导入

```js
import { useI18n } from 'vue-i18n';
const __i18n = useI18n();
// 使用：__i18n.t('key')
```

### 6.4 组件导入命名

- 导入组件时，使用与 UI 库文档一致的 **PascalCase** 名称
- 如需别名（如 UI 库命名冲突），使用 `as` 语法：`import { Button as AButton } from 'ant-design-vue'`
- parser 会自动识别 `as` 别名，将本地名作为组件标签名

```js
// ✅ 正确
import { ElButton, ElForm, ElTable } from 'element-plus';
import { Button as AButton, Table as ATable } from 'ant-design-vue';

// 模板中使用本地名
// <AButton> 而非 <Button>
```

### 6.5 UI 组件 parent 子组件声明

当使用 UI 组件的子组件（如 `AButton.Group`）时，必须显式声明：

```js
import { Button as AButton } from 'ant-design-vue';
const AButtonGroup = AButton.Group; // ✅ 派生声明，parser 会识别并跳过
```

---

## 七、CSS / Style 约定

### 7.1 scoped 样式

优先使用 `<style scoped>` 避免样式污染：

```vue
<style scoped>
  .container {
    padding: 16px;
  }
  .title {
    font-size: 18px;
    color: #333;
  }
</style>
```

### 7.2 动态 class

```vue
<!-- ✅ 字符串表达式 -->
<div :class="__state.isActive ? 'active' : ''">

<!-- ✅ 对象语法 -->
<div :class="{ active: __state.isActive, disabled: !__state.enabled }">

<!-- ✅ 数组语法 -->
<div :class="[__state.baseClass, __state.extraClass]">
```

---

## 八、禁止事项汇总

以下行为会导致 parser 无法正确解析，**严格禁止**：

1. **禁止使用 Options API**，不得出现 `export default { data(), methods:{}, ...}` 结构
2. **禁止手动声明系统内部变量**（`__instance`、`__provider` 等双下划线保留变量）
3. **禁止将 `defineProps` 的接收变量命名为非 `__props`**（如 `props`、`myProps`）
4. **禁止将 `defineEmits` 的接收变量命名为非 `__emit`**（如 `emit`、`emits`）
5. **禁止将主状态 `reactive` 对象命名为非 `__state`**（如 `state`、`data`）
6. **禁止编写"游离顶层语句"**（无法归类到 ref/reactive/computed/method/watch/lifecycle 的顶层语句），如需初始化逻辑，请放在 `onMounted` 中或在声明时赋初值
7. **避免在 `<script setup>` 顶层使用 `await`**（会触发 parser 生成 setupStatements，将 setup 包装为 async 函数）
8. **禁止自行调用全局 composable**（`useRouter`、`useI18n`、`useStore`、`useApis` 等），这些由框架根据 DSL 自动生成
9. **禁止 HTML 原生标签使用 PascalCase**（如 `<Div>`、`<Span>`）

---

## 九、完整示例

```vue
<template>
  <div class="user-list">
    <h2>{{ __state.title }}</h2>
    <ElButton @click="loadData" :loading="__state.loading">刷新</ElButton>
    <ul>
      <li
        v-for="user in __state.users"
        :key="user.id"
        @click="handleUserClick(user)">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
    <ElDialog v-model="dialogVisible" :title="__state.dialogTitle">
      <p>{{ __state.selectedUser?.name }}</p>
    </ElDialog>
  </div>
</template>

<script setup>
  import { ref, reactive, computed, onMounted } from 'vue';
  import { ElButton, ElDialog } from 'element-plus';

  // Props
  const __props = defineProps({
    pageSize: Number
  });

  // Emits
  const __emit = defineEmits(['select']);

  // 主状态
  const __state = reactive({
    title: '用户列表',
    loading: false,
    users: [],
    selectedUser: null,
    dialogTitle: ''
  });

  // 独立 ref
  const dialogVisible = ref(false);

  // Computed
  const hasUsers = computed(() => __state.users.length > 0);

  // 方法
  const loadData = async () => {
    __state.loading = true;
    try {
      const res = await __apis['getUserList']({
        pageSize: __props.pageSize || 10
      });
      __state.users = res.data?.data || [];
    } finally {
      __state.loading = false;
    }
  };

  const handleUserClick = (user) => {
    __state.selectedUser = user;
    __state.dialogTitle = `用户：${user.name}`;
    dialogVisible.value = true;
    __emit('select', user);
  };

  // 生命周期
  onMounted(() => {
    loadData();
  });
</script>

<style scoped>
  .user-list {
    padding: 16px;
  }
</style>
```

<!-- ============================================ -->
<!-- 文档: uni.md -->
<!-- ============================================ -->

# VTJ UniApp 全局配置 — AI 配置指南

> 本文档帮助 AI 理解 UniApp 平台的全局配置项，以及如何通过工具方法进行配置。

---

## 一、配置数据流

```
设计器 UniApp 配置面板             项目数据                      渲染引擎
uni-config/index.vue            project.uniConfig              uni/setup/app.ts
  ├── manifest.json ──┐         ┌── manifestJson ──→ injectUniConfig() → window.__uniConfig
  ├── pages.json ─────┤ setUni  │── pagesJson    ──→ injectUniRoutes() → window.__uniRoutes
  ├── CSS ────────────┤ Config  │── css          ──→ injectUniCSS()   → adoptedStyleSheets
  └── 生命周期 ───────┘         └── onXxx        ──→ createUniAppComponent() → App 组件方法

仅 UniApp 平台有效：
  项目 platform === 'uniapp' 时，配置才会在运行时生效
```

---

## 二、UniConfig 完整字段

```typescript
interface UniConfig {
  manifestJson?: Record<string, any>; // manifest.json 应用配置（JSON 对象）
  pagesJson?: Record<string, any>; // pages.json 页面配置（JSON 对象）
  css?: string; // 全局 CSS 样式字符串

  // ---- 应用生命周期函数 ----
  onLaunch?: JSFunction; // 初始化完成时触发（全局只触发一次）
  onShow?: JSFunction; // 启动或从后台进入前台时触发
  onHide?: JSFunction; // 从前台进入后台时触发
  onError?: JSFunction; // 报错时触发
  onPageNotFound?: JSFunction; // 页面不存在监听
  onUnhandledRejection?: JSFunction; // 未处理的 Promise 拒绝监听（2.8.1+）
  onUniNViewMessage?: JSFunction; // nvue 页面数据通信监听
  onThemeChange?: JSFunction; // 系统主题变化监听
  onLastPageBackPress?: JSFunction; // 最后一个页面按 Android back 键
  onExit?: JSFunction; // 应用退出监听
}
```

---

## 三、配置工具方法

### 3.1 `setUniConfig` — 设置 UniApp 配置

**参数：**

| 参数    | 类型   | 必填 | 说明                                     |
| ------- | ------ | ---- | ---------------------------------------- |
| `key`   | string | ✅   | 配置类型名，必须从枚举值中取（详见下方） |
| `value` | string | ✅   | 配置内容，格式取决于 key                 |

**`key` 可选枚举值（共 13 项）：**

| key                    | value 格式        | 说明                   |
| ---------------------- | ----------------- | ---------------------- |
| `manifestJson`         | JSON 字符串       | manifest.json 应用配置 |
| `pagesJson`            | JSON 字符串       | pages.json 页面配置    |
| `css`                  | CSS 代码字符串    | 全局样式               |
| `onLaunch`             | JS 函数代码字符串 | 应用初始化完成         |
| `onShow`               | JS 函数代码字符串 | 应用进入前台           |
| `onHide`               | JS 函数代码字符串 | 应用进入后台           |
| `onError`              | JS 函数代码字符串 | 应用报错               |
| `onPageNotFound`       | JS 函数代码字符串 | 页面不存在             |
| `onUnhandledRejection` | JS 函数代码字符串 | 未处理 Promise 拒绝    |
| `onThemeChange`        | JS 函数代码字符串 | 主题变化               |
| `onUniNViewMessage`    | JS 函数代码字符串 | nvue 数据通信          |
| `onExit`               | JS 函数代码字符串 | 应用退出               |

**value 格式规则：**

- `manifestJson` / `pagesJson`：JSON 字符串，如 `'{"appid":"xxx","name":"MyApp"}'`
- `css`：CSS 代码字符串，如 `'body { background: #f5f5f5; }'`
- 生命周期：JS 函数代码块，默认模板 `() => { }`
- 传 `null`：清除该配置项

**示例：**

```json
// 配置 manifest.json
{
  "action": "setUniConfig",
  "parameters": [
    "manifestJson",
    "{\"appid\":\"__UNI__123456\",\"name\":\"我的应用\",\"versionName\":\"1.0.0\"}"
  ]
}

// 配置 pages.json（含 tabBar）
{
  "action": "setUniConfig",
  "parameters": [
    "pagesJson",
    "{\"globalStyle\":{\"navigationBarTitleText\":\"首页\"},\"tabBar\":{\"list\":[{\"pagePath\":\"/\",\"text\":\"首页\"}]}}"
  ]
}

// 配置全局 CSS
{
  "action": "setUniConfig",
  "parameters": [
    "css",
    "body { margin: 0; padding: 0; font-family: sans-serif; }"
  ]
}

// 配置 onLaunch 生命周期
{
  "action": "setUniConfig",
  "parameters": [
    "onLaunch",
    "() => {\n  console.log('App 启动', uni.getLaunchOptionsSync())\n}"
  ]
}
```

### 3.2 `getUniConfig` — 获取 UniApp 配置

**参数：** `key: string` — 配置类型名（枚举值同 `setUniConfig`）

**返回：**

- `manifestJson` / `pagesJson`：格式化的 JSON 字符串
- `css`：CSS 字符串
- 生命周期：函数代码字符串

---

## 四、配置详解

### 4.1 `manifestJson` — 应用配置

对应 uni-app 的 `manifest.json`。运行时被解析后注入到 `window.__uniConfig`。

**关键字段参考：**

| 字段             | 说明                                    |
| ---------------- | --------------------------------------- |
| `appid`          | 应用 ID                                 |
| `name`           | 应用名称                                |
| `versionName`    | 版本名称                                |
| `versionCode`    | 版本号                                  |
| `h5.router`      | H5 路由配置（mode、base）               |
| `h5.async`       | 异步组件配置（loading、error、timeout） |
| `networkTimeout` | 网络超时配置                            |
| `h5`             | 其他 H5 平台相关配置                    |

### 4.2 `pagesJson` — 页面配置

对应 uni-app 的 `pages.json`。影响全局样式、tabBar、easycom 等。

**关键字段参考：**

| 字段          | 说明                                      |
| ------------- | ----------------------------------------- |
| `globalStyle` | 全局页面样式（navigationBarTitleText 等） |
| `tabBar`      | 底部 tabBar 配置（list、color 等）        |
| `easycom`     | 自动导入组件配置                          |

**注意：** `pagesJson` 中的 `pages` 字段**不需要手动配置**，VTJ 会根据 `project.pages` 自动生成路由。

### 4.3 `css` — 全局样式

注入为全局 CSS（通过 adoptedStyleSheets），影响所有页面。适用于定义全局字体、重置样式、CSS 变量等。

### 4.4 应用生命周期

对应 uni-app 的 `App.vue` 组件方法。配置的函数将在应用级别的对应生命周期触发。

| 生命周期               | 触发时机               | 参数说明            |
| ---------------------- | ---------------------- | ------------------- |
| `onLaunch`             | 初始化完成（全局一次） | launchOptions       |
| `onShow`               | 启动 / 后台进入前台    | launchOptions       |
| `onHide`               | 前台进入后台           | 无                  |
| `onError`              | 报错时                 | error               |
| `onPageNotFound`       | 页面路由不存在         | { path, query }     |
| `onUnhandledRejection` | 未处理 Promise 拒绝    | { reason, promise } |
| `onThemeChange`        | 系统主题变化           | { theme }           |
| `onUniNViewMessage`    | nvue 数据通讯          | data                |
| `onExit`               | 应用退出               | 无                  |

**生命周期函数示例：**

```javascript
// onShow — 监听应用进入前台
() => {
  const options = uni.getLaunchOptionsSync();
  console.log('App Show', options);
  // 检查登录状态
  // const token = uni.getStorageSync('token')
  // if (!token) uni.reLaunch({ url: '/pages/login' })
};

// onError — 全局错误捕获
() => {
  console.error('应用错误:', arguments);
  // 上报错误到监控平台
};
```

---

## 五、运行时处理流程

```
setupUniApp(opts)
  ├── injectUniFeatures(opts, window)      → 注入 UniApp 特性
  ├── injectUniConfig(opts, window)        → 解析 manifest.json → window.__uniConfig
  ├── injectUniGlobal(UniH5, window)       → 注入 UniApp 全局 API
  ├── injectUniRoutes(Vue, UniH5, ...)     → 根据 pages + pagesJson 生成路由
  ├── injectUniCSS(appid, css, window)     → 注入全局 CSS
  ├── Vue.createApp(setupApp(App))         → 创建 App 实例
  ├── app.use(install, UniH5)              → 安装 UniApp 插件
  └── app.use(plugin)                      → 安装 H5 插件
```

---

## 六、注意事项

1. **仅 UniApp 平台有效：** 只有 `project.platform === 'uniapp'` 时，这些配置才会被加载和执行
2. **`pagesJson` 不配置 pages 字段：** VTJ 根据项目页面树自动生成路由，`pagesJson` 中只需配置 `globalStyle`、`tabBar` 等全局项
3. **manifestJson 影响运行时行为：** 如 `h5.router.mode` 决定路由模式（hash/history），`networkTimeout` 决定请求超时
4. **生命周期函数格式：** 必须是箭头函数或普通函数代码字符串，框架通过 `new Function()` 动态解析
5. **配置为 null 即清除：** 传 `value: null` 可清除对应配置项
6. **CSS 为全局注入：** 通过 adoptedStyleSheets 注入，不要在其中定义页面特定的样式
7. **manifestJson / pagesJson 需完整 JSON：** 每次设置会覆盖对应字段的全部内容，如需部分修改，应先 `getUniConfig` 读取再合并设置
8. **tabBar 来自 pagesJson：** tabBar 配置在 `pagesJson.tabBar` 中，各页面的 `style` 会与路由合并

<!-- ============================================ -->
<!-- 文档: utils.md -->
<!-- ============================================ -->

# VTJ 工具库（@vtj/utils）— AI 使用指南

> 本文档帮助 AI 理解如何使用 `@vtj/utils` 工具库中的各类工具方法，在组件中实现网络请求、数据存储、URL 处理、文件下载等常见功能。

---

## 一、工具库概览

`@vtj/utils` 是 VTJ 平台的通用工具库，提供以下核心功能模块：

| 模块           | 功能                                             | 导入方式                                                               |
| -------------- | ------------------------------------------------ | ---------------------------------------------------------------------- |
| **request**    | HTTP 请求封装（基于 axios）                      | `import { request, createApi, createApis, useApi } from '@vtj/utils'`  |
| **storage**    | 本地存储（localStorage/sessionStorage/内存缓存） | `import { storage } from '@vtj/utils'`                                 |
| **cookie**     | Cookie 操作                                      | `import { cookie } from '@vtj/utils'`                                  |
| **url**        | URL 解析与拼接                                   | `import { url } from '@vtj/utils'`                                     |
| **download**   | 文件下载                                         | `import { downloadUrl, downloadBlob, downloadJson } from '@vtj/utils'` |
| **logger**     | 日志打印                                         | `import { logger } from '@vtj/utils'`                                  |
| **loadScript** | 动态加载外部脚本                                 | `import { loadScript } from '@vtj/utils'`                              |
| **jsonp**      | JSONP 跨域请求                                   | `import { jsonp } from '@vtj/utils'`                                   |
| **client**     | 客户端环境检测                                   | `import { getClientInfo } from '@vtj/utils'`                           |
| **raf**        | requestAnimationFrame 封装                       | `import { rAF, cAF } from '@vtj/utils'`                                |
| **util**       | 通用工具函数                                     | `import { isClient, fileToBase64, formDataToJson } from '@vtj/utils'`  |

---

## 二、HTTP 请求模块（request）

### 2.1 核心 API

| 方法                             | 说明                           | 返回值                             |
| -------------------------------- | ------------------------------ | ---------------------------------- |
| `request(config)`                | 发送 HTTP 请求                 | `Promise<R>`                       |
| `createApi(url \| config, req?)` | 创建单个 API 函数              | `(data?, opts?) => Promise<R>`     |
| `createApis(map, req?)`          | 批量创建 API 函数              | `Record<string, Function>`         |
| `useApi(loader, transform?)`     | Composition API 风格的请求钩子 | `{ data, error, loading, reload }` |

### 2.2 request 实例方法

```javascript
request.send(config)        // 发送请求
request.cancel(id?)         // 取消请求（不传 id 取消全部）
request.setConfig(options)  // 修改全局配置
request.useRequest(fn)      // 请求拦截器
request.useResponse(fn)     // 响应拦截器
```

### 2.3 请求配置（IRequestConfig）

```javascript
{
  url: string,              // 请求地址
  method: string,           // 请求方法：get/post/put/patch/delete
  settings: {
    type: 'form' | 'json' | 'data',  // 数据格式（默认 form）
    loading: boolean,                // 是否显示 loading
    showLoading: () => void,         // 自定义显示 loading
    hideLoading: () => void,         // 自定义隐藏 loading
    failMessage: boolean,            // 是否显示失败提示
    showError: (msg, e) => void,     // 自定义错误提示
    originResponse: boolean,         // 返回原始 axios 响应
    headers: object | function,      // 自定义请求头
    injectHeaders: boolean,          // 是否注入自定义请求头
    proxy: boolean,                  // 是否开启代理
    proxyPath: string                // 代理服务路径
  },
  query: object             // URL path 参数对象（用于路径参数替换）
}
```

### 2.4 使用示例

#### 方式一：直接使用 request

```javascript
import { request } from '@vtj/utils';

// GET 请求
const res = await request({
  url: '/api/users',
  method: 'get',
  settings: {
    type: 'json',
    loading: true
  }
});

// POST 请求（form 格式）
const res = await request({
  url: '/api/users',
  method: 'post',
  data: { name: '张三', age: 25 }
});

// POST 请求（JSON 格式）
const res = await request({
  url: '/api/users',
  method: 'post',
  settings: { type: 'json' },
  data: { name: '张三', age: 25 }
});

// 带路径参数（query 用于替换 URL 中的 :id）
const res = await request({
  url: '/api/users/:id',
  method: 'get',
  query: { id: 123 }
});
// 实际请求：/api/users/123
```

#### 方式二：使用 createApi（推荐）

```javascript
import { createApi } from '@vtj/utils';

// 创建 API 函数
const getUserList = createApi('/api/users');
const getUserById = createApi('/api/users/:id');
const createUser = createApi({
  url: '/api/users',
  method: 'post',
  settings: { type: 'json' }
});

// 调用 API
const list = await getUserList();
const user = await getUserById(null, { query: { id: 123 } });
const newUser = await createUser({ name: '张三' });
```

#### 方式三：使用 createApis 批量创建

```javascript
import { createApis } from '@vtj/utils';

const __apis = createApis({
  getUserList: '/api/users',
  getUserById: '/api/users/:id',
  createUser: {
    url: '/api/users',
    method: 'post',
    settings: { type: 'json' }
  },
  updateUser: {
    url: '/api/users/:id',
    method: 'put',
    settings: { type: 'json' }
  },
  deleteUser: {
    url: '/api/users/:id',
    method: 'delete'
  }
});

// 在组件中使用
onMounted(async () => {
  const res = await __apis.getUserList();
  __state.userList = res.data?.data || [];
});

// 带路径参数调用
await __apis.getUserById(null, { query: { id: 123 } });

// 带请求体调用
await __apis.createUser({ name: '张三', age: 25 });
```

#### 方式四：使用 useApi 钩子

```javascript
import { useApi, createApi } from '@vtj/utils';

const getUserList = createApi('/api/users');

// 自动加载数据
const { data, error, loading, reload } = useApi(getUserList());

// 带数据转换
const { data } = useApi(getUserList(), (res) => res.data?.list || []);
```

### 2.5 params 与 query 的职责区分

> ⚠️ **重要规范：** 在 VTJ 的请求体系中，`params` 和 `query` 有明确的语义区分：

| 参数类型   | 用途                                 | 位置         | 示例                                |
| ---------- | ------------------------------------ | ------------ | ----------------------------------- |
| **query**  | URL **路径参数**（Path Parameters）  | URL 路径中   | `/api/users/:id` → `/api/users/123` |
| **params** | URL **查询参数**（Query Parameters） | URL `?` 后面 | `/api/users?page=1&size=10`         |

**正确使用方式：**

```javascript
// ✅ query 用于路径参数替换
await request({
  url: '/api/users/:id',
  query: { id: 123 }
});
// 实际请求：GET /api/users/123

// ✅ data 中的字段会自动成为查询参数（GET 请求）
await request({
  url: '/api/users',
  method: 'get',
  data: { page: 1, size: 10 }
});
// 实际请求：GET /api/users?page=1&size=10

// ✅ POST 请求中 query 用于路径参数，data 用于请求体
await request({
  url: '/api/users/:id',
  method: 'post',
  query: { id: 123 },
  data: { name: '张三' }
});
// 实际请求：POST /api/users/123
// 请求体：{ name: '张三' }
```

### 2.6 路径参数传参规范

当 API 地址包含路径参数（如 `:id`）时，必须通过 `query` 字段传入：

```javascript
// API 定义
const __apis = createApis({
  getUser: '/api/users/:id',
  updateUser: {
    url: '/api/users/:id',
    method: 'put',
    settings: { type: 'json' }
  }
});

// 正确调用方式
await __apis.getUser(null, { query: { id: 123 } });
await __apis.updateUser({ name: '张三' }, { query: { id: 123 } });
```

**原理说明：** `request` 内部使用 `pathToRegexpCompile` 将 URL 中的 `:paramName` 替换为 `query` 对象中对应的值。

---

## 三、本地存储模块（storage）

### 3.1 核心 API

```javascript
import { storage } from '@vtj/utils';

storage.save(key, value, opts?)   // 保存数据
storage.get(key, opts?)           // 读取数据
storage.remove(key, opts?)        // 删除数据
storage.clear(opts?)              // 清空存储
storage.config(opts?)             // 修改默认配置
```

### 3.2 存储类型（type）

| 类型      | 说明           | 过期支持 | 环境     |
| --------- | -------------- | -------- | -------- |
| `local`   | localStorage   | ✅ 支持  | 浏览器   |
| `session` | sessionStorage | ✅ 支持  | 浏览器   |
| `cache`   | 内存缓存       | ✅ 支持  | 所有环境 |

### 3.3 配置选项

```javascript
{
  type: 'cache' | 'local' | 'session',  // 存储类型（默认 cache）
  expired: number,                       // 过期时间（毫秒），0 为永不过期
  prefix: string                         // key 前缀（默认 '__VTJ_'）
}
```

### 3.4 使用示例

```javascript
import { storage } from '@vtj/utils';

// 保存数据（永不过期）
storage.save('userToken', 'abc123');

// 保存数据（1 小时后过期）
storage.save('userToken', 'abc123', {
  type: 'local',
  expired: 60 * 60 * 1000
});

// 读取数据
const token = storage.get('userToken');

// 读取数据（指定存储类型）
const token = storage.get('userToken', { type: 'local' });

// 删除数据
storage.remove('userToken');

// 清空所有缓存
storage.clear();

// 修改默认配置
storage.config({
  type: 'local',
  prefix: 'MY_APP_'
});
```

### 3.5 在 Composition API 中使用

```javascript
import { reactive, onMounted } from 'vue';
import { storage } from '@vtj/utils';

const __state = reactive({
  user: null,
  token: ''
});

onMounted(() => {
  // 从本地存储读取用户信息
  __state.token = storage.get('userToken') || '';
  __state.user = storage.get('userInfo', { type: 'local' });
});

function login(user) {
  __state.user = user;
  __state.token = user.token;

  // 保存到本地存储
  storage.save('userToken', user.token, {
    type: 'local',
    expired: 7 * 24 * 60 * 60 * 1000 // 7 天
  });
  storage.save('userInfo', user, {
    type: 'local',
    expired: 7 * 24 * 60 * 60 * 1000
  });
}

function logout() {
  storage.remove('userToken', { type: 'local' });
  storage.remove('userInfo', { type: 'local' });
  __state.user = null;
  __state.token = '';
}
```

---

## 四、Cookie 操作模块（cookie）

### 4.1 核心 API

```javascript
import { cookie } from '@vtj/utils';

cookie.set(name, value, opts?)    // 设置 Cookie
cookie.get(name)                  // 读取 Cookie
cookie.remove(name, opts?)        // 删除 Cookie
```

### 4.2 配置选项

```javascript
{
  expires: number,      // 过期天数
  path: string,         // 路径（默认 '/'）
  domain: string,       // 域名
  secure: boolean,      // 仅 HTTPS
  sameSite: string      // SameSite 策略
}
```

### 4.3 使用示例

```javascript
import { cookie } from '@vtj/utils';

// 设置 Cookie（会话级别）
cookie.set('userId', '12345');

// 设置 Cookie（7 天过期）
cookie.set('userId', '12345', { expires: 7 });

// 设置 Cookie（指定路径和域名）
cookie.set('userId', '12345', {
  expires: 7,
  path: '/',
  domain: '.example.com'
});

// 读取 Cookie
const userId = cookie.get('userId');

// 删除 Cookie
cookie.remove('userId');
```

---

## 五、URL 处理模块（url）

### 5.1 核心 API

```javascript
import { url } from '@vtj/utils';

url.stringify(query)              // 对象转查询字符串
url.parse(str?)                   // 查询字符串转对象
url.append(url, query)            // 在 URL 后追加参数
url.getHost(url)                  // 获取 URL 的 host
url.getCurrentHost(includePath)   // 获取当前页面 host
```

### 5.2 使用示例

```javascript
import { url } from '@vtj/utils';

// 对象转查询字符串
const str = url.stringify({ page: 1, size: 10 });
// 结果：'page=1&size=10'

// 查询字符串转对象
const obj = url.parse('page=1&size=10');
// 结果：{ page: '1', size: '10' }

// 获取当前 URL 参数
const params = url.parse();
// 结果：从 location.search 解析参数

// 在 URL 后追加参数
const newUrl = url.append('/api/users', { page: 2 });
// 结果：'/api/users?page=2'

// 追加参数到已有参数的 URL
const newUrl = url.append('/api/users?sort=asc', { page: 2 });
// 结果：'/api/users?sort=asc&page=2'

// 获取 URL 的 host
const host = url.getHost('https://example.com/api/users');
// 结果：'https://example.com'

// 获取当前页面 host
const host = url.getCurrentHost(false);
// 结果：'https://example.com'

const hostWithPath = url.getCurrentHost(true);
// 结果：'https://example.com/path/to/page'
```

---

## 六、文件下载模块（download）

### 6.1 核心 API

```javascript
import { downloadUrl, downloadBlob, downloadJson, downloadRemoteFile } from '@vtj/utils';

downloadUrl(url, filename?)              // 下载指定 URL 的文件
downloadBlob(data, filename?, type?)     // 下载 Blob 数据
downloadJson(data, filename?)            // 下载 JSON 数据
downloadRemoteFile(url, filename?, type?)// 下载远程文件
```

### 6.2 使用示例

```javascript
import {
  downloadUrl,
  downloadBlob,
  downloadJson,
  downloadRemoteFile
} from '@vtj/utils';

// 下载 URL 文件
downloadUrl('https://example.com/file.pdf', 'report.pdf');

// 下载 JSON 数据
const data = { name: '张三', age: 25 };
downloadJson(data, 'user.json');

// 下载 Blob 数据（CSV 文件）
const csvContent = 'name,age\n张三,25';
downloadBlob(csvContent, 'users.csv', 'text/csv');

// 下载远程文件
await downloadRemoteFile(
  'https://example.com/image.png',
  'avatar.png',
  'image/png'
);
```

### 6.3 在组件中使用

```javascript
import { reactive } from 'vue';
import { downloadJson, createApi } from '@vtj/utils';

const exportData = createApi('/api/users/export');

const __state = reactive({
  async exportUsers() {
    const res = await exportData();
    downloadJson(res.data?.data, 'users.json');
  }
});
```

---

## 七、日志模块（logger）

### 7.1 核心 API

```javascript
import { logger, getLogger } from '@vtj/utils';

logger.debug(...args); // 调试日志
logger.log(...args); // 普通日志
logger.info(...args); // 信息日志
logger.warn(...args); // 警告日志
logger.error(...args); // 错误日志
```

### 7.2 日志级别

| 级别     | 值  | 说明     |
| -------- | --- | -------- |
| debug    | -1  | 调试信息 |
| log/info | 0   | 普通信息 |
| warn     | 1   | 警告信息 |
| error    | 2   | 错误信息 |

### 7.3 使用示例

```javascript
import { logger, getLogger } from '@vtj/utils';

// 使用默认 logger（bizName: 'VTJ'）
logger.info('页面加载完成');
logger.warn('用户未登录');
logger.error('请求失败', error);

// 创建自定义 logger
const myLogger = getLogger({
  level: 'debug',
  bizName: 'MyApp'
});

myLogger.debug('调试信息');
myLogger.info('[MyApp] 业务日志');

// 日志输出格式：[bizName] message
// 例如：[VTJ] 页面加载完成
```

### 7.4 动态控制日志级别

通过 URL 参数控制日志输出：

```
https://example.com?__logConf__=debug
https://example.com?__logConf__=warn:MyApp
```

格式：`__logConf__=logLevel[:bizName]`

---

## 八、动态脚本加载（loadScript）

### 8.1 核心 API

```javascript
import { loadScript } from '@vtj/utils';

loadScript(src, options?)  // 动态加载外部 JS 脚本
```

### 8.2 配置选项

```javascript
{
  async: boolean,           // 是否异步加载（默认 true）
  attrs: object,            // 自定义属性
  charset: string,          // 字符集（默认 'utf-8'）
  text: string,             // 追加的脚本内容
  type: string,             // 脚本类型（默认 'text/javascript'）
  library: string           // window 上的全局变量名
}
```

### 8.3 使用示例

```javascript
import { loadScript } from '@vtj/utils';

// 加载外部脚本
await loadScript('https://cdn.example.com/chart.js');

// 加载并获取全局变量
const Chart = await loadScript('https://cdn.example.com/chart.js', {
  library: 'Chart'
});

// 加载带自定义属性
await loadScript('https://cdn.example.com/sdk.js', {
  async: true,
  attrs: {
    'data-app-id': '12345'
  }
});
```

---

## 九、JSONP 跨域请求（jsonp）

### 9.1 核心 API

```javascript
import { jsonp } from '@vtj/utils';

jsonp(url, options?)  // 发送 JSONP 请求
```

### 9.2 配置选项

```javascript
{
  query: object,                // 查询参数
  timeout: number,              // 超时时间（毫秒）
  jsonpCallback: string,        // 回调函数名
  jsonpCallbackFunction: string // 回调函数名
}
```

### 9.3 使用示例

```javascript
import { jsonp } from '@vtj/utils';

// 基础 JSONP 请求
const data = await jsonp('https://api.example.com/data');

// 带查询参数
const data = await jsonp('https://api.example.com/data', {
  query: { page: 1, size: 10 }
});

// 带模板字符串的 URL
const data = await jsonp('https://api.example.com/users/${id}', {
  query: { id: 123 }
});
```

---

## 十、客户端环境检测（client）

### 10.1 核心 API

```javascript
import { getClientInfo } from '@vtj/utils';

getClientInfo(); // 获取客户端环境信息
```

### 10.2 返回值

```javascript
{
  os: string,              // 操作系统：Windows/Mac OS/iOS/Android/Linux
  osVersion: string,       // 系统版本
  browser: string,         // 浏览器：Chrome/Safari/Firefox/Edge/Opera/IE
  browserVersion: string,  // 浏览器版本
  isMobile: boolean        // 是否为移动设备
}
```

### 10.3 使用示例

```javascript
import { getClientInfo } from '@vtj/utils';

const info = getClientInfo();

if (info.isMobile) {
  console.log('移动设备:', info.os, info.browser);
} else {
  console.log('桌面设备:', info.os, info.browser);
}
```

---

## 十一、requestAnimationFrame 封装（raf）

### 11.1 核心 API

```javascript
import { rAF, cAF } from '@vtj/utils';

rAF(fn); // 请求动画帧（兼容 SSR）
cAF(handle); // 取消动画帧
```

### 11.2 使用示例

```javascript
import { rAF, cAF } from '@vtj/utils';

let handle;

function animate() {
  // 动画逻辑
  handle = rAF(animate);
}

// 开始动画
handle = rAF(animate);

// 停止动画
cAF(handle);
```

---

## 十二、通用工具函数（util）

### 12.1 核心 API

```javascript
import {
  isClient,
  fileToBase64,
  formDataToJson,
  dataURLtoBlob,
  blobToFile
} from '@vtj/utils';

isClient; // 是否为浏览器环境
fileToBase64(file); // File 转 Base64
formDataToJson(data); // FormData 转 JSON
dataURLtoBlob(dataurl); // Base64 转 Blob
blobToFile(blob, fileName); // Blob 转 File
```

### 12.2 使用示例

```javascript
import { isClient, fileToBase64, formDataToJson } from '@vtj/utils';

// 环境检测
if (isClient) {
  // 浏览器环境代码
  console.log(window.location.href);
}

// File 转 Base64
const base64 = await fileToBase64(file);

// FormData 转 JSON
const json = formDataToJson(formData);
```

---

## 十三、典型工作流程

### 13.1 数据获取与展示

```javascript
import { reactive, onMounted } from 'vue';
import { createApis, storage } from '@vtj/utils';

const __apis = createApis({
  getUserList: '/api/users',
  getUserById: '/api/users/:id'
});

const __state = reactive({
  users: [],
  loading: false
});

onMounted(async () => {
  // 尝试从缓存读取
  const cached = storage.get('userList', { type: 'local' });
  if (cached) {
    __state.users = cached;
    return;
  }

  // 从服务器获取
  __state.loading = true;
  try {
    const res = await __apis.getUserList();
    __state.users = res.data?.data || [];

    // 缓存 5 分钟
    storage.save('userList', __state.users, {
      type: 'local',
      expired: 5 * 60 * 1000
    });
  } finally {
    __state.loading = false;
  }
});
```

### 13.2 文件上传与下载

```javascript
import { reactive } from 'vue';
import { createApi, downloadBlob, fileToBase64 } from '@vtj/utils';

const uploadFile = createApi({
  url: '/api/upload',
  method: 'post',
  settings: { type: 'data' } // multipart/form-data
});

const __state = reactive({
  async handleUpload(file) {
    // File 转 Base64
    const base64 = await fileToBase64(file);

    // 上传文件
    const formData = new FormData();
    formData.append('file', file);
    const res = await uploadFile(formData);

    return res.data?.data;
  },

  async handleExport() {
    const res = await __apis.exportData();
    downloadBlob(res.data?.data, 'export.xlsx', 'application/vnd.ms-excel');
  }
});
```

### 13.3 动态加载第三方库

```javascript
import { reactive, onMounted } from 'vue';
import { loadScript } from '@vtj/utils';

const __state = reactive({
  chartLoaded: false
});

onMounted(async () => {
  try {
    const Chart = await loadScript('https://cdn.example.com/chart.js', {
      library: 'Chart'
    });

    if (Chart) {
      __state.chartLoaded = true;
      // 使用 Chart 库
    }
  } catch (error) {
    logger.error('加载图表库失败', error);
  }
});
```

---

## 十四、注意事项

1. **request 默认 `originResponse: true`：** 默认返回完整的 axios 响应对象，设置 `originResponse: false` 才会自动提取并返回 `res.data?.data`
2. **query 与 params 语义区分：** `query` 用于路径参数替换，请求体数据使用 `data` 字段
3. **storage 支持过期机制：** 过期时间单位为毫秒，0 表示永不过期
4. **isClient 用于 SSR 兼容：** 在非浏览器环境中访问 `window` 前先判断 `isClient`
5. **logger 支持动态级别控制：** 通过 URL 参数 `__logConf__` 可以动态调整日志输出
6. **loadScript 返回 Promise：** 加载成功返回 window 上的全局变量（如果指定了 `library`）
7. **download 方法需要浏览器环境：** 依赖 `document.createElement`，SSR 环境下不可用
8. **cookie 基于 js-cookie：** 完整 API 参考 [js-cookie 文档](https://github.com/js-cookie/js-cookie)

---

## 十五、从 @vtj/base 继承的工具

`@vtj/utils` 还从 `@vtj/base` 导出了以下常用工具：

```javascript
import {
  merge, // 深度合并对象
  omit, // 排除对象属性
  debounce, // 防抖函数
  throttle, // 节流函数
  uuid, // 生成唯一 ID
  pathToRegexpCompile, // 路径参数编译
  isUrl, // 判断是否为 URL
  template // 模板字符串编译
} from '@vtj/utils';
```

这些工具在 request、storage、url 等模块内部广泛使用，也可以在业务代码中直接使用。

