{"owner":"chillzhuang","repo":"SpringBlade","hasSkills":true,"totalSkillsCount":26,"totalTokensCount":87910,"categories":["anthropic-skill","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":[".claude/skills/avue-design/README.md",".claude/skills/avue-design/SKILL.md",".claude/skills/avue-design/references/api-style-guide.md",".claude/skills/avue-design/references/crud-column.md",".claude/skills/avue-design/references/crud-core.md",".claude/skills/avue-design/references/crud-features.md",".claude/skills/avue-design/references/crud-form-dialog.md",".claude/skills/avue-design/references/crud-search.md",".claude/skills/avue-design/references/data-display.md",".claude/skills/avue-design/references/form-advanced.md",".claude/skills/avue-design/references/form-core.md",".claude/skills/avue-design/references/form-field-types.md",".claude/skills/avue-design/references/global-config.md",".claude/skills/avue-design/references/other-components.md",".claude/skills/avue-design/references/tree-component.md",".claude/skills/avue-design/references/utility-apis.md",".claude/skills/blade-commit/README.md",".claude/skills/blade-commit/SKILL.md",".claude/skills/blade-design/README.md",".claude/skills/blade-design/SKILL.md",".claude/skills/blade-design/references/backend.md",".claude/skills/blade-design/references/database.md",".claude/skills/blade-design/references/frontend.md",".claude/skills/blade-plan/README.md",".claude/skills/blade-plan/SKILL.md","CLAUDE.md"],"skills":{".claude/skills/avue-design/README.md":"# avue-design Skill 集合\n\nAvue 组件代码生成与配置指南，基于 Avue v3.8.2 (Vue3 + Element-Plus) 文档构建。\n\n## 概述\n\n本 Skill 集合为 Claude Code 提供完整的 Avue 组件库知识，使其能够根据用户需求精准生成 Avue 组件代码。覆盖 CRUD 表格、动态表单、树组件、数据展示等全部 Avue 组件，支持 Composition API 和 Options API 两种代码风格。\n\n## 目录结构\n\n```\navue-design/\n├── SKILL.md                           # 主入口 - 概览、路由、速查表\n├── README.md                          # 本文件\n└── references/                        # 参考文档（按需加载）\n    │\n    │── 基础配置 ───────────────────────\n    ├── global-config.md               # 安装、全局配置、国际化、云存储\n    ├── api-style-guide.md             # Composition API vs Options API 完整模板\n    ├── utility-apis.md                # 16个全局工具函数 ($DialogForm, $Export 等)\n    │\n    │── avue-crud 表格组件 ─────────────\n    ├── crud-core.md                   # 组件属性、Option配置、按钮、弹窗、分页、事件、方法、插槽\n    ├── crud-column.md                 # 列配置、字典、Props映射、排序过滤、格式化渲染\n    ├── crud-search.md                 # 搜索栏配置、搜索字段属性、级联搜索\n    ├── crud-form-dialog.md            # 弹窗表单配置、验证规则、字段控制、分组、权限\n    ├── crud-features.md               # 树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽、插件模式\n    │\n    │── avue-form 表单组件 ─────────────\n    ├── form-core.md                   # 组件属性、布局、按钮、Column通用属性、事件、方法、插槽\n    ├── form-field-types.md            # 全部27+字段类型详细配置（input含13子类型、select、date等）\n    ├── form-advanced.md               # 字典、验证、动态子表单、文件上传(含云存储)、字段联动、Mock\n    │\n    │── 其他组件 ─────────────────────\n    ├── tree-component.md              # avue-tree 树组件完整API\n    ├── data-display.md                # 23个数据展示组件\n    └── other-components.md            # Card、DialogForm、Flow、Login、Chat等22个独立组件\n```\n\n## 模块说明\n\n### 1. 基础配置（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `global-config.md` | npm安装、Element-Plus集成、全局crudOption/formOption、阿里云/七牛/腾讯COS云存储配置、水印配置、国际化、外部库CDN |\n| `api-style-guide.md` | Composition API 和 Options API 两种风格的完整 CRUD 页面模板，包含详细的差异对照表和转换规则 |\n| `utility-apis.md` | 全部16个Avue工具函数：$DialogForm、$Clipboard、$ImagePreview、$Print、$Export、$Log、findObject、deepClone等 |\n\n### 2. avue-crud 表格组件（5个文件）\n\n这是 Avue 最核心的组件，文档最为丰富。\n\n| 文件 | 内容 |\n|-----|------|\n| `crud-core.md` | 组件属性（21个）、Option表格级属性（索引/选择/展开/行/菜单/网格/工具栏共80+属性）、按钮配置（25+）、弹窗配置（19个）、分页（8个）、事件（35+）、方法（40+含行内编辑/滚动/搜索/视图方法组）、插槽（22+） |\n| `crud-column.md` | 列通用属性（20+含hide/cell/detail控制）、字典配置（7个属性+Props映射）、字典联动、显示控制、多级表头、排序过滤、formatter/render/html格式化、合计行、合并行列、搜索字段属性（20+含searchDisabled/searchControl/renderSearch）、表单字段属性（20+） |\n| `crud-search.md` | 搜索变量绑定（双API风格）、全局搜索属性（15+）、列级搜索属性（14个）、类型覆盖、折叠、级联联动、自定义插槽 |\n| `crud-form-dialog.md` | 弹窗属性（15个）、beforeOpen/beforeClose回调、验证规则(async-validator)、字段display/disabled控制、默认值、control联动、分组/标签页、数据过滤、权限控制 |\n| `crud-features.md` | 树表(基本/懒加载)、虚拟滚动(3.7.0+)、卡片网格模式(3.4.0+)、行编辑(单击/双击)、Excel导入导出(含对话框选项)、行/列拖拽排序、合计行、父子表、行复制、高级筛选(8种操作符)、列管理器、权限控制(对象/行级函数)、CRUD插件模式(Mixin)、API/Option文件模板、动态配置切换 |\n\n### 3. avue-form 表单组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `form-core.md` | 组件属性（9个含v-model:status）、Option属性（20个含formWidth/tabsActive/tabsType/tabsVerifyAll/filterDic/filterNull）、布局（栅格/分组/标签页/帮助文字）、Group属性（8个含arrow/display/header）、按钮配置（12个）、Column通用属性（25+）、事件（6个含change/tab-click/error/mock-change）、方法（12个含show/hide/scrollToField/getField）、插槽（6个）、两种API风格完整示例 |\n| `form-field-types.md` | 全部27+字段类型完整配置：input(含14子类型:password/textarea/search/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc + formatters/parser)、number、select(含all/noMatchText)、cascader、checkbox、radio、date(含disabledDate/shortcuts/unlinkPanels)、time(含arrowControl + TimeSelect自动切换)、switch、upload(含dragFile/oss/cropperOption)、tree、input-tag(含min/max/prefix/suffix)、mention(含split/prefix/whole)、input-table(含children/dialogWidth)、color、icon(含dialogWidth/分组iconList)、map(含mapChange/dialogWidth/值格式说明)、rate、slider、array/img/url、dynamic(含max + 完整children配置12属性)、title、input-cron |\n| `form-advanced.md` | 字典配置(本地/远程/格式化/联动/更新)、验证规则(基本/类型/自定义/方法)、动态子表单(表格/表单模式)、文件上传(回调/水印/云存储/数据格式)、字段联动(control/Promise)、数据格式(深层绑定/过滤)、自定义插槽、Mock数据 |\n\n### 4. 其他组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `tree-component.md` | avue-tree完整API：Option属性（12个）、Props属性（4个）、事件（7个）、方法（18个）、插槽（3个）、多选/懒加载/拖拽/虚拟模式示例 |\n| `data-display.md` | 23个数据展示组件列表、avue-data-display核心组件属性和数据结构 |\n| `other-components.md` | 22个独立组件：Card、DialogForm、Tabs、Search、Calendar、Flow、Login、Article、Chat、CountUp、Clipboard、Draggable、TextEllipsis、Sign、Print、Export、ImagePreview、Contextmenu、Video、Verify、Screenshot、License |\n\n## 工作原理\n\n1. **主入口 SKILL.md**（< 200行）始终加载到上下文中，提供：\n   - 快速安装指南\n   - 参考文件导航表（根据任务类型指引读取哪个文件）\n   - 代码生成核心规则\n   - 字段类型速查表\n\n2. **参考文件** 按需加载，当用户需要特定组件的配置时才读取对应文件：\n   - 需要创建 CRUD 页面 → 读取 `crud-core.md` + `crud-column.md`\n   - 需要配置搜索 → 读取 `crud-search.md`\n   - 需要特定字段类型 → 读取 `form-field-types.md`\n   - 需要文件上传 → 读取 `form-advanced.md`\n\n3. **双API风格** 支持：\n   - Composition API（推荐）：`<script setup>` + `ref()` + 箭头函数\n   - Options API：`export default` + `data()` + `methods`\n\n## 数据来源\n\n基于以下 Avue 官方文档与源码完整解析：\n\n- `crud/` 目录 32 个文档（CRUD表格组件）\n- `form/` 目录 38 个文档（表单组件）\n- `component/` 目录 20 个文档（独立组件）\n- `default/` 目录 25 个文档（默认组件）\n- `data/` 目录 23 个文档（数据展示组件）\n- `docs/` 目录 10 个文档（全局配置、API、安装等）\n- Saber3 项目参考文件（Composition API vs Options API 对照）\n- Avue v3.8.2 完整源码审计（packages/element-plus/ 全部组件 + packages/core/ 核心模块 + src/ 工具库）\n\n总计 **6,000+ 行** 参考文档，覆盖 Avue v3.8.2 全部组件配置。\n\n## 版本信息\n\n- Avue 版本：v3.8.2 (2026-03-27)\n- Vue 版本：Vue 3\n- UI 框架：Element-Plus\n- 创建日期：2026-04-05\n",".claude/skills/avue-design/SKILL.md":"---\nname: avue-design\ndescription: \"Avue 组件代码生成与配置指南。当用户需要创建、配置或生成基于 Avue (avue-crud, avue-form, avue-tree 等) 的 Vue 页面代码时使用此 skill。覆盖 CRUD 表格、表单、树组件、数据展示等全部 Avue 组件。支持 Composition API 和 Options API 两种代码风格。当用户提到 avue、crud表格、动态表单、JSON配置表单、低代码表单、avue-crud、avue-form 等关键词时，务必使用此 skill。\"\n---\n\n# Avue Design - 组件代码生成 Skill 集合\n\nAvue 是基于 Vue3 + Element-Plus 的低代码前端框架，通过 JSON 配置生成页面。版本：v3.8.2，MIT 协议。\n\n## 快速安装\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\napp.use(ElementPlus)\napp.use(Avue)\n// 若使用字典或上传组件，需传入 axios：\n// import axios from 'axios'\n// app.use(Avue, { axios })\n```\n\n## 参考文件导航\n\n根据用户需求，读取对应的参考文件获取完整配置信息：\n\n### 基础配置\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/global-config.md` | 全局配置、安装、国际化、云存储 |\n| `references/api-style-guide.md` | 需要生成 Composition API 或 Options API 代码时 |\n| `references/utility-apis.md` | 使用 Avue 全局工具函数 ($DialogForm, $Clipboard 等) |\n\n### avue-crud (CRUD 表格组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/crud-core.md` | CRUD 基础属性、事件、方法、插槽、按钮、弹窗、分页 |\n| `references/crud-column.md` | 列配置、字典、排序、过滤、格式化、渲染 |\n| `references/crud-search.md` | 搜索栏配置、搜索字段属性 |\n| `references/crud-form-dialog.md` | CRUD 弹窗表单配置、验证规则、字段状态控制 |\n| `references/crud-features.md` | 高级功能：树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽排序、权限控制等 |\n\n### avue-form (表单组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/form-core.md` | 表单基础属性、事件、方法、插槽、布局 |\n| `references/form-field-types.md` | 全部字段类型 (input/select/date/upload 等 27+ 种) 的详细配置属性 |\n| `references/form-advanced.md` | 字典配置、验证规则、动态子表单、文件上传(含云存储)、字段联动 |\n\n### 其他组件\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/tree-component.md` | avue-tree 树组件 |\n| `references/data-display.md` | 数据展示组件 (DataDisplay, DataCard 等) |\n| `references/other-components.md` | Card、DialogForm、Calendar、Flow、Login、Tabs 等独立组件 |\n\n## 代码生成核心规则\n\n### 1. 两种 API 风格\n\n生成代码时必须确认用户要用哪种风格（默认 Composition API）。详细模板见 `references/api-style-guide.md`。\n\n**Composition API (推荐)**：使用 `<script setup>`，`ref()`/`reactive()` 声明数据，箭头函数定义方法。\n\n**Options API**：使用 `export default {}`，`data()` 返回数据，`methods` 对象定义方法。\n\n### 2. avue-crud 基本结构\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    />\n  </basic-container>\n</template>\n```\n\n### 3. avue-form 基本结构\n\n```vue\n<template>\n  <avue-form\n    ref=\"form\"\n    v-model=\"formData\"\n    :option=\"option\"\n    @submit=\"handleSubmit\"\n    @reset-change=\"handleReset\"\n  />\n</template>\n```\n\n### 4. option 配置核心\n\noption 是 Avue 组件的核心配置对象，包含：\n- 表格/表单级属性（size, height, border, stripe 等）\n- `column` 数组：每个字段的完整配置（label, prop, type, rules, dicData 等）\n- `group` 数组：分组配置（仅表单）\n\n```javascript\nconst option = {\n  // 表格级配置\n  height: 'auto',\n  border: true,\n  stripe: true,\n  index: true,\n  selection: true,\n  // 列配置\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      type: 'input',        // 字段类型\n      search: true,          // 启用搜索\n      span: 12,              // 表单中占栅格数\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n      // 字典配置\n      dicData: [],           // 本地字典\n      dicUrl: '',            // 远程字典URL\n      props: { label: 'label', value: 'value' },\n    }\n  ]\n}\n```\n\n### 5. 字段类型速查\n\n| 类型 | type 值 | 说明 |\n|-----|---------|------|\n| 文本输入 | input | 支持 password/textarea/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc 子类型 |\n| 数字输入 | number | 数字步进器 |\n| 下拉选择 | select | 单选/多选/远程搜索/分组 |\n| 级联选择 | cascader | 多级联动选择 |\n| 多选框 | checkbox | 多选组 |\n| 单选框 | radio | 单选组 |\n| 日期 | date/datetime/daterange/datetimerange/week/month/year/dates | 日期时间选择 |\n| 时间 | time/timerange | 时间选择 |\n| 开关 | switch | 布尔切换 |\n| 文件上传 | upload | 支持阿里云/七牛/腾讯COS |\n| 数组 | array | 数组输入框 |\n| 图片 | img | 图片输入 |\n| 链接 | url | 超链接输入 |\n| 树选择 | tree | 树型选择器 |\n| 标签输入 | input-tag | 标签输入 (3.6.0+) |\n| 提及 | mention | @提及组件 (3.6.0+) |\n| 表格选择 | input-table | 表格弹窗选择 |\n| 颜色选择 | color | 颜色选择器 |\n| Cron表达式 | input-cron | Cron可视化配置 (3.8.2+) |\n| 图标选择 | icon | 图标选择器 |\n| 地图选择 | map | 高德地图坐标选择 |\n| 评分 | rate | 星级评分 |\n| 滑块 | slider | 滑动条 |\n| 标题 | title | 分组标题 |\n| 动态表单 | dynamic | 子表单/子表格 |\n\n### 6. 关键注意事项\n\n- `column` 可以是数组也可以是对象格式（3.2.15+，用 prop 做 key）\n- 深层数据绑定用 `bind` 属性：`bind: 'info.address'`\n- 字典联动用 `cascader` 属性指定子字段 prop\n- 字段动态控制用 `control` 属性（支持 Promise，3.5.0+）\n- 表单分组用 `group` 配置，标签页显示设 `tabs: true`\n- 所有组件都支持 `render` 函数自定义渲染（3.4.2+）\n- CRUD 弹窗类型用 `dialogType: 'drawer'` 可改为抽屉模式\n- 搜索字段通过在 column 中设 `search: true` 启用\n",".claude/skills/avue-design/references/api-style-guide.md":"# Avue 代码风格指南：Composition API vs Options API\n\n生成 Avue 代码时，需根据用户偏好选择以下两种风格之一。默认使用 Composition API。\n\n## 目录\n\n1. [Composition API 完整模板](#composition-api-完整模板)\n2. [Options API 完整模板](#options-api-完整模板)\n3. [关键差异对照表](#关键差异对照表)\n4. [转换规则](#转换规则)\n\n---\n\n## Composition API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <!-- 自定义左侧按钮 -->\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <!-- 自定义列内容 -->\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed, onMounted } from 'vue'\nimport { useStore } from 'vuex'\nimport { ElMessage, ElMessageBox } from 'element-plus'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\n// ============ 响应式数据 ============\nconst crud = ref(null)\nconst form = ref({})\nconst search = ref({})\nconst loading = ref(true)\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n})\nconst selectionList = ref([])\nconst data = ref([])\n\n// ============ Vuex ============\nconst store = useStore()\nconst permission = computed(() => store.getters.permission)\n\n// ============ 计算属性 ============\nconst permissionList = computed(() => ({\n  addBtn: !!permission.value.module_add,\n  viewBtn: !!permission.value.module_view,\n  delBtn: !!permission.value.module_delete,\n  editBtn: !!permission.value.module_edit,\n}))\n\nconst ids = computed(() =>\n  selectionList.value.map(ele => ele.id).join(',')\n)\n\n// ============ Option 配置 ============\nconst option = reactive({\n  height: 'auto',\n  calcHeight: 32,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  dialogClickModal: false,\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      search: true,\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      search: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n})\n\n// ============ 生命周期 ============\nonMounted(() => {\n  // 初始化逻辑\n})\n\n// ============ 方法 ============\nconst onLoad = () => {\n  loading.value = true\n  getList(page.value.currentPage, page.value.pageSize, Object.assign({}, search.value)).then(res => {\n    const resData = res.data.data\n    page.value.total = resData.total\n    data.value = resData.records\n    loading.value = false\n    selectionClear()\n  })\n}\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(row.id))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据')\n    return\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(ids.value))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n    })\n  }\n  done()\n}\n\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\n\nconst searchReset = () => {\n  onLoad()\n}\n\nconst selectionChange = (list) => {\n  selectionList.value = list\n}\n\nconst selectionClear = () => {\n  selectionList.value = []\n  crud.value?.toggleSelection()\n}\n\nconst refreshChange = () => {\n  onLoad()\n}\n</script>\n```\n\n---\n\n## Options API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      data: [],\n      option: {\n        height: 'auto',\n        calcHeight: 32,\n        tip: false,\n        searchShow: true,\n        searchMenuSpan: 6,\n        border: true,\n        index: true,\n        selection: true,\n        dialogClickModal: false,\n        column: [\n          {\n            label: '名称',\n            prop: 'name',\n            search: true,\n            rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n          },\n          {\n            label: '状态',\n            prop: 'status',\n            type: 'select',\n            search: true,\n            dicData: [\n              { label: '启用', value: 1 },\n              { label: '禁用', value: 0 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: !!this.permission.module_add,\n        viewBtn: !!this.permission.module_view,\n        delBtn: !!this.permission.module_delete,\n        editBtn: !!this.permission.module_edit,\n      }\n    },\n    ids() {\n      return this.selectionList.map(ele => ele.id).join(',')\n    },\n  },\n  methods: {\n    onLoad(page, params = {}) {\n      this.loading = true\n      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n        const data = res.data.data\n        this.page.total = data.total\n        this.data = data.records\n        this.loading = false\n        this.selectionClear()\n      })\n    },\n    rowSave(row, done, loading) {\n      add(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowUpdate(row, index, done, loading) {\n      update(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(row.id))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n        })\n    },\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据')\n        return\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(this.ids))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n          this.$refs.crud.toggleSelection()\n        })\n    },\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data\n        })\n      }\n      done()\n    },\n    searchChange(params, done) {\n      this.query = params\n      this.page.currentPage = 1\n      this.onLoad(this.page, params)\n      done()\n    },\n    searchReset() {\n      this.query = {}\n      this.onLoad(this.page)\n    },\n    selectionChange(list) {\n      this.selectionList = list\n    },\n    selectionClear() {\n      this.selectionList = []\n      this.$refs.crud.toggleSelection()\n    },\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage\n    },\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize\n    },\n    refreshChange() {\n      this.onLoad(this.page)\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 关键差异对照表\n\n| 特性 | Composition API | Options API |\n|-----|----------------|-------------|\n| **脚本标签** | `<script setup>` | `<script> export default {}` |\n| **数据声明** | `const x = ref(val)` / `reactive({})` | `data() { return { x: val } }` |\n| **脚本中访问数据** | `x.value` | `this.x` |\n| **模板中访问数据** | `{{ x }}` (自动解包) | `{{ x }}` |\n| **计算属性** | `const x = computed(() => ...)` | `computed: { x() { ... } }` |\n| **方法** | `const fn = () => {}` | `methods: { fn() {} }` |\n| **生命周期** | `onMounted(() => {})` | `mounted() {}` |\n| **Vuex** | `const store = useStore()` | `...mapGetters([...])` |\n| **消息提示** | `ElMessage.success(...)` | `this.$message(...)` |\n| **确认框** | `ElMessageBox.confirm(...)` | `this.$confirm(...)` |\n| **组件引用** | `const crud = ref(null)` | `this.$refs.crud` |\n| **搜索参数** | `v-model:search=\"search\"` 自动同步 | 手动赋值 `this.query = params` |\n| **分页事件** | `v-model:page` 自动处理 | 需要 `currentChange`/`sizeChange` |\n\n## 转换规则\n\n从 Composition API 转换为 Options API：\n1. `ref()` 声明 → 移入 `data()` return\n2. 移除所有 `.value` 访问\n3. 箭头函数 → `methods` 对象方法\n4. `computed(() => ...)` → `computed` 对象\n5. `ElMessage` / `ElMessageBox` → `this.$message` / `this.$confirm`\n6. `useStore()` → `mapGetters`\n7. `onMounted()` → `mounted()`\n8. 添加 `currentChange` / `sizeChange` 事件处理\n\n从 Options API 转换为 Composition API：\n1. `data()` 中的属性 → `ref()` 或 `reactive()`\n2. 添加 `.value` 访问\n3. `methods` 中的方法 → 顶层箭头函数\n4. `this.$xxx` → 导入对应模块\n5. `v-model:search=\"search\"` 替代 `query` 变量\n6. 移除 `currentChange` / `sizeChange`（由 v-model:page 处理）\n",".claude/skills/avue-design/references/crud-column.md":"# avue-crud 列配置参考\n\n## 目录\n1. [列通用属性](#列通用属性)\n2. [字典配置](#字典配置)\n3. [显示控制](#显示控制)\n4. [排序与过滤](#排序与过滤)\n5. [格式化与渲染](#格式化与渲染)\n6. [搜索字段属性](#搜索字段属性)\n7. [表单字段属性](#表单字段属性)\n\n---\n\n## 列通用属性\n\ncolumn 数组中每个对象的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 列标题 |\n| prop | string | - | 列属性名 |\n| type | string | - | 字段类型 (input/select/date等，详见form-field-types) |\n| width | string | - | 列宽度 |\n| minWidth | string | - | 列最小宽度 |\n| fixed | boolean/string | - | 固定列: true/left/right |\n| align | string | 'left' | 对齐方式: left/center/right |\n| headerAlign | string | 'left' | 表头对齐方式 |\n| className | string | - | 单元格自定义class |\n| labelClassName | string | - | 表头自定义class |\n| resizable | boolean | true | 列宽可拖拽 |\n| slot | boolean | - | 启用具名插槽（插槽名=prop） |\n| bind | string | - | 深层数据绑定，如 'info.address' |\n| value | - | - | 列默认值 |\n| order | number | - | 字段排序，数字越大越靠前 |\n| gridRow | boolean | false | 网格模式新起一行 |\n| hide | boolean | false | 隐藏列（不在表格中显示） |\n| cell | boolean | false | 该列可行内编辑 |\n| addDetail | boolean | false | 新增时详情模式 |\n| editDetail | boolean | false | 编辑时详情模式 |\n| detail | boolean | false | 全局详情模式 |\n\n### 主键配置\n\n```javascript\noption: {\n  rowKey: 'id',           // 主键字段名，默认 'id'\n  rowParentKey: 'parentId' // 父级主键(树表用)\n}\n```\n\n### 对象格式列配置 (3.2.15+)\n\ncolumn 可以用对象格式替代数组，以 prop 为 key：\n\n```javascript\n// 数组格式\ncolumn: [\n  { label: '名称', prop: 'name' },\n  { label: '状态', prop: 'status' }\n]\n\n// 对象格式\ncolumn: {\n  name: { label: '名称' },\n  status: { label: '状态' }\n}\n```\n\n---\n\n## 字典配置\n\n### 字典属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| dicData | array/function/promise | - | 本地字典数据 (3.5.0+ 支持函数/Promise) |\n| dicUrl | string | - | 远程字典URL |\n| dicFlag | boolean | false | 打开表单时重新请求字典 |\n| dicQuery | object | - | 字典请求查询参数 |\n| dicHeaders | object | - | 字典请求头参数 |\n| dicFormatter | function(res) | (res)=>res | 字典返回数据格式化 |\n| dicMethod | string | 'GET' | 字典请求方法 |\n\n### Props 字段映射\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 选项标签属性 |\n| value | string | 'value' | 选项值属性 |\n| children | string | 'children' | 子选项属性 |\n| disabled | string | 'disabled' | 禁用属性 |\n| res | string | - | 响应数据层级，如 'data.data' |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  // 本地字典\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n  // 或远程字典\n  dicUrl: '/api/dict/type',\n  props: {\n    label: 'name',\n    value: 'code',\n    res: 'data.records',\n  },\n}\n```\n\n### 字典联动\n\n通过 `cascader` 属性实现级联联动：\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city'],  // 指定子级字段prop\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}', // {{key}} 为父级选中值\n  },\n]\n```\n\n### 更新字典\n\n```javascript\n// 更新指定字段的字典\ncrudRef.value.updateDic('prop', [{ label: '新选项', value: 1 }])\n// 重新加载所有字典\ncrudRef.value.dicInit()\n```\n\n---\n\n## 显示控制\n\n### 溢出省略\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| overHidden | boolean/object | false | 内容溢出显示tooltip |\n| showOverflowTooltip | boolean/object | false | 同上 |\n| ellipsis | boolean | false | 文本省略 (3.6.4+) |\n\n### 多级表头\n\n通过 `children` 嵌套实现：\n\n```javascript\ncolumn: [\n  {\n    label: '用户信息',\n    children: [\n      { label: '姓名', prop: 'name' },\n      { label: '年龄', prop: 'age' },\n    ]\n  }\n]\n```\n\n### 表头固定\n\n```javascript\noption: {\n  height: '500px',  // 设置高度后表头自动固定\n  showHeader: true,  // 默认true\n}\n```\n\n---\n\n## 排序与过滤\n\n### 排序\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| sortable | boolean/string | - | 排序: true(本地)/custom(远程) |\n| defaultSort | object | - | 默认排序: {prop, order} |\n| columnSort | boolean | - | 列拖拽排序 |\n\n```javascript\n// 远程排序\n{\n  label: '日期',\n  prop: 'date',\n  sortable: 'custom',  // 需监听 sort-change 事件\n}\n\n// 默认排序\noption: {\n  defaultSort: { prop: 'date', order: 'descending' }\n}\n```\n\n### 过滤\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| filters | boolean/array | false | 数据过滤 |\n| filterMethod | function | - | 自定义过滤逻辑 |\n| filter-multiple | boolean | true | 多选过滤 |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  filters: true,  // 自动从 dicData 生成\n  // 或手动指定\n  filters: [\n    { text: '启用', value: 1 },\n    { text: '禁用', value: 0 },\n  ],\n}\n```\n\n---\n\n## 格式化与渲染\n\n### formatter 格式化\n\n```javascript\n{\n  label: '金额',\n  prop: 'amount',\n  formatter: (row, value, label, column) => {\n    return '¥' + value.toFixed(2)\n  },\n}\n```\n\n### tooltipFormatter (3.6.3+)\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  overHidden: true,\n  tooltipFormatter: (row, value, label, column) => {\n    return `详细内容: ${value}`\n  },\n}\n```\n\n### html 属性\n\n```javascript\n{\n  label: '内容',\n  prop: 'content',\n  html: true,  // 支持HTML翻译\n}\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  // 列区域渲染\n  render: (h, { row, index, dic }) => {\n    return h('el-tag', { type: row.status === 1 ? 'success' : 'danger' }, row.statusName)\n  },\n  // 表单区域渲染\n  renderForm: (h, { column, $index, dic }) => {\n    return h('el-input', { modelValue: form.value[column.prop] })\n  },\n  // 表头渲染\n  renderHeader: (h, { column }) => {\n    return h('span', { style: 'color: red' }, column.label)\n  },\n}\n```\n\n### 合计行\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },     // 求和\n    { name: 'price', type: 'average' },   // 平均值\n    { name: 'count', type: 'count' },     // 计数\n  ],\n}\n\n// 或自定义合计\n// 通过 summary-method 属性传入函数\n```\n\n### 合并行列\n\n```javascript\n// 通过 span-method 属性设置\n<avue-crud :span-method=\"spanMethod\">\n\nconst spanMethod = ({ row, column, rowIndex, columnIndex }) => {\n  if (rowIndex === 0 && columnIndex === 0) {\n    return { rowspan: 2, colspan: 1 }\n  }\n}\n```\n\n---\n\n## 搜索字段属性\n\n在 column 中通过 `search: true` 启用搜索，以下为搜索相关属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | string | - | 搜索初始值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索字段排序 |\n| searchGutter | number | - | 搜索字段间距 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索字段类型（覆盖 type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 搜索可清空 |\n| searchMultiple | boolean | false | 搜索多选 |\n| searchTags | boolean | false | 搜索标签显示 |\n| searchLabelPosition | string | 'left' | 搜索标签位置 |\n| searchRules | array | - | 搜索验证规则 |\n| searchDisabled | boolean | false | 搜索字段禁用 |\n| searchReadonly | boolean | false | 搜索字段只读 |\n| searchIndex | number | - | 搜索栏初始显示时包含此字段 |\n| searchControl | function | - | 搜索字段联动控制 |\n| renderSearch | function | - | 搜索区域自定义渲染函数 (3.4.2+) |\n\n### 搜索栏全局配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| searchBtnIcon | string | - | 搜索按钮图标 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| emptyBtnIcon | string | - | 清空按钮图标 |\n| searchShow | boolean | true | 初始显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留数量 |\n| searchMenuPosition | string | 'center' | 搜索按钮位置 |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示内容 |\n| searchTipPlacement | string | 'bottom' | 提示方向 |\n| searchMenuSpan | number | - | 搜索按钮栅格数 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索默认栅格数 |\n| searchGutter | number | - | 搜索默认间距 |\n| searchLabelWidth | number | 80 | 搜索默认标签宽度 |\n| searchLabelPosition | string | 'left' | 搜索默认标签位置 |\n\n---\n\n## 表单字段属性\n\ncolumn 中控制字段在表单（新增/编辑/查看弹窗）中表现的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| display | boolean | true | 在表单中显示 |\n| disabled | boolean | false | 在表单中禁用 |\n| addDisabled | boolean | false | 新增时禁用 |\n| editDisabled | boolean | false | 编辑时禁用 |\n| viewDisabled | boolean | false | 查看时禁用 |\n| addDisplay | boolean | true | 新增时显示 |\n| editDisplay | boolean | true | 编辑时显示 |\n| viewDisplay | boolean | true | 查看时显示 |\n| span | number | 12 | 表单栅格数 |\n| gutter | number | 0 | 表单间距 |\n| offset | number | 0 | 表单偏移 |\n| row | boolean | false | 表单换行 |\n| labelWidth | string | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| rules | array | - | 验证规则 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 帮助文字方向 |\n| labelTip | string | - | 标题帮助文字 |\n| labelTipPlacement | string | 'top' | 标题帮助文字方向 |\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n| render | function | - | 列区域渲染 (3.4.2+) |\n| renderForm | function | - | 表单区域渲染 (3.4.2+) |\n| renderHeader | function | - | 表头渲染 (3.4.2+) |\n",".claude/skills/avue-design/references/crud-core.md":"# avue-crud 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表格级属性](#option-表格级属性)\n3. [按钮配置](#按钮配置)\n4. [弹窗配置](#弹窗配置)\n5. [分页配置](#分页配置)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n获取表格原生引用：\n```javascript\nconst crudRef = ref(null)\n// 访问 el-table 实例\ncrudRef.value.$refs.table\n```\n\n---\n\n## 组件属性\n\n在 `<avue-crud>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| data | array | - | 表格显示数据 |\n| option | object | - | 组件配置项 |\n| before-open | function(done, type, loading) | - | 打开弹窗前回调，type: add/edit/view |\n| before-close | function(done, type) | - | 关闭弹窗前回调 |\n| permission | function/object | - | 按钮权限控制（支持行级） |\n| v-model:search | object | - | 搜索变量双向绑定 |\n| v-model:page | object | - | 分页变量双向绑定 |\n| cell-class-name | function/string | - | 单元格 className 回调 |\n| cell-style | function/object | - | 单元格样式回调 |\n| header-cell-class-name | function/string | - | 表头单元格 className |\n| header-row-class-name | function/string | - | 表头行 className |\n| header-row-style | function/object | - | 表头行样式 |\n| header-cell-style | function/object | - | 表头单元格样式 |\n| row-class-name | function/string | - | 行 className 回调 |\n| row-style | function/object | - | 行样式回调 |\n| span-method | function | - | 合并行或列 |\n| summary-method | function | - | 自定义合计计算 |\n| table-loading | boolean | - | 加载状态 |\n| upload-before | function(file, done, loading) | - | 上传组件上传前回调 |\n| upload-after | function(res, done) | - | 上传组件上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 上传预览回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制回调 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小回调 (3.4.4+) |\n| disabled | boolean | false | 禁用所有交互元素 |\n| width | number/string | - | 表格宽度 |\n| height | number/string | - | 表格高度 |\n\n---\n\n## Option 表格级属性\n\n在 `option` 对象中设置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 列配置数组 |\n| height | string | - | 表格高度，设 'auto' 为自适应窗口 |\n| maxHeight | string | - | 表格最大高度 |\n| title | string | - | 表格标题 |\n| border | boolean | false | 纵向边框 |\n| stripe | boolean | false | 斑马纹 |\n| fit | boolean | true | 列宽自适应 |\n| showHeader | boolean | true | 显示表头 |\n| header | boolean | false | 隐藏表格头部操作栏 |\n| emptyText | string | '暂无数据' | 空数据提示文字 |\n| tooltipEffect | string | 'dark' | tooltip 主题: dark/light |\n| tooltipOptions | object | - | tooltip 配置项 |\n| scrollbarAlwaysOn | boolean | true | 始终显示滚动条 (3.6.0+) |\n\n### 索引列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| index | boolean | false | 显示序号 |\n| indexLabel | string | '#' | 序号列标题 |\n| indexWidth | number | 50 | 序号列宽度 |\n| indexFixed | boolean/string | true | 序号列固定: true/left/right |\n| indexClassName | string | - | 序号列单元格自定义class |\n| indexLabelClassName | string | - | 序号列表头自定义class |\n\n### 选择列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| selection | boolean | false | 显示多选框 |\n| selectable | function | - | 判断行是否可选 |\n| selectionWidth | number | 50 | 选择列宽度 |\n| selectionFixed | boolean/string | true | 选择列固定 |\n| selectionClassName | string | - | 选择列单元格class |\n| selectionLabelClassName | string | - | 选择列表头class |\n| reserveSelection | boolean | false | 翻页保留选择 |\n\n### 展开行\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| expand | boolean | false | 显示展开 |\n| expandWidth | number | 50 | 展开列宽度 |\n| expandFixed | boolean/string | true | 展开列固定 |\n| expandClassName | string | - | 展开列单元格class |\n| expandLabelClassName | string | - | 展开列表头class |\n| defaultExpandAll | boolean | false | 默认展开所有行 |\n| expandRowKeys | array | - | 展开行的keys |\n\n### 行配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowKey | string | 'id' | 行数据主键 |\n| rowParentKey | string | 'parentId' | 父行主键(树表用) |\n| highlightCurrentRow | boolean | false | 高亮当前行 |\n| defaultSort | object | - | 默认排序 {prop, order: 'ascending'/'descending'} |\n| showOverflowTooltip | boolean/object | false | 溢出提示 |\n| showSummary | boolean | false | 显示合计行 |\n| lazy | boolean | false | 懒加载子节点 |\n\n### 菜单列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menu | boolean | true | 显示操作菜单 |\n| menuWidth | number | 220 | 菜单列宽度 |\n| menuTitle | string | '操作' | 菜单列标题 |\n| menuFixed | boolean/string | true | 菜单列固定 |\n| menuType | string | 'text' | 菜单按钮样式: button/icon/text/menu |\n| menuHeaderAlign | string | 'center' | 菜单表头对齐 |\n| menuAlign | string | 'center' | 菜单按钮对齐 |\n| menuClassName | string | - | 菜单单元格class |\n| menuLabelClassName | string | - | 菜单表头class |\n\n### 卡片/网格模式 (3.4.0+)\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| grid | boolean | false | 启用网格模式 |\n| gridBtn | boolean | true | 显示模式切换按钮 |\n| gridSpan | number | 8 | 网格栅格数 |\n| gridBackgroundImage | string | - | 网格背景图片 |\n| gridBackground | string/function | - | 网格背景色 |\n\n### 工具栏与功能开关\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addRowBtn | boolean | false | 显示新增行内编辑按钮 |\n| cellBtn | boolean | false | 显示行内编辑操作按钮 |\n| dateBtn | boolean | false | 显示日期范围选择按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮 |\n| selectClearBtn | boolean | true | 显示清空选中按钮 |\n| tip | boolean | true | 显示选中项提示 |\n| page | boolean | true | 显示分页 |\n| searchShow | boolean | true | 搜索栏默认显示 |\n| searchIcon | boolean | true | 搜索栏展开/收起图标 |\n| searchIndex | number | - | 搜索栏初始显示字段数（超出折叠） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchSpan | number | 6 | 搜索栅格数 |\n| calcHeight | number | 300 | 表格自适应高度偏移量 |\n| menuXsWidth | number | 100 | 移动端菜单列宽度 |\n| gridXsSpan | number | 12 | 移动端网格栅格数 |\n| headerAlign | string | 'left' | 表头默认对齐方式 |\n| width | string | '100%' | 表格宽度 |\n| formFullscreen | boolean | false | 弹窗显示全屏切换按钮 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| filterMultiple | boolean | true | 过滤器默认多选 |\n\n---\n\n## 按钮配置\n\n### 操作按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| addBtnText | string | '新增' | 新增按钮文字 |\n| addBtnIcon | string | - | 新增按钮图标 |\n| editBtn | boolean | true | 修改按钮 |\n| editBtnText | string | '修改' | 修改按钮文字 |\n| editBtnIcon | string | - | 修改按钮图标 |\n| delBtn | boolean | true | 删除按钮 |\n| delBtnText | string | '删除' | 删除按钮文字 |\n| delBtnIcon | string | - | 删除按钮图标 |\n| viewBtn | boolean | false | 查看按钮 |\n| viewBtnText | string | '查看' | 查看按钮文字 |\n| viewBtnIcon | string | - | 查看按钮图标 |\n| copyBtn | boolean | false | 复制按钮 |\n\n### 弹窗按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| updateBtn | boolean | true | 弹窗修改按钮 |\n| updateBtnText | string | '修改' | 修改按钮文字 |\n| updateBtnIcon | string | - | 修改按钮图标 |\n| saveBtn | boolean | true | 弹窗保存按钮 |\n| saveBtnText | string | '保存' | 保存按钮文字 |\n| saveBtnIcon | string | - | 保存按钮图标 |\n| cancelBtn | boolean | true | 弹窗取消按钮 |\n| cancelBtnText | string | '取消' | 取消按钮文字 |\n| cancelBtnIcon | string | - | 取消按钮图标 |\n\n### 工具栏按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| printBtn | boolean | false | 打印按钮 |\n| excelBtn | boolean | false | 导出按钮 |\n| filterBtn | boolean | false | 筛选按钮 |\n| refreshBtn | boolean | true | 刷新按钮 |\n| columnBtn | boolean | true | 列操作按钮 |\n| searchShowBtn | boolean | true | 搜索显隐按钮 |\n| addRowBtn | boolean | false | 新增行内编辑按钮 |\n| dateBtn | boolean | false | 日期范围选择按钮 |\n| gridBtn | boolean | true | 网格/表格切换按钮 |\n\n### 按钮文字图标规则\n\n按钮名 + `Text` = 按钮文字，按钮名 + `Icon` = 按钮图标。设置图标为 `' '`（空格）可禁用图标。\n\n---\n\n## 弹窗配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogDrag | boolean | false | 弹窗可拖拽 |\n| dialogFullscreen | boolean | false | 弹窗全屏 |\n| dialogCustomClass | string | - | 弹窗自定义class |\n| dialogEscape | boolean | true | ESC关闭弹窗 |\n| dialogClickModal | boolean | false | 点击遮罩关闭弹窗 |\n| dialogCloseBtn | boolean | true | 显示关闭按钮 |\n| dialogModal | boolean | true | 显示遮罩层 |\n| dialogMenuPosition | string | 'right' | 弹窗按钮位置 |\n| dialogTop | number | 25 | 弹窗距顶部距离 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| headerClass | string | - | 弹窗头部自定义class (3.7.0+) |\n| bodyClass | string | - | 弹窗内容区自定义class (3.7.0+) |\n| footerClass | string | - | 弹窗底部自定义class (3.7.0+) |\n\n---\n\n## 分页配置\n\n通过 `v-model:page` 绑定分页对象：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currentPage | number | 1 | 当前页码 |\n| pageSize | number | 20 | 每页条数 |\n| total | number | 0 | 总条数 |\n| pagerCount | number | 7 | 页码按钮数量 |\n| background | boolean | true | 分页按钮背景 |\n| layout | string | 'prev, pager, next, jumper, ->, total' | 分页组件布局 |\n| pageSizes | number[] | [10, 20, 30, 40, 50, 100] | 每页条数选项 |\n| simplePage | boolean | false | 只有一页时隐藏分页 |\n\n---\n\n## 事件\n\n### 数据操作事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-save | (row, done, loading) | 新增数据后触发。done() 关闭表单，loading() 保持表单 |\n| row-update | (row, index, done, loading) | 修改数据后触发 |\n| row-del | (row, index) | 删除行触发 |\n| refresh-change | - | 点击刷新按钮后触发 |\n\n### 搜索事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| search-change | (form, done) | 搜索后触发 |\n| search-reset | - | 清空搜索回调 |\n| date-change | (date) | 日期选择回调 |\n\n### 选择事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| select | (selection, row) | 用户勾选 |\n| select-all | (selection) | 用户全选 |\n| selection-change | (selection) | 选择项变化 |\n| current-row-change | (currentRow, oldCurrentRow) | 当前行变化 |\n| selection-clear | (selection) | 清空选中时触发 |\n\n### 行/单元格事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| cell-mouse-enter | (row, column, cell, event) | 单元格hover进入 |\n| cell-mouse-leave | (row, column, cell, event) | 单元格hover离开 |\n| cell-click | (row, column, cell, event) | 单元格点击 |\n| cell-dblclick | (row, column, cell, event) | 单元格双击 |\n| row-click | (row, column, event) | 行点击 |\n| row-contextmenu | (row, column, event) | 行右击 |\n| row-dblclick | (row, column, event) | 行双击 |\n\n### 表头事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| header-click | (column, event) | 表头点击 |\n| header-contextmenu | (column, event) | 表头右击 |\n| header-dragend | (newWidth, oldWidth, column, event) | 列宽改变 |\n\n### 排序/过滤事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| sort-change | ({ column, prop, order }) | 排序变化 |\n| filter-change | (filters) | 过滤变化 |\n| sortable-change | ({ newIndex, oldIndex }) | 行拖拽排序 |\n| column-sortable-change | ({ newIndex, oldIndex }) | 列拖拽排序 (3.5.2+) |\n\n### 其他事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| on-load | (page) | 页面加载/分页变化时触发 |\n| tree-load | (row, treeNode, resolve) | 加载子节点 (lazy=true) |\n| expand-change | (row, expandedRows/expanded) | 行展开/收起 |\n| grid-status-change | (status) | 网格/表格模式切换 (3.4.7+) |\n| scroll | ({scrollLeft, scrollTop}) | 表格滚动 (3.6.0+) |\n| tab-click | (tab, event) | 弹窗标签页点击 |\n| error | (error) | 表单验证失败 |\n| filter | (filterData) | 高级筛选应用 |\n| date-change | (date) | 日期范围选择回调 |\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowAdd | - | 打开新增弹窗 |\n| rowEdit | (row, index) | 打开编辑弹窗 |\n| rowView | (row, index) | 打开查看弹窗 |\n| updateDic | (prop, dic) | 更新字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | (valid, done, msg) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置所有字段 |\n| clearValidate | (props) | 清除验证结果 |\n| clearSelection | - | 清除多选 |\n| toggleSelection | (array) | 切换选择 |\n| toggleAllSelection | - | 切换全选 |\n| toggleRowExpansion | (row, expanded) | 切换行展开 |\n| setCurrentRow | (row) | 设置当前行 |\n| clearSort | - | 清除排序 |\n| clearFilter | (columnKey) | 清除过滤 |\n| doLayout | - | 重新布局表格 |\n| refreshTable | - | 重新初始化表格 |\n| sort | (prop, order) | 手动排序 |\n| updateKeyChildren | (key, data) | 更新懒加载子节点 (3.6.0+) |\n\n### 行内编辑方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowCopy | (row) | 复制行数据到新增弹窗 |\n| rowCellAdd | (row) | 新增一行行内编辑数据 |\n| rowCell | (row, index) | 切换行编辑状态 |\n| rowCellEdit | (row, index) | 进入行编辑模式 |\n| rowCellUpdate | (row, index) | 保存行编辑数据 |\n| rowCancel | (row, index) | 取消行编辑 |\n| closeDialog | (row, index) | 关闭弹窗并更新数据 |\n| validateCellForm | - | 验证所有行内编辑表单 |\n| validateCellField | (index) | 验证指定行的编辑表单 |\n\n### 数据方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| findData | (id) | 通过主键值查找行数据 |\n| getPermission | (key, row, index) | 获取按钮权限 |\n| getTableHeight | - | 重新计算表格高度 |\n\n### 滚动方法 (3.7.0+ 虚拟滚动)\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| scrollTo | (coordinates) | 滚动到指定位置 |\n| scrollToTop | - | 滚动到顶部 |\n| scrollToLeft | - | 滚动到最左侧 |\n| scrollRow | (rowKey) | 滚动到指定行（虚拟滚动模式） |\n| setScrollTop | (top) | 设置垂直滚动位置 |\n| setScrollLeft | (left) | 设置水平滚动位置 |\n\n### 搜索方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| searchReset | - | 重置搜索表单 |\n| searchChange | (form, done) | 手动触发搜索 |\n\n### 视图方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| handleGridShow | - | 切换网格/表格视图 |\n| columns | - | 获取当前列配置 |\n\n---\n\n## 插槽\n\n### 列相关插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义列内容，参数: {row, index, dic, size, label} |\n| `{prop}-header` | 自定义列表头 |\n| `{prop}-search` | 自定义搜索输入 |\n| `{prop}-form` | 自定义表单字段 |\n| `{prop}-label` | 自定义表单标签 |\n| `{prop}-error` | 自定义表单错误提示 |\n| `{prop}-desc` | 自定义表单描述 (3.6.4+) |\n| `{prop}-type` | 自定义字段类型选择器 |\n\n### 区域插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| search | 自定义整个搜索区域 |\n| searchMenu | 自定义搜索菜单 |\n| header | 自定义表格头部 |\n| body | 自定义表格主体 (3.4.3+) |\n| page | 自定义分页 |\n| empty | 自定义空状态 |\n| tip | 自定义选中提示区域 |\n\n### 按钮插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu-left | 自定义菜单左侧 |\n| menu-right | 自定义菜单右侧 |\n| menu | 自定义整个操作菜单，参数: {row, index, type, size} |\n| menu-before | 操作菜单前 (3.4.3+) |\n| menu-btn | 自定义菜单按钮 |\n| menu-btn-before | 菜单按钮前 (3.4.3+) |\n| menu-form | 自定义表单菜单 |\n| menu-form-before | 表单菜单前 (3.4.3+) |\n| menu-header | 菜单表头插槽 |\n",".claude/skills/avue-design/references/crud-features.md":"# avue-crud 高级功能参考\n\n## 目录\n1. [树形表格](#树形表格)\n2. [虚拟滚动](#虚拟滚动)\n3. [卡片/网格模式](#卡片网格模式)\n4. [行编辑（单元格编辑）](#行编辑)\n5. [导入导出](#导入导出)\n6. [拖拽排序](#拖拽排序)\n7. [合计行](#合计行)\n8. [父子表](#父子表)\n9. [加载状态](#加载状态)\n10. [CRUD 插件模式](#crud-插件模式)\n11. [行复制](#行复制)\n12. [高级筛选](#高级筛选-filterbtn)\n13. [列管理器](#列管理器-columnbtn)\n14. [Excel 导出对话框](#excel-导出对话框)\n15. [权限控制](#权限控制)\n\n---\n\n## 树形表格\n\n### 基本树表\n\n```javascript\noption: {\n  rowKey: 'id',\n  rowParentKey: 'parentId',\n  defaultExpandAll: true,  // 默认展开所有\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n  ],\n}\n\n// 数据需要有 id 和 parentId 字段\ndata: [\n  { id: 1, parentId: 0, name: '父节点', code: 'P1' },\n  { id: 2, parentId: 1, name: '子节点', code: 'C1' },\n]\n```\n\n### 懒加载树表\n\n```vue\n<avue-crud :option=\"option\" @tree-load=\"treeLoad\">\n\noption: {\n  rowKey: 'id',\n  lazy: true,\n  column: [\n    { label: '名称', prop: 'name', hasChildren: 'hasChildren' },\n  ],\n}\n\nconst treeLoad = (row, treeNode, resolve) => {\n  getChildren(row.id).then(res => {\n    resolve(res.data.data)\n  })\n}\n```\n\n注意：非懒加载时需去掉 `hasChildren` 字段。\n\n---\n\n## 虚拟滚动 (3.7.0+)\n\n用于大数据量场景的虚拟滚动：\n\n```javascript\noption: {\n  virtualize: true,\n  height: 500,  // 必须为数字\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      width: 200,   // 虚拟滚动时 width 必须为数字\n      fixed: true,   // 列自适应\n    },\n  ],\n}\n```\n\n注意：虚拟滚动模式下 `width` 和 `height` 必须是数字类型。\n\n自适应高度：\n```javascript\noption: {\n  virtualize: true,\n  height: 'auto',  // 自适应\n}\n```\n\n---\n\n## 卡片/网格模式 (3.4.0+)\n\n将表格显示为卡片网格形式：\n\n```javascript\noption: {\n  grid: true,           // 启用网格模式\n  gridBtn: true,        // 显示切换按钮（默认true）\n  gridSpan: 8,          // 每卡片占栅格数\n  gridBackgroundImage: 'url(xxx)',  // 背景图\n  gridBackground: '#f5f5f5',       // 背景色\n}\n\n// 背景色也可以是函数\noption: {\n  gridBackground: (row, index) => {\n    return index % 2 === 0 ? '#f0f9ff' : '#fff'\n  },\n}\n```\n\n自定义卡片插槽：\n```vue\n<template #default=\"{ row, index }\">\n  <div class=\"custom-card\">\n    <h3>{{ row.name }}</h3>\n    <p>{{ row.description }}</p>\n  </div>\n</template>\n```\n\n---\n\n## 行编辑\n\n### 基本行编辑\n\n```javascript\noption: {\n  cellBtn: true,     // 显示行编辑按钮\n  addRowBtn: true,   // 显示行新增按钮\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      cell: true,    // 该列可编辑\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      cell: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n}\n```\n\n### 首次进入编辑模式\n\n```javascript\n// 数据中设置 $cellEdit: true\ndata: [\n  { id: 1, name: 'test', $cellEdit: true },\n]\n```\n\n### 单击/双击编辑\n\n```vue\n<!-- 单击编辑 -->\n<avue-crud @cell-click=\"cellClick\">\n\nconst cellClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n\n<!-- 双击编辑 -->\n<avue-crud @cell-dblclick=\"cellDblClick\">\n\nconst cellDblClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n```\n\n### 保存行编辑\n\n数据必须包含 `rowKey`（默认 id）字段才能保存。\n\n---\n\n## 导入导出\n\n### 导出 Excel\n\n```javascript\noption: {\n  excelBtn: true,  // 显示导出按钮\n}\n```\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n### 编程式导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 导入 Excel\n\n参考 `default/export/import` 示例。\n\n---\n\n## 拖拽排序\n\n### 行拖拽排序\n\n```javascript\noption: {\n  sortable: true,  // 启用行拖拽\n  rowKey: 'id',    // 必须设置主键\n}\n```\n\n需引入 Sortable.js：\n```html\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n```\n\n监听排序事件：\n```vue\n<avue-crud @sortable-change=\"sortableChange\">\n\nconst sortableChange = ({ newIndex, oldIndex }) => {\n  // 处理排序逻辑\n}\n```\n\n### 列拖拽排序 (3.5.2+)\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    columnSort: true,  // 启用列拖拽\n  },\n]\n```\n\n监听事件：\n```vue\n<avue-crud @column-sortable-change=\"columnSortableChange\">\n```\n\n---\n\n## 合计行\n\n### 内置合计\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },      // 求和\n    { name: 'price', type: 'average' },    // 平均值\n    { name: 'count', type: 'count' },      // 计数\n  ],\n}\n```\n\n### 自定义合计\n\n```vue\n<avue-crud :summary-method=\"summaryMethod\">\n\nconst summaryMethod = ({ columns, data }) => {\n  const sums = []\n  columns.forEach((column, index) => {\n    if (index === 0) {\n      sums[index] = '合计'\n      return\n    }\n    const values = data.map(item => Number(item[column.property]))\n    if (!values.every(isNaN)) {\n      sums[index] = values.reduce((a, b) => a + b, 0).toFixed(2)\n    } else {\n      sums[index] = '-'\n    }\n  })\n  return sums\n}\n```\n\n---\n\n## 父子表\n\n```vue\n<avue-crud :option=\"parentOption\" :data=\"parentData\" @row-click=\"parentRowClick\">\n  <!-- 子表通过展开行或独立组件实现 -->\n  <template #expand=\"{ row }\">\n    <avue-crud :option=\"childOption\" :data=\"row.children\" />\n  </template>\n</avue-crud>\n```\n\n或使用独立组件封装子表，通过 `crud-children` 模式。\n\n---\n\n## 加载状态\n\n```vue\n<avue-crud :table-loading=\"loading\">\n\nconst loading = ref(true)\n// 数据加载完成后\nloading.value = false\n```\n\n---\n\n## CRUD 插件模式\n\n### Mixin 模式（Options API）\n\n```javascript\n// @/mixins/crud.js\nexport default (app, option = {}) => {\n  let optionObj = import.meta.glob(`../option/**/**`)[`../option/${option.name}.js`]\n  let apiObj = import.meta.glob(`../api/**/**`)[`../api/${option.name}.js`]\n  // ... mixin 逻辑\n}\n\n// 全局注册\n// main.js\nimport crudCommon from '@/mixins/crud'\nwindow.$crudCommon = crudCommon\n\n// 使用\nexport default window.$crudCommon({\n  data() { return {} },\n  methods: {\n    listBefore() {},\n    listAfter() {},\n    addBefore() { this.form.createUser = 'admin' },\n    addAfter() {},\n    updateBefore() {},\n    updateAfter() {},\n    delBefore() {},\n    delAfter() {},\n  }\n}, {\n  name: 'system/params',\n  list: 'list',\n  update: 'update',\n  add: 'add',\n  del: 'del',\n  rowKey: 'id',\n  pageNumber: 'pageNumber',\n  pageSize: 'pageSize',\n  res: (data) => ({ total: data.total, data: data.records }),\n  total: 'total',\n  data: 'records',\n})\n```\n\n### 绑定辅助（Options API）\n\n```vue\n<avue-crud v-bind=\"bindVal\" v-on=\"onEvent\" v-model=\"form\" v-model:page=\"page\">\n\ncomputed: {\n  bindVal() {\n    return {\n      ref: 'crud',\n      option: this.option,\n      data: this.data,\n      tableLoading: this.loading,\n    }\n  },\n  onEvent() {\n    return {\n      'on-load': this.getList,\n      'row-save': this.rowSave,\n      'row-update': this.rowUpdate,\n      'row-del': this.rowDel,\n      'refresh-change': this.refreshChange,\n      'search-reset': this.searchChange,\n      'search-change': this.searchChange,\n    }\n  },\n}\n```\n\n### API 文件模板\n\n```javascript\n// @/api/module/entity.js\nimport request from '@/router/axios'\n\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/api/entity/list',\n    method: 'get',\n    params: { current, size, ...params },\n  })\n}\n\nexport const getDetail = (id) => {\n  return request({\n    url: '/api/entity/detail',\n    method: 'get',\n    params: { id },\n  })\n}\n\nexport const add = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const update = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const remove = (ids) => {\n  return request({\n    url: '/api/entity/remove',\n    method: 'post',\n    params: { ids },\n  })\n}\n```\n\n### Option 配置文件模板\n\n```javascript\n// @/option/module/entity.js\nexport default (safe) => {\n  // safe => vue 的 this 对象 (Options API)\n  return {\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    border: true,\n    stripe: true,\n    column: [\n      {\n        label: \"名称\",\n        prop: \"name\",\n        search: true,\n        rules: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n      },\n    ],\n  }\n}\n```\n\n---\n\n## 配置动态切换\n\n### 初始化加载\n\n```vue\n<avue-crud v-if=\"option.column\" :option=\"option\">\n\nconst option = ref({})\nonMounted(() => {\n  // 从服务器加载配置\n  getConfig().then(res => {\n    option.value = res.data\n  })\n})\n```\n\n### 动态切换配置\n\n```javascript\nconst changeOption = (type) => {\n  if (type === 'simple') {\n    option.value = simpleOption\n  } else {\n    option.value = fullOption\n  }\n}\n```\n\n---\n\n## 空状态自定义\n\n```javascript\noption: {\n  emptyText: '暂无数据',  // 空状态文字\n}\n```\n\n```vue\n<!-- 自定义空状态 -->\n<template #empty>\n  <div class=\"custom-empty\">\n    <img src=\"empty.png\" />\n    <p>暂无数据，请添加</p>\n  </div>\n</template>\n```\n\n---\n\n## 深层数据绑定\n\n```javascript\n// 数据结构: { id: 1, info: { address: '北京' } }\ncolumn: [\n  {\n    label: '地址',\n    prop: 'address',      // prop 仍然需要\n    bind: 'info.address', // 绑定深层路径\n  },\n]\n```\n\n---\n\n## 行复制\n\n通过 `rowCopy` 方法复制已有行数据到新增弹窗：\n\n```vue\n<template #menu=\"{ row, index, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowCopy(row)\">复制</el-button>\n</template>\n```\n\n需设置 `copyBtn: true` 在 option 中也可直接显示内置复制按钮。\n\n---\n\n## 高级筛选 (filterBtn)\n\n通过 `filterBtn: true` 启用高级筛选对话框，支持多条件组合查询：\n\n```javascript\noption: {\n  filterBtn: true,  // 显示筛选按钮\n}\n```\n\n### 筛选操作符\n\n| 符号 | 含义 |\n|-----|------|\n| = | 等于 |\n| ≠ | 不等于 |\n| like | 模糊匹配 |\n| > | 大于 |\n| ≥ | 大于等于 |\n| < | 小于 |\n| ≤ | 小于等于 |\n| ∈ | 包含于 |\n\n### 筛选事件\n\n```vue\n<avue-crud @filter=\"handleFilter\">\n\nconst handleFilter = (filterData) => {\n  // filterData 格式: [[prop, operator, value], ...]\n  // 示例: [['name', 'like', '张'], ['age', '>', 18]]\n  console.log('筛选条件:', filterData)\n}\n```\n\n---\n\n## 列管理器 (columnBtn)\n\n通过 `columnBtn: true`（默认开启）显示列管理按钮，可动态控制列的显隐、固定、过滤和排序：\n\n```javascript\noption: {\n  columnBtn: true,  // 显示列管理按钮（默认true）\n}\n```\n\n列管理器支持以下操作：\n- **显隐** - 控制列是否在表格中显示\n- **固定** - 设置列固定在左侧或右侧\n- **过滤** - 启用列的筛选功能\n- **排序** - 启用列的排序功能\n- **拖拽排序** - 拖拽调整列的显示顺序\n\n---\n\n## Excel 导出对话框\n\n当设置 `excelBtn: true` 时，点击导出按钮会打开导出配置对话框：\n\n### 导出选项\n\n| 选项 | 说明 |\n|-----|------|\n| 导出范围 | 导出全部数据 / 仅导出选中行 |\n| 包含表头 | 是否包含列标题行 |\n| 包含数据 | 是否包含数据行 |\n| 包含合计 | 是否包含合计/汇总行 |\n| 选择列 | 选择要导出的列 |\n\n### 编程式导出（跳过对话框）\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 编程式导入\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.xlsx(file).then(({ header, results }) => {\n  console.log('表头:', header)\n  console.log('数据:', results)\n})\n```\n\n---\n\n## 权限控制\n\n### 对象模式\n\n```javascript\n// 通过 permission 对象控制按钮权限\nconst permissionList = {\n  addBtn: true,\n  editBtn: true,\n  delBtn: false,\n  viewBtn: true,\n}\n```\n\n### 函数模式（行级权限）\n\n```javascript\n// 通过函数实现行级权限控制\nconst permission = (key, row, index) => {\n  // key: 按钮标识 (addBtn/editBtn/delBtn/viewBtn)\n  // row: 当前行数据\n  // index: 当前行索引\n  if (key === 'editBtn') {\n    return row.status !== 'locked'\n  }\n  return true\n}\n```\n\n---\n\n## 自定义按钮\n\n```vue\n<!-- 隐藏默认按钮，使用自定义 -->\noption: { addBtn: false, editBtn: false, delBtn: false, viewBtn: false }\n\n<!-- 自定义新增按钮 -->\n<template #menu-left>\n  <el-button type=\"primary\" @click=\"$refs.crud.rowAdd()\">自定义新增</el-button>\n</template>\n\n<!-- 自定义操作按钮 -->\n<template #menu=\"{ row, index, type, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowEdit(row, index)\">编辑</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowView(row, index)\">查看</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowDel(row, index)\">删除</el-button>\n</template>\n\n<!-- 自定义弹窗按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\">自定义保存</el-button>\n  <el-button v-if=\"type === 'edit'\">自定义更新</el-button>\n</template>\n```\n",".claude/skills/avue-design/references/crud-form-dialog.md":"# avue-crud 弹窗表单配置参考\n\n## 弹窗基本配置\n\noption 中的弹窗属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogTop | number | 25 | 弹窗距顶部 |\n| dialogDrag | boolean | false | 可拖拽 |\n| dialogFullscreen | boolean | false | 全屏 |\n| dialogCustomClass | string | - | 自定义class |\n| dialogEscape | boolean | true | ESC关闭 |\n| dialogClickModal | boolean | false | 点击遮罩关闭 |\n| dialogCloseBtn | boolean | true | 关闭按钮 |\n| dialogModal | boolean | true | 遮罩层 |\n| dialogMenuPosition | string | 'right' | 按钮位置: left/center/right |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n\n## 打开/关闭回调\n\n```vue\n<avue-crud :before-open=\"beforeOpen\" :before-close=\"beforeClose\">\n\n<!-- Composition API -->\nconst beforeOpen = (done, type, loading) => {\n  // type: 'add' | 'edit' | 'view'\n  if (['edit', 'view'].includes(type)) {\n    // 加载详情数据\n    loading()  // 显示loading (3.5.2+)\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n      done()   // 完成加载\n    })\n  } else {\n    done()\n  }\n}\n\nconst beforeClose = (done, type) => {\n  done()\n}\n```\n\n## 表单验证规则\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    rules: [\n      { required: true, message: '请输入名称', trigger: 'blur' },\n      { min: 2, max: 20, message: '长度在2到20之间', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '邮箱',\n    prop: 'email',\n    rules: [\n      { required: true, message: '请输入邮箱', trigger: 'blur' },\n      { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '年龄',\n    prop: 'age',\n    type: 'number',\n    rules: [{\n      required: true,\n      validator: (rule, value, callback) => {\n        if (value < 18) {\n          callback(new Error('年龄不能小于18岁'))\n        } else {\n          callback()\n        }\n      },\n      trigger: 'blur',\n    }],\n  },\n]\n```\n\n验证规则基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n## 字段显示/禁用控制\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    // 全局控制\n    display: true,       // 是否显示\n    disabled: false,     // 是否禁用\n    // 按操作类型控制\n    addDisplay: true,    // 新增时显示\n    editDisplay: true,   // 编辑时显示\n    viewDisplay: true,   // 查看时显示\n    addDisabled: false,  // 新增时禁用\n    editDisabled: true,  // 编辑时禁用（如ID不可编辑）\n    viewDisabled: false,\n  },\n]\n```\n\n## 表单默认值\n\n```javascript\ncolumn: [\n  {\n    label: '状态',\n    prop: 'status',\n    type: 'select',\n    value: 1,  // 新增时默认值\n    dicData: [\n      { label: '启用', value: 1 },\n      { label: '禁用', value: 0 },\n    ],\n  },\n]\n```\n\n## 字段联动控制 (control)\n\n```javascript\ncolumn: [\n  {\n    label: '类型',\n    prop: 'type',\n    type: 'select',\n    dicData: [\n      { label: '文章', value: 1 },\n      { label: '链接', value: 2 },\n    ],\n    control: (val, form) => {\n      if (val === 1) {\n        return {\n          content: { display: true },\n          url: { display: false },\n        }\n      } else {\n        return {\n          content: { display: false },\n          url: { display: true },\n        }\n      }\n    },\n  },\n  { label: '内容', prop: 'content', type: 'textarea' },\n  { label: '链接', prop: 'url', display: false },\n]\n```\n\n3.5.0+ 支持 Promise：\n```javascript\ncontrol: async (val, form) => {\n  const res = await getConfig(val)\n  return { subType: { dicData: res.data } }\n}\n```\n\n## 表单分组 (group)\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,  // 默认展开\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name' },\n        { label: '编码', prop: 'code' },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', type: 'textarea' },\n      ],\n    },\n  ],\n  // 使用标签页显示分组\n  tabs: true,\n  // column 也要配置（表格列用）\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n    { label: '描述', prop: 'description' },\n  ],\n}\n```\n\n## 表单标签宽度\n\n```javascript\noption: {\n  labelWidth: 120,        // 全局标签宽度\n  labelPosition: 'right', // 全局标签位置: left/right/top\n}\n\n// 也可在 column 中单独设置\n{\n  label: '很长的标签名称',\n  prop: 'longLabel',\n  labelWidth: 180,\n}\n```\n\n## 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤字典翻译字段（$开头的字段）\n  filterNull: true,  // 过滤空值字段\n}\n```\n\n## 防止重复提交\n\n```javascript\n// row-save 和 row-update 中的 done 和 loading 参数：\n// - done(): 关闭表单弹窗\n// - loading(): 关闭loading但保持表单打开（用于接口报错时）\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    ElMessage.success('操作成功')\n    done()     // 关闭弹窗\n    onLoad()\n  }).catch(() => {\n    loading()  // 关闭loading，保持弹窗\n  })\n}\n```\n\n## 表单字段排序\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', order: 1 },\n  { label: '编码', prop: 'code', order: 3 },  // order大的排前面\n  { label: '状态', prop: 'status', order: 2 },\n]\n```\n\n## 获取字段组件引用\n\n```javascript\nconst ref = crudRef.value.getPropRef('name')\n// ref 即为该字段对应的组件实例\n```\n\n## 连续新增（不关闭弹窗）\n\n通过不调用 `done()` 来保持弹窗打开，实现连续新增。\n\n## 自定义表单插槽\n\n```vue\n<!-- 自定义表单字段 -->\n<template #name-form=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"form.name\" />\n</template>\n\n<!-- 自定义表单标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义错误提示 -->\n<template #name-error=\"{ error }\">\n  <span style=\"color: orange\">{{ error }}</span>\n</template>\n\n<!-- 自定义描述 (3.6.4+) -->\n<template #name-desc=\"{ column }\">\n  <span>这是一段描述文字</span>\n</template>\n\n<!-- 自定义表单按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\" type=\"primary\">自定义保存</el-button>\n</template>\n```\n\n## 权限控制\n\n```javascript\n// 对象形式\n<avue-crud :permission=\"{ addBtn: true, editBtn: true, delBtn: false }\">\n\n// 函数形式（行级控制）\n<avue-crud :permission=\"getPermission\">\n\nconst getPermission = (key, row, index) => {\n  // key: 'addBtn' | 'editBtn' | 'delBtn' | 'viewBtn' | 'menu'\n  if (key === 'editBtn' && row?.status === 1) {\n    return false  // 已启用的不允许编辑\n  }\n  return true\n}\n```\n",".claude/skills/avue-design/references/crud-search.md":"# avue-crud 搜索配置参考\n\n## 基本用法\n\n在 column 中设置 `search: true` 开启搜索字段：\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    search: true,\n    searchSpan: 6,\n  },\n  {\n    label: '日期',\n    prop: 'date',\n    type: 'daterange',\n    search: true,\n    searchRange: true,\n    searchSpan: 8,\n  },\n]\n```\n\n## 搜索变量\n\nComposition API 使用 `v-model:search` 自动同步：\n```vue\n<avue-crud v-model:search=\"search\" @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nconst search = ref({})\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\nconst searchReset = () => {\n  onLoad()\n}\n```\n\nOptions API 使用 query 变量手动同步：\n```vue\n<avue-crud @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nsearchChange(params, done) {\n  this.query = params\n  this.page.currentPage = 1\n  this.onLoad(this.page, params)\n  done()\n},\nsearchReset() {\n  this.query = {}\n  this.onLoad(this.page)\n},\n```\n\n## 搜索栏配置\n\n### 全局搜索属性（option 级别）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| searchShow | boolean | true | 首次加载显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留字段数 |\n| searchMenuPosition | string | 'center' | 按钮位置: left/center/right |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示文字 |\n| searchMenuSpan | number | - | 按钮栅格（单独一行时设24） |\n| searchSpan | number | 8 | 默认搜索栅格 |\n| searchGutter | number | - | 默认搜索间距 |\n| searchLabelWidth | number | 80 | 默认搜索标签宽度 |\n| searchLabelPosition | string | 'left' | 默认搜索标签位置 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n\n### 列级搜索属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | any | - | 搜索默认值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索排序 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索类型（覆盖type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 可清空 |\n| searchMultiple | boolean | false | 多选 |\n| searchTags | boolean | false | 标签显示 |\n| searchLabelPosition | string | 'left' | 标签位置 |\n| searchRules | array | - | 验证规则 |\n\n## 搜索字段类型覆盖\n\n可以用 searchType 让搜索框使用不同于表格列的类型：\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  type: 'date',           // 表格/表单中是日期选择\n  search: true,\n  searchType: 'daterange', // 搜索中是日期范围\n  searchRange: true,\n}\n```\n\n## 搜索折叠\n\n```javascript\noption: {\n  searchIcon: true,    // 显示折叠按钮\n  searchIndex: 2,      // 折叠时保留2个字段\n}\n```\n\n## 搜索级联联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/province',\n    cascader: ['city'],\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/city?province={{key}}',\n  },\n]\n```\n\n## 自定义搜索插槽\n\n```vue\n<!-- 自定义搜索菜单 -->\n<template #searchMenu=\"{ row, search, reset }\">\n  <el-button type=\"primary\" @click=\"search\">搜索</el-button>\n  <el-button @click=\"reset\">重置</el-button>\n  <el-button type=\"warning\" @click=\"handleExport\">导出</el-button>\n</template>\n\n<!-- 自定义搜索字段 -->\n<template #name-search=\"{ search }\">\n  <el-input v-model=\"search.name\" placeholder=\"自定义搜索\" />\n</template>\n```\n\n## 日期快捷按钮\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  dateBtn: true,  // 显示日期快捷按钮\n}\n```\n",".claude/skills/avue-design/references/data-display.md":"# Avue 数据展示组件参考\n\n## 组件列表\n\n| 组件 | 说明 |\n|-----|------|\n| avue-data-display | 数据展示卡片（核心组件） |\n| avue-data-pay | 支付数据 |\n| avue-data-tabs | 标签数据 |\n| avue-data-box | 数据盒子 |\n| avue-data-card | 数据卡片 |\n| avue-data-cardtext | 文字卡片 |\n| avue-data-icons | 图标数据 |\n| avue-data-operatext | 操作文字 |\n| avue-data-progress | 进度数据 |\n| avue-data-rotate | 旋转数据 |\n| avue-data-imgtext | 图文数据 |\n| avue-data-panel | 面板 |\n| avue-data-price | 价格 |\n| avue-data-countdown | 倒计时 |\n| avue-data-dashboard | 仪表盘 |\n| avue-data-list | 数据列表 |\n| avue-data-notice | 公告 |\n| avue-data-product | 商品卡片 |\n| avue-data-profile | 人员档案 |\n| avue-data-rank | 排行榜 |\n| avue-data-statistic | 统计值 |\n| avue-data-task | 任务列表 |\n| avue-data-weather | 天气卡片 |\n\n## avue-data-display（核心组件）\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| animation | boolean | true | 是否动画 |\n| decimals | number | 0 | 小数位数 |\n| span | string | '8' | 栅格数 |\n| data | array | - | 数据数组 |\n\n### 数据结构\n\n```javascript\nconst data = [\n  {\n    title: '今日访问',\n    count: 1234,\n    icon: 'el-icon-view',\n    color: '#40c9c6',\n    decimals: 0,\n  },\n  {\n    title: '销售额',\n    count: 56789.50,\n    icon: 'el-icon-money',\n    color: '#36a3f7',\n    decimals: 2,\n  },\n]\n```\n\n### 使用示例\n\n```vue\n<avue-data-display :data=\"displayData\" :option=\"{ decimals: 0, animation: true }\" />\n```\n\n## 通用使用模式\n\n大部分数据展示组件遵循相同模式：\n\n```vue\n<component :data=\"data\" :option=\"option\">\n  <!-- 可选插槽自定义 -->\n</component>\n```\n\n各组件的具体配置请参考对应的示例页面：\n- `data/data0` ~ `data/data12` - 基础数据组件示例\n- `data/countdown` - 倒计时\n- `data/dashboard` - 仪表盘\n- `data/list` - 数据列表\n- `data/notice` - 公告\n- `data/product` - 商品卡片\n- `data/profile` - 人员档案\n- `data/rank` - 排行榜\n- `data/statistic` - 统计值\n- `data/task` - 任务列表\n- `data/weather` - 天气卡片\n",".claude/skills/avue-design/references/form-advanced.md":"# avue-form 高级功能参考\n\n## 目录\n1. [字典配置](#字典配置)\n2. [验证规则](#验证规则)\n3. [动态子表单](#动态子表单)\n4. [文件上传](#文件上传)\n5. [字段联动](#字段联动)\n6. [数据格式](#数据格式)\n7. [自定义插槽](#自定义插槽)\n8. [Mock数据](#mock数据)\n\n---\n\n## 字典配置\n\n### 安装配置\n\n字典组件需要 axios：\n```javascript\nimport axios from 'axios'\napp.use(Avue, { axios })\n```\n\n### 本地字典\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n}\n```\n\n3.5.0+ 支持函数和 Promise：\n```javascript\n{\n  dicData: () => {\n    return [{ label: 'A', value: 1 }]\n  },\n  // 或\n  dicData: async () => {\n    const res = await fetchOptions()\n    return res.data\n  },\n}\n```\n\n### 远程字典\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'select',\n  dicUrl: '/api/dept/list',\n  dicMethod: 'GET',      // 请求方法，默认GET\n  dicQuery: { status: 1 }, // 请求参数\n  dicHeaders: {},          // 请求头\n  props: {\n    label: 'name',\n    value: 'id',\n    children: 'children',\n    res: 'data.records',  // 响应数据层级\n  },\n}\n```\n\n### 字典格式化\n\n```javascript\n{\n  dicUrl: '/api/dict/list',\n  dicFormatter: (res) => {\n    // 自定义处理响应数据\n    return res.data.map(item => ({\n      label: item.dictValue,\n      value: item.dictKey,\n    }))\n  },\n}\n```\n\n### 字典联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city', 'district'],  // 指定子级字段\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}',  // {{key}} = 父级选中值\n    cascader: ['district'],\n  },\n  {\n    label: '区县',\n    prop: 'district',\n    type: 'select',\n    dicUrl: '/api/district?city={{key}}',\n  },\n]\n```\n\n### 动态更新字典\n\n```javascript\n// 更新指定字段字典\nformRef.value.updateDic('type', [\n  { label: '新选项A', value: 'a' },\n  { label: '新选项B', value: 'b' },\n])\n\n// 重新加载所有字典\nformRef.value.dicInit()\n```\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 值存储为字符串\n  separator: ',',         // 分隔符，默认逗号\n  // 实际存储: \"1,2,3\"\n  // 组件显示: [1, 2, 3]\n}\n\n{\n  label: '年龄',\n  prop: 'age',\n  dataType: 'number',    // 强制转数字\n}\n```\n\n---\n\n## 验证规则\n\n基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n### 基本验证\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  rules: [\n    { required: true, message: '请输入名称', trigger: 'blur' },\n    { min: 2, max: 20, message: '长度在2-20之间', trigger: 'blur' },\n  ],\n}\n```\n\n### 类型验证\n\n```javascript\nrules: [\n  { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n  { type: 'url', message: '请输入正确的URL', trigger: 'blur' },\n  { type: 'number', message: '请输入数字', trigger: 'blur' },\n]\n```\n\n### 自定义验证\n\n```javascript\n{\n  label: '密码',\n  prop: 'password',\n  rules: [{\n    required: true,\n    validator: (rule, value, callback) => {\n      if (!value) {\n        callback(new Error('请输入密码'))\n      } else if (value.length < 6) {\n        callback(new Error('密码长度不能少于6位'))\n      } else {\n        callback()\n      }\n    },\n    trigger: 'blur',\n  }],\n}\n```\n\n### 表单验证方法\n\n```javascript\n// 验证整个表单\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败\n    console.log('错误字段:', fields)\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'email'], (errorMsg) => {\n  if (!errorMsg) console.log('验证通过')\n})\n\n// 清除验证\nformRef.value.clearValidate()            // 清除所有\nformRef.value.clearValidate(['name'])    // 清除指定字段\n\n// 重置表单\nformRef.value.resetFields()\n```\n\n### 搜索验证 (CRUD)\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  searchRules: [{ required: true, message: '请选择日期', trigger: 'change' }],\n}\n```\n\n---\n\n## 动态子表单\n\n### 表格模式\n\n```javascript\n{\n  label: '订单明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    index: true,\n    column: [\n      {\n        label: '商品名称',\n        prop: 'productName',\n        rules: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],\n      },\n      {\n        label: '数量',\n        prop: 'quantity',\n        type: 'number',\n        min: 1,\n      },\n      {\n        label: '单价',\n        prop: 'price',\n        type: 'number',\n        precision: 2,\n      },\n    ],\n  },\n}\n```\n\n### 表单模式\n\n```javascript\n{\n  label: '联系方式',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  limit: 5,  // 最多5条\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 8 },\n      { label: '电话', prop: 'phone', span: 8 },\n      { label: '职位', prop: 'position', span: 8 },\n    ],\n  },\n}\n```\n\n### 自定义增删\n\n```javascript\n{\n  type: 'dynamic',\n  rowAdd: (done) => {\n    // 自定义新增逻辑\n    done({ name: '默认值', quantity: 1 })\n  },\n  rowDel: (row, done) => {\n    ElMessageBox.confirm('确定删除?').then(() => done())\n  },\n}\n```\n\n### 父子联动\n\n动态子表单支持与父表单字段联动。\n\n---\n\n## 文件上传\n\n### 基本上传\n\n```javascript\n{\n  label: '附件',\n  prop: 'file',\n  type: 'upload',\n  action: '/api/upload',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n    fileName: 'file',\n  },\n}\n```\n\n### 上传回调\n\n```vue\n<avue-form\n  :upload-before=\"uploadBefore\"\n  :upload-after=\"uploadAfter\"\n  :upload-delete=\"uploadDelete\"\n  :upload-preview=\"uploadPreview\"\n  :upload-error=\"uploadError\"\n  :upload-exceed=\"uploadExceed\"\n>\n\nconst uploadBefore = (file, done, loading) => {\n  // 验证文件\n  if (file.size > 10 * 1024 * 1024) {\n    ElMessage.error('文件不能超过10MB')\n    loading()  // 中断上传\n    return\n  }\n  done(file)  // 继续上传\n}\n\nconst uploadAfter = (res, done) => {\n  // 上传完成\n  done()\n}\n\nconst uploadDelete = (file, column) => {\n  // 返回 false 或 rejected Promise 阻止删除\n  return new Promise((resolve, reject) => {\n    ElMessageBox.confirm('确定删除?')\n      .then(() => resolve())\n      .catch(() => reject())\n  })\n}\n\nconst uploadPreview = (file, column, done) => {\n  // 自定义预览\n  window.open(file.url)\n}\n\nconst uploadError = (error, column) => {\n  ElMessage.error('上传失败')\n}\n\nconst uploadExceed = (limit, files, fileList, column) => {\n  ElMessage.warning(`最多上传${limit}个文件`)\n}\n```\n\n### 图片水印\n\n```javascript\n{\n  type: 'upload',\n  canvasOption: {\n    text: '公司名称',\n    fontFamily: 'microsoft yahei',\n    color: 'rgba(0,0,0,0.3)',\n    fontSize: 20,\n    opacity: 50,\n    bottom: 20,\n    right: 20,\n    ratio: 0.8,\n  },\n}\n```\n\n### 云存储上传\n\n全局配置见 `global-config.md`。\n\n```javascript\n// 阿里云OSS\n{ type: 'upload', oss: 'ali' }\n\n// 七牛云\n{ type: 'upload', oss: 'qiniu' }\n\n// 腾讯COS\n{ type: 'upload', oss: 'cos' }\n```\n\n### 数据格式\n\n```javascript\n// 字符串格式（逗号分隔URL）\n{ type: 'upload', dataType: 'string' }\n\n// 对象格式\n{\n  type: 'upload',\n  dataType: 'object',\n  props: { label: 'name', value: 'url' },\n}\n\n// JSON字符串格式\n{ type: 'upload', dataType: 'json' }\n```\n\n---\n\n## 字段联动\n\n### control 属性\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'radio',\n  dicData: [\n    { label: '个人', value: 1 },\n    { label: '企业', value: 2 },\n  ],\n  control: (val, form) => {\n    if (val === 1) {\n      return {\n        idCard: { display: true, rules: [{ required: true }] },\n        companyName: { display: false },\n        businessLicense: { display: false },\n      }\n    } else {\n      return {\n        idCard: { display: false },\n        companyName: { display: true, rules: [{ required: true }] },\n        businessLicense: { display: true },\n      }\n    }\n  },\n},\n{ label: '身份证', prop: 'idCard' },\n{ label: '企业名称', prop: 'companyName', display: false },\n{ label: '营业执照', prop: 'businessLicense', type: 'upload', display: false },\n```\n\n### control 返回值可控制的属性\n\n可以动态控制字段的以下属性：\n- `display` - 显示/隐藏\n- `disabled` - 启用/禁用\n- `rules` - 验证规则\n- `dicData` - 字典数据\n- `label` - 标签文字\n- 其他 column 属性\n\n### Promise 支持 (3.5.0+)\n\n```javascript\ncontrol: async (val, form) => {\n  const res = await getSubOptions(val)\n  return {\n    subType: {\n      dicData: res.data,\n      display: true,\n    },\n  }\n}\n```\n\n### 获取组件引用\n\n```javascript\nconst ref = formRef.value.getPropRef('tableProp')\n// 可用于操作 input-table 等复杂组件\n```\n\n---\n\n## 数据格式\n\n### 深层数据绑定\n\n```javascript\n// 数据: { id: 1, info: { name: '张三', address: { city: '北京' } } }\ncolumn: [\n  {\n    label: '姓名',\n    prop: 'name',\n    bind: 'info.name',\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    bind: 'info.address.city',\n  },\n]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤 $xxx 字典翻译字段\n  filterNull: true,  // 过滤空值字段\n  filterParams: ['id'], // 不被清空的字段（默认包含 rowKey）\n}\n```\n\n---\n\n## 自定义插槽\n\n### 字段内容插槽\n\n```vue\n<avue-form v-model=\"form\" :option=\"option\">\n  <!-- 自定义字段 -->\n  <template #name=\"{ value, column, dic, size, disabled }\">\n    <el-input v-model=\"form.name\" :disabled=\"disabled\" />\n  </template>\n\n  <!-- 自定义标签 -->\n  <template #name-label=\"{ column }\">\n    <span style=\"color: red\">*</span> {{ column.label }}\n  </template>\n\n  <!-- 自定义错误 -->\n  <template #name-error=\"{ error }\">\n    <span style=\"color: orange\">{{ error }}</span>\n  </template>\n\n  <!-- 自定义描述 (3.6.4+) -->\n  <template #name-desc=\"{ column }\">\n    <el-text type=\"info\">请输入真实姓名</el-text>\n  </template>\n\n  <!-- 自定义按钮 -->\n  <template #menu-form>\n    <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n    <el-button @click=\"handleDraft\">保存草稿</el-button>\n  </template>\n</avue-form>\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-switch', {\n      modelValue: form.value.status,\n      'onUpdate:modelValue': (val) => { form.value.status = val },\n    })\n  },\n}\n```\n\n### 第三方组件集成\n\n```vue\n<template #editor=\"{ value, column }\">\n  <vue-editor v-model=\"form.content\" />\n</template>\n```\n\n### className 样式控制\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  className: 'highlight-field',\n}\n```\n\n```css\n.highlight-field {\n  background: #fef0f0;\n  border-left: 3px solid #f56c6c;\n}\n```\n\n---\n\n## Mock数据\n\n需引入 Mock.js：\n```html\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n\n```javascript\noption: {\n  mockBtn: true,  // 显示Mock按钮\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      mock: { type: 'cname' },  // 中文姓名\n    },\n    {\n      label: '邮箱',\n      prop: 'email',\n      mock: { type: 'email' },\n    },\n    {\n      label: '地址',\n      prop: 'address',\n      mock: { type: 'county', prefix: true },\n    },\n  ],\n}\n\n// 或整体 mock 函数\noption: {\n  mock: () => ({\n    name: '张三',\n    email: 'zhangsan@test.com',\n    address: '北京市朝阳区',\n  }),\n}\n```\n",".claude/skills/avue-design/references/form-core.md":"# avue-form 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表单级属性](#option-表单级属性)\n3. [布局配置](#布局配置)\n4. [按钮配置](#按钮配置)\n5. [Column 通用属性](#column-通用属性)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n---\n\n## 组件属性\n\n在 `<avue-form>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| option | object | - | 表单配置项 |\n| upload-before | function(file, done, loading) | - | 上传前回调 |\n| upload-after | function(res, done) | - | 上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 预览文件回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小 (3.4.4+) |\n| v-model:status | boolean | false | 表单提交状态（禁用状态双向绑定） |\n\n---\n\n## Option 表单级属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 字段配置数组 |\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置: left/right/top |\n| labelSuffix | string | ':' | 标签后缀 |\n| enter | boolean | false | 回车提交 |\n| group | array | - | 分组配置 |\n| tabs | boolean | false | 分组标签页显示 |\n| detail | boolean | false | 详情/只读模式 |\n| disabled | boolean | false | 全部禁用 |\n| readonly | boolean | false | 全部只读 |\n| formWidth | string | - | 表单容器宽度 |\n| tabsActive | number | 0 | 默认激活标签页索引 |\n| tabsType | string | - | 标签页类型(同 el-tabs type) |\n| tabsVerifyAll | boolean | false | 提交时验证所有标签页（默认仅当前页） |\n| filterDic | boolean | false | 过滤 $ 开头的字典翻译字段 |\n| filterNull | boolean | false | 过滤空值字段 |\n| filterParams | array | - | 重置时保留的字段 |\n\n---\n\n## 布局配置\n\n### 栅格布局\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 字段占用栅格数 (24栅格) |\n| gutter | number | 0 | 栅格间距 |\n| offset | number | 0 | 栅格左偏移 |\n| push | number | 0 | 栅格右移动 |\n| pull | number | 0 | 栅格左移动 |\n| row | boolean | false | 是否新起一行 |\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', span: 12 },\n  { label: '编码', prop: 'code', span: 12 },\n  { label: '备注', prop: 'remark', span: 24, row: true },\n]\n```\n\n### 分组显示\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name', span: 12 },\n        { label: '编码', prop: 'code', span: 12 },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', span: 24 },\n      ],\n    },\n  ],\n}\n```\n\n#### Group 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 分组标题 |\n| prop | string | - | 分组标识 |\n| icon | string | - | 分组图标 |\n| collapse | boolean | true | 是否可折叠 |\n| arrow | boolean | true | 是否显示折叠箭头 |\n| display | boolean | true | 是否显示该分组 |\n| header | boolean | true | 是否显示分组头部 |\n| column | array | - | 分组内的字段配置 |\n\n### 标签页显示\n\n```javascript\noption: {\n  tabs: true,\n  // tabsVerifyAll: true, // 提交时验证所有标签页\n  group: [\n    {\n      label: '基本信息',\n      prop: 'tab1',\n      column: [...],\n    },\n    {\n      label: '扩展信息',\n      prop: 'tab2',\n      column: [...],\n    },\n  ],\n}\n```\n\n### 帮助文字\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    tip: '请输入唯一标识名称',      // 字段右侧帮助文字\n    tipPlacement: 'bottom',          // 帮助文字方向\n    labelTip: '该字段用于系统唯一标识', // 标签帮助文字\n    labelTipPlacement: 'bottom',\n  },\n]\n```\n\n### className 样式\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    className: 'custom-field-class',\n  },\n]\n```\n\n---\n\n## 按钮配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menuBtn | boolean | true | 显示按钮区 |\n| menuSpan | number | 24 | 按钮栅格数 |\n| menuPosition | string | 'center' | 按钮位置: left/center/right |\n| submitBtn | boolean | true | 显示提交按钮 |\n| submitText | string | '提交' | 提交按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyText | string | '清空' | 清空按钮文字 |\n| printBtn | boolean | false | 显示打印按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮（需引入Mock.js） |\n| mockText | string | 'Mock' | Mock按钮文字 |\n| printText | string | '打印' | 打印按钮文字 |\n| submitIcon | string | - | 提交按钮图标 |\n| emptyIcon | string | - | 清空按钮图标 |\n\n---\n\n## Column 通用属性\n\n每个字段的通用配置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 标签文字 |\n| prop | string | - | 字段属性名 |\n| type | string | 'input' | 字段类型 |\n| value | any | - | 默认值 |\n| bind | string | - | 深层数据绑定 |\n| disabled | boolean | false | 禁用 |\n| display | boolean | true | 显示 |\n| order | number | - | 排序，数字越大越靠前 |\n| size | string | 'small' | 组件尺寸 |\n| separator | string | ',' | 选项分隔符 (string dataType) |\n| rules | array | - | 验证规则 |\n\n### 栅格属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 栅格数 |\n| gutter | number | 0 | 间距 |\n| offset | number | 0 | 偏移 |\n| push | number | 0 | 右移 |\n| pull | number | 0 | 左移 |\n| row | boolean | false | 换行 |\n\n### 标签属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 内容帮助方向 |\n| labelTip | string | - | 标签帮助文字 |\n| labelTipPlacement | string | 'top' | 标签帮助方向 |\n\n### 联动控制\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [{ label: 'A', value: 1 }, { label: 'B', value: 2 }],\n  control: (val, form) => {\n    return {\n      subField: {\n        display: val === 1,\n        disabled: val === 2,\n        dicData: val === 1 ? listA : listB,\n      }\n    }\n  },\n}\n```\n\n### 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-tag', {}, '自定义内容')\n  },\n}\n```\n\n---\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| submit | (form, done) | 表单提交回调。done() 关闭loading |\n| reset-change | - | 表单清空回调 |\n| change | (form) | 表单数据变化时触发 |\n| tab-click | (tab, event) | 标签页点击时触发 |\n| error | (error) | 表单验证失败时触发 |\n| mock-change | (form) | Mock 数据生成后触发 |\n\n```javascript\n// Composition API\nconst handleSubmit = (form, done) => {\n  saveData(form).then(() => {\n    ElMessage.success('保存成功')\n    done()  // 关闭loading\n  }).catch(() => {\n    done()\n  })\n}\n\nconst handleReset = () => {\n  // 清空后的逻辑\n}\n```\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| submit | - | 触发表单提交 |\n| resetForm | - | 重置表单 |\n| updateDic | (prop, dic) | 更新指定字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | callback(boolean, object) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置表单到初始值 |\n| clearValidate | (props) | 清除验证结果 |\n| show | - | 禁用表单（显示loading状态） |\n| hide | - | 启用表单（隐藏loading状态） |\n| scrollToField | (prop) | 滚动到指定字段 |\n| getField | (prop) | 获取字段实例 |\n\n```javascript\n// 手动验证\nconst formRef = ref(null)\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败，fields 包含错误信息\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'code'], (errorMsg) => {\n  if (!errorMsg) {\n    // 验证通过\n  }\n})\n```\n\n---\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义字段内容 |\n| `{prop}-label` | 自定义字段标签 |\n| `{prop}-error` | 自定义错误提示 |\n| `{prop}-desc` | 自定义描述 (3.6.4+) |\n| menu-form | 自定义按钮区域 |\n| menu-form-before | 按钮区域前内容 (3.4.3+) |\n\n```vue\n<!-- 自定义字段 -->\n<template #name=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"formData.name\" />\n</template>\n\n<!-- 自定义标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义按钮 -->\n<template #menu-form>\n  <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n  <el-button @click=\"handleCancel\">取消</el-button>\n</template>\n```\n\n---\n\n## 独立表单使用示例\n\n### Composition API\n\n```vue\n<template>\n  <avue-form ref=\"formRef\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script setup>\nimport { ref, reactive } from 'vue'\nimport { ElMessage } from 'element-plus'\n\nconst formRef = ref(null)\nconst formData = ref({})\n\nconst option = reactive({\n  labelWidth: 120,\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      span: 12,\n      rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n    },\n    {\n      label: '性别',\n      prop: 'gender',\n      type: 'radio',\n      span: 12,\n      dicData: [\n        { label: '男', value: 1 },\n        { label: '女', value: 2 },\n      ],\n    },\n  ],\n})\n\nconst handleSubmit = (form, done) => {\n  console.log('提交数据:', form)\n  done()\n}\n</script>\n```\n\n### Options API\n\n```vue\n<template>\n  <avue-form ref=\"form\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      formData: {},\n      option: {\n        labelWidth: 120,\n        column: [\n          {\n            label: '姓名',\n            prop: 'name',\n            span: 12,\n            rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n          },\n          {\n            label: '性别',\n            prop: 'gender',\n            type: 'radio',\n            span: 12,\n            dicData: [\n              { label: '男', value: 1 },\n              { label: '女', value: 2 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  methods: {\n    handleSubmit(form, done) {\n      console.log('提交数据:', form)\n      done()\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 数据类型与过滤\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 存储为逗号分隔字符串\n  separator: ',',         // 分隔符，默认逗号\n}\n// 值: \"1,2,3\" 而不是 [1, 2, 3]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,  // 过滤 $ 开头的字典翻译字段\n  filterNull: true, // 过滤空值字段\n}\n```\n",".claude/skills/avue-design/references/form-field-types.md":"# avue-form 字段类型完整参考\n\n本文档列出 avue-form / avue-crud 中所有可用的字段类型及其完整配置属性。\n\n## 目录\n\n1. [input - 文本输入](#input---文本输入)\n2. [number - 数字输入](#number---数字输入)\n3. [select - 下拉选择](#select---下拉选择)\n4. [cascader - 级联选择](#cascader---级联选择)\n5. [checkbox - 多选框](#checkbox---多选框)\n6. [radio - 单选框](#radio---单选框)\n7. [date - 日期选择](#date---日期选择)\n8. [time - 时间选择](#time---时间选择)\n9. [switch - 开关](#switch---开关)\n10. [upload - 文件上传](#upload---文件上传)\n11. [tree - 树选择](#tree---树选择)\n12. [input-tag - 标签输入](#input-tag---标签输入)\n13. [mention - 提及](#mention---提及)\n14. [input-table - 表格选择](#input-table---表格选择)\n15. [color - 颜色选择](#color---颜色选择)\n16. [icon - 图标选择](#icon---图标选择)\n17. [map - 地图选择](#map---地图选择)\n18. [rate - 评分](#rate---评分)\n19. [slider - 滑块](#slider---滑块)\n20. [array/img/url - 数组输入](#arrayimgurl---数组输入)\n21. [dynamic - 动态子表单](#dynamic---动态子表单)\n22. [title - 标题分隔](#title---标题分隔)\n23. [input-cron - Cron表达式](#input-cron---cron表达式)\n\n---\n\n## 通用属性（所有类型共享）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '请输入/请选择'+label | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| readonly | boolean | false | 只读 |\n| disabled | boolean | false | 禁用 |\n| change | function({column, value}) | - | 值变化事件 |\n| click | function({column, value}) | - | 点击事件 |\n| focus | function({column, value}) | - | 聚焦事件 |\n| blur | function({column, value}) | - | 失焦事件 |\n\n---\n\n## input - 文本输入\n\n`type: 'input'`（默认类型，可省略）\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| showWordLimit | boolean | false | 显示字数统计 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prependClick | function | - | 前置文字点击 |\n| appendClick | function | - | 后置文字点击 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| showPassword | boolean | true | 密码切换图标 |\n| autocomplete | string | 'off' | HTML autocomplete 属性 |\n| formatters | function | - | 格式化函数 (3.7.0+) |\n| parser | function | - | 解析函数（formatters 的逆操作）(3.7.0+) |\n| target | string | '_blank' | 链接目标 (url类型时有效) |\n\n### textarea 子类型\n\n```javascript\n{ type: 'textarea', rows: 4, minRows: 2, maxRows: 8 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rows | number | 2 | 文本域行数 |\n| minRows | number | 2 | 最小行数 |\n| maxRows | number | 6 | 最大行数 |\n\n### password 子类型\n\n```javascript\n{ type: 'password', showPassword: true }\n```\n\n### phone 子类型\n\n```javascript\n{ type: 'phone', defaultPhoneCode: '+86' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultPhoneCode | string | '+86' | 默认区号 |\n| phoneCodeOptions | array | - | 区号选项 |\n| phoneMaxLength | number | 11 | 手机号最大长度 |\n| phoneCodePlaceholder | string | '区号' | 区号占位 |\n| phonePlaceholder | string | '请输入手机号' | 手机号占位 |\n\n### currency 子类型\n\n```javascript\n{ type: 'currency', currency: 'CNY', currencyPrecision: 2 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currency | string | 'CNY' | 货币类型 |\n| currencySymbolCustom | string | - | 自定义货币符号 |\n| currencyUnit | string | - | 货币单位 |\n| currencyPrecision | number | 2 | 小数位数 |\n| currencyPlaceholder | string | '请输入金额' | 占位 |\n| currencyMin | number | - | 最小值 |\n| currencyMax | number | - | 最大值 |\n\n### bankCard 子类型\n\n```javascript\n{ type: 'bankCard', showBankCardType: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| bankCardSeparator | string | ' ' | 分隔符 |\n| bankCardSegment | number | 4 | 分段长度 |\n| bankCardMaxDigits | number | 19 | 最大长度 |\n| bankCardPlaceholder | string | '请输入银行卡号' | 占位 |\n| showBankCardType | boolean | true | 显示卡类型 |\n\n### idCard 子类型\n\n```javascript\n{ type: 'idCard', showIdCardInfo: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| idCardPlaceholder | string | '请输入身份证号' | 占位 |\n| showIdCardInfo | boolean | true | 显示身份信息 |\n| idCardRealtimeValidate | boolean | true | 实时验证 |\n\n### email 子类型\n\n```javascript\n{ type: 'email', showEmailSuffix: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| emailPlaceholder | string | '请输入邮箱' | 占位 |\n| emailSuffixes | array | ['@qq.com','@163.com',...] | 邮箱后缀建议 |\n| showEmailSuffix | boolean | true | 显示建议 |\n\n### code 子类型\n\n```javascript\n{ type: 'code', codeLength: 6 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| codePlaceholder | string | - | 占位 |\n| codeLength | number | 6 | 验证码长度 |\n| codeSeparator | string | - | 分隔符 |\n\n### plate 子类型（车牌号）\n\n```javascript\n{ type: 'plate', defaultPlateProvince: '京' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| platePlaceholder | string | '请输入车牌号' | 占位 |\n| defaultPlateProvince | string | '京' | 默认省份 |\n| plateProvinceList | array | ['京','津','沪',...] | 省份列表 |\n\n### ip 子类型\n\n```javascript\n{ type: 'ip', ipVersion: 4 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| ipPlaceholder | string | '请输入IP地址' | 占位 |\n| ipVersion | number | 4 | IP版本 |\n\n### mac 子类型\n\n```javascript\n{ type: 'mac', macSeparator: ':' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| macPlaceholder | string | '请输入MAC地址' | 占位 |\n| macSeparator | string | ':' | 分隔符 |\n\n### uscc 子类型（统一社会信用代码）\n\n```javascript\n{ type: 'uscc' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| usccPlaceholder | string | '请输入统一社会信用代码' | 占位 |\n| usccRealtimeValidate | boolean | true | 实时验证 |\n\n### search 子类型\n\n```javascript\n{ type: 'search' }\n```\n\n带搜索按钮的输入框，点击搜索按钮触发事件。属性同基础 input。\n\n---\n\n## number - 数字输入\n\n`type: 'number'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | -Infinity | 最小值 |\n| max | number | Infinity | 最大值 |\n| step | number | 1 | 步长 |\n| stepStrictly | boolean | false | 只能输入步长倍数 |\n| precision | number | 2 | 小数精度 |\n| controls | boolean | true | 显示控制按钮 |\n| controlsPosition | string | 'top' | 按钮位置: top/right |\n| suffix | string | - | 后缀文字 (3.5.6+) |\n| prefix | string | - | 前缀文字 (3.5.6+) |\n| suffixClick | function | - | 后缀点击 (3.5.6+) |\n| prefixClick | function | - | 前缀点击 (3.5.6+) |\n| align | string | 'left' | 文本对齐 (3.7.2+) |\n| disabledScientific | boolean | false | 禁用科学计数法 (3.7.2+) |\n\n```javascript\n{\n  label: '价格',\n  prop: 'price',\n  type: 'number',\n  min: 0,\n  max: 99999,\n  step: 0.01,\n  precision: 2,\n  controlsPosition: 'right',\n}\n```\n\n---\n\n## select - 下拉选择\n\n`type: 'select'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| limit | number | - | 多选最大数量 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠标签tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| allowCreate | boolean | false | 允许创建新条目 |\n| filterable | boolean | false | 可搜索 |\n| remote | boolean | false | 远程搜索 |\n| defaultFirstOption | boolean | false | 回车选中第一个 |\n| loadingText | string | '加载中' | 加载文字 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n| virtualize | boolean | false | 虚拟DOM (大数据) |\n| group | boolean | false | 分组显示 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| cascader | array | - | 级联子字段prop |\n| cascaderIndex | number | - | 级联默认选项索引 |\n| emptyValues | array | - | 空值配置 (3.4.4+) |\n| valueOnClear | any | - | 清空时的值 (3.4.4+) |\n| removeTag | function({value, column}) | - | 移除标签事件 (3.7.3+) |\n| all | boolean | false | 显示\"全选\"选项 |\n| noMatchText | string | '无匹配数据' | 无匹配时文字 |\n| noDataText | string | '无数据' | 无数据时文字 |\n\n```javascript\n// 基本用法\n{\n  label: '状态',\n  prop: 'status',\n  type: 'select',\n  dicData: [\n    { label: '启用', value: 1 },\n    { label: '禁用', value: 0 },\n  ],\n}\n\n// 远程搜索\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  remote: true,\n  filterable: true,\n  dicUrl: '/api/user/search?name={{key}}',\n  props: { label: 'name', value: 'id' },\n}\n\n// 自定义模板\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  slot: true,  // 使用 userId + Type 插槽\n}\n\n// 分组\n{\n  label: '城市',\n  prop: 'city',\n  type: 'select',\n  group: true,\n  dicData: [\n    {\n      label: '热门城市',\n      children: [\n        { label: '北京', value: 'bj' },\n        { label: '上海', value: 'sh' },\n      ],\n    },\n  ],\n}\n```\n\n---\n\n## cascader - 级联选择\n\n`type: 'cascader'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAllLevels | boolean | true | 显示完整路径 |\n| tags | boolean | false | 标签显示 |\n| separator | string | '/' | 分隔符 |\n| filterable | boolean | false | 可搜索 |\n| filterMethod | function | - | 自定义搜索逻辑 |\n| expandTrigger | string | 'hover' | 展开触发方式: hover/click |\n| multiple | boolean | false | 多选 |\n| collapseTags | boolean | false | 折叠标签 |\n| collapseTagsTooltip | boolean | false | 折叠tooltip |\n| maxCollapseTags | number | - | 最大显示标签数 |\n| checkStrictly | boolean | false | 父子节点独立 |\n| emitPath | boolean | true | 返回完整路径数组 |\n| lazy | boolean | false | 懒加载 |\n| lazyLoad | function(node, resolve) | - | 懒加载函数 |\n| effect | string | 'dark' | tooltip主题 (3.7.2+) |\n| showCheckedStrategy | string | 'SHOW_ALL' | 选中项回填策略 (3.7.2+) |\n| popperClass | string | - | 下拉框class |\n\n```javascript\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  dicData: [\n    {\n      label: '北京',\n      value: 'bj',\n      children: [\n        { label: '朝阳区', value: 'cy' },\n        { label: '海淀区', value: 'hd' },\n      ],\n    },\n  ],\n}\n\n// 懒加载\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  lazy: true,\n  lazyLoad: (node, resolve) => {\n    const { level, value } = node\n    getAreaChildren(value, level).then(res => {\n      resolve(res.data.map(item => ({\n        label: item.name,\n        value: item.id,\n        leaf: level >= 2,  // 标记叶子节点\n      })))\n    })\n  },\n}\n```\n\n---\n\n## checkbox - 多选框\n\n`type: 'checkbox'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| all | boolean | false | 全选按钮 |\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n| min | number | - | 最少选择数 |\n| max | number | - | 最多选择数 |\n| fill | string | '#409eff' | 选中颜色 |\n| textColor | string | '#ffffff' | 选中文字颜色 |\n\n```javascript\n{\n  label: '爱好',\n  prop: 'hobbies',\n  type: 'checkbox',\n  dicData: [\n    { label: '读书', value: 1 },\n    { label: '运动', value: 2 },\n    { label: '旅行', value: 3 },\n  ],\n  all: true,\n  min: 1,\n  max: 3,\n}\n```\n\n---\n\n## radio - 单选框\n\n`type: 'radio'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n\n```javascript\n{\n  label: '性别',\n  prop: 'gender',\n  type: 'radio',\n  dicData: [\n    { label: '男', value: 1 },\n    { label: '女', value: 2 },\n  ],\n}\n```\n\n---\n\n## date - 日期选择\n\n`type: 'date'`\n\n可用 type 值：`date` | `datetime` | `daterange` | `datetimerange` | `week` | `month` | `year` | `dates` | `monthrange` | `yearrange`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultTime | string[] | - | 范围默认时间 |\n| defaultValue | Date | - | 默认日期 |\n| pickerOptions | object | - | 日期选项 |\n| popperClass | string | - | 下拉框class |\n| prefix-icon | string | - | 前缀图标 |\n| cellClassName | function | - | 自定义单元格class |\n| showNow | boolean | true | 显示\"此刻\"按钮 (3.6.0+) |\n| disabledHours | function | - | 禁用小时 (3.8.2+) |\n| disabledMinutes | function | - | 禁用分钟 (3.8.2+) |\n| disabledSeconds | function | - | 禁用秒 (3.8.2+) |\n| disabledDate | function(date) | - | 禁用日期判断函数 |\n| shortcuts | array/function | - | 快捷日期选项 |\n| editable | boolean | true | 输入框可编辑 |\n| unlinkPanels | boolean | false | 范围选择时取消面板联动 |\n\n**日期格式表：**\n\n| 格式 | 含义 | 示例 |\n|-----|------|------|\n| YYYY | 年 | 2024 |\n| MM | 月 | 01 |\n| DD | 日 | 02 |\n| HH | 时(24h) | 03 |\n| mm | 分 | 04 |\n| ss | 秒 | 05 |\n| X | JS时间戳 | 1483326245000 |\n\n```javascript\n// 日期\n{ type: 'date', format: 'YYYY-MM-DD', valueFormat: 'YYYY-MM-DD' }\n\n// 日期时间\n{ type: 'datetime', format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'YYYY-MM-DD HH:mm:ss' }\n\n// 日期范围\n{ type: 'daterange', startPlaceholder: '开始日期', endPlaceholder: '结束日期' }\n\n// 带默认时间的范围\n{\n  type: 'datetimerange',\n  defaultTime: ['00:00:00', '23:59:59'],\n}\n```\n\n---\n\n## time - 时间选择\n\n`type: 'time'` | `'timerange'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultValue | Date | - | 默认值 |\n| pickerOptions | object | - | 时间选项 |\n| popperClass | string | - | 下拉框class |\n| arrowControl | boolean | false | 箭头控制时间 |\n| editable | boolean | true | 输入框可编辑 |\n\n```javascript\n// 基本时间选择\n{ type: 'time', format: 'HH:mm:ss', valueFormat: 'HH:mm:ss' }\n\n// 固定时间点\n{\n  type: 'time',\n  pickerOptions: {\n    start: '08:00',\n    end: '18:00',\n    step: '00:30',\n  },\n}\n```\n\n> **TimeSelect 模式**：当配置了 `start`、`end`、`step`、`maxTime`、`minTime` 中的任一属性时，组件自动切换为 TimeSelect（固定时间点选择）模式。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | string | - | 开始时间 (TimeSelect) |\n| end | string | - | 结束时间 (TimeSelect) |\n| step | string | - | 时间步长 (TimeSelect) |\n| maxTime | string | - | 最大可选时间 (TimeSelect) |\n| minTime | string | - | 最小可选时间 (TimeSelect) |\n\n```javascript\n// TimeSelect 固定时间点选择\n{\n  type: 'time',\n  start: '08:00',\n  end: '18:00',\n  step: '00:30',\n  maxTime: '17:30',\n}\n```\n\n---\n\n## switch - 开关\n\n`type: 'switch'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| len | number | 40 | 开关宽度 |\n| activeColor | string | '#409EFF' | 打开背景色 |\n| inactiveColor | string | '#C0CCDA' | 关闭背景色 |\n| activeIcon | string | - | 打开图标 (3.2.20+) |\n| inactiveIcon | string | - | 关闭图标 (3.2.20+) |\n| activeActionIcon | string | - | 打开动作图标 (3.2.20+) |\n| inactiveActionIcon | string | - | 关闭动作图标 (3.2.20+) |\n| inlinePrompt | boolean | false | 只显示首字符 (3.2.20+) |\n| beforeChange | function(done) | - | 切换前钩子 (3.2.20+) |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  type: 'switch',\n  dicData: [\n    { label: '关闭', value: 0 },\n    { label: '开启', value: 1 },\n  ],\n}\n```\n\n---\n\n## upload - 文件上传\n\n`type: 'upload'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| action | string | **必填** | 上传地址 |\n| accept | string/array | - | 文件类型限制 |\n| fileSize | number | - | 文件大小限制(KB) |\n| limit | number | - | 最大文件数 |\n| multiple | boolean | false | 多文件上传 |\n| directory | boolean | false | 文件夹上传 (3.8.0+) |\n| listType | string | 'text' | 文件列表类型: text/picture/picture-card |\n| showFileList | boolean | true | 显示文件列表 |\n| fileType | string | 'img' | 文件类型: img/audio/video |\n| fileText | string | '点击上传' | 上传按钮文字 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| data | object | - | 额外参数 |\n| headers | object | - | 请求头 |\n| httpRequest | function(file, column) | - | 自定义上传逻辑 |\n| canvasOption | object | - | 图片水印配置 |\n| propsHttp | object | - | 响应结构配置 |\n| dataType | string | - | 数据类型: string/object/json |\n| loadText | string | - | 加载文字 |\n| tip | string | - | 提示文字 |\n| dragFile | boolean | false | 拖拽上传区域（区别于 drag 拖拽排序）|\n| oss | string | - | 云存储类型: ali/qiniu/cos |\n| paramsList | array | - | 额外参数列表 |\n| cropperOption | object | - | 图片裁剪配置 |\n\n### propsHttp 响应配置\n\n| 属性 | 类型 | 说明 |\n|-----|------|------|\n| home | string | 图片根路径 |\n| res | string | 响应层级 |\n| url | string | 图片地址属性 |\n| name | string | 图片名称属性 |\n| fileName | string | 上传文件名（默认'file'） |\n\n### canvasOption 水印配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | 'avuejs.com' | 水印文字 |\n| fontFamily | string | 'microsoft yahei' | 字体 |\n| color | string | '#999' | 颜色 |\n| fontSize | number | 16 | 字号 |\n| opacity | number | 100 | 透明度 |\n| bottom | number | 10 | 底部距离 |\n| right | number | 10 | 右侧距离 |\n| ratio | number | 1 | 压缩比(0-1) |\n\n```javascript\n{\n  label: '附件',\n  prop: 'files',\n  type: 'upload',\n  action: '/api/upload',\n  listType: 'picture-card',\n  multiple: true,\n  limit: 5,\n  fileSize: 10240,\n  accept: 'image/*',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n  },\n  tip: '支持jpg/png格式，单文件不超过10MB',\n}\n```\n\n---\n\n## tree - 树选择\n\n`type: 'tree'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| checkStrictly | boolean | false | 父子独立 |\n| parent | boolean | true | 父节点可选 |\n| accordion | boolean | false | 手风琴模式 |\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| expandOnClickNode | boolean | false | 点击展开 |\n| checkOnClickLeaf | boolean | false | 点击叶子勾选 (3.6.3+) |\n| defaultExpandedKeys | array | - | 默认展开keys |\n| defaultCheckedKeys | array | - | 默认勾选keys |\n| filterable | boolean | false | 可筛选 |\n| filterText | string | '请输入搜索关键字' | 筛选占位 |\n| iconClass | string | - | 自定义图标 |\n| leafOnly | boolean | true | 仅叶子节点 |\n| lazy | boolean | false | 懒加载 |\n| treeLoad | function | - | 加载函数 |\n| cacheData | array | - | 懒加载缓存 (3.2.20+) |\n| nodeClick | function | - | 节点点击回调 |\n| checked | function | - | 勾选回调 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'tree',\n  dicUrl: '/api/dept/tree',\n  props: { label: 'name', value: 'id' },\n  defaultExpandAll: true,\n  checkStrictly: true,\n}\n```\n\n---\n\n## input-tag - 标签输入 (3.6.0+)\n\n`type: 'input-tag'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| drag | boolean | false | 拖拽排序 |\n| size | string | - | 尺寸: large/small |\n| min | number | -Infinity | 最小标签数 |\n| max | number | Infinity | 最大标签数 |\n| prefix | string | - | 前置内容 |\n| prefixClick | function | - | 前置内容点击回调 |\n| suffix | string | - | 后置内容 |\n| suffixClick | function | - | 后置内容点击回调 |\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'input-tag',\n  // 按回车添加标签\n}\n```\n\n---\n\n## mention - 提及 (3.6.0+)\n\n`type: 'mention'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | - | 最大提及数 |\n| remote | boolean | false | 远程搜索 |\n| maxlength | number | - | 最大长度 |\n| minlength | number | - | 最小长度 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| split | string | ',' | 提及分隔符 |\n| prefix | string | '@' | 触发前缀 |\n| whole | boolean | false | 仅匹配完整提及 |\n| checkIsWhole | function | - | 自定义完整匹配检测 |\n| prependClick | function | - | 前置点击回调 |\n| appendClick | function | - | 后置点击回调 |\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  dicData: [\n    { label: '张三', value: 'zhangsan' },\n    { label: '李四', value: 'lisi' },\n  ],\n}\n\n// 远程搜索\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  remote: true,\n  dicUrl: '/api/user/search?name={{key}}',\n}\n```\n\n---\n\n## input-table - 表格选择\n\n`type: 'input-table'`\n\n内部使用 avue-crud 组件，配置参考 crud 文档。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| formatter | function | - | 数据格式化 |\n| onLoad | function | - | 加载/查询/分页方法 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| multiple | boolean | false | 多选 |\n| children | object | - | 表格 CRUD 配置（支持所有 avue-crud option 属性） |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n\n```javascript\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'input-table',\n  formatter: (row) => row.name,\n  onLoad: ({ page, value, data }, callback) => {\n    getList(page.currentPage, page.pageSize, data).then(res => {\n      callback({\n        total: res.data.total,\n        data: res.data.records,\n      })\n    })\n  },\n  children: {\n    border: true,\n    column: [\n      { label: '姓名', prop: 'name', search: true },\n      { label: '手机号', prop: 'phone' },\n    ],\n  },\n}\n```\n\n---\n\n## color - 颜色选择\n\n`type: 'color'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAlpha | boolean | true | 显示透明度 |\n| colorFormat | string | 'rgb' | 颜色格式: hsl/hsv/hex/rgb |\n| popperClass | string | - | 下拉框class |\n| predefine | array | - | 预定义颜色 |\n\n```javascript\n{\n  label: '主题色',\n  prop: 'themeColor',\n  type: 'color',\n  colorFormat: 'hex',\n  predefine: ['#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff'],\n}\n```\n\n---\n\n## icon - 图标选择\n\n`type: 'icon'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| iconList | array | - | 图标数组 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n```javascript\n{\n  label: '图标',\n  prop: 'icon',\n  type: 'icon',\n  iconList: [\n    { label: '首页', value: 'el-icon-s-home' },\n    { label: '设置', value: 'el-icon-setting' },\n  ],\n}\n```\n\niconList 支持分组结构：\n```javascript\niconList: [\n  {\n    label: 'Element Plus',\n    list: [\n      { label: 'Plus', value: 'el-icon-plus' },\n      { label: 'Edit', value: 'el-icon-edit' },\n    ]\n  },\n  {\n    label: 'Font Awesome',\n    list: [\n      { label: 'Home', value: 'fa fa-home' },\n    ]\n  }\n]\n```\n\n需引入图标库：\n```html\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n```\n\n---\n\n## map - 地图选择\n\n`type: 'map'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| params | object | - | 高德地图初始化参数 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| mapChange | function | - | 地图变化回调 |\n| rows | number | - | 文本域行数 |\n| minRows | number | 1 | 最小行数 |\n| maxRows | number | - | 最大行数 |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n需引入高德地图SDK（见 global-config.md）。\n\n```javascript\n{\n  label: '位置',\n  prop: 'location',\n  type: 'map',\n  params: { zoom: 15, center: [116.397428, 39.90923] },\n}\n```\n\n> 值格式为数组：`[经度, 纬度, 格式化地址]`，需引入高德地图 SDK。\n\n---\n\n## rate - 评分\n\n`type: 'rate'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | 5 | 最大分数 |\n| allowHalf | boolean | false | 允许半星 |\n| lowThreshold | number | 2 | 低分界限 |\n| highThreshold | number | 4 | 高分界限 |\n| colors | array/object | ['#F7BA2A','#F7BA2A','#F7BA2A'] | 颜色 |\n| voidColor | string | '#C6D1DE' | 未选颜色 |\n| disabledVoidColor | string | '#EFF2F7' | 禁用未选颜色 |\n| iconClasses | array/object | - | 图标class |\n| voidIconClass | string | 'el-icon-star-off' | 未选图标 |\n| showText | boolean | false | 显示文字 |\n| showScore | boolean | false | 显示分数 |\n| textColor | string | '#1F2D3D' | 文字颜色 |\n| texts | array | ['极差','失望','一般','满意','惊喜'] | 文字列表 |\n| scoreTemplate | string | '{value}' | 分数模板 |\n\n```javascript\n{\n  label: '评分',\n  prop: 'score',\n  type: 'rate',\n  max: 5,\n  allowHalf: true,\n  showText: true,\n  texts: ['极差', '失望', '一般', '满意', '惊喜'],\n}\n```\n\n---\n\n## slider - 滑块\n\n`type: 'slider'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | 0 | 最小值 |\n| max | number | 100 | 最大值 |\n| step | number | 1 | 步长 |\n| showInput | boolean | false | 显示输入框 |\n| showInputControls | boolean | true | 输入框控制按钮 |\n| showStops | boolean | false | 显示间断点 |\n| showTooltip | boolean | true | 显示tooltip |\n| formatTooltip | function | - | tooltip格式化 |\n| tooltipClass | string | - | tooltip class |\n| range | boolean | false | 范围选择 |\n| vertical | boolean | false | 垂直模式 |\n| height | string | - | 垂直模式高度 |\n| marks | object | - | 标记 |\n\n```javascript\n{\n  label: '进度',\n  prop: 'progress',\n  type: 'slider',\n  min: 0,\n  max: 100,\n  step: 10,\n  showStops: true,\n  marks: { 0: '0%', 50: '50%', 100: '100%' },\n}\n```\n\n---\n\n## array/img/url - 数组输入\n\n`type: 'array'` | `'img'` | `'url'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| alone | boolean | false | 单条模式 |\n| limit | number | - | 最大条数 |\n| fileType | string | 'img' | 文件类型 (img类型): img/audio/video |\n\n```javascript\n// 数组输入\n{ label: '标签', prop: 'tags', type: 'array', limit: 5 }\n\n// 图片输入\n{ label: '图片', prop: 'images', type: 'img', limit: 3, fileType: 'img' }\n\n// 链接输入\n{ label: '链接', prop: 'links', type: 'url', limit: 5 }\n```\n\n---\n\n## dynamic - 动态子表单\n\n`type: 'dynamic'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| type | string | 'crud' | 显示类型: crud/form |\n| limit | number | - | 最大行数 |\n| children | object | - | 子表配置 |\n| index | boolean | true | 显示序号 |\n| max | number | - | 最大行数 |\n\n### children 配置属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| type | string | 'crud' | 子表类型: crud/form |\n| column | array | - | 子表字段配置 |\n| index | boolean | true | 显示序号 |\n| indexLabel | string | '#' | 序号标题 |\n| addBtn | boolean | true | 显示新增按钮 |\n| delBtn | boolean | true | 显示删除按钮 |\n| viewBtn | boolean | true | 显示查看按钮 |\n| align | string | - | 内容对齐方式 |\n| headerAlign | string | - | 表头对齐方式 |\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| selectionChange | function | - | 选择变化回调 |\n| sortableChange | function | - | 拖拽排序回调 |\n\n```javascript\n// 表格模式（默认）\n{\n  label: '明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    column: [\n      { label: '商品', prop: 'product' },\n      { label: '数量', prop: 'quantity', type: 'number' },\n      { label: '单价', prop: 'price', type: 'number', precision: 2 },\n    ],\n  },\n}\n\n// 表单模式\n{\n  label: '联系人',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 12 },\n      { label: '电话', prop: 'phone', span: 12 },\n    ],\n  },\n}\n```\n\n---\n\n## title - 标题分隔\n\n`type: 'title'`\n\n用于表单中的分组标题，无输入功能。\n\n```javascript\n{\n  label: '基本信息',\n  prop: 'title1',\n  type: 'title',\n  span: 24,\n}\n```\n\n---\n\n## input-cron - Cron表达式 (3.8.2+)\n\n`type: 'input-cron'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '点击配置Cron表达式' | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| dialogWidth | string | '700px' | 弹窗宽度 |\n\n```javascript\n{\n  label: '定时任务',\n  prop: 'cron',\n  type: 'input-cron',\n  span: 24,\n}\n```\n",".claude/skills/avue-design/references/global-config.md":"# Avue 全局配置参考\n\n## 安装依赖\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n**必须依赖：**\n- `element-plus` - UI组件库\n- `@element-plus/icons-vue` - 图标库\n\n**可选依赖：**\n- `axios` - 字典组件和上传组件需要\n\n## 完整安装示例\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\n\n// 注册所有图标\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\n\napp.use(ElementPlus)\napp.use(Avue)\n```\n\n## 带 axios 的安装（字典/上传组件必需）\n\n```javascript\nimport axios from 'axios'\nconst app = createApp({})\napp.use(Avue, { axios })\n```\n\n## 全局配置选项\n\n```javascript\napp.use(AVUE, {\n  size: 'small',           // 组件默认尺寸: 'small' | 'mini' | 'medium'\n  crudOption: {},           // Crud组件全局默认配置\n  formOption: {},           // Form组件全局默认配置\n  appendToBody: true,       // 弹窗是否追加到body\n  modalAppendToBody: true,  // 遮罩层是否追加到body\n  cos: {},                  // 腾讯云COS配置\n  qiniu: {},                // 七牛云配置\n  ali: {},                  // 阿里云OSS配置\n  canvas: {},               // 全局水印配置\n})\n```\n\n### Crud 全局配置示例\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    border: true,\n    stripe: true,\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    menuWidth: 200,\n    searchLabelWidth: 110,\n    height: 'auto',\n  }\n})\n```\n\n### Form 全局配置示例\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    labelWidth: 120,\n    labelPosition: 'right',\n    size: 'small',\n  }\n})\n```\n\n## 云存储配置\n\n### 阿里云 OSS\n\n```javascript\napp.use(AVUE, {\n  ali: {\n    region: 'oss-cn-beijing',\n    endpoint: 'oss-cn-beijing.aliyuncs.com',\n    stsToken: '',\n    accessKeyId: 'your-access-key-id',\n    accessKeySecret: 'your-access-key-secret',\n    bucket: 'your-bucket-name',\n  }\n})\n```\n\n### 七牛云\n\n```javascript\n// 需引入 CryptoJS：\n// <script src=\"https://cdn.staticfile.net/crypto-js/4.0.0/crypto-js.min.js\"></script>\napp.use(AVUE, {\n  qiniu: {\n    AK: 'your-access-key',\n    SK: 'your-secret-key',\n    scope: 'your-scope',\n    url: 'https://upload.qiniup.com',\n    deadline: 1,\n  }\n})\n```\n\n### 腾讯云 COS\n\n```javascript\n// 需引入 cos-js-sdk-v5：\n// <script src=\"https://cdn.staticfile.net/cos-js-sdk-v5/1.3.2/cos-js-sdk-v5.min.js\"></script>\napp.use(AVUE, {\n  cos: {\n    SecretId: 'your-secret-id',\n    SecretKey: 'your-secret-key',\n    Bucket: 'your-bucket',\n    Region: 'ap-beijing',\n  }\n})\n```\n\n## 全局水印配置\n\n```javascript\napp.use(AVUE, {\n  canvas: {\n    text: 'avuejs.com',        // 水印文字\n    fontFamily: 'microsoft yahei', // 字体\n    color: '#999',              // 颜色\n    fontSize: 16,               // 字号\n    opacity: 100,               // 透明度\n    bottom: 10,                 // 底部距离\n    right: 10,                  // 右侧距离\n    ratio: 1,                   // 压缩比 0-1\n  }\n})\n```\n\n## 国际化\n\n```javascript\nimport Avue from '@smallwei/avue'\nimport enLocale from '@smallwei/avue/lib/locale/lang/en'\nimport zhLocale from '@smallwei/avue/lib/locale/lang/zh'\n\n// 使用英文\napp.use(Avue, { locale: enLocale })\n\n// 配合 vue-i18n\nconst messages = {\n  en: { ...enLocale },\n  zh: { ...zhLocale },\n}\n```\n\n支持的语言：`zh`（简体中文）、`en`（英文）。\n\n## 外部库引入（按需）\n\n```html\n<!-- Excel 导入导出 -->\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n\n<!-- 拖拽排序 -->\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n\n<!-- 高德地图 -->\n<script>\n  window._AMapSecurityConfig = { securityJsCode: 'xxxxx' }\n</script>\n<script src=\"https://webapi.amap.com/maps?v=2.0&key=xxxxxxxx&plugin=AMap.PlaceSearch,AMap.Geocoder\"></script>\n<script src=\"https://webapi.amap.com/ui/1.1/main.js?v=1.0.11\"></script>\n\n<!-- 图标库 -->\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n\n<!-- 流程图 -->\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n\n<!-- 截图 -->\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n\n<!-- PDF导出 -->\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n\n<!-- Mock数据 -->\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n",".claude/skills/avue-design/references/other-components.md":"# Avue 其他组件参考\n\n## 目录\n1. [Card 卡片](#card-卡片)\n2. [DialogForm 弹窗表单](#dialogform-弹窗表单)\n3. [Tabs 选项卡](#tabs-选项卡)\n4. [Search 搜索](#search-搜索)\n5. [Calendar 日历](#calendar-日历)\n6. [Flow 流程图](#flow-流程图)\n7. [Login 登录](#login-登录)\n8. [Article 文章](#article-文章)\n9. [Chat 聊天](#chat-聊天)\n10. [CountUp 数字动画](#countup-数字动画)\n11. [Clipboard 剪切板](#clipboard-剪切板)\n12. [Draggable 拖拽](#draggable-拖拽)\n13. [TextEllipsis 文本省略](#textellipsis-文本省略)\n14. [Sign 电子签名](#sign-电子签名)\n15. [Print 打印](#print-打印)\n16. [Export 导入导出](#export-导入导出)\n17. [ImagePreview 图片预览](#imagepreview-图片预览)\n18. [Contextmenu 右键菜单](#contextmenu-右键菜单)\n19. [Video 摄像头](#video-摄像头)\n20. [Verify 验证码](#verify-验证码)\n21. [Screenshot 截图](#screenshot-截图)\n22. [License 授权书](#license-授权书)\n\n---\n\n## Card 卡片\n\n```vue\n<avue-card :option=\"option\" :data=\"data\" @row-click=\"rowClick\" @row-add=\"rowAdd\">\n  <template #menu=\"{ row, index }\">\n    <el-button @click=\"handleEdit(row)\">编辑</el-button>\n  </template>\n</avue-card>\n```\n\n### Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| span | number | 8 | 栅格数 |\n| gutter | number | 20 | 间距 |\n\n### Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| title | string | 'title' | 标题属性 |\n| img | string | 'img' | 头像属性 |\n| info | string | 'info' | 副标题属性 |\n\n### 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-add | - | 新增 |\n| row-click | (row, index) | 行点击 |\n\n---\n\n## DialogForm 弹窗表单\n\n全局API方式调用 (3.4.1+)：\n\n```javascript\nimport { $DialogForm } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\n\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"确认\",\n    column: [\n      { label: \"姓名\", prop: \"name\", span: 24 },\n    ],\n  },\n})\n\n// 3.5.2+ 主动关闭\ndialog.close()\n```\n\n也可作为组件使用，参考 `default/dialog-form/index` 示例。\n\n---\n\n## Tabs 选项卡\n\n```vue\n<avue-tabs :option=\"option\" @change=\"tabChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Search 搜索\n\n```vue\n<avue-search v-model=\"searchForm\" :option=\"option\" @change=\"searchChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Calendar 日历\n\n```vue\n<avue-calendar :option=\"option\" :data=\"calendarData\" />\n```\n\n---\n\n## Flow 流程图\n\n需引入 jsPlumb：\n```html\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n```\n\n```vue\n<avue-flow :option=\"option\" :width=\"800\" :height=\"600\" @click=\"nodeClick\" />\n```\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| width | number | - | 画布宽度 |\n| height | number | - | 画布高度 |\n| option | object | - | 配置 |\n\n### 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| nodeAdd | (name) | 添加节点 |\n| deleteNode | (id) | 删除节点 |\n\n---\n\n## Login 登录\n\n```vue\n<avue-login :option=\"option\" @submit=\"handleLogin\" />\n```\n\n---\n\n## Article 文章\n\n```vue\n<avue-article :id=\"container\" :offset-top=\"0\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | 'window' | 指定父元素 |\n| offset-top | number | 0 | 触发偏移量 |\n\n---\n\n## Chat 聊天\n\n```vue\n<avue-chat :option=\"option\" />\n```\n\n支持图片、视频、文件、地图等消息类型。\n\n---\n\n## CountUp 数字动画\n\n```vue\n<avue-count-up :start=\"0\" :end=\"12345\" :decimals=\"0\" :duration=\"2\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | number | 0 | 起始数字 |\n| end | number | - | 结束数字 |\n| decimals | number | 0 | 小数位数 |\n| duration | number | 2 | 动画时长(秒) |\n| options | object | - | CountUp.js配置 |\n| callback | function | - | 开始回调 |\n\n---\n\n## Clipboard 剪切板\n\n```vue\n<avue-clipboard :text=\"copyText\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 要复制的文字 |\n\n---\n\n## Draggable 拖拽\n\n```vue\n<avue-draggable :width=\"200\" :height=\"100\" :top=\"50\" :left=\"100\" :disabled=\"false\">\n  <div>可拖拽内容</div>\n</avue-draggable>\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| disabled | boolean | false | 禁用拖拽 |\n| width | string | - | 宽度 |\n| height | string | - | 高度 |\n| top | number | 0 | X位置 |\n| left | number | 0 | Y位置 |\n| z-index | number | 1 | 层级 |\n\n### 事件\n\n| 事件名 | 说明 |\n|-------|------|\n| focus | 获取焦点 |\n| blur | 失去焦点 |\n\n---\n\n## TextEllipsis 文本省略\n\n```vue\n<avue-text-ellipsis :text=\"longText\" :height=\"60\" :is-limit-height=\"true\" :use-tooltip=\"true\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 文本内容 |\n| width | number | - | 宽度限制 |\n| height | number | - | 高度限制 |\n| is-limit-height | boolean | true | 启用高度限制 |\n| use-tooltip | boolean | false | 使用tooltip |\n| placement | string | - | tooltip方向 |\n\n---\n\n## Sign 电子签名\n\n```vue\n<avue-sign />\n```\n\n兼容移动端和PC端。\n\n---\n\n## Print 打印\n\n```vue\n<avue-print :id=\"printId\" :html=\"htmlContent\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | - | DOM元素ID |\n| html | string | - | HTML代码片段 |\n\n---\n\n## Export 导入导出\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 导出\n$Export.excel({\n  title: '导出标题',\n  columns: [{ label: '姓名', prop: 'name' }],\n  data: [{ name: '张三' }],\n})\n```\n\n---\n\n## ImagePreview 图片预览\n\n```javascript\nimport { $ImagePreview } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\nconst images = [\n  { thumbUrl: '缩略图.jpg', url: '原图.jpg' },\n]\n$ImagePreview(appContext)(images, 0)\n```\n\n---\n\n## Contextmenu 右键菜单\n\n```vue\n<div v-contextmenu=\"menuOptions\">\n  右键点击此区域\n</div>\n```\n\n---\n\n## Video 摄像头\n\n```vue\n<avue-video ref=\"video\" />\n```\n\n支持 start/end 方法控制录制。\n\n---\n\n## Verify 验证码\n\n```vue\n<avue-verify />\n```\n\n---\n\n## Screenshot 截图\n\n需引入 html2canvas：\n```html\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n```\n\n---\n\n## License 授权书\n\n需引入 jsPDF (用于PDF导出)：\n```html\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n```\n",".claude/skills/avue-design/references/tree-component.md":"# avue-tree 树组件参考\n\n## 基本用法\n\n```vue\n<avue-tree\n  :option=\"option\"\n  :data=\"treeData\"\n  @node-click=\"nodeClick\"\n  @save=\"handleSave\"\n  @update=\"handleUpdate\"\n  @del=\"handleDel\"\n/>\n```\n\n## Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| virtualize | boolean | false | 虚拟DOM模式 |\n| dialogWidth | string | '50%' | 弹窗宽度 |\n| formOption | object | - | 自定义表单（参考avue-form） |\n| menu | boolean | true | 显示菜单栏 |\n| addBtn | boolean | true | 新增按钮 |\n| editBtn | boolean | true | 编辑按钮 |\n| delBtn | boolean | true | 删除按钮 |\n| filter | boolean | true | 显示搜索框 |\n| props | object | - | 配置选项 |\n| lazy | boolean | false | 懒加载 |\n\n## Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 节点名称属性 |\n| labelText | string | '名称' | 弹窗新增名称 |\n| children | string | 'children' | 子节点属性 |\n| value | string | 'value' | 节点值属性和nodeKey |\n\n## 数据结构\n\n```javascript\nconst treeData = ref([\n  {\n    label: '一级节点',\n    value: '1',\n    children: [\n      { label: '二级节点A', value: '1-1' },\n      { label: '二级节点B', value: '1-2' },\n    ],\n  },\n])\n```\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| save | (parent, data, done, loading) | 新增回调 |\n| update | (parent, data, done, loading) | 编辑回调 |\n| del | (data, done) | 删除回调 |\n| before-open | (done, type) | 打开前回调 |\n| before-close | (done, type) | 关闭前回调 |\n| node-click | (data, node, component) | 节点点击 |\n| check-change | (data, checked, hasCheckedChildren) | 勾选变化 |\n\n## 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| filter | (value) | 筛选树节点 |\n| updateKeyChildren | (key, data) | 更新子节点 |\n| getCheckedNodes | (leafOnly, includeHalfChecked) | 获取勾选节点 |\n| setCheckedNodes | (nodes) | 设置勾选节点 |\n| getCheckedKeys | (leafOnly) | 获取勾选keys |\n| setCheckedKeys | (keys, leafOnly) | 设置勾选keys |\n| setChecked | (key/data, checked, deep) | 设置勾选状态 |\n| getHalfCheckedNodes | - | 获取半选节点 |\n| getHalfCheckedKeys | - | 获取半选keys |\n| getCurrentKey | - | 获取当前选中key |\n| getCurrentNode | - | 获取当前选中数据 |\n| setCurrentKey | (key) | 设置当前选中 |\n| setCurrentNode | (node) | 设置当前选中 |\n| getNode | (data) | 获取节点 |\n| remove | (data) | 删除节点 |\n| append | (data, parentNode) | 追加子节点 |\n| insertBefore | (data, refNode) | 在前面插入 |\n| insertAfter | (data, refNode) | 在后面插入 |\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu | 按钮插槽 |\n| add-btn | 新增按钮插槽 |\n| default | 节点内容插槽 |\n\n## 示例\n\n### 多选模式\n\n```vue\n<avue-tree :option=\"{ ...option, multiple: true }\" :data=\"treeData\" @check-change=\"checkChange\" />\n```\n\n### 懒加载\n\n```vue\n<avue-tree :option=\"option\" :data=\"treeData\">\n\nconst option = reactive({\n  lazy: true,\n  props: { label: 'name', value: 'id' },\n})\n\n// 在 treeLoad 中处理加载逻辑\n```\n\n### 拖拽\n\n参考 `default/tree/drag` 示例。\n\n### 权限控制\n\n参考 `default/tree/permission` 示例。\n\n### 虚拟模式\n\n```javascript\noption: { virtualize: true }\n// 适用于大数据量场景\n```\n",".claude/skills/avue-design/references/utility-apis.md":"# Avue 全局工具函数参考\n\n## $DialogForm - 弹窗表单\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $DialogForm } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\n\n// 打开弹窗表单\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"完成\",\n    column: [\n      {\n        label: \"姓名\",\n        prop: \"name\",\n        span: 24,\n        rules: [{ required: true, message: \"请输入姓名\", trigger: \"blur\" }],\n      },\n    ],\n  },\n})\n\n// 关闭弹窗\ndialog.close()\n```\n\n3.5.2+ 版本支持 `close()` 方法主动关闭。\n\n## $Clipboard - 复制剪切板\n\n```javascript\nimport { $Clipboard } from \"@smallwei/avue\"\n\n$Clipboard({ text: \"要复制的文本\" })\n  .then(() => ElMessage.success(\"复制成功\"))\n  .catch(() => ElMessage.error(\"复制失败\"))\n```\n\n## $ImagePreview - 图片预览\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $ImagePreview } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\nconst datas = [\n  { thumbUrl: \"缩略图URL\", url: \"大图URL\" },\n  { thumbUrl: \"缩略图URL2\", url: \"大图URL2\" },\n]\n\n$ImagePreview(appContext)(datas, 0)  // 第二个参数为起始索引\n```\n\n## $Print - 打印\n\n```javascript\nimport { $Print } from \"@smallwei/avue\"\n\n// 传入 DOM 元素的 id 或 class\n$Print(\"#test\")\n```\n\n## $Export - Excel 导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 需引入 FileSaver.js 和 xlsx\n$Export.excel({\n  title: \"文档标题\",\n  columns: [\n    { label: \"标题\", prop: \"title\" },\n    { label: \"内容\", prop: \"content\" },\n  ],\n  data: [\n    { title: \"测试1\", content: \"内容1\" },\n    { title: \"测试2\", content: \"内容2\" },\n  ],\n})\n```\n\n## $Log - 彩色日志\n\n```javascript\nimport { $Log } from \"@smallwei/avue\"\n\n// 内置颜色: default(#35495E), primary(#3488ff), success(#43B883), warning(#e6a23c), danger(#f56c6c)\n$Log.capsule(\"标题\", \"内容\", \"primary\")\n$Log.primary(\"内容\")\n$Log.success(\"内容\")\n$Log.warning(\"内容\")\n$Log.danger(\"内容\")\n```\n\n## findObject - 查找列配置对象\n\n```javascript\nimport { findObject } from \"@smallwei/avue\"\n\nconst option = { column: [{ label: '名称', prop: 'name' }] }\nconst nameColumn = findObject(option.column, 'name')\n// 返回: { label: '名称', prop: 'name' }\n```\n\n## findNode - 在数组中查找对象\n\n```javascript\nimport { findNode } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst obj = findNode(list, \"sex\", \"prop\")  // 返回 {prop: 'sex'}\n```\n\n## findArray - 查找对象在数组中的索引\n\n```javascript\nimport { findArray } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst index = findArray(list, \"sex\", \"prop\")  // 返回 1\n```\n\n## downFile - 文件下载\n\n```javascript\nimport { downFile } from \"@smallwei/avue\"\n\ndownFile(\"https://example.com/file.pdf\", \"document.pdf\")\n```\n\n## randomId - 生成随机ID\n\n```javascript\nimport { randomId } from \"@smallwei/avue\"\n\nconst id = randomId()  // 返回随机字符串\n```\n\n## loadScript - 动态加载脚本\n\n```javascript\nimport { loadScript } from \"@smallwei/avue\"\n\n// 加载 JS\nloadScript(\"js\", \"https://cdn.example.com/lib.js\").then(() => {\n  // JS 加载完成\n})\n\n// 加载 CSS\nloadScript(\"css\", \"https://cdn.example.com/style.css\").then(() => {\n  // CSS 加载完成\n})\n```\n\n## deepClone - 深拷贝\n\n```javascript\nimport { deepClone } from \"@smallwei/avue\"\n\nconst original = { name: \"张三\", info: { age: 18 } }\nconst cloned = deepClone(original)\n```\n\n## setPx - 设置像素值\n\n```javascript\nimport { setPx } from \"@smallwei/avue\"\n\nsetPx(23)       // '23px'\nsetPx(\"100%\")   // '100%'\nsetPx(\"23px\")   // '23px'\n```\n\n## validatenull - 判断空值\n\n```javascript\nimport { validatenull } from \"@smallwei/avue\"\n\nvalidatenull({})         // true\nvalidatenull([])         // true\nvalidatenull(\"\")         // true\nvalidatenull(undefined)  // true\nvalidatenull(null)       // true\nvalidatenull(\"hello\")    // false\n```\n\n## validData - 数据验证并提供默认值\n\n```javascript\nimport { validData } from \"@smallwei/avue\"\n\nvalidData(undefined, \"默认值\")     // '默认值'\nvalidData({}, { name: 11 })        // { name: 11 }\nvalidData(true, false)             // true (非空返回原值)\n```\n",".claude/skills/blade-commit/README.md":"# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 快速开始\n\n```bash\n# 简单模式（默认）— 单行 gitmoji + 描述\n/blade-commit\n\n# 详细模式 — gitmoji + conventional commit + 变更列表\n/blade-commit -d\n\n# 自定义信息 — 自动补全 gitmoji 格式\n/blade-commit -m \"新增接口权限模块\"\n```\n\n## 两种模式\n\n### 简单模式（默认）\n\n单行格式，适用于日常小改动：\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n```\n\n### 详细模式（`-d`）\n\n多行格式，适用于涉及多文件的较大提交：\n\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n```\n\n## 常用 Gitmoji\n\n| Gitmoji | 图标 | 场景 |\n|---|---|---|\n| `:sparkles:` | ✨ | 新增功能 |\n| `:bug:` | 🐛 | 修复 Bug |\n| `:zap:` | ⚡ | 性能优化 |\n| `:recycle:` | ♻️ | 重构 |\n| `:memo:` | 📝 | 文档变更 |\n| `:wrench:` | 🔧 | 配置变更 |\n| `:fire:` | 🔥 | 移除代码 |\n| `:tada:` | 🎉 | 发布版本 |\n| `:white_check_mark:` | ✅ | 测试 |\n| `:lock:` | 🔒 | 安全修复 |\n\n完整对照表见 SKILL.md。\n\n## 安全保障\n\n- **只做 commit**，禁止 push、pull 等任何远程操作\n- **零署名**，不添加任何 AI/工具署名\n- **逐文件暂存**，不使用 `git add .`，排除敏感文件\n- **自动适配**，根据项目历史提交记录匹配语言和风格\n",".claude/skills/blade-commit/SKILL.md":"---\nname: blade-commit\ndescription: SpringBlade 通用 Git 提交工具。基于 Gitmoji 规范生成提交信息。当用户要求提交代码、commit 变更、生成 commit message、或使用 /blade-commit 时触发。默认强制简单模式（gitmoji + 一句话描述），仅当显式传入 --d 或 --detail 参数时才启用详细模式（gitmoji + conventional commit + 变更列表）。只执行 commit，绝不 push 或执行其他远程操作。确保每次提交都带有 gitmoji 图标。\n---\n\n# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 铁律\n\n- **只做 commit**：禁止执行 `git push`、`git pull`、`git rebase`、`git merge`、`git reset --hard` 或任何非暂存/提交的 git 操作\n- **零署名**：提交信息中不得出现 `Powered by`、`Created by`、`Co-authored-by`、`Generated by` 或任何形式的工具/AI 署名\n- **必须有 Gitmoji**：每条提交信息必须以 gitmoji 代码开头（如 `:sparkles:`）\n\n## 使用方式\n\n- `/blade-commit` — 简单模式（默认，强制单行）\n- `/blade-commit --d` 或 `/blade-commit --detail` — 详细模式（多行带变更列表）\n- `/blade-commit -m \"自定义信息\"` — 使用用户指定的信息，自动补全 gitmoji 格式\n\n## 模式判定规则\n\n**无论变更多大、涉及多少文件，只要用户没有显式传入 `--d` 或 `--detail` 参数，就必须使用简单模式。** 不要因为 diff 内容多就自动升级为详细模式——模式选择权在用户手中，而非由变更规模决定。\n\n## 两种模式\n\n### 简单模式（默认，强制）\n\n单行格式：`:<gitmoji>: <简要描述>`\n\n一句话说清楚改了什么。无论改动大小，只要没有 `--d` / `--detail` 参数，一律使用此格式。\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n:recycle: 重构租户删除逻辑，迁移至 TenantService\n:memo: 补充 OSS 接入文档\n:wrench: 调整 application-dev.yml 数据源配置\n:tada: x.x.x.RELEASE\n```\n\n### 详细模式（需显式传入 `--d` 或 `--detail`）\n\n多行格式，适用于涉及多文件、多项改动的较大提交：\n\n```\n:<gitmoji>: <type>(<scope>): <标题摘要>\n\n- <变更点1>\n- <变更点2>\n- ...\n```\n\n标题行由 gitmoji + conventional commit 类型 + 作用域 + 摘要组成，空一行后用 bullet points 逐条列出具体变更。\n\n**示例 1 — 新功能：**\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\n```\n\n**示例 2 — Bug 修复：**\n```\n:bug: fix(auth): 修复登录 IP 锁定后刷新令牌仍可通过的问题\n\n- RefreshTokenGranter 增加 IP 锁定状态校验\n- LoginAttemptService 补充刷新场景下的失败计数复位逻辑\n- 新增单元测试覆盖刷新 Token 的 IP 锁定分支\n```\n\n**示例 3 — 重构：**\n```\n:recycle: refactor(resource): 重构 OssEndpoint 按职责拆分 Controller\n\n- 拆分文件上传、文件下载、文件删除为独立 Controller\n- 抽取 OssService 处理跨厂商统一逻辑\n- 原 OssEndpoint 保留兼容入口，内部委派至新 Controller\n```\n\n**示例 4 — 性能优化：**\n```\n:zap: perf(system): 优化字典批量查询性能\n\n- DictCache 改用 Caffeine 二级缓存，减少 Redis 往返\n- DictMapper 补充 code + dict_key 联合索引提示\n- 调整 DictService 的 getList 为按租户分片查询\n```\n\n## Gitmoji 对照表\n\n根据变更内容选择最匹配的 gitmoji：\n\n| Gitmoji | 图标 | type | 场景 |\n|---|---|---|---|\n| `:sparkles:` | ✨ | feat | 新增功能、新特性 |\n| `:bug:` | 🐛 | fix | 修复 Bug |\n| `:zap:` | ⚡ | perf | 性能优化、改进提升 |\n| `:recycle:` | ♻️ | refactor | 重构（不改变外部行为） |\n| `:memo:` | 📝 | docs | 文档新增或变更 |\n| `:wrench:` | 🔧 | chore | 配置变更、杂项 |\n| `:white_check_mark:` | ✅ | test | 添加或修改测试 |\n| `:tada:` | 🎉 | release | 发布新版本、里程碑 |\n| `:fire:` | 🔥 | remove | 移除代码或文件 |\n| `:lipstick:` | 💄 | style | UI / 样式调整 |\n| `:ambulance:` | 🚑 | hotfix | 紧急修复 |\n| `:construction:` | 🚧 | wip | 开发进行中 |\n| `:arrow_up:` | ⬆️ | deps | 升级依赖版本 |\n| `:arrow_down:` | ⬇️ | deps | 降级依赖版本 |\n| `:lock:` | 🔒 | security | 安全相关修复 |\n| `:truck:` | 🚚 | move | 移动 / 重命名文件 |\n| `:art:` | 🎨 | style | 改进代码结构 / 格式化 |\n| `:package:` | 📦 | build | 构建系统变更 |\n| `:rewind:` | ⏪ | revert | 回退变更 |\n| `:heavy_plus_sign:` | ➕ | deps | 添加依赖 |\n| `:heavy_minus_sign:` | ➖ | deps | 移除依赖 |\n\n## 执行流程\n\n### 第一步：收集信息\n\n并行执行：\n- `git status` — 查看工作区状态\n- `git diff --staged` — 已暂存的变更\n- `git diff` — 未暂存的变更\n- `git log --oneline -5` — 最近提交记录，确认当前项目的提交风格\n\n### 第二步：适配项目风格\n\n阅读 `git log` 结果，判断当前项目的提交语言（中文/英文）和习惯。提交信息的语言应与项目历史保持一致：\n- 如果历史提交以中文为主，使用中文\n- 如果历史提交以英文为主，使用英文\n- 如果混合使用，默认中文\n\n### 第三步：分析变更\n\n阅读 diff 内容，判断：\n1. **变更类型**：新功能？Bug修复？重构？文档？配置？性能优化？\n2. **影响范围（scope）**：涉及哪个模块或功能（如 `system`、`auth`、`desk`、`resource`、`develop`、`gateway`、`tool` 等）\n3. **核心改动**：最关键的变更是什么\n4. 如果变更混合了多种类型，选最主要的类型\n\n### 第四步：暂存文件\n\n- 使用 `git add <具体文件路径>` 逐个添加，不要用 `git add .` 或 `git add -A`\n- 排除不该提交的文件：`.env`、密钥文件、IDE 配置（`.idea/`、`.vscode/`）、编译产物（`target/`、`dist/`）、系统文件（`.DS_Store`）\n- **前端工程排除规则**：如果项目根目录存在 `package.json`、`vite.config.*`、`vue.config.*` 等前端工程标识文件，则自动将 `website.js`（通常位于 `src/config/website.js`）从暂存列表中排除，即使该文件有变更也不提交。如果 `website.js` 已被暂存，执行 `git reset HEAD <website.js路径>` 将其移出暂存区\n- 如果用户已经手动暂存过文件，跳过此步（但仍需检查并移除已暂存的 `website.js`）\n\n### 第五步：生成提交信息并提交\n\n根据模式选择对应格式，使用 HEREDOC 执行 commit：\n\n**简单模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: 新增角色授权接口\nEOF\n)\"\n```\n\n**详细模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\nEOF\n)\"\n```\n\n### 第六步：确认结果\n\n执行 `git status` 确认提交成功，向用户展示提交结果。\n\n## 写好提交信息的要点\n\n- **简单模式**：一句话说清楚\"做了什么\"，精炼但不含糊，80 字符以内为佳\n- **详细模式**：\n  - 标题行概括全局，不超过 72 字符\n  - bullet points 按重要性排序：先新增，后优化，再修复\n  - 每个 bullet 要具体，提到文件名或模块名让人知道改了哪里\n  - 不要超过 8 条 bullet，太多说明应该拆分 commit\n- **选对 gitmoji**：看对照表选最贴合的。不确定时 `:sparkles:` 适用于大多数新功能，`:zap:` 适用于优化改进\n- 如果用户通过 `-m` 指定了信息，保留用户原意，只在格式上对齐规范（补 gitmoji、调整格式）\n",".claude/skills/blade-design/README.md":"# blade-design - SpringBlade 全栈代码生成器\n\n基于 SpringBlade 开源版框架规范的智能代码生成 Skill，覆盖后端、前端、数据库三层，支持 Boot 单体和 Cloud 微服务两种架构模式。\n\n## 功能概览\n\n```\nblade-design\n├── 后端子技能 (backend)\n│   ├── Entity / DTO / VO / Excel    ← 数据模型层\n│   ├── Mapper / Mapper.xml          ← 数据访问层\n│   ├── Service / ServiceImpl        ← 业务逻辑层\n│   ├── Controller                   ← 接口控制层\n│   ├── Wrapper                      ← 数据转换层\n│   └── Feign Client (Cloud)         ← 微服务通信层\n│\n├── 前端子技能 (frontend)\n│   ├── API 模块                      ← Axios 请求封装\n│   ├── Option 配置                   ← Avue CRUD 列/表单配置\n│   └── Vue 页面                      ← Options API / Composition API\n│\n└── 数据库子技能 (database)\n    ├── MySQL 建表 SQL                ← 开源版官方支持方言\n    └── 菜单 SQL                      ← 权限菜单数据\n```\n\n## 支持的架构模式\n\n### Boot 单体架构\n- 所有代码在同一模块内\n- 包名：`org.springblade.modules.{module}`\n- Controller 路由含服务名前缀：`AppConstant.APPLICATION_XXX_NAME + \"/path\"`\n- 无 Feign Client\n\n### Cloud 微服务架构\n- API 模块（契约）与 Service 模块（实现）分离\n- 包名：`org.springblade.{module}`\n- Controller 路由无前缀（Gateway 自动路由）\n- 含 Feign Client 接口 + 实现\n\n## 支持的实体模式\n\n| 模式 | 基类 | 适用场景 | 审计字段 | 租户隔离 |\n|------|------|---------|---------|---------|\n| **tenant** | `TenantEntity` | 多租户业务表 | 自动继承 | 有 |\n| **base** | `BaseEntity` | 跨租户业务表 | 自动继承 | 无 |\n| **raw** | `Serializable` | 系统 / 关系表 | 手动定义 | 无 |\n\n## 支持的数据库\n\nSpringBlade 开源版**仅官方支持 MySQL**，建表脚本固定输出 MySQL 方言。\n\n| 方言 | 引号 | 大小写 | 注释方式 | 引擎 |\n|------|------|--------|---------|------|\n| MySQL | `` ` `` | 小写 | `COMMENT` 内联 | InnoDB + utf8mb4 |\n\n## 支持的前端风格\n\n| 风格 | 说明 | 适用场景 |\n|------|------|---------|\n| **Options API** | Vue 3 传统选项式写法 | 与现有代码保持一致 |\n| **Composition API** | Vue 3 组合式 `<script setup>` | 新功能开发推荐 |\n\n## 支持的模板类型\n\n| 类型 | 说明 | 适用场景 |\n|------|------|---------|\n| **crud** | 标准增删改查 | 通用业务管理 |\n| **tree** | 树形结构 CRUD | 分类、部门、菜单等层级数据 |\n| **sub** | 主子表 CRUD | 订单-订单项、合同-条款等主从关系 |\n\n## 使用方式\n\n### 快速开始\n```\n帮我生成一个订单管理模块的代码\n```\n\n### 指定参数\n```\n帮我生成一个产品管理的 CRUD：\n- 模块名：product\n- 实体名：Product\n- 架构：boot\n- 字段：\n  - productName (String) 产品名称，必填，搜索\n  - category (Integer) 产品分类，字典 product_category\n  - price (BigDecimal) 单价\n  - description (String/text) 产品描述\n  - status (Integer) 上架状态\n```\n\n### 仅生成部分\n```\n帮我生成 Order 的建表语句（MySQL）\n帮我生成前端页面，用 Composition API\n只需要后端代码，Cloud 架构\n```\n\n## 文件结构\n\n```\nblade-design/\n├── SKILL.md                  ← 主技能文件（路由、配置、工作流）\n├── README.md                 ← 本文档\n└── references/\n    ├── backend.md            ← 后端代码模板（Entity/DTO/VO/Mapper/Service/Controller/Wrapper/Feign）\n    ├── frontend.md           ← 前端代码模板（API/Option/Vue 页面）\n    └── database.md           ← MySQL 建表模板 + 菜单 SQL\n```\n\n## Boot vs Cloud 核心差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| 基础包名 | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| Controller 路由 | `AppConstant.APPLICATION_XXX_NAME + \"/path\"` | `\"/path\"` |\n| Entity 位置 | 同模块 | API 模块 |\n| 服务间调用 | 直接注入 | Feign Client |\n| Feign Client | 无 | 接口在 API 模块，实现在 Service 模块 |\n| POM 结构 | 单一 pom.xml | API + Service 各一个 |\n| 服务注册 | 不需要 | Nacos |\n| 网关路由 | 不需要 | Spring Cloud Gateway |\n\n## 代码规范要点\n\n1. Long 类型 ID 必须 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n2. Date 字段同时添加 `@DateTimeFormat` 和 `@JsonFormat`\n3. 逻辑删除字段 `isDeleted` 添加 `@TableLogic`（基类自动处理）\n4. Swagger 使用 OpenAPI 3 注解（`@Tag`/`@Operation`/`@Schema`/`@Parameter`），控制器类级标注无参 `@ApiOrder` 排序\n5. 响应统一使用 `R<T>` 包装\n6. Wrapper 中通过 `DictCache.getValue()` 翻译字典字段\n7. 前端权限标识格式：`{modelCode}_{action}`\n8. 表名统一 `blade_` 前缀\n9. 主键使用雪花算法（ASSIGN_ID）\n10. 所有表包含标准审计字段\n11. Entity 类名不带 `Entity` 后缀（如 `User` 而非 `UserEntity`）\n12. 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`，开源版仅支持角色级权限\n",".claude/skills/blade-design/SKILL.md":"---\nname: blade-design\ndescription: SpringBlade 全栈代码生成器。根据用户提供的模块名、实体名和字段列表，自动生成符合 SpringBlade 开源版框架规范的后端代码（Boot 单体/Cloud 微服务）、前端页面（Saber Options API/Composition API）和 MySQL 建表语句。当用户说\"帮我生成代码\"、\"创建一个新模块\"、\"生成 CRUD\"、\"新建实体\"、\"建表语句\"、\"生成前端页面\"、\"生成后端接口\"、\"生成 SpringBlade 代码\"、\"写一个 xxx 管理功能\"、\"帮我设计 xxx 表\"、\"blade-design\"等时触发。即使用户只是描述了业务字段或给出了表结构，也应触发此技能来生成对应的 SpringBlade 规范代码。\n---\n\n# SpringBlade 全栈代码生成器 (blade-design)\n\n根据用户需求生成符合 SpringBlade 开源版框架规范的完整 CRUD 代码，覆盖后端、前端、数据库三层。生成的代码与 SpringBlade 现有模块（如 Notice、Dict、Post、User）风格完全一致。\n\n## 信息收集协议\n\n在生成代码前，必须确认以下信息。如用户未提供，主动询问：\n\n### 必需信息\n| 参数 | 说明 | 示例 |\n|------|------|------|\n| **模块名** | 业务模块标识（英文小写） | `desk`、`system`、`order` |\n| **实体名** | 实体类名（PascalCase） | `Notice`、`Order`、`Product` |\n| **中文名** | 业务描述 | `通知公告`、`订单管理` |\n| **字段列表** | 业务字段（不含审计字段） | 见下方格式 |\n\n### 可选信息（有默认值）\n| 参数 | 默认值 | 可选值 |\n|------|--------|--------|\n| **架构类型** | `boot` | `boot`（单体）、`cloud`（微服务） |\n| **实体模式** | `tenant` | `tenant`（租户隔离）、`base`（无租户）、`raw`（原生 Serializable） |\n| **前端风格** | `options` | `options`（Options API）、`composition`（Composition API） |\n| **模板类型** | `crud` | `crud`（基础增删改查）、`tree`（树形结构）、`sub`（主子表） |\n| **是否含 Wrapper** | `true` | `true`、`false` |\n| **生成范围** | `all` | `all`、`backend`、`frontend`、`database` |\n| **服务名（Cloud）** | `blade-{模块名}` | 微服务名称 |\n| **作者** | `BladeX` | 代码注释中的作者 |\n\n> SpringBlade 开源版仅官方支持 MySQL，生成的建表脚本固定为 MySQL 方言。\n\n### 字段定义格式\n\n用户可以用表格或简洁文本描述字段，你需要解析为以下结构：\n\n| 属性 | 说明 |\n|------|------|\n| `name` | Java 属性名（camelCase） |\n| `column` | 数据库列名（snake_case） |\n| `type` | Java 类型：`String`、`Integer`、`Long`、`Date`、`BigDecimal`、`Double` |\n| `jdbcType` | 数据库类型：`varchar(n)`、`int`、`bigint`、`datetime`、`text`、`decimal(m,n)` |\n| `comment` | 中文字段说明 |\n| `required` | 是否必填（默认 false） |\n| `query` | 是否为查询字段（默认 false） |\n| `queryType` | 查询方式：`EQ`、`LIKE`、`BETWEEN`（默认 EQ） |\n| `list` | 是否在列表显示（默认 true） |\n| `form` | 是否在表单显示（默认 true） |\n| `component` | 前端组件：`input`、`textarea`、`select`、`radio`、`checkbox`、`switch`、`date`、`datetime`、`number`、`upload`（默认 input） |\n| `dictCode` | 字典编码（用于 select/radio 类型） |\n\n## Boot vs Cloud 核心架构差异\n\n生成代码时根据架构类型自动调整以下差异点：\n\n### 模块结构\n\n**Boot（单体应用）**：所有代码在同一模块内\n```\nsrc/main/java/org/springblade/modules/{module}/\n├── controller/\n├── service/ + impl/\n├── mapper/\n├── wrapper/\n├── entity/ + dto/ + vo/\n└── excel/\n```\n\n**Cloud（微服务）**：API 与实现分离为两个 Maven 模块\n```\nblade-service-api/blade-{module}-api/          ← 契约层\n└── org/springblade/{module}/\n    ├── entity/ + dto/ + vo/\n    └── feign/I{Name}Client.java               ← Feign 接口\n\nblade-service/blade-{module}/                  ← 实现层\n└── org/springblade/{module}/\n    ├── controller/\n    ├── service/ + impl/\n    ├── mapper/\n    ├── wrapper/\n    └── feign/{Name}Client.java                ← Feign 实现\n```\n\n### 关键差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| **基础包名** | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| **Controller 路由** | `@RequestMapping(AppConstant.APPLICATION_XXX_NAME + \"/{path}\")` | `@RequestMapping(\"/{path}\")` |\n| **服务间调用** | 直接注入 Service | Feign Client 远程调用 |\n| **Entity/VO 位置** | 与 Service 同模块 | 独立 API 模块中 |\n| **Feign Client** | 无 | API 模块：接口定义；Service 模块：实现类 |\n| **POM 依赖** | 单一 pom.xml | API 和 Service 各自的 pom.xml |\n| **服务注册** | 不需要 | Nacos 服务注册与发现 |\n| **Gateway 路由** | 不需要 | Gateway 自动按服务名路由 |\n\n### Boot 路由规则\nBoot 模式下 Controller 需要手动添加服务名前缀，因为没有 Gateway 路由：\n```java\n// Boot: 完整路径 /desk/notice/detail\n@RequestMapping(AppConstant.APPLICATION_DESK_NAME + \"/notice\")\n\n// Cloud: Gateway 自动添加 /blade-desk 前缀，只需 /notice/detail\n@RequestMapping(\"/notice\")\n```\n\n## 实体继承模式\n\n根据业务场景选择合适的实体基类：\n\n### 模式一：TenantEntity（默认，租户隔离业务表）\n- 继承 `TenantEntity`（`org.springblade.core.mp.base.TenantEntity`），自动拥有 `tenantId` + 全套审计字段\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 删除使用 `deleteLogic()`（逻辑删除）\n- 适用：User、Post 等绝大多数多租户业务表\n\n### 模式二：BaseEntity（无租户业务表）\n- 继承 `BaseEntity`（`org.springblade.core.mp.base.BaseEntity`），拥有审计字段但无 `tenantId`\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 适用：Tenant、Notice 等跨租户业务表\n- 也可通过在 `application.yml`（Boot）或 Nacos `blade.yaml`（Cloud）的 `blade.tenant.tables` 中配置表名实现字段隔离，不强制继承 TenantEntity\n\n### 模式三：Raw Serializable（原生模式）\n- 实现 `Serializable`，所有字段手动定义\n- Service 继承 `IService<Entity>` / `ServiceImpl<Mapper, Entity>`\n- 不继承 SpringBlade 基类，使用 MyBatis-Plus 原生 CRUD\n- 删除使用 `removeBatchByIds()`\n- 适用：Menu、Role、Dept、Dict、RoleMenu 等跨租户系统/关系表\n\n## 代码生成工作流\n\n### Step 1: 收集信息\n确认所有必需参数和可选参数，构建完整的生成上下文。\n\n### Step 2: 生成代码\n根据用户选择的生成范围，读取对应的参考文件并生成代码：\n\n| 生成范围 | 参考文件 | 生成产物 |\n|----------|----------|----------|\n| **后端** | 读取 `references/backend.md` | Entity、DTO、VO、Mapper、Mapper.xml、Service、ServiceImpl、Controller、Wrapper、Excel、Feign（Cloud） |\n| **前端** | 读取 `references/frontend.md` | API 模块、Option 配置、Vue 页面 |\n| **数据库** | 读取 `references/database.md` | MySQL 建表 SQL、菜单 SQL |\n\n### Step 3: 输出代码\n按文件维度逐个输出，每个文件包含：\n1. 完整文件路径（相对于项目根目录）\n2. 完整可运行的代码内容\n3. 必要的说明注释\n\n## 输出文件清单\n\n### 后端产物（Boot）\n```\nsrc/main/java/org/springblade/modules/{module}/entity/{Name}.java\nsrc/main/java/org/springblade/modules/{module}/dto/{Name}DTO.java\nsrc/main/java/org/springblade/modules/{module}/vo/{Name}VO.java\nsrc/main/java/org/springblade/modules/{module}/excel/{Name}Excel.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.xml\nsrc/main/java/org/springblade/modules/{module}/service/I{Name}Service.java\nsrc/main/java/org/springblade/modules/{module}/service/impl/{Name}ServiceImpl.java\nsrc/main/java/org/springblade/modules/{module}/controller/{Name}Controller.java\nsrc/main/java/org/springblade/modules/{module}/wrapper/{Name}Wrapper.java\n```\n\n### 后端产物（Cloud）\n```\nblade-service-api/blade-{module}-api/.../entity/{Name}.java\nblade-service-api/blade-{module}-api/.../dto/{Name}DTO.java\nblade-service-api/blade-{module}-api/.../vo/{Name}VO.java\nblade-service-api/blade-{module}-api/.../feign/I{Name}Client.java\nblade-service/blade-{module}/.../excel/{Name}Excel.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.xml\nblade-service/blade-{module}/.../service/I{Name}Service.java\nblade-service/blade-{module}/.../service/impl/{Name}ServiceImpl.java\nblade-service/blade-{module}/.../controller/{Name}Controller.java\nblade-service/blade-{module}/.../wrapper/{Name}Wrapper.java\nblade-service/blade-{module}/.../feign/{Name}Client.java\n```\n\n### 前端产物\n```\nsrc/api/{module}/{modelCode}.js\nsrc/option/{module}/{modelCode}.js\nsrc/views/{module}/{modelCode}.vue\n```\n\n### 数据库产物\n```\n{name}.mysql.sql              -- MySQL 建表语句\n{name}.menu.sql               -- 菜单数据 SQL\n```\n\n## 命名约定\n\n| 维度 | 规则 | 示例（实体名：OrderItem） |\n|------|------|--------------------------|\n| 实体类 | `{Name}` | `OrderItem` |\n| VO 类 | `{Name}VO` | `OrderItemVO` |\n| DTO 类 | `{Name}DTO` | `OrderItemDTO` |\n| Excel 类 | `{Name}Excel` | `OrderItemExcel` |\n| Mapper 接口 | `{Name}Mapper` | `OrderItemMapper` |\n| Mapper XML | `{Name}Mapper.xml` | `OrderItemMapper.xml` |\n| Service 接口 | `I{Name}Service` | `IOrderItemService` |\n| Service 实现 | `{Name}ServiceImpl` | `OrderItemServiceImpl` |\n| Controller | `{Name}Controller` | `OrderItemController` |\n| Wrapper | `{Name}Wrapper` | `OrderItemWrapper` |\n| Feign 接口 | `I{Name}Client` | `IOrderItemClient` |\n| Feign 实现 | `{Name}Client` | `OrderItemClient` |\n| 数据库表名 | `blade_{snake_case}` | `blade_order_item` |\n| API 路径 | `/{modelCode}` | `/order-item` |\n| 前端文件 | `{modelCode}` | `orderItem` |\n\n> SpringBlade 开源版 Entity 统一**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。请严格遵循此命名。\n\n## 重要规范\n\n1. **所有 Long 类型的 ID 字段**必须添加 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n2. **日期字段**必须同时添加 `@DateTimeFormat` 和 `@JsonFormat`，pattern 统一使用 `DateUtil.PATTERN_DATETIME`\n3. **逻辑删除字段** `isDeleted` 必须添加 `@TableLogic` 注解（继承 BaseEntity / TenantEntity 时由基类自动处理）\n4. **Swagger 文档**使用 OpenAPI 3 注解：`@Tag`、`@Operation`、`@Schema`、`@Parameter`\n5. **接口排序**：控制器类级标注无参 `@ApiOrder`（置于 `@Tag` 上方），分组内接口按源码声明顺序展示；个别接口需调整顺序时才在方法上标 `@ApiOrder(n)`\n6. **响应包装**统一使用 `R<T>` 类\n7. **Wrapper 的 entityVO() 方法**中通过 `DictCache.getValue()` 翻译字典字段\n8. **Controller 的 remove 方法**：TenantEntity / BaseEntity 用 `deleteLogic()`，Raw 模式用 `removeBatchByIds()`\n9. **权限控制**：使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 做角色级控制（SpringBlade 开源版仅支持角色权限）\n10. **前端权限标识**格式：`{modelCode}_{action}`，如 `notice_add`、`notice_edit`、`notice_delete`、`notice_view`\n\n## 参考文件索引\n\n| 生成任务 | 参考文件 | 说明 |\n|----------|----------|------|\n| 后端代码 | `references/backend.md` | 完整的后端各层代码模板，含 Boot/Cloud 变体 |\n| 前端代码 | `references/frontend.md` | API、Option 配置、Vue 页面模板（Options/Composition API） |\n| 数据库 SQL | `references/database.md` | MySQL 建表模板、字段类型映射与菜单 SQL |\n",".claude/skills/blade-design/references/backend.md":"# 后端代码生成参考\n\n本文档包含 SpringBlade 开源版后端各层的完整代码模板。所有模板均来源于 SpringBlade 开源工程真实业务代码（参考 `modules/system`、`modules/desk` 及 Cloud 的 `blade-service/blade-system` 等）。\n\n## 目录\n- [一、Entity 实体类](#一entity-实体类)\n- [二、DTO 数据传输对象](#二dto-数据传输对象)\n- [三、VO 视图对象](#三vo-视图对象)\n- [四、Excel 导出类](#四excel-导出类)\n- [五、Mapper 接口](#五mapper-接口)\n- [六、Mapper XML](#六mapper-xml)\n- [七、Service 接口](#七service-接口)\n- [八、ServiceImpl 实现](#八serviceimpl-实现)\n- [九、Controller 控制器](#九controller-控制器)\n- [十、Wrapper 数据转换器](#十wrapper-数据转换器)\n- [十一、Feign Client 接口（Cloud）](#十一feign-client-接口cloud)\n- [十二、Feign Client 实现（Cloud）](#十二feign-client-实现cloud)\n\n---\n\n## 一、Entity 实体类\n\n> 开源版 Entity 命名**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。\n\n### 模式A：TenantEntity（多租户业务表，默认）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.TenantEntity;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends TenantEntity {\n\n    /**\n     * {字段注释}\n     */\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 字段类型注解模式 ---\n\n    // Long 类型字段（防止前端精度丢失）\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"关联ID\")\n    private Long refId;\n\n    // Date 类型字段\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"发布时间\")\n    private Date releaseTime;\n\n    // Integer 字典字段（配合 Wrapper 翻译）\n    @Schema(description = \"分类\")\n    private Integer category;\n\n    // String 普通文本\n    @Schema(description = \"标题\")\n    private String title;\n\n    // String 大文本\n    @Schema(description = \"内容\")\n    private String content;\n}\n```\n\n**TenantEntity 自动继承的字段**（不需要在子类中声明）：\n- `id` (Long) - 主键，@TableId(type = IdType.ASSIGN_ID)，雪花 ID\n- `tenantId` (String) - 租户 ID\n- `createUser` (Long) - 创建人\n- `createDept` (Long) - 创建部门\n- `createTime` (Date) - 创建时间\n- `updateUser` (Long) - 更新人\n- `updateTime` (Date) - 更新时间\n- `status` (Integer) - 业务状态，默认 1\n- `isDeleted` (Integer) - 逻辑删除标记，@TableLogic，默认 0\n\n### 模式B：BaseEntity（跨租户业务表）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends BaseEntity {\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n}\n```\n\n**BaseEntity 继承字段**：与 TenantEntity 相同但不含 `tenantId`。\n\n### 模式C：Raw Serializable（原生模式）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@Schema(description = \"{中文名}\")\npublic class {Name} implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 主键id\n     */\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键id\")\n    @TableId(value = \"id\", type = IdType.ASSIGN_ID)\n    private Long id;\n\n    // --- 业务字段 ---\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 审计字段（手动定义） ---\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建人\", hidden = true)\n    private Long createUser;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建部门\", hidden = true)\n    private Long createDept;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"创建时间\", hidden = true)\n    private Date createTime;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"更新人\", hidden = true)\n    private Long updateUser;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"更新时间\", hidden = true)\n    private Date updateTime;\n\n    @Schema(description = \"业务状态\", hidden = true)\n    private Integer status;\n\n    @TableLogic\n    @Schema(description = \"是否已删除\", hidden = true)\n    private Integer isDeleted;\n}\n```\n\n### Cloud 版本 Entity 差异\n\nCloud 中 Entity 放在 API 模块，包名不同：\n```java\n// Boot\npackage org.springblade.modules.{module}.entity;\n\n// Cloud\npackage org.springblade.{module}.entity;\n```\n\n其余代码完全相同。\n\n---\n\n## 二、DTO 数据传输对象\n\n### 标准 DTO（继承 Entity）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}DTO extends {Name} {\n}\n```\n\n大多数情况下 DTO 为空类，仅作为扩展点。如有特定传输需求可添加字段。\n\n### 精简 DTO（仅特定字段）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\npublic class {Name}DTO implements Serializable {\n\n    @Schema(description = \"特定字段\")\n    private String specificField;\n}\n```\n\n---\n\n## 三、VO 视图对象\n\n### 标准 VO（简单扩展）\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}视图对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} {\n\n    /**\n     * 分类名称（由 Wrapper 通过字典翻译填充）\n     */\n    @Schema(description = \"分类名称\")\n    private String categoryName;\n}\n```\n\n对于每个需要字典翻译的字段，在 VO 中添加对应的 `{字段名}Name` 字段。\n\n### 树形 VO（实现 INode 接口）\n\n用于 Menu、Dict、Dept 等树形结构：\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.tool.node.INode;\nimport org.springblade.modules.{module}.entity.{Name};\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {中文名}视图对象（树形）\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} implements INode<{Name}VO> {\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键ID\")\n    private Long id;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"父节点ID\")\n    private Long parentId;\n\n    @JsonInclude(JsonInclude.Include.NON_EMPTY)\n    @Schema(description = \"子节点\")\n    private List<{Name}VO> children;\n\n    @Schema(description = \"是否有子节点\")\n    private Boolean hasChildren;\n\n    @Schema(description = \"上级名称\")\n    private String parentName;\n\n    @Override\n    public List<{Name}VO> getChildren() {\n        if (this.children == null) {\n            this.children = new ArrayList<>();\n        }\n        return this.children;\n    }\n}\n```\n\n---\n\n## 四、Excel 导出类\n\n```java\npackage org.springblade.modules.{module}.excel;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.idev.excel.annotation.write.style.ColumnWidth;\nimport cn.idev.excel.annotation.write.style.ContentRowHeight;\nimport cn.idev.excel.annotation.write.style.HeadRowHeight;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}导出对象\n *\n * @author {author}\n */\n@Data\n@ColumnWidth(25)\n@HeadRowHeight(20)\n@ContentRowHeight(18)\npublic class {Name}Excel implements Serializable {\n\n    @ExcelProperty(\"{字段中文名}\")\n    private {Type} {fieldName};\n\n    // 不导出审计字段（createUser、createTime 等）\n}\n```\n\n> 开源版使用 FastExcel（`cn.idev.excel.*`，由 `blade-starter-excel` 引入）。\n\n---\n\n## 五、Mapper 接口\n\n```java\npackage org.springblade.modules.{module}.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.excel.{Name}Excel;\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\nimport java.util.List;\n\n/**\n * {中文名} Mapper 接口\n *\n * @author {author}\n */\npublic interface {Name}Mapper extends BaseMapper<{Name}> {\n\n    /**\n     * 自定义分页查询\n     */\n    List<{Name}VO> select{Name}Page(IPage page, {Name}VO {modelCode});\n\n    /**\n     * 导出数据查询\n     */\n    List<{Name}Excel> export{Name}(@Param(\"ew\") Wrapper<{Name}> queryWrapper);\n}\n```\n\n**树形模板追加方法**：\n```java\n    /**\n     * 树形结构查询\n     */\n    List<{Name}VO> tree(String tenantId);\n```\n\n---\n\n## 六、Mapper XML\n\n### 标准 CRUD\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"org.springblade.modules.{module}.mapper.{Name}Mapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"{modelCode}ResultMap\" type=\"org.springblade.modules.{module}.entity.{Name}\">\n        <result column=\"id\" property=\"id\"/>\n        <!-- 每个业务字段一行映射 -->\n        <result column=\"{column_name}\" property=\"{fieldName}\"/>\n        <!-- 审计字段映射（TenantEntity 模式需要） -->\n        <result column=\"create_user\" property=\"createUser\"/>\n        <result column=\"create_dept\" property=\"createDept\"/>\n        <result column=\"create_time\" property=\"createTime\"/>\n        <result column=\"update_user\" property=\"updateUser\"/>\n        <result column=\"update_time\" property=\"updateTime\"/>\n        <result column=\"status\" property=\"status\"/>\n        <result column=\"is_deleted\" property=\"isDeleted\"/>\n    </resultMap>\n\n    <!-- 自定义分页查询 -->\n    <select id=\"select{Name}Page\" resultMap=\"{modelCode}ResultMap\">\n        SELECT\n            t.*,\n            d.dict_value AS categoryName\n        FROM blade_{table_name} t\n        LEFT JOIN (SELECT * FROM blade_dict WHERE code = '{dictCode}') d\n            ON t.category = d.dict_key\n        WHERE t.is_deleted = 0\n        <if test=\"{modelCode}.tenantId != null and {modelCode}.tenantId != ''\">\n            AND t.tenant_id = #{{{modelCode}.tenantId}}\n        </if>\n        <!-- 动态查询条件 -->\n        <if test=\"{modelCode}.{queryField} != null and {modelCode}.{queryField} != ''\">\n            AND t.{query_column} LIKE CONCAT('%', #{{{modelCode}.{queryField}}}, '%')\n        </if>\n        <if test=\"{modelCode}.{eqField} != null\">\n            AND t.{eq_column} = #{{{modelCode}.{eqField}}}\n        </if>\n    </select>\n\n    <!-- 导出查询 -->\n    <select id=\"export{Name}\" resultType=\"org.springblade.modules.{module}.excel.{Name}Excel\">\n        SELECT * FROM blade_{table_name}\n        ${ew.customSqlSegment}\n    </select>\n\n</mapper>\n```\n\n**注意事项**：\n- 如果有字典关联查询，在 select 中 LEFT JOIN blade_dict 表\n- 动态条件中 `LIKE` 用于模糊查询字段，`=` 用于精确查询字段\n- TenantEntity 模式需要 `tenant_id` 过滤条件\n- resultMap 中每个数据库列名（snake_case）映射到 Java 属性（camelCase）\n\n---\n\n## 七、Service 接口\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends BaseService<{Name}> {\n\n    /**\n     * 自定义分页\n     */\n    IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode});\n}\n```\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends IService<{Name}> {\n}\n```\n\n---\n\n## 八、ServiceImpl 实现\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends BaseServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n\n    @Override\n    public IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode}) {\n        {modelCode}.setTenantId(AuthUtil.getTenantId());\n        return page.setRecords(baseMapper.select{Name}Page(page, {modelCode}));\n    }\n}\n```\n\n**注意**：BaseEntity 模式下不需要 `setTenantId()` 那行。\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends ServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n}\n```\n\n---\n\n## 九、Controller 控制器\n\n### Boot 版本\n\n```java\npackage org.springblade.modules.{module}.controller;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springblade.modules.{module}.wrapper.{Name}Wrapper;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.Map;\n\n/**\n * {中文名} 控制器\n *\n * @author {author}\n */\n@RestController\n@AllArgsConstructor\n@RequestMapping(AppConstant.APPLICATION_{MODULE}_NAME + \"/{modelCode}\")\n@ApiOrder\n@Tag(name = \"{中文名}\", description = \"{中文名}接口\")\npublic class {Name}Controller extends BladeController {\n\n    private final I{Name}Service {modelCode}Service;\n\n    /**\n     * 详情\n     */\n    @GetMapping(\"/detail\")\n    @Operation(summary = \"详情\", description = \"传入{modelCode}\")\n    public R<{Name}VO> detail({Name} {modelCode}) {\n        {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n        return R.data({Name}Wrapper.build().entityVO(detail));\n    }\n\n    /**\n     * 分页列表\n     */\n    @GetMapping(\"/list\")\n    @Operation(summary = \"分页\", description = \"传入{modelCode}\")\n    @PreAuth(RoleConstant.HAS_ROLE_ADMIN)\n    public R<IPage<{Name}VO>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n        IPage<{Name}> pages = {modelCode}Service.page(\n            Condition.getPage(query),\n            Condition.getQueryWrapper({modelCode}, {Name}.class)\n        );\n        return R.data({Name}Wrapper.build().pageVO(pages));\n    }\n\n    /**\n     * 自定义分页\n     */\n    @GetMapping(\"/page\")\n    @Operation(summary = \"自定义分页\", description = \"传入{modelCode}\")\n    public R<IPage<{Name}VO>> page({Name}VO {modelCode}, Query query) {\n        IPage<{Name}VO> pages = {modelCode}Service.select{Name}Page(Condition.getPage(query), {modelCode});\n        return R.data(pages);\n    }\n\n    /**\n     * 新增\n     */\n    @PostMapping(\"/save\")\n    @Operation(summary = \"新增\", description = \"传入{modelCode}\")\n    public R save(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.save({modelCode}));\n    }\n\n    /**\n     * 修改\n     */\n    @PostMapping(\"/update\")\n    @Operation(summary = \"修改\", description = \"传入{modelCode}\")\n    public R update(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.updateById({modelCode}));\n    }\n\n    /**\n     * 新增或修改\n     */\n    @PostMapping(\"/submit\")\n    @Operation(summary = \"新增或修改\", description = \"传入{modelCode}\")\n    public R submit(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.saveOrUpdate({modelCode}));\n    }\n\n    /**\n     * 删除\n     */\n    @PostMapping(\"/remove\")\n    @Operation(summary = \"逻辑删除\", description = \"传入ids\")\n    public R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n        return R.status({modelCode}Service.deleteLogic(Func.toLongList(ids)));\n    }\n}\n```\n\n### Cloud 版本差异\n\nCloud 版本仅以下几处不同：\n\n```java\n// 1. 包名不同\npackage org.springblade.{module}.controller;\n\n// 2. import 路径不同（Entity/VO/Service 都在 org.springblade.{module} 下）\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.vo.{Name}VO;\nimport org.springblade.{module}.service.I{Name}Service;\n\n// 3. RequestMapping 不需要服务名前缀\n@RequestMapping(\"/{modelCode}\")\n```\n\n### 无 Wrapper 版本\n\n如不使用 Wrapper，detail 和 list 方法简化为：\n\n```java\n@GetMapping(\"/detail\")\npublic R<{Name}> detail({Name} {modelCode}) {\n    {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n    return R.data(detail);\n}\n\n@GetMapping(\"/list\")\npublic R<IPage<{Name}>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n    IPage<{Name}> pages = {modelCode}Service.page(\n        Condition.getPage(query),\n        Condition.getQueryWrapper({modelCode}, {Name}.class)\n    );\n    return R.data(pages);\n}\n```\n\n### Raw Serializable 模式的 remove 方法\n\n```java\n@PostMapping(\"/remove\")\n@Operation(summary = \"删除\", description = \"传入ids\")\npublic R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n    return R.status({modelCode}Service.removeBatchByIds(Func.toLongList(ids)));\n}\n```\n\n### 权限注解使用规则\n\nSpringBlade 开源版仅支持角色级权限：\n\n| 场景 | 写法 |\n|------|------|\n| 仅管理员可访问（类级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在类上 |\n| 仅管理员可访问（方法级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在方法上 |\n| 隐藏 Swagger 文档 | `@Hidden`（来自 OpenAPI 3） |\n| 全公开 | 不加任何权限注解 |\n\n### 导出接口\n\n```java\n@GetMapping(\"/export-{modelCode}\")\n@Operation(summary = \"导出数据\", description = \"传入{modelCode}\")\n@PreAuth(RoleConstant.HAS_ROLE_ADMIN)\npublic void export{Name}(@RequestParam Map<String, Object> {modelCode}, HttpServletResponse response) {\n    QueryWrapper<{Name}> queryWrapper = Condition.getQueryWrapper({modelCode}, {Name}.class);\n    List<{Name}Excel> list = {modelCode}Service.export{Name}(queryWrapper);\n    ExcelUtil.export(response, \"{中文名}数据\", \"{中文名}数据表\", list, {Name}Excel.class);\n}\n```\n\n---\n\n## 十、Wrapper 数据转换器\n\n```java\npackage org.springblade.modules.{module}.wrapper;\n\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.modules.system.cache.DictCache;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名}包装类，返回视图层所需的字段\n *\n * @author {author}\n */\npublic class {Name}Wrapper extends BaseEntityWrapper<{Name}, {Name}VO> {\n\n    public static {Name}Wrapper build() {\n        return new {Name}Wrapper();\n    }\n\n    @Override\n    public {Name}VO entityVO({Name} {modelCode}) {\n        {Name}VO {modelCode}VO = BeanUtil.copyProperties({modelCode}, {Name}VO.class);\n        // 字典翻译：将字典键值转换为可读名称\n        String categoryName = DictCache.getValue(\"{dictCode}\", {modelCode}.getCategory());\n        {modelCode}VO.setCategoryName(categoryName);\n        return {modelCode}VO;\n    }\n}\n```\n\n**常见翻译模式**：\n\n```java\n// 1. 字典翻译\nString categoryName = DictCache.getValue(\"{dictCode}\", entity.getCategory());\n\n// 2. 关联实体名称（通过缓存）\nUser user = UserCache.getUser(entity.getUserId());\nif (Func.isNotEmpty(user)) {\n    vo.setUserName(user.getAccount());\n}\n\n// 3. 父节点名称（树形结构）\nif (Func.equals(entity.getParentId(), BladeConstant.TOP_PARENT_ID)) {\n    vo.setParentName(BladeConstant.TOP_PARENT_NAME);\n} else {\n    {Name} parent = {modelCode}Service.getById(entity.getParentId());\n    vo.setParentName(parent.getName());\n}\n```\n\n**树形 Wrapper 追加方法**：\n\n```java\npublic List<{Name}VO> listNodeVO(List<{Name}> list) {\n    List<{Name}VO> collect = list.stream()\n        .map(entity -> BeanUtil.copyProperties(entity, {Name}VO.class))\n        .collect(Collectors.toList());\n    return ForestNodeMerger.merge(collect);\n}\n```\n\n---\n\n## 十一、Feign Client 接口（Cloud）\n\n放在 API 模块中（`blade-service-api/blade-{module}-api`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.{module}.entity.{Name};\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n/**\n * {中文名} Feign 接口\n *\n * @author {author}\n */\n@FeignClient(value = AppConstant.APPLICATION_{MODULE}_NAME)\npublic interface I{Name}Client {\n\n    String API_PREFIX = \"/feign/client/{modelCode}\";\n    String TOP = API_PREFIX + \"/top\";\n\n    /**\n     * 获取分页数据\n     */\n    @GetMapping(TOP)\n    BladePage<{Name}> top(@RequestParam(\"current\") Integer current, @RequestParam(\"size\") Integer size);\n}\n```\n\n---\n\n## 十二、Feign Client 实现（Cloud）\n\n放在 Service 模块中（`blade-service/blade-{module}`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Hidden;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.service.I{Name}Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * {中文名} Feign 实现\n *\n * @author {author}\n */\n@Hidden\n@RestController\n@AllArgsConstructor\npublic class {Name}Client implements I{Name}Client {\n\n    private final I{Name}Service service;\n\n    @Override\n    @GetMapping(TOP)\n    public BladePage<{Name}> top(Integer current, Integer size) {\n        Query query = new Query();\n        query.setCurrent(current);\n        query.setSize(size);\n        IPage<{Name}> page = service.page(Condition.getPage(query));\n        return BladePage.of(page);\n    }\n}\n```\n\n**Feign 实现要点**：\n- `@Hidden`：对 Swagger 文档隐藏（内部接口）\n- `@RestController`：作为 HTTP 端点暴露\n- 路径常量复用接口中定义的常量\n\n---\n\n## 附：完整 import 参考\n\n### Entity 常用 import\n```java\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;      // BaseEntity\nimport org.springblade.core.mp.base.TenantEntity;   // TenantEntity\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n```\n\n### Controller 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;      // Boot 路由前缀使用\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springframework.web.bind.annotation.*;\n```\n\n### Service 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springframework.stereotype.Service;\n```\n\n### Wrapper 常用 import\n```java\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.constant.BladeConstant;\nimport org.springblade.core.tool.node.ForestNodeMerger;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.system.cache.DictCache;        // 字典缓存（Boot 路径）\n```\n",".claude/skills/blade-design/references/database.md":"# 数据库建表语句生成参考\n\n本文档包含 SpringBlade 开源版 MySQL 建表模板与菜单数据 SQL。所有模板均来源于开源工程 `doc/sql/` 下的真实 SQL 脚本。\n\n> SpringBlade 开源版仅官方支持 **MySQL**，生成的建表脚本固定为 MySQL 方言。\n\n## 目录\n- [一、通用审计字段](#一通用审计字段)\n- [二、字段类型映射表](#二字段类型映射表)\n- [三、MySQL 建表模板](#三mysql-建表模板)\n- [四、菜单 SQL 生成](#四菜单-sql-生成)\n\n---\n\n## 一、通用审计字段\n\n所有继承 `TenantEntity` 或 `BaseEntity` 的业务表都包含以下固定审计字段：\n\n| 字段名 | 说明 | Java 类型 | 是否可空 | 默认值 |\n|--------|------|-----------|----------|--------|\n| `id` | 主键（雪花 ID） | Long | NOT NULL | 无 |\n| `tenant_id` | 租户 ID（仅 TenantEntity） | String | NULL | '000000' |\n| `create_user` | 创建人 | Long | NULL | NULL |\n| `create_dept` | 创建部门 | Long | NULL | NULL |\n| `create_time` | 创建时间 | Date | NULL | NULL |\n| `update_user` | 修改人 | Long | NULL | NULL |\n| `update_time` | 修改时间 | Date | NULL | NULL |\n| `status` | 业务状态 | Integer | NULL | 1 |\n| `is_deleted` | 逻辑删除标记 | Integer | NULL | 0 |\n\n- **BaseEntity 模式**不包含 `tenant_id` 字段\n- **Raw Serializable 模式**手动定义全部字段（结构相同）\n\n---\n\n## 二、字段类型映射表\n\n### Java 类型 → MySQL 类型映射\n\n| Java 类型 | MySQL 类型 | 说明 |\n|-----------|-----------|------|\n| `String` (短) | `varchar(n)` | `n` 为字符数，通常 `varchar(255)` 以内 |\n| `String` (长) | `text` | 超过 255 字符或富文本 |\n| `Integer` | `int` | 32 位整型 |\n| `Long` | `bigint` | 64 位整型（雪花 ID 必须用） |\n| `Double` | `double` | 双精度浮点 |\n| `BigDecimal` | `decimal(m,n)` | 金额、精确数值 |\n| `Date` | `datetime` | 日期 + 时间 |\n| `Boolean` | `tinyint(1)` | 0 = false，1 = true |\n\n### 审计字段各字段定义（MySQL）\n\n| 字段 | MySQL 定义 |\n|------|-----------|\n| id | `bigint NOT NULL` |\n| tenant_id | `varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000'` |\n| create_user | `bigint NULL DEFAULT NULL` |\n| create_dept | `bigint NULL DEFAULT NULL` |\n| create_time | `datetime NULL DEFAULT NULL` |\n| update_user | `bigint NULL DEFAULT NULL` |\n| update_time | `datetime NULL DEFAULT NULL` |\n| status | `int NULL DEFAULT 1` |\n| is_deleted | `int NULL DEFAULT 0` |\n\n---\n\n## 三、MySQL 建表模板\n\n### TenantEntity 模式（多租户业务表）\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for blade_{table_name}\n-- ----------------------------\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  `tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### BaseEntity 模式（跨租户业务表）\n\n与 TenantEntity 模板一致，**仅删除 `tenant_id` 这一行**：\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### Raw Serializable 模式（系统 / 关系表）\n\n结构同 BaseEntity 模板（同样无 `tenant_id`）。差异在于 Java 侧手动定义全部字段，数据库层面建表语句本身不变。\n\n### 索引建议\n\n对于查询频繁的字段：\n\n```sql\n-- 普通索引\nCREATE INDEX idx_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 唯一索引\nCREATE UNIQUE INDEX uk_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 复合索引（多字段）\nCREATE INDEX idx_{table_name}_{col1}_{col2} ON blade_{table_name} ({col1}, {col2});\n```\n\n**命名约定**：\n- 唯一索引前缀 `uk_`\n- 普通索引前缀 `idx_`\n- 字段名用下划线拼接\n\n### MySQL 约定\n\n- 列名使用反引号 `` ` `` 包裹\n- 注释直接用 `COMMENT` 关键字写在列定义后\n- 表注释在 `CREATE TABLE` 末尾\n- 字符集固定 `utf8mb4`，排序规则 `utf8mb4_general_ci`\n- 存储引擎 `InnoDB`\n- 主键索引类型 `USING BTREE`\n- 租户 ID 列 `varchar(12)` 默认 `'000000'`\n\n---\n\n## 四、菜单 SQL 生成\n\n每个新模块需要插入 5 条菜单记录：1 个主菜单 + 4 个操作按钮（新增、修改、删除、查看）。\n\n### MySQL 菜单 SQL\n\n```sql\n-- ----------------------------\n-- {中文名}菜单数据\n-- ----------------------------\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({menuId}, {parentMenuId}, '{modelCode}', '{中文名}', 'menu', '/{module}/{modelCode}', NULL, 1, 1, 0, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({addMenuId}, {menuId}, '{modelCode}_add', '新增', 'add', '/{module}/{modelCode}/add', 'plus', 1, 2, 1, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({editMenuId}, {menuId}, '{modelCode}_edit', '修改', 'edit', '/{module}/{modelCode}/edit', 'form', 2, 2, 2, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({removeMenuId}, {menuId}, '{modelCode}_delete', '删除', 'delete', '/{module}/{modelCode}/delete', 'delete', 3, 2, 3, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({viewMenuId}, {menuId}, '{modelCode}_view', '查看', 'view', '/{module}/{modelCode}/view', 'eye-open', 4, 2, 2, 1, NULL, 0);\n```\n\n### 菜单字段说明\n\n| 字段 | 说明 | 值 |\n|------|------|-----|\n| `id` | 主菜单 ID | 生成雪花 ID 或自定义（如 1123598812738675301） |\n| `parent_id` | 父菜单 ID | 由用户指定或放在顶级（0） |\n| `code` | 权限编码 | 主菜单: `{modelCode}`，按钮: `{modelCode}_{action}` |\n| `name` | 显示名称 | 主菜单: `{中文名}`，按钮: 新增/修改/删除/查看 |\n| `alias` | 别名 | 主菜单: `menu`，按钮: add/edit/delete/view |\n| `path` | 路由路径 | `/{module}/{modelCode}` |\n| `source` | 图标 | 按钮: plus/form/delete/eye-open |\n| `sort` | 排序 | 按钮从 1 到 4 |\n| `category` | 类别 | 1=菜单，2=按钮 |\n| `action` | 操作类型 | 0=无，1=新增，2=修改，3=删除 |\n| `is_open` | 是否打开 | 1=是 |\n| `is_deleted` | 是否删除 | 0=否 |\n\n---\n\n## 附：无租户表模式速查\n\n如果实体不需要 `tenant_id`（BaseEntity 或 Raw 模式），从建表模板中直接移除 `tenant_id` 行即可，其余审计字段保持不变。\n",".claude/skills/blade-design/references/frontend.md":"# 前端代码生成参考\n\n本文档包含 SpringBlade 开源版 Saber 前端各层的完整代码模板。基于 Vue 3 + Element Plus + Avue 技术栈，支持 Options API 和 Composition API 两种风格。\n\n> 前端 API 路径在 Boot 单体与 Cloud 微服务下**完全一致**，都走 `/blade-{module}/...`。Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 前缀对齐该路径，Cloud 则由 Gateway 按服务名 `blade-{module}` 自动路由。\n\n## 目录\n- [一、API 层](#一api-层)\n- [二、Option 配置层](#二option-配置层)\n- [三、Vue 页面 - Options API](#三vue-页面---options-api)\n- [四、Vue 页面 - Composition API](#四vue-页面---composition-api)\n- [五、树形结构模式](#五树形结构模式)\n- [六、主子表模式](#六主子表模式)\n\n---\n\n## 一、API 层\n\n文件位置：`src/api/{module}/{modelCode}.js`\n\n### 标准 CRUD API\n\n```javascript\nimport request from '@/axios';\n\n/**\n * 获取{中文名}列表（分页）\n */\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n    },\n  });\n};\n\n/**\n * 获取{中文名}详情\n */\nexport const getDetail = (id) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/detail',\n    method: 'get',\n    params: {\n      id,\n    },\n  });\n};\n\n/**\n * 删除{中文名}\n */\nexport const remove = (ids) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/remove',\n    method: 'post',\n    params: {\n      ids,\n    },\n  });\n};\n\n/**\n * 新增{中文名}\n */\nexport const add = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\n/**\n * 修改{中文名}\n */\nexport const update = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n```\n\n**注意事项**：\n- 新增和修改共用 `/submit` 接口（后端 `saveOrUpdate`）\n- `remove` 的 `ids` 是逗号分隔的字符串\n- GET 请求用 `params`，POST 请求用 `data`（请求体）\n- 如需加密传输，添加 `cryptoToken: true` 和 `cryptoData: true`\n\n### 树形模式追加\n\n```javascript\n/**\n * 获取{中文名}树形结构\n */\nexport const getTree = (tenantId) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/tree',\n    method: 'get',\n    params: {\n      tenantId,\n    },\n  });\n};\n```\n\n### 导出接口\n\n```javascript\nimport { exportBlob } from '@/api/common';\n\n/**\n * 导出{中文名}数据\n */\nexport const exportData = (params) => {\n  return exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}`, params);\n};\n```\n\n---\n\n## 二、Option 配置层\n\n文件位置：`src/option/{module}/{modelCode}.js`\n\n### 标准 CRUD 配置\n\n```javascript\nexport const tableOption = {\n  height: 'auto',\n  calcHeight: 30,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  viewBtn: true,\n  dialogClickModal: false,\n  column: [\n    // --- 文本输入框 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      search: true,                    // 是否为搜索字段\n      rules: [\n        {\n          required: true,\n          message: '请输入{字段中文名}',\n          trigger: 'blur',\n        },\n      ],\n    },\n\n    // --- 数字输入 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'number',\n      align: 'right',\n      width: 100,\n    },\n\n    // --- 下拉选择（远程字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      search: true,\n      dicUrl: '/blade-system/dict/dictionary?code={dictCode}',\n      props: {\n        label: 'dictValue',\n        value: 'dictKey',\n      },\n      rules: [\n        {\n          required: true,\n          message: '请选择{字段中文名}',\n          trigger: 'change',\n        },\n      ],\n    },\n\n    // --- 下拉选择（本地字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 开关 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'switch',\n      align: 'center',\n      width: 100,\n      dicData: [\n        { label: '否', value: 0 },\n        { label: '是', value: 1 },\n      ],\n      value: 0,\n    },\n\n    // --- 单选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'radio',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n      value: 1,\n    },\n\n    // --- 多选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'checkbox',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 日期选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'date',\n      format: 'YYYY-MM-DD',\n      valueFormat: 'YYYY-MM-DD',\n    },\n\n    // --- 日期时间选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetime',\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 日期范围搜索 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetimerange',\n      searchRange: true,\n      search: true,\n      hide: true,                      // 列表不显示\n      display: false,                  // 表单不显示\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 多行文本 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'textarea',\n      span: 24,                        // 占满整行\n      hide: true,                      // 列表不显示\n      minRows: 3,\n    },\n\n    // --- 富文本编辑器 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      component: 'avue-ueditor',\n      options: {\n        action: '/blade-resource/oss/endpoint/put-file',\n        props: {\n          res: 'data',\n          url: 'link',\n        },\n      },\n      span: 24,\n      hide: true,\n      minRows: 6,\n    },\n\n    // --- 仅列表显示，表单不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      addDisplay: false,\n      editDisplay: false,\n    },\n\n    // --- 仅表单显示，列表不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      hide: true,\n    },\n\n    // --- 自定义插槽 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      slot: true,                      // 启用自定义渲染插槽\n    },\n\n    // --- 备注（隐藏在列表） ---\n    {\n      label: '备注',\n      prop: 'remark',\n      hide: true,\n    },\n  ],\n};\n```\n\n### 分组表单配置\n\n用于复杂表单（如用户管理），将字段分组到多个 Tab 页：\n\n```javascript\nexport const tableOption = (safe) => {\n  // 自定义校验器（使用闭包访问组件实例）\n  const validatePass = (rule, value, callback) => {\n    if (value === '') {\n      callback(new Error('请输入密码'));\n    } else {\n      callback();\n    }\n  };\n\n  return {\n    height: 'auto',\n    calcHeight: 150,\n    tip: false,\n    searchShow: true,\n    searchMenuSpan: 6,\n    border: true,\n    index: true,\n    selection: true,\n    viewBtn: true,\n    dialogType: 'drawer',\n    dialogWidth: 1000,\n    dialogClickModal: false,\n\n    // 列表使用 column\n    column: [\n      { label: '字段A', prop: 'fieldA', search: true },\n      { label: '字段B', prop: 'fieldB' },\n    ],\n\n    // 表单使用 group（分组标签页）\n    group: [\n      {\n        label: '基础信息',\n        prop: 'baseInfo',\n        icon: 'el-icon-user-solid',\n        column: [\n          { label: '字段A', prop: 'fieldA', rules: [{ required: true, message: '请输入', trigger: 'blur' }] },\n          { label: '字段B', prop: 'fieldB', type: 'select', dicData: [] },\n        ],\n      },\n      {\n        label: '详细信息',\n        prop: 'detailInfo',\n        icon: 'el-icon-s-order',\n        column: [\n          { label: '字段C', prop: 'fieldC' },\n          { label: '字段D', prop: 'fieldD', type: 'textarea', span: 24 },\n        ],\n      },\n    ],\n  };\n};\n```\n\n### 列属性速查\n\n| 属性 | 类型 | 说明 |\n|------|------|------|\n| `label` | String | 列标题/表单标签 |\n| `prop` | String | 字段名 |\n| `type` | String | 组件类型：input/select/radio/checkbox/switch/date/datetime/textarea/number/tree/upload |\n| `search` | Boolean | 是否为搜索字段 |\n| `hide` | Boolean | 列表中隐藏 |\n| `display` | Boolean | 表单中隐藏（false=不显示） |\n| `addDisplay` | Boolean | 新增表单中隐藏 |\n| `editDisplay` | Boolean | 编辑表单中隐藏 |\n| `addDisabled` | Boolean | 新增时禁用 |\n| `editDisabled` | Boolean | 编辑时禁用 |\n| `span` | Number | 表单栅格宽度（24=整行） |\n| `slot` | Boolean | 启用自定义插槽 |\n| `rules` | Array | 校验规则 |\n| `dicUrl` | String | 远程字典 URL |\n| `dicData` | Array | 本地字典数据 |\n| `props` | Object | 字典映射 { label, value } |\n| `value` | Any | 默认值 |\n| `width` | Number | 列宽度 |\n| `align` | String | 对齐方式：left/center/right |\n| `overHidden` | Boolean | 超长文本省略显示 |\n| `component` | String | 自定义组件名 |\n| `searchRange` | Boolean | 搜索使用范围选择 |\n| `format` | String | 显示格式 |\n| `valueFormat` | String | 值格式 |\n| `minRows` | Number | textarea 最小行数 |\n\n---\n\n## 三、Vue 页面 - Options API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"$refs.crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n\n      <!-- 自定义列插槽示例 -->\n      <template #{slotField}=\"{ row }\">\n        <el-tag>{{ row.{slotField} }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\nimport { mapGetters } from 'vuex';\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      option: tableOption,\n      data: [],\n    };\n  },\n\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: this.validData(this.permission.{modelCode}_add, false),\n        viewBtn: this.validData(this.permission.{modelCode}_view, false),\n        delBtn: this.validData(this.permission.{modelCode}_delete, false),\n        editBtn: this.validData(this.permission.{modelCode}_edit, false),\n      };\n    },\n    ids() {\n      let ids = [];\n      this.selectionList.forEach(ele => {\n        ids.push(ele.id);\n      });\n      return ids.join(',');\n    },\n  },\n\n  methods: {\n    rowSave(row, done, loading) {\n      add(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdate(row, index, done, loading) {\n      update(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(row.id);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n      });\n    },\n\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据');\n        return;\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(this.ids);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n        this.$refs.crud.toggleSelection();\n      });\n    },\n\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data;\n        });\n      }\n      done();\n    },\n\n    searchChange(params, done) {\n      this.query = params;\n      this.page.currentPage = 1;\n      this.onLoad(this.page, params);\n      done();\n    },\n\n    searchReset() {\n      this.query = {};\n      this.onLoad(this.page);\n    },\n\n    selectionChange(list) {\n      this.selectionList = list;\n    },\n\n    selectionClear() {\n      this.selectionList = [];\n      this.$refs.crud.toggleSelection();\n    },\n\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage;\n    },\n\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize;\n    },\n\n    refreshChange() {\n      this.onLoad(this.page, this.query);\n    },\n\n    onLoad(page, params = {}) {\n      this.loading = true;\n      getList(\n        page.currentPage,\n        page.pageSize,\n        Object.assign(params, this.query)\n      ).then(res => {\n        const data = res.data.data;\n        this.page.total = data.total;\n        this.data = data.records;\n        this.loading = false;\n        this.selectionClear();\n      });\n    },\n  },\n};\n</script>\n```\n\n---\n\n## 四、Vue 页面 - Composition API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed } from 'vue';\nimport { useStore } from 'vuex';\nimport { ElMessage, ElMessageBox } from 'element-plus';\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\n\nconst store = useStore();\nconst crud = ref(null);\n\n// 数据\nconst form = ref({});\nconst query = ref({});\nconst loading = ref(true);\nconst data = ref([]);\nconst selectionList = ref([]);\nconst option = reactive(tableOption);\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n});\n\n// 权限\nconst permission = computed(() => store.getters.permission);\nconst permissionList = computed(() => {\n  const validData = (value, defaultVal = false) => (value ? true : defaultVal);\n  return {\n    addBtn: validData(permission.value.{modelCode}_add),\n    viewBtn: validData(permission.value.{modelCode}_view),\n    delBtn: validData(permission.value.{modelCode}_delete),\n    editBtn: validData(permission.value.{modelCode}_edit),\n  };\n});\n\nconst ids = computed(() => {\n  return selectionList.value.map(ele => ele.id).join(',');\n});\n\n// 方法\nconst rowSave = (row, done, loading) => {\n  add(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(row.id);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n  });\n};\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据');\n    return;\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(ids.value);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n    crud.value.toggleSelection();\n  });\n};\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data;\n    });\n  }\n  done();\n};\n\nconst searchChange = (params, done) => {\n  query.value = params;\n  page.value.currentPage = 1;\n  onLoad(page.value, params);\n  done();\n};\n\nconst searchReset = () => {\n  query.value = {};\n  onLoad(page.value);\n};\n\nconst selectionChange = (list) => {\n  selectionList.value = list;\n};\n\nconst currentChange = (currentPage) => {\n  page.value.currentPage = currentPage;\n};\n\nconst sizeChange = (pageSize) => {\n  page.value.pageSize = pageSize;\n};\n\nconst refreshChange = () => {\n  onLoad(page.value, query.value);\n};\n\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(\n    page.currentPage,\n    page.pageSize,\n    Object.assign(params, query.value)\n  ).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n    selectionList.value = [];\n  });\n};\n</script>\n```\n\n### Options API vs Composition API 转换对照\n\n| Options API | Composition API |\n|-------------|-----------------|\n| `data() { return { form: {} } }` | `const form = ref({})` |\n| `computed: { ids() { ... } }` | `const ids = computed(() => { ... })` |\n| `methods: { rowSave() { ... } }` | `const rowSave = () => { ... }` |\n| `this.form` | `form.value`（模板中直接用 `form`） |\n| `this.$refs.crud` | `crud.value` |\n| `this.$message.success(...)` | `ElMessage.success(...)` |\n| `this.$confirm(...)` | `ElMessageBox.confirm(...)` |\n| `...mapGetters(['permission'])` | `const permission = computed(() => store.getters.permission)` |\n| `mounted() { ... }` | `onMounted(() => { ... })` |\n| `watch: { ... }` | `watch(source, callback)` |\n\n---\n\n## 五、树形结构模式\n\n### Option 配置（追加树形父节点字段）\n\n```javascript\n// 在 column 中添加树形父节点字段\n{\n  label: '上级{中文名}',\n  prop: 'parentId',\n  type: 'tree',\n  dicData: [],                    // 动态加载\n  hide: true,\n  value: 0,                       // 默认值为顶级\n  props: {\n    label: 'title',\n    value: 'value',\n    children: 'children',\n  },\n},\n```\n\n### Vue 页面（树形数据加载）\n\n在 `onLoad` 之后加载树形数据并更新列配置：\n\n```javascript\n// Options API\nmethods: {\n  onLoad(page, params = {}) {\n    this.loading = true;\n    getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n      const data = res.data.data;\n      this.page.total = data.total;\n      this.data = data.records;\n      this.loading = false;\n\n      // 加载树形数据\n      getTree().then(treeRes => {\n        const column = this.findObject(this.option.column, 'parentId');\n        column.dicData = treeRes.data.data;\n      });\n    });\n  },\n},\n\n// Composition API\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(page.currentPage, page.pageSize, Object.assign(params, query.value)).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n\n    getTree().then(treeRes => {\n      const column = findObject(option.column, 'parentId');\n      column.dicData = treeRes.data.data;\n    });\n  });\n};\n```\n\n### 表格树形展示\n\n在 avue-crud 模板中使用树形属性：\n\n```vue\n<avue-crud\n  :option=\"option\"\n  :data=\"data\"\n  row-key=\"id\"\n  :tree-props=\"{ children: 'children', hasChildren: 'hasChildren' }\"\n>\n```\n\n---\n\n## 六、主子表模式\n\n### API 层（主表 + 子表）\n\n```javascript\n// 主表 API（同标准 CRUD）\nexport const getList = (current, size, params) => { ... };\nexport const add = (row) => { ... };\nexport const update = (row) => { ... };\nexport const remove = (ids) => { ... };\n\n// 子表 API\nexport const getSubList = (current, size, {fkFieldName}, params) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n      {fkFieldName},\n    },\n  });\n};\n\nexport const addSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const updateSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const removeSub = (ids) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/remove',\n    method: 'post',\n    params: { ids },\n  });\n};\n```\n\n### Vue 页面（主子表 Drawer 模式）\n\n```vue\n<template>\n  <basic-container>\n    <!-- 主表 -->\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu=\"{ row }\">\n        <el-button text type=\"primary\" @click=\"openSub(row)\">子表</el-button>\n      </template>\n    </avue-crud>\n\n    <!-- 子表抽屉 -->\n    <el-drawer\n      title=\"{子表中文名}\"\n      v-model=\"subBox\"\n      size=\"60%\"\n      append-to-body\n    >\n      <avue-crud\n        :option=\"subOption\"\n        :table-loading=\"subLoading\"\n        :data=\"subData\"\n        :page=\"subPage\"\n        ref=\"crudSub\"\n        v-model=\"subForm\"\n        @row-del=\"rowDelSub\"\n        @row-update=\"rowUpdateSub\"\n        @row-save=\"rowSaveSub\"\n        @on-load=\"onLoadSub\"\n      />\n    </el-drawer>\n  </basic-container>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      // 主表数据\n      form: {},\n      data: [],\n      loading: true,\n      page: { pageSize: 10, currentPage: 1, total: 0 },\n      option: tableOption,\n\n      // 子表数据\n      subBox: false,\n      subForm: {},\n      subData: [],\n      subLoading: true,\n      subPage: { pageSize: 10, currentPage: 1, total: 0 },\n      subOption: subTableOption,\n      mainId: '',\n    };\n  },\n\n  methods: {\n    openSub(row) {\n      this.mainId = row.id;\n      this.subBox = true;\n      this.onLoadSub(this.subPage);\n    },\n\n    rowSaveSub(row, done, loading) {\n      row.{fkFieldName} = this.mainId;\n      addSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdateSub(row, index, done, loading) {\n      updateSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDelSub(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return removeSub(row.id);\n      }).then(() => {\n        this.onLoadSub(this.subPage);\n        this.$message.success('操作成功!');\n      });\n    },\n\n    onLoadSub(page, params = {}) {\n      this.subLoading = true;\n      getSubList(\n        page.currentPage,\n        page.pageSize,\n        this.mainId,\n        params\n      ).then(res => {\n        const data = res.data.data;\n        this.subPage.total = data.total;\n        this.subData = data.records;\n        this.subLoading = false;\n      });\n    },\n\n    // ... 主表方法同标准 CRUD\n  },\n};\n</script>\n```\n\n---\n\n## 附：常用工具函数\n\n### 数组与逗号字符串互转\n\n后端用逗号字符串存储多选值（如 deptId=\"1,2,3\"），前端需要转换：\n\n```javascript\nimport func from '@/utils/func';\n\n// 提交时：数组 → 逗号字符串\nrow.deptId = func.join(row.deptId);\n\n// 回显时：逗号字符串 → 数组\nthis.form.deptId = func.split(this.form.deptId);\n```\n\n### 导出功能\n\n```javascript\nimport NProgress from 'nprogress';\nimport { exportBlob } from '@/api/common';\nimport { downloadXls } from '@/utils/util';\n\nconst handleExport = () => {\n  NProgress.start();\n  exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}?${qs.stringify(this.query)}`).then(res => {\n    downloadXls(res.data, `{中文名}数据${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);\n    NProgress.done();\n  });\n};\n```\n\n### 辅助方法 findObject / findColumn\n\n```javascript\n// 在 option 的 column 中查找指定 prop 的列\nconst column = this.findObject(this.option.column, 'fieldName');\n\n// 在 group 中查找\nconst column = this.findColumn(this.option.group, 'fieldName');\n```\n\n这两个是全局挂载的工具方法（在 `main.js` 中注册）。\n",".claude/skills/blade-plan/README.md":"# Blade Plan\n\nSpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为结构化任务，逐个执行，全程无辅助文件。\n\n## 核心理念\n\n**先想清楚，再动手写。** 面对涉及多个文件的改动，不急着写代码，而是先理清思路、拆好任务、确认方案，再逐个执行。用最小的流程开销，获得结构化开发的核心收益。\n\n```\n需求 → 分析规划 → 确认 → 逐任务执行 → 完成总结\n```\n\n## 快速开始\n\n```bash\n# 启动规划执行 — 展示方案后等待确认\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n\n# 自动模式 — 展示方案后直接执行，不等待确认\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n```\n\n## 工作流程\n\n**交互模式（默认）：** 展示规划方案后暂停等待确认，审批通过后开始逐任务执行。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析与规划                                              │\n  │  理解需求 → 扫描代码 → 制定方案 → 拆解任务                 │\n  │       ↓ 用户确认                                            │\n  │                                                             │\n  │  🔨 逐任务执行（TaskCreate/TaskUpdate 追踪进度）            │\n  │  Task 1 ✅ → Task 2 ✅ → Task 3 🔄 → Task 4 ⏳ → ...      │\n  │       ↓                                                     │\n  │  ✅ 完成总结                                                │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n**自动模式（--auto）：** 展示规划后直接开始执行，不等待确认。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析规划  →  🔨 逐任务执行  →  ✅ 完成总结             │\n  │  （展示方案后直接执行，不暂停）                             │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n## 适用场景\n\n| 场景 | 适合 | 不适合 |\n|------|------|--------|\n| 新增中等功能模块（如限流注解、审计日志） | ✅ | |\n| 跨多个文件的 Bug 修复 | ✅ | |\n| 模块级重构（如拆分策略类） | ✅ | |\n| 新增 Starter 模块 | ✅ | |\n| 现有功能增强（添加新维度、新策略） | ✅ | |\n| 单文件简单修改 | | ❌ 直接对话即可 |\n| 快速问答或查询 | | ❌ 直接对话即可 |\n| 从零开发完整业务系统 | | ❌ 建议拆成多次会话分阶段推进 |\n| 需要先独立设计数据模型或跨服务契约 | | ❌ 先单独设计再进入 Plan |\n\n## 复杂度分层\n\n| 复杂度 | 文件变更量 | 建议方式 |\n|--------|-----------|---------|\n| 低 | 1~5 个文件 | 直接对话 |\n| **中** | **5~15 个文件** | **Blade Plan** |\n| 高 | 15+ 个文件 | 拆成多次会话，每次走一次 `/blade-plan` |\n\n## 特性\n\n- **零文件开销**：不生成任何辅助文件，规划在对话中展示，进度由内置任务系统追踪\n- **上下文锚定**：每个任务执行前重读相关代码，防止偏离\n- **构建验证**：每组任务完成后自动执行编译检查\n- **技能协同**：自动识别可借助 `/blade-design`、`/avue-design` 加速的任务\n- **智能降级**：过于简单时直接执行，复杂度过高时建议拆成多次会话\n- **SpringBlade 体系适配**：内置 Boot / Cloud 分层最佳实践，同时可适配其他技术栈\n\n## 文件结构\n\n```\nblade-plan/             # Skill 定义\n├── README.md           # 本文件\n└── SKILL.md            # Skill 主指令（全部内容，无 references 目录）\n```\n\n运行时不产生任何文件，所有状态由 Claude Code 内置任务系统管理。\n",".claude/skills/blade-plan/SKILL.md":"---\nname: blade-plan\ndescription: >\n  SpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为「分析规划 → 逐任务执行 → 完成总结」\n  三步流程，用 Claude Code 内置任务系统追踪进度，全程无辅助文件。适用于新增中等功能模块、修复跨模块 Bug、\n  中等规模重构、添加新 Starter 等涉及 5~15 个文件变更的开发任务。\n  当用户说\"帮我做一个XX功能\"、\"这个改动涉及几个文件\"、\"帮我重构一下XX\"、\"帮我加个XX\"、\n  \"plan\"、\"规划一下再做\"、\"先想清楚再写\"、\"这个稍微有点复杂\"等时触发。也可直接使用 /blade-plan 调用。\n  不适用于简单单文件修改（直接对话即可），也不适用于需要完整架构设计的复杂系统（建议分阶段拆解为多次会话）。\n  即使用户没有说\"plan\"，只要需求涉及多文件改动或需要一定设计思考，也应主动建议使用本工作流。\n---\n\n# Blade Plan — 轻量规划执行工作流\n\n先想清楚，再动手写。用最小的流程开销，获得结构化开发的核心收益。\n\n## 定位\n\nBlade Plan 面向**中等复杂度**的开发任务，在\"直接对话\"和\"分阶段设计\"之间提供一条中间路径：\n\n| 复杂度 | 方式 | 文件变更量 | 典型场景 |\n|--------|------|-----------|---------|\n| 低 | 直接对话 | 1~5 个文件 | 单个 bug 修复、配置调整、工具方法添加 |\n| **中** | **Blade Plan** | **5~15 个文件** | **新增中等功能、跨模块重构、复杂 bug、新 Starter** |\n| 高 | 分阶段执行 | 15+ 个文件 | 从零开发完整模块、复杂系统设计、大型架构重构 |\n\n判断标准不是绝对的文件数，而是：需求是否清晰到可以直接拆任务？改动是否能在一次会话内完成？如果两个答案都是\"是\"，Blade Plan 就是合适的选择。\n\n## 核心理念\n\n- **先规划后执行**：先把思路理清、任务拆好，再逐个动手——这能避免写到一半发现方向错了\n- **上下文锚定**：每个任务执行前重读相关代码，防止在对话累积中逐渐偏离\n- **最小变更**：只做计划中的事，不夹带\"顺手优化\"\n- **构建验证**：编译不过的代码等于没写\n- **零文件开销**：不生成任何辅助文件（无 spec.json、tasks.json、design.md 等），所有规划直接在对话中呈现，进度由 Claude Code 内置 TaskCreate / TaskUpdate 追踪\n\n## 使用方式\n\n```\n/blade-plan <需求描述>           # 启动规划执行（展示方案后等待确认）\n/blade-plan --auto <需求描述>    # 启动规划执行（自动模式，展示方案后直接执行）\n```\n\n**示例：**\n\n```\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n/blade-plan 修复多租户场景下动态数据源切换时连接池泄漏问题\n```\n\n## 路由逻辑\n\n收到用户输入后：\n\n1. **带 `--auto` 参数**：提取需求描述 → 自动模式（规划后直接执行，不等确认）\n2. **带需求描述**：→ 交互模式（规划后等待用户确认）\n3. **无参数**：提示输入需求描述\n\n---\n\n## 工作流\n\n```\n用户需求 → [分析与规划] → 确认 → [逐任务执行] → 完成总结\n                              ↑\n                         自动模式跳过\n```\n\n三个步骤，一次确认，全程用 Claude Code 内置任务系统追踪进度。\n\n---\n\n### 步骤一：分析与规划\n\n**目标**：理解需求，摸清现状，制定可执行的任务清单。\n\n**执行动作：**\n\n1. **理解需求**：\n   - 解析用户的需求描述，提炼核心功能点\n   - 识别隐含需求和边界条件\n   - 如有歧义，先向用户澄清关键问题再继续\n\n2. **扫描现状**：\n   - 阅读 CLAUDE.md 了解项目规范（如有）\n   - 扫描相关模块现有代码，理解代码结构和风格\n   - 搜索是否有可复用的类、工具方法或已有实现\n   - 评估改动对现有功能的影响范围\n\n3. **制定方案**：\n   - 确定技术实现策略（用什么方式解决问题）\n   - 识别需要新增和修改的文件\n   - 将改动拆解为有序的任务列表（按依赖关系排序）\n   - 任务粒度：一个任务对应一个逻辑完整的改动单元（通常 1~3 个文件）\n\n4. **展示规划**，格式如下：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  📋 Blade Plan\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n需求：{一句话概述}\n\n方案：\n  {2~4 句话描述技术方案的核心思路，\n   包括关键的技术选型和架构决策}\n\n任务清单（共 X 个）：\n  1. {任务标题}  — {涉及的文件或模块}\n  2. {任务标题}  — {涉及的文件或模块}\n  3. {任务标题}  — {涉及的文件或模块}\n  ...\n\n影响范围：\n  新增 X 个文件 | 修改 Y 个文件\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n5. **等待确认或自动推进**：\n   - **交互模式**：追加 `请确认方案后开始执行，或提出修改意见。` 并等待用户回复\n   - **自动模式**：展示规划后直接进入步骤二\n   - 用户说\"可以\"/\"没问题\"/\"开始\"等视为确认通过\n   - 用户提出修改意见则调整方案后重新展示\n\n6. **创建任务**：确认后，使用 **TaskCreate** 为每个任务创建跟踪条目，便于用户实时看到进度\n\n---\n\n### 步骤二：逐任务执行\n\n**目标**：按计划逐个完成任务，保持纪律性。\n\n**对每个任务执行以下循环：**\n\n1. **TaskUpdate** → 标记当前任务为 `in_progress`\n\n2. **加载上下文**（上下文锚定）：\n   - 重新阅读目标文件及其关联代码——即使刚才读过，也要再看一眼\n   - 查看前序任务的产出文件，确保接续一致\n   - 模仿同模块现有代码的风格和模式\n\n3. **执行编码**：\n   - 严格遵循项目 CLAUDE.md 编码规范（如有）\n   - 只做当前任务描述的事，不附带额外改动\n   - 执行前判断是否可借助其他 Blade 技能加速（见「技能协同」）\n\n4. **TaskUpdate** → 标记当前任务为 `completed`\n\n5. **继续下一个任务**\n\n> 为什么要逐任务而不是一口气全写完？因为每个任务完成后的代码状态是下一个任务的输入。逐步推进让每一步都建立在稳固的基础上，也让用户能实时看到进度。\n\n#### 构建检查点\n\n每完成一组逻辑相关的任务（或每 3~4 个任务），执行构建验证：\n\n- **Maven 项目**：`mvn clean compile -DskipTests`\n- **Gradle 项目**：`gradle compileJava`\n- **Node.js 项目**：`npm run build` 或 `tsc --noEmit`\n- **其他项目**：根据项目构建配置自动识别\n\n构建失败则立即修复后再继续，不跳过，不拖延。\n\n#### 技能协同\n\n执行任务时，识别任务类型并借助对应技能提升效率：\n\n| 任务特征 | 推荐技能 | 说明 |\n|---------|---------|------|\n| 后端 CRUD 全套（Entity → Mapper → Service → Controller） | `/blade-design` | 生成骨架代码，再根据实际需求补充业务逻辑 |\n| Avue 组件前端页面 | `/avue-design` | 生成配置化的 Avue 前端代码 |\n\n**协同原则**：技能生成的代码是起点而非终点——生成后必须根据实际需求调整，并通过构建验证。\n\n> **关于代码提交**：`/blade-commit` 绝不自动执行。完成总结后将提交作为后续建议告知用户，由用户自行决定何时提交。仅当用户明确要求\"帮我提交\"时才执行。\n\n---\n\n### 步骤三：完成总结\n\n所有任务完成后，输出总结报告：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  ✅ Blade Plan 执行完毕\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n任务: X/X 完成\n\n新增文件:\n  + path/to/NewFile.java\n  + path/to/AnotherFile.java\n\n修改文件:\n  ~ path/to/ExistingFile.java\n\n后续建议:\n  1. 功能测试（交由用户执行）\n  2. 确认无误后可使用 /blade-commit 提交代码\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n---\n\n## 关键原则\n\n### 上下文锚定\n\n每个任务执行前，重新阅读相关代码。哪怕觉得\"刚才看过了\"，也要再扫一眼。中等任务的对话虽不如大型任务长，但 5~6 个任务执行下来，上下文已经积累了不少内容，记忆会产生微妙偏差。花几秒重读比花几分钟修偏划算得多。\n\n### 遵守项目规范\n\n先看同模块现有代码怎么写的，再动手。代码风格、命名习惯、继承关系、分层结构——模仿现有代码是最可靠的规范来源。如有 CLAUDE.md，严格遵循其中的编码规范。在 SpringBlade 体系中，Boot 和 Cloud 共享统一分层：Controller → Service → Mapper → Wrapper → Entity，差异仅在包路径和路由前缀（Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 手动拼前缀，Cloud 由网关按服务名自动路由）。\n\n### 最小变更原则\n\n每个任务只做计划中描述的事。发现\"顺手可以优化\"的地方，记下来在总结中告诉用户，但不自行改动。未计划的改动是 bug 的温床。\n\n### 复用优先\n\n编写新代码前，先在项目中搜索是否已有可复用的工具类、基类或配置。尤其在 SpringBlade 体系中，`blade-core-tool` 和各 Starter 模块提供了大量基础设施，重复造轮子不仅浪费时间，还会引入风格不一致。\n\n### 构建验证\n\n代码变更后主动执行构建验证。编译不过就立即修，不要想着\"后面一起修\"——错误会像雪球一样越滚越大。\n\n---\n\n## 何时降级为直接对话\n\n如果分析后发现：\n\n- 只涉及 1~5 个文件的简单改动\n- 不需要拆解任务，直接改就行\n\n主动提示：`这个改动比较简单，不需要走规划流程，直接帮你改。` 然后直接执行。\n\n## 何时建议分阶段拆解\n\n如果在规划阶段发现以下信号，建议用户把需求拆成多次会话分阶段推进：\n\n- 任务数量超过 15 个，工作量超出一次会话的合理范围\n- 涉及复杂的数据库表结构设计（多表关联、索引策略），需要先单独设计数据模型\n- 需要跨多个服务的 API 协调，应当先固定契约再逐服务落地\n- 需求本身还不清晰，应先通过对话澄清、对齐理解后再进入 Plan 流程\n\n主动提示：`这个需求的复杂度较高，建议先拆成几个独立阶段（如：设计数据模型 → 落地后端 → 补全前端），每个阶段单独走一次 /blade-plan 会更稳。`\n","CLAUDE.md":"# CLAUDE.md\n\n欢迎来到 SpringBlade 开源微服务版（master 分支）。本文档汇总了微服务拓扑、API/Service 分层、Feign 调用、网关与配置中心等常见约定，帮助 Claude Code 快速上手，产出与项目风格一致的代码。\n\n作为 AI 助手参与本项目开发时，你必须：\n\n- 动手前先看一看目标服务及其 API 模块的现有实现，模仿它的结构与风格通常是最快的路径\n- 需要外部知识时，优先查阅 Spring Boot、Spring Cloud、MyBatis-Plus、Nacos 等官方文档，而不是凭记忆作答\n- 需求不够清晰时，先复述已知信息、抛出关键澄清问题，再动手\n- 面对跨服务或跨模块改动，拆成可验证的小步骤逐步推进比一气呵成更稳妥\n\n> 所有开发内容必须建立在深度思考过的基础之上，禁止机械生成与错误填充。\n> 如果你已了解所有规范，请在用户第一次对话时说明：\"我已充分了解 SpringBlade 开源微服务版开发规范。\"\n\n---\n\n## 1. 项目概述\n\nSpringBlade 是 BladeX 平台的**开源微服务版本**（master 分支），基于 Spring Cloud + Spring Boot 构建，由 Nacos 做服务发现与配置中心，Sentinel 做熔断限流，Spring Cloud Gateway 做统一入口，依赖 blade-tool 核心库。\n\n---\n\n## 2. 项目架构\n\n```\nSpringBlade/                            # Maven 父工程（pom 聚合）\n├── blade-auth/                         # 认证授权服务（多 granter：password/refresh/social/captcha）\n├── blade-gateway/                      # Spring Cloud Gateway 网关（WebFlux + JWT 鉴权过滤器）\n├── blade-common/                       # 公共模块（常量、启动器、工具、缓存键）\n├── blade-ops/                          # 运维服务父模块\n│   ├── blade-admin/                    #   Spring Boot Admin 监控\n│   ├── blade-develop/                  #   代码生成（Velocity 模板）\n│   ├── blade-report/                   #   报表服务（UReport2）\n│   ├── blade-resource/                 #   资源 / OSS 管理\n│   ├── blade-seata-order/              #   Seata 分布式事务演示：订单\n│   └── blade-seata-storage/            #   Seata 分布式事务演示：库存\n├── blade-service/                      # 业务服务父模块\n│   ├── blade-system/                   #   系统管理（用户/角色/菜单/字典/租户/部门/岗位）\n│   ├── blade-desk/                     #   工作台（通知公告）\n│   ├── blade-log/                      #   日志服务（API/Error/Usual 日志落库）\n│   └── blade-demo/                     #   业务演示模块\n├── blade-service-api/                  # 业务契约父模块（Entity / VO / DTO / Feign）\n│   ├── blade-system-api/               #   ISysClient（部门、租户、客户端信息）\n│   ├── blade-user-api/                 #   IUserClient（用户核心信息）\n│   ├── blade-desk-api/                 #   桌面 API 契约\n│   ├── blade-dict-api/                 #   IDictClient（字典翻译）\n│   ├── blade-scope-api/                #   数据权限契约\n│   └── blade-demo-api/                 #   演示模块契约\n├── doc/\n│   ├── nacos/                          # Nacos 配置脚本（blade.yaml、各环境 datasource）\n│   └── sql/                            # 多数据库建表 & 升级脚本\n├── script/                             # Docker / FatJar / 服务脚本\n└── pom.xml                             # 父 POM（blade-tool 依赖管理）\n```\n\n> **与开源 Boot 版的关键区别**：Cloud 为多模块 Maven 工程，存在 **API/Service 分离**，跨服务调用通过 Feign，Controller 路由**不带** `AppConstant` 前缀（网关按服务名路由）。\n\n### 2.1 分层架构\n\n每个业务域遵循 **API 模块 + Service 模块**双层划分：\n\n| 层次 | 包路径 | 所在模块 |\n| --- | --- | --- |\n| Controller | `controller/` | Service 模块（`blade-service/*` 或 `blade-ops/*`） |\n| Service / ServiceImpl | `service/` + `service/impl/` | Service 模块 |\n| Mapper（接口 + XML 同包） | `mapper/` | Service 模块 |\n| Wrapper | `wrapper/`（Entity → VO 转换） | Service 模块 |\n| Excel 导入导出 | `excel/` | Service 模块 |\n| Feign 实现类 | `feign/` | Service 模块（实现对应 API 模块的 Feign 接口） |\n| Entity | `entity/` | **API 模块（`blade-*-api`）** |\n| VO / DTO | `vo/` `dto/` | **API 模块** |\n| Feign Client + Fallback | `feign/` | **API 模块** |\n\n### 2.2 API / Service 分离原则\n\n- **API 模块**（`blade-*-api`）：定义 Entity、VO、DTO、Feign 接口 + Fallback，供其他服务依赖；**不含**具体业务逻辑\n- **Service 模块**（`blade-service/*`、`blade-ops/*`）：实现 Controller、ServiceImpl、Mapper、Wrapper 与 Feign 接口的服务端实现\n- 跨服务调用**必须**通过 Feign Client，禁止直接依赖其他 Service 模块\n- 公共常量与工具放在 `blade-common`\n\n---\n\n## 3. 技术栈\n\n| 类别 | 技术 |\n| --- | --- |\n| JDK | Java 21 |\n| 基础框架 | Spring Boot / Spring Cloud / Spring Cloud Alibaba / Maven |\n| 核心库 | blade-core-bom、blade-core-cloud、blade-core-boot |\n| 注册中心 / 配置中心 | Nacos |\n| 熔断限流 | Sentinel（`spring-cloud-starter-alibaba-sentinel`） |\n| 分布式事务 | Seata（`blade-starter-transaction`，见 `blade-seata-order`、`blade-seata-storage`） |\n| RPC | OpenFeign（与 Sentinel 集成） |\n| 网关 | Spring Cloud Gateway（WebFlux） |\n| ORM | MyBatis-Plus + dynamic-datasource-spring-boot4-starter |\n| 安全 | blade-core-secure（JWT + 可选 AES 加密 + SM2 国密登录） |\n| 缓存 | BladeRedis（Protostuff 序列化，响应式 Gateway 侧用 Reactive Redis） |\n| 数据权限 | blade-starter-datascope（DataScopeHandler 行级过滤） |\n| 监控 | Spring Boot Admin（`blade-admin`）+ Actuator |\n| 文档 | springdoc（各服务 OpenAPI 3 原生 UI） |\n| 报表 | UReport2（`blade-report`） |\n| 代码生成 | blade-starter-develop + Apache Velocity（`blade-develop`） |\n| 验证码 | easy-captcha |\n| JWT | jjwt-impl + jjwt-jackson |\n| 数据库 | MySQL / PostgreSQL / Oracle / SQL Server / 达梦 / 人大金仓 / 崖山 |\n\n---\n\n## 4. 开发规范\n\n### 4.1 编写新功能前\n\n1. 先阅读目标服务中已有的类，理解其结构、命名和风格\n2. 标准参考模块：`blade-service/blade-system`（Service 最完整）、`blade-service-api/blade-system-api`（API 契约最完整）、`blade-service/blade-desk` + `blade-desk-api`（最简样板）\n3. 主动模仿现有代码风格，包括缩进（Java 用 Tab，YAML/JSON 用 2 空格）、注解顺序、Javadoc 格式\n4. 新建 Java 源文件必须包含 Apache 2.0 许可证头部和 Javadoc 类注释（`@author Chill`）\n\n### 4.2 编写完成后\n\n1. 使用 `mvn clean compile -DskipTests` 编译验证（父工程根目录执行会编译所有子模块），编译不通过必须修复\n2. 引入模块依赖前先检查循环依赖；**跨服务依赖必须经由 `blade-*-api` + Feign**，禁止 `blade-service/A` 直接依赖 `blade-service/B`\n3. 编译通过后将测试交由用户执行，**不得自行执行任何测试**（包括 `mvn test`、启动 Nacos/服务、访问接口）\n4. 除非用户明确要求，不应撰写示例或额外文档\n\n### 4.3 代码生成\n\n当需要生成 CRUD 全套代码（Entity、VO、Service、Controller、Wrapper、Mapper、Feign、建表语句等）时，优先使用 **`/blade-design`** skill。该 skill 可根据模块名、实体名和字段列表，自动生成符合 BladeX 框架规范的后端代码和多数据库建表语句，确保生成结果与项目风格完全一致。\n\n本工程亦内置 `blade-ops/blade-develop` 代码生成服务（Velocity 模板），可在运行时通过后台界面生成。\n\n---\n\n## 5. 命名规范\n\n### 5.1 包名\n\n统一前缀 `org.springblade`，按业务域划分：`system`、`system.user`、`desk`、`auth`、`gateway`、`develop`、`report`、`resource`、`common` 等。API 与 Service 两侧使用相同业务域包名，彼此仅通过模块边界隔离。\n\n### 5.2 类名\n\n| 类型 | 规则 | 示例 |\n| --- | --- | --- |\n| Entity | 直接类名 | `Post`、`User`、`Notice` |\n| VO / DTO | `XxxVO` / `XxxDTO` | `MenuVO`、`RoleDTO` |\n| Service 接口 | `IXxxService` | `IParamService`、`IUserService` |\n| Service 实现 | `XxxServiceImpl` | `ParamServiceImpl` |\n| Controller | `XxxController` | `ParamController` |\n| Mapper | `XxxMapper` | `ParamMapper` |\n| Wrapper | `XxxWrapper` | `UserWrapper` |\n| Feign 接口 / 降级 | `IXxxClient` / `IXxxClientFallback` | `ISysClient` / `ISysClientFallback`、`IUserClient` / `IUserClientFallback` |\n| 表名 | `blade_` + 下划线 | `blade_user`、`blade_post` |\n\n### 5.3 变量命名\n\n- 必须具有明确语义：`Exception exception`（✅）`Exception e`（❌）；`List<Post> postList`（✅）`List<Post> list`（❌）\n- 冲突时提升语义：`Cache cache; Cache postCache`（✅）`Cache cache1; Cache cache2`（❌）\n\n---\n\n## 6. 编码规范\n\n### 6.1 注解顺序\n\n- **Controller 类**：`@RestController` → Lombok（`@AllArgsConstructor`） → `@RequestMapping(\"/resource\")` → `@Tag`\n- **Controller 方法**：HTTP 方法注解（`@GetMapping`/`@PostMapping`）→ `@Operation` → `@PreAuth`（如需角色控制）\n- **Feign 接口**：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- **Feign Fallback**：`@Component` → 实现对应接口\n- **Entity 类**：`@Data` → `@TableName` → `@EqualsAndHashCode(callSuper = true)`（继承 BaseEntity/TenantEntity 时必须） → `@Schema`（字段级使用）\n- **VO 类**：`@Data` → `@EqualsAndHashCode(callSuper = true)` → `@Schema`\n\n### 6.2 Entity-Service 继承体系（核心）\n\nEntity 基类的选择**直接决定** Service 和 ServiceImpl 的继承方式，三者必须配套：\n\n| 场景 | Entity | Service 接口 | ServiceImpl | 示例 |\n| --- | --- | --- | --- | --- |\n| **多租户业务表** | `extends TenantEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `User`、`Post`、`Notice` |\n| **非租户业务表** | `extends BaseEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `Tenant` |\n| **轻量级/关系表** | `implements Serializable` | `extends IService<T>` | `extends ServiceImpl<M, T>` | `RoleMenu`、`Dict`、`Role` |\n\n- `TenantEntity` 继承自 `BaseEntity` 并额外包含 `tenantId`\n- `BaseEntity` 包含：`id`、`createUser`、`createDept`、`createTime`、`updateUser`、`updateTime`、`status`、`isDeleted`\n- `BaseService`/`BaseServiceImpl` 是 BladeX 增强版，提供 `deleteLogic(List<Long>)` 等方法，**要求 Entity 继承 BaseEntity 或 TenantEntity**\n- `IService`/`ServiceImpl` 是 MyBatis-Plus 原生版，用于 `implements Serializable` 的轻量实体，删除用 `removeByIds()`\n\n### 6.3 Controller 约定\n\n- 视需要继承 `BladeController`（获取 `getUser()` 等便捷方法），使用 `@AllArgsConstructor` 注入依赖\n- **路由直接使用资源路径**（如 `@RequestMapping(\"/param\")`），网关按服务名路由，**无需** `AppConstant` 前缀（与 Boot 版区别点）\n- 统一返回 `R<T>` 响应体（`R.data(...)` / `R.status(...)` / `R.success(...)`）\n- Entity → VO 转换通过 `XxxWrapper.build().entityVO(entity)` / `.pageVO(page)` / `.listVO(list)`\n- 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（角色级）；**本版本不支持** 菜单级 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 写法，也不含 ApiScopeHandler 接口权限\n- 需要跳过 Swagger 展示时使用 `@Hidden`\n\n### 6.4 Feign Client（Cloud 特有）\n\n- **接口定义在 API 模块** 的 `feign/` 包中，由所有调用方共享依赖\n- 注解签名：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- 路径常量定义在接口内部：`String API_PREFIX = \"/xxx\"`；方法路径使用 `API_PREFIX + \"/yyy\"` 拼接\n- 返回类型统一使用 `R<T>`（除非明确只返回原始对象，如 `ISysClient#getDept` 返回 `Dept`）\n- **降级类**：与接口同包，命名 `IXxxClientFallback`，标注 `@Component` 并实现全部方法，方法返回 `null` 或安全缺省值\n- Feign 实现（服务端）位于对应 Service 模块的 `feign/` 包，`@RestController` 实现接口并复用内部 Service\n- Feign + Sentinel 默认开启（`feign.sentinel.enabled=true`）\n\n### 6.5 依赖注入\n\n- Controller 使用 `@AllArgsConstructor` + `private` 字段\n- Service 需要额外依赖时使用 `@AllArgsConstructor` 或 `@RequiredArgsConstructor` + `private final` 字段\n- Feign 客户端通过构造器注入，Spring Cloud 自动代理\n\n### 6.6 Lombok\n\n禁止手写 getter/setter，统一使用 `@Data`、`@EqualsAndHashCode(callSuper = true)`、`@AllArgsConstructor`、`@RequiredArgsConstructor`、`@Slf4j`、`@SneakyThrows` 等\n\n### 6.7 Java 21 特性\n\n- **基线 JDK 21 LTS**，优先采用已稳定的现代语言特性提升可读性：`switch` 表达式与模式匹配（含 Record Patterns）、`instanceof` 模式匹配、Text Blocks（承载多行 SQL / JSON 字面量）、`@Serial`（标注 `serialVersionUID` 与序列化方法）\n- 实体 / VO 沿用 Lombok（`@Data` 等），**不改用 record**——需保留可变字段与无参构造以适配 MyBatis-Plus 映射\n- **禁止 `var`**，所有变量显式声明类型；优先使用 Stream API，Lambda 保持简洁\n- 虚拟线程（Virtual Threads）属运行期能力，由 Spring Boot 4 配置 `spring.threads.virtual.enabled` 按需开启，非编码约定\n\n### 6.8 MyBatis-Plus\n\n- 简单查询使用 `LambdaQueryWrapper` 或 `Wrappers.<T>query()`\n- 复杂查询写在 Mapper XML 中，Mapper XML 与接口**同包**放置（位于 `src/main/java` 下，由 `pom.xml` `<resources>` 声明打包）\n- 分页统一使用 `Condition.getPage(query)` + `Condition.getQueryWrapper(param, Entity.class)`\n- 禁止 JDBC 直连查询\n- 主键使用雪花算法 `IdType.ASSIGN_ID`，Long 类型主键必须标注 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n- 多数据源切换通过 dynamic-datasource 的 `@DS(\"...\")` 注解，数据源定义在 Nacos 配置\n\n### 6.9 日志\n\n- 使用 `@Slf4j`，占位符传参 `log.info(\"user {} login\", userId)`（禁止字符串拼接）\n- 包含关键业务标识，异常必须携带堆栈 `log.error(\"...\", e)`，禁止打印敏感信息（密码、Token、手机号等）\n- API 调用日志、错误日志、业务日志由 `blade-service/blade-log` 异步落库，事件通过 `AsyncEventDispatcher` 发布\n\n---\n\n## 7. 数据库规范\n\n- **表名前缀**：`blade_`（如 `blade_user`、`blade_role_menu`）\n- **业务表必含字段**：`id`（BIGINT）、`tenant_id`（VARCHAR，多租户表）、`create_user`、`create_dept`、`create_time`、`update_user`、`update_time`、`status`、`is_deleted`\n- **主键策略**：雪花算法（`IdType.ASSIGN_ID`），Long 字段用 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n- **逻辑删除**：`is_deleted`（INT），`0` = 未删除，`1` = 已删除\n- **索引命名**：唯一索引 `uk_` 前缀，普通索引 `idx_` 前缀\n- **多数据库**：支持 7 种数据库，SQL 脚本位于 `doc/sql/`（全量建表 + 版本升级脚本），修改表结构时需同步所有脚本\n\n---\n\n## 8. 多租户与安全\n\n### 8.1 多租户\n\n- 默认**字段隔离模式**（`tenant_id`），MP 内置方法自动注入\n- 租户表通过 Nacos `blade.yaml` 中 `blade.tenant.tables` 配置声明\n- 自定义 SQL 需手动过滤：`AuthUtil.getTenantId()` 或 `SecureUtil.getTenantId()`\n- **本版本不支持** `@TenantDS` / `@NonDS` 数据源切换注解；如需物理隔离，可结合 dynamic-datasource 的 `@DS` 配合业务逻辑实现\n\n### 8.2 权限体系\n\n- **角色权限**：`@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（或自定义 SpEL 表达式）\n- **数据权限**：`blade-starter-datascope` DataScopeHandler，行级过滤，通过 `RoleScope` 配置\n- **本版本不含**：接口权限（ApiScopeHandler）、菜单级权限表达式 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 通配放行\n- 接口放行请在 Nacos `blade.secure.skip-url` 中配置 URL 通配，或在 `blade-gateway` 的 `AuthFilter` 白名单中配置\n\n### 8.3 登录与 Token\n\n- 登录密码使用 **SM2 国密**加解密（`blade.auth.public-key` / `blade.auth.private-key`，由 `blade-auth` 模块的 `Sm2KeyGenerator` 生成）\n- Token 使用 JWT 签名（`blade.token.sign-key`）+ 可选 AES 加密（`blade.token.aes-key`）\n- 多 Granter 支持（`blade-auth/granter/`）：`PasswordTokenGranter`、`CaptchaTokenGranter`、`RefreshTokenGranter`、`SocialTokenGranter`\n- **网关统一鉴权**：`blade-gateway/filter/AuthFilter` 作为 `GlobalFilter` 验证 JWT，下游服务通过 `exchange attributes` 透传用户上下文\n- 获取当前用户：`SecureUtil.getUser()` / `SecureUtil.getUserId()` / `SecureUtil.getTenantId()` / `SecureUtil.isAdministrator()`\n\n### 8.4 分布式事务\n\n- 由 `blade-starter-transaction`（Seata）提供支持，参考 `blade-ops/blade-seata-order` + `blade-ops/blade-seata-storage` 双服务示例\n- 需要跨服务事务的场景标注 `@GlobalTransactional`\n\n---\n\n## 9. 缓存规范\n\n- `CacheUtil` 统一操作，常量命名在 `blade-common/cache/CacheNames`\n- 数据变更后清除缓存：`CacheUtil.clear(CACHE_NAME, Boolean.FALSE)`\n- 字典翻译：推荐跨服务通过 `IDictClient.getValue(code, dictKey)`（Feign）；服务本地可用 `DictCache.getValue(...)`\n- Redis 序列化使用 Protostuff（默认配置），键分布式作用域；Gateway 侧使用 Reactive Redis\n\n---\n\n## 10. 开源许可头部\n\n所有新建的 Java 源文件必须在文件顶部包含以下 Apache 2.0 许可头部：\n\n```java\n/**\n * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n```\n\n> **重要**：本工程为开源版本，**不要**写入 BladeX 商业许可证头部。若需要迁移为商业版本，请以商业项目代码样例为准。\n\n---\n\n## 11. 构建与部署\n\n### 11.1 Maven 构建\n\n```bash\nmvn clean compile -DskipTests                      # 根目录执行：编译所有子模块\nmvn clean package -DskipTests                      # 打包所有服务为 FatJar\nmvn clean package -DskipTests -pl blade-service/blade-system -am  # 只打包 system 服务及其依赖模块\n```\n\n- 父 POM 使用 `${revision}` 占位符管理版本，由 `flatten-maven-plugin` 在 `process-resources` 阶段展开\n- 各服务打包产物通过 `directory-maven-plugin` 汇总复制到根目录 `target/`\n- `finalName` 使用 `${project.name}`，产物命名与 artifactId 一致\n\n### 11.2 Nacos 配置\n\n- 配置文件位于 `doc/nacos/`：`blade.yaml`（公共配置）+ `blade-dev.yaml`/`blade-test.yaml`/`blade-prod.yaml`（环境差异）\n- 数据源、密钥、Swagger、Sentinel、Feign 等共享参数统一在 Nacos 配置\n- 各服务 `src/main/resources/application-{env}.yml` 仅保留 `server.port` 与 `spring.datasource.url` 等少量本地覆盖\n\n### 11.3 Docker 部署\n\n- `script/docker/docker-compose.yml` 提供一体化编排：Nacos + Sentinel + Redis + Nginx + 全部微服务\n- 默认网段 `blade_net`，Nacos `172.30.0.48`，Sentinel `172.30.0.58`\n- Harbor 镜像推送通过父 POM 的 `docker.registry.url` 属性控制\n\n### 11.4 必备环境变量\n\n```bash\nBLADE_OAUTH2_PUBLIC_KEY    # SM2 公钥\nBLADE_OAUTH2_PRIVATE_KEY   # SM2 私钥\nBLADE_TOKEN_SIGN_KEY       # JWT 签名密钥\nBLADE_TOKEN_CRYPTO_KEY     # Token AES 加密密钥\n```\n\n### 11.5 服务端口（默认）\n\n| 服务 | 端口 |\n| --- | --- |\n| blade-gateway | 80 |\n| blade-auth | 8100 |\n| blade-admin | 7002 |\n| blade-system | 8106 |\n| blade-desk | 8103 |\n| blade-log | 8102 |\n| blade-develop | 8108 |\n| blade-report | 8109 |\n| blade-resource | 8110 |\n\n> 实际端口以 `src/main/resources/application-{env}.yml` 中 `server.port` 为准。\n\n---\n\n## 12. Git 提交规范\n\n当需要提交代码时，优先使用 **`/blade-commit`** skill，它会自动分析变更内容并生成符合项目规范的 Gitmoji 提交信息。\n\n采用 **Gitmoji** 风格，中文描述。格式：`:<gitmoji>: 简要描述`\n\n| Gitmoji | 场景 | 示例 |\n| --- | --- | --- |\n| `:sparkles:` | 新增功能 | `:sparkles: 登录认证新增IP锁定逻辑` |\n| `:zap:` | 优化重构 | `:zap: 优化字典查询排序逻辑` |\n| `:bug:` | 修复缺陷 | `:bug: 修复用户查询未过滤已删除数据` |\n| `:tada:` | 版本发布 | `:tada: x.x.x.RELEASE` |\n| `:recycle:` | 代码重构 | `:recycle: 重构租户删除逻辑` |\n\n---\n\n## 13. 框架组件速查\n\n| 组件 | 用途 |\n| --- | --- |\n| `R<T>` | 统一 API 响应（`org.springblade.core.tool.api.R`） |\n| `BladeController` | Controller 基类（`org.springblade.core.boot.ctrl`） |\n| `BladeCloudApplication` | Cloud 服务启动注解（`blade-auth`、业务服务使用） |\n| `TenantEntity` / `BaseEntity` | 实体基类（`org.springblade.core.mp.base`） |\n| `BaseService` / `BaseServiceImpl` | BladeX 增强 Service（含 `deleteLogic`） |\n| `BaseEntityWrapper` | Entity→VO 转换基类 |\n| `BladeUser` | 当前登录用户（含租户信息） |\n| `Condition` / `Query` | 查询条件构建 / 分页参数 |\n| `CacheUtil` / `DictCache` | 缓存工具 / 字典缓存 |\n| `SecureUtil` / `AuthUtil` | 获取当前用户/租户信息 |\n| `BladeRedis` | Redis 操作封装 |\n| `Func` / `BeanUtil` / `StringUtil` | 通用工具类 |\n| `ServiceException` | 业务异常 |\n| `@PreAuth` | 角色权限注解（`RoleConstant.HAS_ROLE_ADMIN`） |\n| `@FeignClient` + `AppConstant.APPLICATION_XXX_NAME` | 服务间调用 |\n| `ISysClient` / `IUserClient` / `IDictClient` | 常用 Feign 契约 |\n| `@GlobalTransactional` | Seata 分布式事务 |\n| `@Operation` / `@Tag` / `@Schema` | springdoc / OpenAPI 3 文档注解 |\n| `LauncherConstant` / `AppConstant` / `CommonConstant` | 启动常量 / 应用名 / 公共常量 |\n\n---\n\n## 14. 风格一致性\n\n1. 风格不确定时，优先查阅现有服务并模仿当前业务域的实现方式\n2. 新增业务域时，**先建 API 模块再建 Service 模块**；API 模块只放契约，不引入 Service 依赖\n3. 新服务参考 `blade-service/blade-system` + `blade-service-api/blade-system-api`（最完整） 或 `blade-service/blade-desk` + `blade-desk-api`（最简样板）\n4. 现有服务或契约已满足需求时，禁止自写替代实现；跨服务使用数据请走 Feign，不要重复落库\n5. 与用户交互全程使用**中文**，代码注释和 Javadoc 亦使用中文\n6. 本工程为开源版，**不要**引用商业版特有能力（Flowable、PowerJob、`@TenantDS`、`@BladeView`、接口权限、`blade-ops-api`、`blade-plugin` 等）；如确实需要，请先向用户确认是否切换到商业版代码库\n"},"files":{".claude/skills/avue-design/README.md":"# avue-design Skill 集合\n\nAvue 组件代码生成与配置指南，基于 Avue v3.8.2 (Vue3 + Element-Plus) 文档构建。\n\n## 概述\n\n本 Skill 集合为 Claude Code 提供完整的 Avue 组件库知识，使其能够根据用户需求精准生成 Avue 组件代码。覆盖 CRUD 表格、动态表单、树组件、数据展示等全部 Avue 组件，支持 Composition API 和 Options API 两种代码风格。\n\n## 目录结构\n\n```\navue-design/\n├── SKILL.md                           # 主入口 - 概览、路由、速查表\n├── README.md                          # 本文件\n└── references/                        # 参考文档（按需加载）\n    │\n    │── 基础配置 ───────────────────────\n    ├── global-config.md               # 安装、全局配置、国际化、云存储\n    ├── api-style-guide.md             # Composition API vs Options API 完整模板\n    ├── utility-apis.md                # 16个全局工具函数 ($DialogForm, $Export 等)\n    │\n    │── avue-crud 表格组件 ─────────────\n    ├── crud-core.md                   # 组件属性、Option配置、按钮、弹窗、分页、事件、方法、插槽\n    ├── crud-column.md                 # 列配置、字典、Props映射、排序过滤、格式化渲染\n    ├── crud-search.md                 # 搜索栏配置、搜索字段属性、级联搜索\n    ├── crud-form-dialog.md            # 弹窗表单配置、验证规则、字段控制、分组、权限\n    ├── crud-features.md               # 树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽、插件模式\n    │\n    │── avue-form 表单组件 ─────────────\n    ├── form-core.md                   # 组件属性、布局、按钮、Column通用属性、事件、方法、插槽\n    ├── form-field-types.md            # 全部27+字段类型详细配置（input含13子类型、select、date等）\n    ├── form-advanced.md               # 字典、验证、动态子表单、文件上传(含云存储)、字段联动、Mock\n    │\n    │── 其他组件 ─────────────────────\n    ├── tree-component.md              # avue-tree 树组件完整API\n    ├── data-display.md                # 23个数据展示组件\n    └── other-components.md            # Card、DialogForm、Flow、Login、Chat等22个独立组件\n```\n\n## 模块说明\n\n### 1. 基础配置（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `global-config.md` | npm安装、Element-Plus集成、全局crudOption/formOption、阿里云/七牛/腾讯COS云存储配置、水印配置、国际化、外部库CDN |\n| `api-style-guide.md` | Composition API 和 Options API 两种风格的完整 CRUD 页面模板，包含详细的差异对照表和转换规则 |\n| `utility-apis.md` | 全部16个Avue工具函数：$DialogForm、$Clipboard、$ImagePreview、$Print、$Export、$Log、findObject、deepClone等 |\n\n### 2. avue-crud 表格组件（5个文件）\n\n这是 Avue 最核心的组件，文档最为丰富。\n\n| 文件 | 内容 |\n|-----|------|\n| `crud-core.md` | 组件属性（21个）、Option表格级属性（索引/选择/展开/行/菜单/网格/工具栏共80+属性）、按钮配置（25+）、弹窗配置（19个）、分页（8个）、事件（35+）、方法（40+含行内编辑/滚动/搜索/视图方法组）、插槽（22+） |\n| `crud-column.md` | 列通用属性（20+含hide/cell/detail控制）、字典配置（7个属性+Props映射）、字典联动、显示控制、多级表头、排序过滤、formatter/render/html格式化、合计行、合并行列、搜索字段属性（20+含searchDisabled/searchControl/renderSearch）、表单字段属性（20+） |\n| `crud-search.md` | 搜索变量绑定（双API风格）、全局搜索属性（15+）、列级搜索属性（14个）、类型覆盖、折叠、级联联动、自定义插槽 |\n| `crud-form-dialog.md` | 弹窗属性（15个）、beforeOpen/beforeClose回调、验证规则(async-validator)、字段display/disabled控制、默认值、control联动、分组/标签页、数据过滤、权限控制 |\n| `crud-features.md` | 树表(基本/懒加载)、虚拟滚动(3.7.0+)、卡片网格模式(3.4.0+)、行编辑(单击/双击)、Excel导入导出(含对话框选项)、行/列拖拽排序、合计行、父子表、行复制、高级筛选(8种操作符)、列管理器、权限控制(对象/行级函数)、CRUD插件模式(Mixin)、API/Option文件模板、动态配置切换 |\n\n### 3. avue-form 表单组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `form-core.md` | 组件属性（9个含v-model:status）、Option属性（20个含formWidth/tabsActive/tabsType/tabsVerifyAll/filterDic/filterNull）、布局（栅格/分组/标签页/帮助文字）、Group属性（8个含arrow/display/header）、按钮配置（12个）、Column通用属性（25+）、事件（6个含change/tab-click/error/mock-change）、方法（12个含show/hide/scrollToField/getField）、插槽（6个）、两种API风格完整示例 |\n| `form-field-types.md` | 全部27+字段类型完整配置：input(含14子类型:password/textarea/search/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc + formatters/parser)、number、select(含all/noMatchText)、cascader、checkbox、radio、date(含disabledDate/shortcuts/unlinkPanels)、time(含arrowControl + TimeSelect自动切换)、switch、upload(含dragFile/oss/cropperOption)、tree、input-tag(含min/max/prefix/suffix)、mention(含split/prefix/whole)、input-table(含children/dialogWidth)、color、icon(含dialogWidth/分组iconList)、map(含mapChange/dialogWidth/值格式说明)、rate、slider、array/img/url、dynamic(含max + 完整children配置12属性)、title、input-cron |\n| `form-advanced.md` | 字典配置(本地/远程/格式化/联动/更新)、验证规则(基本/类型/自定义/方法)、动态子表单(表格/表单模式)、文件上传(回调/水印/云存储/数据格式)、字段联动(control/Promise)、数据格式(深层绑定/过滤)、自定义插槽、Mock数据 |\n\n### 4. 其他组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `tree-component.md` | avue-tree完整API：Option属性（12个）、Props属性（4个）、事件（7个）、方法（18个）、插槽（3个）、多选/懒加载/拖拽/虚拟模式示例 |\n| `data-display.md` | 23个数据展示组件列表、avue-data-display核心组件属性和数据结构 |\n| `other-components.md` | 22个独立组件：Card、DialogForm、Tabs、Search、Calendar、Flow、Login、Article、Chat、CountUp、Clipboard、Draggable、TextEllipsis、Sign、Print、Export、ImagePreview、Contextmenu、Video、Verify、Screenshot、License |\n\n## 工作原理\n\n1. **主入口 SKILL.md**（< 200行）始终加载到上下文中，提供：\n   - 快速安装指南\n   - 参考文件导航表（根据任务类型指引读取哪个文件）\n   - 代码生成核心规则\n   - 字段类型速查表\n\n2. **参考文件** 按需加载，当用户需要特定组件的配置时才读取对应文件：\n   - 需要创建 CRUD 页面 → 读取 `crud-core.md` + `crud-column.md`\n   - 需要配置搜索 → 读取 `crud-search.md`\n   - 需要特定字段类型 → 读取 `form-field-types.md`\n   - 需要文件上传 → 读取 `form-advanced.md`\n\n3. **双API风格** 支持：\n   - Composition API（推荐）：`<script setup>` + `ref()` + 箭头函数\n   - Options API：`export default` + `data()` + `methods`\n\n## 数据来源\n\n基于以下 Avue 官方文档与源码完整解析：\n\n- `crud/` 目录 32 个文档（CRUD表格组件）\n- `form/` 目录 38 个文档（表单组件）\n- `component/` 目录 20 个文档（独立组件）\n- `default/` 目录 25 个文档（默认组件）\n- `data/` 目录 23 个文档（数据展示组件）\n- `docs/` 目录 10 个文档（全局配置、API、安装等）\n- Saber3 项目参考文件（Composition API vs Options API 对照）\n- Avue v3.8.2 完整源码审计（packages/element-plus/ 全部组件 + packages/core/ 核心模块 + src/ 工具库）\n\n总计 **6,000+ 行** 参考文档，覆盖 Avue v3.8.2 全部组件配置。\n\n## 版本信息\n\n- Avue 版本：v3.8.2 (2026-03-27)\n- Vue 版本：Vue 3\n- UI 框架：Element-Plus\n- 创建日期：2026-04-05\n",".claude/skills/avue-design/SKILL.md":"---\nname: avue-design\ndescription: \"Avue 组件代码生成与配置指南。当用户需要创建、配置或生成基于 Avue (avue-crud, avue-form, avue-tree 等) 的 Vue 页面代码时使用此 skill。覆盖 CRUD 表格、表单、树组件、数据展示等全部 Avue 组件。支持 Composition API 和 Options API 两种代码风格。当用户提到 avue、crud表格、动态表单、JSON配置表单、低代码表单、avue-crud、avue-form 等关键词时，务必使用此 skill。\"\n---\n\n# Avue Design - 组件代码生成 Skill 集合\n\nAvue 是基于 Vue3 + Element-Plus 的低代码前端框架，通过 JSON 配置生成页面。版本：v3.8.2，MIT 协议。\n\n## 快速安装\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\napp.use(ElementPlus)\napp.use(Avue)\n// 若使用字典或上传组件，需传入 axios：\n// import axios from 'axios'\n// app.use(Avue, { axios })\n```\n\n## 参考文件导航\n\n根据用户需求，读取对应的参考文件获取完整配置信息：\n\n### 基础配置\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/global-config.md` | 全局配置、安装、国际化、云存储 |\n| `references/api-style-guide.md` | 需要生成 Composition API 或 Options API 代码时 |\n| `references/utility-apis.md` | 使用 Avue 全局工具函数 ($DialogForm, $Clipboard 等) |\n\n### avue-crud (CRUD 表格组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/crud-core.md` | CRUD 基础属性、事件、方法、插槽、按钮、弹窗、分页 |\n| `references/crud-column.md` | 列配置、字典、排序、过滤、格式化、渲染 |\n| `references/crud-search.md` | 搜索栏配置、搜索字段属性 |\n| `references/crud-form-dialog.md` | CRUD 弹窗表单配置、验证规则、字段状态控制 |\n| `references/crud-features.md` | 高级功能：树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽排序、权限控制等 |\n\n### avue-form (表单组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/form-core.md` | 表单基础属性、事件、方法、插槽、布局 |\n| `references/form-field-types.md` | 全部字段类型 (input/select/date/upload 等 27+ 种) 的详细配置属性 |\n| `references/form-advanced.md` | 字典配置、验证规则、动态子表单、文件上传(含云存储)、字段联动 |\n\n### 其他组件\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/tree-component.md` | avue-tree 树组件 |\n| `references/data-display.md` | 数据展示组件 (DataDisplay, DataCard 等) |\n| `references/other-components.md` | Card、DialogForm、Calendar、Flow、Login、Tabs 等独立组件 |\n\n## 代码生成核心规则\n\n### 1. 两种 API 风格\n\n生成代码时必须确认用户要用哪种风格（默认 Composition API）。详细模板见 `references/api-style-guide.md`。\n\n**Composition API (推荐)**：使用 `<script setup>`，`ref()`/`reactive()` 声明数据，箭头函数定义方法。\n\n**Options API**：使用 `export default {}`，`data()` 返回数据，`methods` 对象定义方法。\n\n### 2. avue-crud 基本结构\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    />\n  </basic-container>\n</template>\n```\n\n### 3. avue-form 基本结构\n\n```vue\n<template>\n  <avue-form\n    ref=\"form\"\n    v-model=\"formData\"\n    :option=\"option\"\n    @submit=\"handleSubmit\"\n    @reset-change=\"handleReset\"\n  />\n</template>\n```\n\n### 4. option 配置核心\n\noption 是 Avue 组件的核心配置对象，包含：\n- 表格/表单级属性（size, height, border, stripe 等）\n- `column` 数组：每个字段的完整配置（label, prop, type, rules, dicData 等）\n- `group` 数组：分组配置（仅表单）\n\n```javascript\nconst option = {\n  // 表格级配置\n  height: 'auto',\n  border: true,\n  stripe: true,\n  index: true,\n  selection: true,\n  // 列配置\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      type: 'input',        // 字段类型\n      search: true,          // 启用搜索\n      span: 12,              // 表单中占栅格数\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n      // 字典配置\n      dicData: [],           // 本地字典\n      dicUrl: '',            // 远程字典URL\n      props: { label: 'label', value: 'value' },\n    }\n  ]\n}\n```\n\n### 5. 字段类型速查\n\n| 类型 | type 值 | 说明 |\n|-----|---------|------|\n| 文本输入 | input | 支持 password/textarea/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc 子类型 |\n| 数字输入 | number | 数字步进器 |\n| 下拉选择 | select | 单选/多选/远程搜索/分组 |\n| 级联选择 | cascader | 多级联动选择 |\n| 多选框 | checkbox | 多选组 |\n| 单选框 | radio | 单选组 |\n| 日期 | date/datetime/daterange/datetimerange/week/month/year/dates | 日期时间选择 |\n| 时间 | time/timerange | 时间选择 |\n| 开关 | switch | 布尔切换 |\n| 文件上传 | upload | 支持阿里云/七牛/腾讯COS |\n| 数组 | array | 数组输入框 |\n| 图片 | img | 图片输入 |\n| 链接 | url | 超链接输入 |\n| 树选择 | tree | 树型选择器 |\n| 标签输入 | input-tag | 标签输入 (3.6.0+) |\n| 提及 | mention | @提及组件 (3.6.0+) |\n| 表格选择 | input-table | 表格弹窗选择 |\n| 颜色选择 | color | 颜色选择器 |\n| Cron表达式 | input-cron | Cron可视化配置 (3.8.2+) |\n| 图标选择 | icon | 图标选择器 |\n| 地图选择 | map | 高德地图坐标选择 |\n| 评分 | rate | 星级评分 |\n| 滑块 | slider | 滑动条 |\n| 标题 | title | 分组标题 |\n| 动态表单 | dynamic | 子表单/子表格 |\n\n### 6. 关键注意事项\n\n- `column` 可以是数组也可以是对象格式（3.2.15+，用 prop 做 key）\n- 深层数据绑定用 `bind` 属性：`bind: 'info.address'`\n- 字典联动用 `cascader` 属性指定子字段 prop\n- 字段动态控制用 `control` 属性（支持 Promise，3.5.0+）\n- 表单分组用 `group` 配置，标签页显示设 `tabs: true`\n- 所有组件都支持 `render` 函数自定义渲染（3.4.2+）\n- CRUD 弹窗类型用 `dialogType: 'drawer'` 可改为抽屉模式\n- 搜索字段通过在 column 中设 `search: true` 启用\n",".claude/skills/avue-design/references/api-style-guide.md":"# Avue 代码风格指南：Composition API vs Options API\n\n生成 Avue 代码时，需根据用户偏好选择以下两种风格之一。默认使用 Composition API。\n\n## 目录\n\n1. [Composition API 完整模板](#composition-api-完整模板)\n2. [Options API 完整模板](#options-api-完整模板)\n3. [关键差异对照表](#关键差异对照表)\n4. [转换规则](#转换规则)\n\n---\n\n## Composition API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <!-- 自定义左侧按钮 -->\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <!-- 自定义列内容 -->\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed, onMounted } from 'vue'\nimport { useStore } from 'vuex'\nimport { ElMessage, ElMessageBox } from 'element-plus'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\n// ============ 响应式数据 ============\nconst crud = ref(null)\nconst form = ref({})\nconst search = ref({})\nconst loading = ref(true)\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n})\nconst selectionList = ref([])\nconst data = ref([])\n\n// ============ Vuex ============\nconst store = useStore()\nconst permission = computed(() => store.getters.permission)\n\n// ============ 计算属性 ============\nconst permissionList = computed(() => ({\n  addBtn: !!permission.value.module_add,\n  viewBtn: !!permission.value.module_view,\n  delBtn: !!permission.value.module_delete,\n  editBtn: !!permission.value.module_edit,\n}))\n\nconst ids = computed(() =>\n  selectionList.value.map(ele => ele.id).join(',')\n)\n\n// ============ Option 配置 ============\nconst option = reactive({\n  height: 'auto',\n  calcHeight: 32,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  dialogClickModal: false,\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      search: true,\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      search: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n})\n\n// ============ 生命周期 ============\nonMounted(() => {\n  // 初始化逻辑\n})\n\n// ============ 方法 ============\nconst onLoad = () => {\n  loading.value = true\n  getList(page.value.currentPage, page.value.pageSize, Object.assign({}, search.value)).then(res => {\n    const resData = res.data.data\n    page.value.total = resData.total\n    data.value = resData.records\n    loading.value = false\n    selectionClear()\n  })\n}\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(row.id))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据')\n    return\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(ids.value))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n    })\n  }\n  done()\n}\n\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\n\nconst searchReset = () => {\n  onLoad()\n}\n\nconst selectionChange = (list) => {\n  selectionList.value = list\n}\n\nconst selectionClear = () => {\n  selectionList.value = []\n  crud.value?.toggleSelection()\n}\n\nconst refreshChange = () => {\n  onLoad()\n}\n</script>\n```\n\n---\n\n## Options API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      data: [],\n      option: {\n        height: 'auto',\n        calcHeight: 32,\n        tip: false,\n        searchShow: true,\n        searchMenuSpan: 6,\n        border: true,\n        index: true,\n        selection: true,\n        dialogClickModal: false,\n        column: [\n          {\n            label: '名称',\n            prop: 'name',\n            search: true,\n            rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n          },\n          {\n            label: '状态',\n            prop: 'status',\n            type: 'select',\n            search: true,\n            dicData: [\n              { label: '启用', value: 1 },\n              { label: '禁用', value: 0 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: !!this.permission.module_add,\n        viewBtn: !!this.permission.module_view,\n        delBtn: !!this.permission.module_delete,\n        editBtn: !!this.permission.module_edit,\n      }\n    },\n    ids() {\n      return this.selectionList.map(ele => ele.id).join(',')\n    },\n  },\n  methods: {\n    onLoad(page, params = {}) {\n      this.loading = true\n      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n        const data = res.data.data\n        this.page.total = data.total\n        this.data = data.records\n        this.loading = false\n        this.selectionClear()\n      })\n    },\n    rowSave(row, done, loading) {\n      add(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowUpdate(row, index, done, loading) {\n      update(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(row.id))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n        })\n    },\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据')\n        return\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(this.ids))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n          this.$refs.crud.toggleSelection()\n        })\n    },\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data\n        })\n      }\n      done()\n    },\n    searchChange(params, done) {\n      this.query = params\n      this.page.currentPage = 1\n      this.onLoad(this.page, params)\n      done()\n    },\n    searchReset() {\n      this.query = {}\n      this.onLoad(this.page)\n    },\n    selectionChange(list) {\n      this.selectionList = list\n    },\n    selectionClear() {\n      this.selectionList = []\n      this.$refs.crud.toggleSelection()\n    },\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage\n    },\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize\n    },\n    refreshChange() {\n      this.onLoad(this.page)\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 关键差异对照表\n\n| 特性 | Composition API | Options API |\n|-----|----------------|-------------|\n| **脚本标签** | `<script setup>` | `<script> export default {}` |\n| **数据声明** | `const x = ref(val)` / `reactive({})` | `data() { return { x: val } }` |\n| **脚本中访问数据** | `x.value` | `this.x` |\n| **模板中访问数据** | `{{ x }}` (自动解包) | `{{ x }}` |\n| **计算属性** | `const x = computed(() => ...)` | `computed: { x() { ... } }` |\n| **方法** | `const fn = () => {}` | `methods: { fn() {} }` |\n| **生命周期** | `onMounted(() => {})` | `mounted() {}` |\n| **Vuex** | `const store = useStore()` | `...mapGetters([...])` |\n| **消息提示** | `ElMessage.success(...)` | `this.$message(...)` |\n| **确认框** | `ElMessageBox.confirm(...)` | `this.$confirm(...)` |\n| **组件引用** | `const crud = ref(null)` | `this.$refs.crud` |\n| **搜索参数** | `v-model:search=\"search\"` 自动同步 | 手动赋值 `this.query = params` |\n| **分页事件** | `v-model:page` 自动处理 | 需要 `currentChange`/`sizeChange` |\n\n## 转换规则\n\n从 Composition API 转换为 Options API：\n1. `ref()` 声明 → 移入 `data()` return\n2. 移除所有 `.value` 访问\n3. 箭头函数 → `methods` 对象方法\n4. `computed(() => ...)` → `computed` 对象\n5. `ElMessage` / `ElMessageBox` → `this.$message` / `this.$confirm`\n6. `useStore()` → `mapGetters`\n7. `onMounted()` → `mounted()`\n8. 添加 `currentChange` / `sizeChange` 事件处理\n\n从 Options API 转换为 Composition API：\n1. `data()` 中的属性 → `ref()` 或 `reactive()`\n2. 添加 `.value` 访问\n3. `methods` 中的方法 → 顶层箭头函数\n4. `this.$xxx` → 导入对应模块\n5. `v-model:search=\"search\"` 替代 `query` 变量\n6. 移除 `currentChange` / `sizeChange`（由 v-model:page 处理）\n",".claude/skills/avue-design/references/crud-column.md":"# avue-crud 列配置参考\n\n## 目录\n1. [列通用属性](#列通用属性)\n2. [字典配置](#字典配置)\n3. [显示控制](#显示控制)\n4. [排序与过滤](#排序与过滤)\n5. [格式化与渲染](#格式化与渲染)\n6. [搜索字段属性](#搜索字段属性)\n7. [表单字段属性](#表单字段属性)\n\n---\n\n## 列通用属性\n\ncolumn 数组中每个对象的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 列标题 |\n| prop | string | - | 列属性名 |\n| type | string | - | 字段类型 (input/select/date等，详见form-field-types) |\n| width | string | - | 列宽度 |\n| minWidth | string | - | 列最小宽度 |\n| fixed | boolean/string | - | 固定列: true/left/right |\n| align | string | 'left' | 对齐方式: left/center/right |\n| headerAlign | string | 'left' | 表头对齐方式 |\n| className | string | - | 单元格自定义class |\n| labelClassName | string | - | 表头自定义class |\n| resizable | boolean | true | 列宽可拖拽 |\n| slot | boolean | - | 启用具名插槽（插槽名=prop） |\n| bind | string | - | 深层数据绑定，如 'info.address' |\n| value | - | - | 列默认值 |\n| order | number | - | 字段排序，数字越大越靠前 |\n| gridRow | boolean | false | 网格模式新起一行 |\n| hide | boolean | false | 隐藏列（不在表格中显示） |\n| cell | boolean | false | 该列可行内编辑 |\n| addDetail | boolean | false | 新增时详情模式 |\n| editDetail | boolean | false | 编辑时详情模式 |\n| detail | boolean | false | 全局详情模式 |\n\n### 主键配置\n\n```javascript\noption: {\n  rowKey: 'id',           // 主键字段名，默认 'id'\n  rowParentKey: 'parentId' // 父级主键(树表用)\n}\n```\n\n### 对象格式列配置 (3.2.15+)\n\ncolumn 可以用对象格式替代数组，以 prop 为 key：\n\n```javascript\n// 数组格式\ncolumn: [\n  { label: '名称', prop: 'name' },\n  { label: '状态', prop: 'status' }\n]\n\n// 对象格式\ncolumn: {\n  name: { label: '名称' },\n  status: { label: '状态' }\n}\n```\n\n---\n\n## 字典配置\n\n### 字典属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| dicData | array/function/promise | - | 本地字典数据 (3.5.0+ 支持函数/Promise) |\n| dicUrl | string | - | 远程字典URL |\n| dicFlag | boolean | false | 打开表单时重新请求字典 |\n| dicQuery | object | - | 字典请求查询参数 |\n| dicHeaders | object | - | 字典请求头参数 |\n| dicFormatter | function(res) | (res)=>res | 字典返回数据格式化 |\n| dicMethod | string | 'GET' | 字典请求方法 |\n\n### Props 字段映射\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 选项标签属性 |\n| value | string | 'value' | 选项值属性 |\n| children | string | 'children' | 子选项属性 |\n| disabled | string | 'disabled' | 禁用属性 |\n| res | string | - | 响应数据层级，如 'data.data' |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  // 本地字典\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n  // 或远程字典\n  dicUrl: '/api/dict/type',\n  props: {\n    label: 'name',\n    value: 'code',\n    res: 'data.records',\n  },\n}\n```\n\n### 字典联动\n\n通过 `cascader` 属性实现级联联动：\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city'],  // 指定子级字段prop\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}', // {{key}} 为父级选中值\n  },\n]\n```\n\n### 更新字典\n\n```javascript\n// 更新指定字段的字典\ncrudRef.value.updateDic('prop', [{ label: '新选项', value: 1 }])\n// 重新加载所有字典\ncrudRef.value.dicInit()\n```\n\n---\n\n## 显示控制\n\n### 溢出省略\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| overHidden | boolean/object | false | 内容溢出显示tooltip |\n| showOverflowTooltip | boolean/object | false | 同上 |\n| ellipsis | boolean | false | 文本省略 (3.6.4+) |\n\n### 多级表头\n\n通过 `children` 嵌套实现：\n\n```javascript\ncolumn: [\n  {\n    label: '用户信息',\n    children: [\n      { label: '姓名', prop: 'name' },\n      { label: '年龄', prop: 'age' },\n    ]\n  }\n]\n```\n\n### 表头固定\n\n```javascript\noption: {\n  height: '500px',  // 设置高度后表头自动固定\n  showHeader: true,  // 默认true\n}\n```\n\n---\n\n## 排序与过滤\n\n### 排序\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| sortable | boolean/string | - | 排序: true(本地)/custom(远程) |\n| defaultSort | object | - | 默认排序: {prop, order} |\n| columnSort | boolean | - | 列拖拽排序 |\n\n```javascript\n// 远程排序\n{\n  label: '日期',\n  prop: 'date',\n  sortable: 'custom',  // 需监听 sort-change 事件\n}\n\n// 默认排序\noption: {\n  defaultSort: { prop: 'date', order: 'descending' }\n}\n```\n\n### 过滤\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| filters | boolean/array | false | 数据过滤 |\n| filterMethod | function | - | 自定义过滤逻辑 |\n| filter-multiple | boolean | true | 多选过滤 |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  filters: true,  // 自动从 dicData 生成\n  // 或手动指定\n  filters: [\n    { text: '启用', value: 1 },\n    { text: '禁用', value: 0 },\n  ],\n}\n```\n\n---\n\n## 格式化与渲染\n\n### formatter 格式化\n\n```javascript\n{\n  label: '金额',\n  prop: 'amount',\n  formatter: (row, value, label, column) => {\n    return '¥' + value.toFixed(2)\n  },\n}\n```\n\n### tooltipFormatter (3.6.3+)\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  overHidden: true,\n  tooltipFormatter: (row, value, label, column) => {\n    return `详细内容: ${value}`\n  },\n}\n```\n\n### html 属性\n\n```javascript\n{\n  label: '内容',\n  prop: 'content',\n  html: true,  // 支持HTML翻译\n}\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  // 列区域渲染\n  render: (h, { row, index, dic }) => {\n    return h('el-tag', { type: row.status === 1 ? 'success' : 'danger' }, row.statusName)\n  },\n  // 表单区域渲染\n  renderForm: (h, { column, $index, dic }) => {\n    return h('el-input', { modelValue: form.value[column.prop] })\n  },\n  // 表头渲染\n  renderHeader: (h, { column }) => {\n    return h('span', { style: 'color: red' }, column.label)\n  },\n}\n```\n\n### 合计行\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },     // 求和\n    { name: 'price', type: 'average' },   // 平均值\n    { name: 'count', type: 'count' },     // 计数\n  ],\n}\n\n// 或自定义合计\n// 通过 summary-method 属性传入函数\n```\n\n### 合并行列\n\n```javascript\n// 通过 span-method 属性设置\n<avue-crud :span-method=\"spanMethod\">\n\nconst spanMethod = ({ row, column, rowIndex, columnIndex }) => {\n  if (rowIndex === 0 && columnIndex === 0) {\n    return { rowspan: 2, colspan: 1 }\n  }\n}\n```\n\n---\n\n## 搜索字段属性\n\n在 column 中通过 `search: true` 启用搜索，以下为搜索相关属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | string | - | 搜索初始值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索字段排序 |\n| searchGutter | number | - | 搜索字段间距 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索字段类型（覆盖 type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 搜索可清空 |\n| searchMultiple | boolean | false | 搜索多选 |\n| searchTags | boolean | false | 搜索标签显示 |\n| searchLabelPosition | string | 'left' | 搜索标签位置 |\n| searchRules | array | - | 搜索验证规则 |\n| searchDisabled | boolean | false | 搜索字段禁用 |\n| searchReadonly | boolean | false | 搜索字段只读 |\n| searchIndex | number | - | 搜索栏初始显示时包含此字段 |\n| searchControl | function | - | 搜索字段联动控制 |\n| renderSearch | function | - | 搜索区域自定义渲染函数 (3.4.2+) |\n\n### 搜索栏全局配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| searchBtnIcon | string | - | 搜索按钮图标 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| emptyBtnIcon | string | - | 清空按钮图标 |\n| searchShow | boolean | true | 初始显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留数量 |\n| searchMenuPosition | string | 'center' | 搜索按钮位置 |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示内容 |\n| searchTipPlacement | string | 'bottom' | 提示方向 |\n| searchMenuSpan | number | - | 搜索按钮栅格数 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索默认栅格数 |\n| searchGutter | number | - | 搜索默认间距 |\n| searchLabelWidth | number | 80 | 搜索默认标签宽度 |\n| searchLabelPosition | string | 'left' | 搜索默认标签位置 |\n\n---\n\n## 表单字段属性\n\ncolumn 中控制字段在表单（新增/编辑/查看弹窗）中表现的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| display | boolean | true | 在表单中显示 |\n| disabled | boolean | false | 在表单中禁用 |\n| addDisabled | boolean | false | 新增时禁用 |\n| editDisabled | boolean | false | 编辑时禁用 |\n| viewDisabled | boolean | false | 查看时禁用 |\n| addDisplay | boolean | true | 新增时显示 |\n| editDisplay | boolean | true | 编辑时显示 |\n| viewDisplay | boolean | true | 查看时显示 |\n| span | number | 12 | 表单栅格数 |\n| gutter | number | 0 | 表单间距 |\n| offset | number | 0 | 表单偏移 |\n| row | boolean | false | 表单换行 |\n| labelWidth | string | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| rules | array | - | 验证规则 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 帮助文字方向 |\n| labelTip | string | - | 标题帮助文字 |\n| labelTipPlacement | string | 'top' | 标题帮助文字方向 |\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n| render | function | - | 列区域渲染 (3.4.2+) |\n| renderForm | function | - | 表单区域渲染 (3.4.2+) |\n| renderHeader | function | - | 表头渲染 (3.4.2+) |\n",".claude/skills/avue-design/references/crud-core.md":"# avue-crud 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表格级属性](#option-表格级属性)\n3. [按钮配置](#按钮配置)\n4. [弹窗配置](#弹窗配置)\n5. [分页配置](#分页配置)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n获取表格原生引用：\n```javascript\nconst crudRef = ref(null)\n// 访问 el-table 实例\ncrudRef.value.$refs.table\n```\n\n---\n\n## 组件属性\n\n在 `<avue-crud>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| data | array | - | 表格显示数据 |\n| option | object | - | 组件配置项 |\n| before-open | function(done, type, loading) | - | 打开弹窗前回调，type: add/edit/view |\n| before-close | function(done, type) | - | 关闭弹窗前回调 |\n| permission | function/object | - | 按钮权限控制（支持行级） |\n| v-model:search | object | - | 搜索变量双向绑定 |\n| v-model:page | object | - | 分页变量双向绑定 |\n| cell-class-name | function/string | - | 单元格 className 回调 |\n| cell-style | function/object | - | 单元格样式回调 |\n| header-cell-class-name | function/string | - | 表头单元格 className |\n| header-row-class-name | function/string | - | 表头行 className |\n| header-row-style | function/object | - | 表头行样式 |\n| header-cell-style | function/object | - | 表头单元格样式 |\n| row-class-name | function/string | - | 行 className 回调 |\n| row-style | function/object | - | 行样式回调 |\n| span-method | function | - | 合并行或列 |\n| summary-method | function | - | 自定义合计计算 |\n| table-loading | boolean | - | 加载状态 |\n| upload-before | function(file, done, loading) | - | 上传组件上传前回调 |\n| upload-after | function(res, done) | - | 上传组件上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 上传预览回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制回调 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小回调 (3.4.4+) |\n| disabled | boolean | false | 禁用所有交互元素 |\n| width | number/string | - | 表格宽度 |\n| height | number/string | - | 表格高度 |\n\n---\n\n## Option 表格级属性\n\n在 `option` 对象中设置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 列配置数组 |\n| height | string | - | 表格高度，设 'auto' 为自适应窗口 |\n| maxHeight | string | - | 表格最大高度 |\n| title | string | - | 表格标题 |\n| border | boolean | false | 纵向边框 |\n| stripe | boolean | false | 斑马纹 |\n| fit | boolean | true | 列宽自适应 |\n| showHeader | boolean | true | 显示表头 |\n| header | boolean | false | 隐藏表格头部操作栏 |\n| emptyText | string | '暂无数据' | 空数据提示文字 |\n| tooltipEffect | string | 'dark' | tooltip 主题: dark/light |\n| tooltipOptions | object | - | tooltip 配置项 |\n| scrollbarAlwaysOn | boolean | true | 始终显示滚动条 (3.6.0+) |\n\n### 索引列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| index | boolean | false | 显示序号 |\n| indexLabel | string | '#' | 序号列标题 |\n| indexWidth | number | 50 | 序号列宽度 |\n| indexFixed | boolean/string | true | 序号列固定: true/left/right |\n| indexClassName | string | - | 序号列单元格自定义class |\n| indexLabelClassName | string | - | 序号列表头自定义class |\n\n### 选择列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| selection | boolean | false | 显示多选框 |\n| selectable | function | - | 判断行是否可选 |\n| selectionWidth | number | 50 | 选择列宽度 |\n| selectionFixed | boolean/string | true | 选择列固定 |\n| selectionClassName | string | - | 选择列单元格class |\n| selectionLabelClassName | string | - | 选择列表头class |\n| reserveSelection | boolean | false | 翻页保留选择 |\n\n### 展开行\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| expand | boolean | false | 显示展开 |\n| expandWidth | number | 50 | 展开列宽度 |\n| expandFixed | boolean/string | true | 展开列固定 |\n| expandClassName | string | - | 展开列单元格class |\n| expandLabelClassName | string | - | 展开列表头class |\n| defaultExpandAll | boolean | false | 默认展开所有行 |\n| expandRowKeys | array | - | 展开行的keys |\n\n### 行配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowKey | string | 'id' | 行数据主键 |\n| rowParentKey | string | 'parentId' | 父行主键(树表用) |\n| highlightCurrentRow | boolean | false | 高亮当前行 |\n| defaultSort | object | - | 默认排序 {prop, order: 'ascending'/'descending'} |\n| showOverflowTooltip | boolean/object | false | 溢出提示 |\n| showSummary | boolean | false | 显示合计行 |\n| lazy | boolean | false | 懒加载子节点 |\n\n### 菜单列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menu | boolean | true | 显示操作菜单 |\n| menuWidth | number | 220 | 菜单列宽度 |\n| menuTitle | string | '操作' | 菜单列标题 |\n| menuFixed | boolean/string | true | 菜单列固定 |\n| menuType | string | 'text' | 菜单按钮样式: button/icon/text/menu |\n| menuHeaderAlign | string | 'center' | 菜单表头对齐 |\n| menuAlign | string | 'center' | 菜单按钮对齐 |\n| menuClassName | string | - | 菜单单元格class |\n| menuLabelClassName | string | - | 菜单表头class |\n\n### 卡片/网格模式 (3.4.0+)\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| grid | boolean | false | 启用网格模式 |\n| gridBtn | boolean | true | 显示模式切换按钮 |\n| gridSpan | number | 8 | 网格栅格数 |\n| gridBackgroundImage | string | - | 网格背景图片 |\n| gridBackground | string/function | - | 网格背景色 |\n\n### 工具栏与功能开关\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addRowBtn | boolean | false | 显示新增行内编辑按钮 |\n| cellBtn | boolean | false | 显示行内编辑操作按钮 |\n| dateBtn | boolean | false | 显示日期范围选择按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮 |\n| selectClearBtn | boolean | true | 显示清空选中按钮 |\n| tip | boolean | true | 显示选中项提示 |\n| page | boolean | true | 显示分页 |\n| searchShow | boolean | true | 搜索栏默认显示 |\n| searchIcon | boolean | true | 搜索栏展开/收起图标 |\n| searchIndex | number | - | 搜索栏初始显示字段数（超出折叠） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchSpan | number | 6 | 搜索栅格数 |\n| calcHeight | number | 300 | 表格自适应高度偏移量 |\n| menuXsWidth | number | 100 | 移动端菜单列宽度 |\n| gridXsSpan | number | 12 | 移动端网格栅格数 |\n| headerAlign | string | 'left' | 表头默认对齐方式 |\n| width | string | '100%' | 表格宽度 |\n| formFullscreen | boolean | false | 弹窗显示全屏切换按钮 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| filterMultiple | boolean | true | 过滤器默认多选 |\n\n---\n\n## 按钮配置\n\n### 操作按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| addBtnText | string | '新增' | 新增按钮文字 |\n| addBtnIcon | string | - | 新增按钮图标 |\n| editBtn | boolean | true | 修改按钮 |\n| editBtnText | string | '修改' | 修改按钮文字 |\n| editBtnIcon | string | - | 修改按钮图标 |\n| delBtn | boolean | true | 删除按钮 |\n| delBtnText | string | '删除' | 删除按钮文字 |\n| delBtnIcon | string | - | 删除按钮图标 |\n| viewBtn | boolean | false | 查看按钮 |\n| viewBtnText | string | '查看' | 查看按钮文字 |\n| viewBtnIcon | string | - | 查看按钮图标 |\n| copyBtn | boolean | false | 复制按钮 |\n\n### 弹窗按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| updateBtn | boolean | true | 弹窗修改按钮 |\n| updateBtnText | string | '修改' | 修改按钮文字 |\n| updateBtnIcon | string | - | 修改按钮图标 |\n| saveBtn | boolean | true | 弹窗保存按钮 |\n| saveBtnText | string | '保存' | 保存按钮文字 |\n| saveBtnIcon | string | - | 保存按钮图标 |\n| cancelBtn | boolean | true | 弹窗取消按钮 |\n| cancelBtnText | string | '取消' | 取消按钮文字 |\n| cancelBtnIcon | string | - | 取消按钮图标 |\n\n### 工具栏按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| printBtn | boolean | false | 打印按钮 |\n| excelBtn | boolean | false | 导出按钮 |\n| filterBtn | boolean | false | 筛选按钮 |\n| refreshBtn | boolean | true | 刷新按钮 |\n| columnBtn | boolean | true | 列操作按钮 |\n| searchShowBtn | boolean | true | 搜索显隐按钮 |\n| addRowBtn | boolean | false | 新增行内编辑按钮 |\n| dateBtn | boolean | false | 日期范围选择按钮 |\n| gridBtn | boolean | true | 网格/表格切换按钮 |\n\n### 按钮文字图标规则\n\n按钮名 + `Text` = 按钮文字，按钮名 + `Icon` = 按钮图标。设置图标为 `' '`（空格）可禁用图标。\n\n---\n\n## 弹窗配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogDrag | boolean | false | 弹窗可拖拽 |\n| dialogFullscreen | boolean | false | 弹窗全屏 |\n| dialogCustomClass | string | - | 弹窗自定义class |\n| dialogEscape | boolean | true | ESC关闭弹窗 |\n| dialogClickModal | boolean | false | 点击遮罩关闭弹窗 |\n| dialogCloseBtn | boolean | true | 显示关闭按钮 |\n| dialogModal | boolean | true | 显示遮罩层 |\n| dialogMenuPosition | string | 'right' | 弹窗按钮位置 |\n| dialogTop | number | 25 | 弹窗距顶部距离 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| headerClass | string | - | 弹窗头部自定义class (3.7.0+) |\n| bodyClass | string | - | 弹窗内容区自定义class (3.7.0+) |\n| footerClass | string | - | 弹窗底部自定义class (3.7.0+) |\n\n---\n\n## 分页配置\n\n通过 `v-model:page` 绑定分页对象：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currentPage | number | 1 | 当前页码 |\n| pageSize | number | 20 | 每页条数 |\n| total | number | 0 | 总条数 |\n| pagerCount | number | 7 | 页码按钮数量 |\n| background | boolean | true | 分页按钮背景 |\n| layout | string | 'prev, pager, next, jumper, ->, total' | 分页组件布局 |\n| pageSizes | number[] | [10, 20, 30, 40, 50, 100] | 每页条数选项 |\n| simplePage | boolean | false | 只有一页时隐藏分页 |\n\n---\n\n## 事件\n\n### 数据操作事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-save | (row, done, loading) | 新增数据后触发。done() 关闭表单，loading() 保持表单 |\n| row-update | (row, index, done, loading) | 修改数据后触发 |\n| row-del | (row, index) | 删除行触发 |\n| refresh-change | - | 点击刷新按钮后触发 |\n\n### 搜索事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| search-change | (form, done) | 搜索后触发 |\n| search-reset | - | 清空搜索回调 |\n| date-change | (date) | 日期选择回调 |\n\n### 选择事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| select | (selection, row) | 用户勾选 |\n| select-all | (selection) | 用户全选 |\n| selection-change | (selection) | 选择项变化 |\n| current-row-change | (currentRow, oldCurrentRow) | 当前行变化 |\n| selection-clear | (selection) | 清空选中时触发 |\n\n### 行/单元格事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| cell-mouse-enter | (row, column, cell, event) | 单元格hover进入 |\n| cell-mouse-leave | (row, column, cell, event) | 单元格hover离开 |\n| cell-click | (row, column, cell, event) | 单元格点击 |\n| cell-dblclick | (row, column, cell, event) | 单元格双击 |\n| row-click | (row, column, event) | 行点击 |\n| row-contextmenu | (row, column, event) | 行右击 |\n| row-dblclick | (row, column, event) | 行双击 |\n\n### 表头事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| header-click | (column, event) | 表头点击 |\n| header-contextmenu | (column, event) | 表头右击 |\n| header-dragend | (newWidth, oldWidth, column, event) | 列宽改变 |\n\n### 排序/过滤事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| sort-change | ({ column, prop, order }) | 排序变化 |\n| filter-change | (filters) | 过滤变化 |\n| sortable-change | ({ newIndex, oldIndex }) | 行拖拽排序 |\n| column-sortable-change | ({ newIndex, oldIndex }) | 列拖拽排序 (3.5.2+) |\n\n### 其他事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| on-load | (page) | 页面加载/分页变化时触发 |\n| tree-load | (row, treeNode, resolve) | 加载子节点 (lazy=true) |\n| expand-change | (row, expandedRows/expanded) | 行展开/收起 |\n| grid-status-change | (status) | 网格/表格模式切换 (3.4.7+) |\n| scroll | ({scrollLeft, scrollTop}) | 表格滚动 (3.6.0+) |\n| tab-click | (tab, event) | 弹窗标签页点击 |\n| error | (error) | 表单验证失败 |\n| filter | (filterData) | 高级筛选应用 |\n| date-change | (date) | 日期范围选择回调 |\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowAdd | - | 打开新增弹窗 |\n| rowEdit | (row, index) | 打开编辑弹窗 |\n| rowView | (row, index) | 打开查看弹窗 |\n| updateDic | (prop, dic) | 更新字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | (valid, done, msg) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置所有字段 |\n| clearValidate | (props) | 清除验证结果 |\n| clearSelection | - | 清除多选 |\n| toggleSelection | (array) | 切换选择 |\n| toggleAllSelection | - | 切换全选 |\n| toggleRowExpansion | (row, expanded) | 切换行展开 |\n| setCurrentRow | (row) | 设置当前行 |\n| clearSort | - | 清除排序 |\n| clearFilter | (columnKey) | 清除过滤 |\n| doLayout | - | 重新布局表格 |\n| refreshTable | - | 重新初始化表格 |\n| sort | (prop, order) | 手动排序 |\n| updateKeyChildren | (key, data) | 更新懒加载子节点 (3.6.0+) |\n\n### 行内编辑方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowCopy | (row) | 复制行数据到新增弹窗 |\n| rowCellAdd | (row) | 新增一行行内编辑数据 |\n| rowCell | (row, index) | 切换行编辑状态 |\n| rowCellEdit | (row, index) | 进入行编辑模式 |\n| rowCellUpdate | (row, index) | 保存行编辑数据 |\n| rowCancel | (row, index) | 取消行编辑 |\n| closeDialog | (row, index) | 关闭弹窗并更新数据 |\n| validateCellForm | - | 验证所有行内编辑表单 |\n| validateCellField | (index) | 验证指定行的编辑表单 |\n\n### 数据方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| findData | (id) | 通过主键值查找行数据 |\n| getPermission | (key, row, index) | 获取按钮权限 |\n| getTableHeight | - | 重新计算表格高度 |\n\n### 滚动方法 (3.7.0+ 虚拟滚动)\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| scrollTo | (coordinates) | 滚动到指定位置 |\n| scrollToTop | - | 滚动到顶部 |\n| scrollToLeft | - | 滚动到最左侧 |\n| scrollRow | (rowKey) | 滚动到指定行（虚拟滚动模式） |\n| setScrollTop | (top) | 设置垂直滚动位置 |\n| setScrollLeft | (left) | 设置水平滚动位置 |\n\n### 搜索方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| searchReset | - | 重置搜索表单 |\n| searchChange | (form, done) | 手动触发搜索 |\n\n### 视图方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| handleGridShow | - | 切换网格/表格视图 |\n| columns | - | 获取当前列配置 |\n\n---\n\n## 插槽\n\n### 列相关插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义列内容，参数: {row, index, dic, size, label} |\n| `{prop}-header` | 自定义列表头 |\n| `{prop}-search` | 自定义搜索输入 |\n| `{prop}-form` | 自定义表单字段 |\n| `{prop}-label` | 自定义表单标签 |\n| `{prop}-error` | 自定义表单错误提示 |\n| `{prop}-desc` | 自定义表单描述 (3.6.4+) |\n| `{prop}-type` | 自定义字段类型选择器 |\n\n### 区域插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| search | 自定义整个搜索区域 |\n| searchMenu | 自定义搜索菜单 |\n| header | 自定义表格头部 |\n| body | 自定义表格主体 (3.4.3+) |\n| page | 自定义分页 |\n| empty | 自定义空状态 |\n| tip | 自定义选中提示区域 |\n\n### 按钮插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu-left | 自定义菜单左侧 |\n| menu-right | 自定义菜单右侧 |\n| menu | 自定义整个操作菜单，参数: {row, index, type, size} |\n| menu-before | 操作菜单前 (3.4.3+) |\n| menu-btn | 自定义菜单按钮 |\n| menu-btn-before | 菜单按钮前 (3.4.3+) |\n| menu-form | 自定义表单菜单 |\n| menu-form-before | 表单菜单前 (3.4.3+) |\n| menu-header | 菜单表头插槽 |\n",".claude/skills/avue-design/references/crud-features.md":"# avue-crud 高级功能参考\n\n## 目录\n1. [树形表格](#树形表格)\n2. [虚拟滚动](#虚拟滚动)\n3. [卡片/网格模式](#卡片网格模式)\n4. [行编辑（单元格编辑）](#行编辑)\n5. [导入导出](#导入导出)\n6. [拖拽排序](#拖拽排序)\n7. [合计行](#合计行)\n8. [父子表](#父子表)\n9. [加载状态](#加载状态)\n10. [CRUD 插件模式](#crud-插件模式)\n11. [行复制](#行复制)\n12. [高级筛选](#高级筛选-filterbtn)\n13. [列管理器](#列管理器-columnbtn)\n14. [Excel 导出对话框](#excel-导出对话框)\n15. [权限控制](#权限控制)\n\n---\n\n## 树形表格\n\n### 基本树表\n\n```javascript\noption: {\n  rowKey: 'id',\n  rowParentKey: 'parentId',\n  defaultExpandAll: true,  // 默认展开所有\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n  ],\n}\n\n// 数据需要有 id 和 parentId 字段\ndata: [\n  { id: 1, parentId: 0, name: '父节点', code: 'P1' },\n  { id: 2, parentId: 1, name: '子节点', code: 'C1' },\n]\n```\n\n### 懒加载树表\n\n```vue\n<avue-crud :option=\"option\" @tree-load=\"treeLoad\">\n\noption: {\n  rowKey: 'id',\n  lazy: true,\n  column: [\n    { label: '名称', prop: 'name', hasChildren: 'hasChildren' },\n  ],\n}\n\nconst treeLoad = (row, treeNode, resolve) => {\n  getChildren(row.id).then(res => {\n    resolve(res.data.data)\n  })\n}\n```\n\n注意：非懒加载时需去掉 `hasChildren` 字段。\n\n---\n\n## 虚拟滚动 (3.7.0+)\n\n用于大数据量场景的虚拟滚动：\n\n```javascript\noption: {\n  virtualize: true,\n  height: 500,  // 必须为数字\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      width: 200,   // 虚拟滚动时 width 必须为数字\n      fixed: true,   // 列自适应\n    },\n  ],\n}\n```\n\n注意：虚拟滚动模式下 `width` 和 `height` 必须是数字类型。\n\n自适应高度：\n```javascript\noption: {\n  virtualize: true,\n  height: 'auto',  // 自适应\n}\n```\n\n---\n\n## 卡片/网格模式 (3.4.0+)\n\n将表格显示为卡片网格形式：\n\n```javascript\noption: {\n  grid: true,           // 启用网格模式\n  gridBtn: true,        // 显示切换按钮（默认true）\n  gridSpan: 8,          // 每卡片占栅格数\n  gridBackgroundImage: 'url(xxx)',  // 背景图\n  gridBackground: '#f5f5f5',       // 背景色\n}\n\n// 背景色也可以是函数\noption: {\n  gridBackground: (row, index) => {\n    return index % 2 === 0 ? '#f0f9ff' : '#fff'\n  },\n}\n```\n\n自定义卡片插槽：\n```vue\n<template #default=\"{ row, index }\">\n  <div class=\"custom-card\">\n    <h3>{{ row.name }}</h3>\n    <p>{{ row.description }}</p>\n  </div>\n</template>\n```\n\n---\n\n## 行编辑\n\n### 基本行编辑\n\n```javascript\noption: {\n  cellBtn: true,     // 显示行编辑按钮\n  addRowBtn: true,   // 显示行新增按钮\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      cell: true,    // 该列可编辑\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      cell: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n}\n```\n\n### 首次进入编辑模式\n\n```javascript\n// 数据中设置 $cellEdit: true\ndata: [\n  { id: 1, name: 'test', $cellEdit: true },\n]\n```\n\n### 单击/双击编辑\n\n```vue\n<!-- 单击编辑 -->\n<avue-crud @cell-click=\"cellClick\">\n\nconst cellClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n\n<!-- 双击编辑 -->\n<avue-crud @cell-dblclick=\"cellDblClick\">\n\nconst cellDblClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n```\n\n### 保存行编辑\n\n数据必须包含 `rowKey`（默认 id）字段才能保存。\n\n---\n\n## 导入导出\n\n### 导出 Excel\n\n```javascript\noption: {\n  excelBtn: true,  // 显示导出按钮\n}\n```\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n### 编程式导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 导入 Excel\n\n参考 `default/export/import` 示例。\n\n---\n\n## 拖拽排序\n\n### 行拖拽排序\n\n```javascript\noption: {\n  sortable: true,  // 启用行拖拽\n  rowKey: 'id',    // 必须设置主键\n}\n```\n\n需引入 Sortable.js：\n```html\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n```\n\n监听排序事件：\n```vue\n<avue-crud @sortable-change=\"sortableChange\">\n\nconst sortableChange = ({ newIndex, oldIndex }) => {\n  // 处理排序逻辑\n}\n```\n\n### 列拖拽排序 (3.5.2+)\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    columnSort: true,  // 启用列拖拽\n  },\n]\n```\n\n监听事件：\n```vue\n<avue-crud @column-sortable-change=\"columnSortableChange\">\n```\n\n---\n\n## 合计行\n\n### 内置合计\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },      // 求和\n    { name: 'price', type: 'average' },    // 平均值\n    { name: 'count', type: 'count' },      // 计数\n  ],\n}\n```\n\n### 自定义合计\n\n```vue\n<avue-crud :summary-method=\"summaryMethod\">\n\nconst summaryMethod = ({ columns, data }) => {\n  const sums = []\n  columns.forEach((column, index) => {\n    if (index === 0) {\n      sums[index] = '合计'\n      return\n    }\n    const values = data.map(item => Number(item[column.property]))\n    if (!values.every(isNaN)) {\n      sums[index] = values.reduce((a, b) => a + b, 0).toFixed(2)\n    } else {\n      sums[index] = '-'\n    }\n  })\n  return sums\n}\n```\n\n---\n\n## 父子表\n\n```vue\n<avue-crud :option=\"parentOption\" :data=\"parentData\" @row-click=\"parentRowClick\">\n  <!-- 子表通过展开行或独立组件实现 -->\n  <template #expand=\"{ row }\">\n    <avue-crud :option=\"childOption\" :data=\"row.children\" />\n  </template>\n</avue-crud>\n```\n\n或使用独立组件封装子表，通过 `crud-children` 模式。\n\n---\n\n## 加载状态\n\n```vue\n<avue-crud :table-loading=\"loading\">\n\nconst loading = ref(true)\n// 数据加载完成后\nloading.value = false\n```\n\n---\n\n## CRUD 插件模式\n\n### Mixin 模式（Options API）\n\n```javascript\n// @/mixins/crud.js\nexport default (app, option = {}) => {\n  let optionObj = import.meta.glob(`../option/**/**`)[`../option/${option.name}.js`]\n  let apiObj = import.meta.glob(`../api/**/**`)[`../api/${option.name}.js`]\n  // ... mixin 逻辑\n}\n\n// 全局注册\n// main.js\nimport crudCommon from '@/mixins/crud'\nwindow.$crudCommon = crudCommon\n\n// 使用\nexport default window.$crudCommon({\n  data() { return {} },\n  methods: {\n    listBefore() {},\n    listAfter() {},\n    addBefore() { this.form.createUser = 'admin' },\n    addAfter() {},\n    updateBefore() {},\n    updateAfter() {},\n    delBefore() {},\n    delAfter() {},\n  }\n}, {\n  name: 'system/params',\n  list: 'list',\n  update: 'update',\n  add: 'add',\n  del: 'del',\n  rowKey: 'id',\n  pageNumber: 'pageNumber',\n  pageSize: 'pageSize',\n  res: (data) => ({ total: data.total, data: data.records }),\n  total: 'total',\n  data: 'records',\n})\n```\n\n### 绑定辅助（Options API）\n\n```vue\n<avue-crud v-bind=\"bindVal\" v-on=\"onEvent\" v-model=\"form\" v-model:page=\"page\">\n\ncomputed: {\n  bindVal() {\n    return {\n      ref: 'crud',\n      option: this.option,\n      data: this.data,\n      tableLoading: this.loading,\n    }\n  },\n  onEvent() {\n    return {\n      'on-load': this.getList,\n      'row-save': this.rowSave,\n      'row-update': this.rowUpdate,\n      'row-del': this.rowDel,\n      'refresh-change': this.refreshChange,\n      'search-reset': this.searchChange,\n      'search-change': this.searchChange,\n    }\n  },\n}\n```\n\n### API 文件模板\n\n```javascript\n// @/api/module/entity.js\nimport request from '@/router/axios'\n\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/api/entity/list',\n    method: 'get',\n    params: { current, size, ...params },\n  })\n}\n\nexport const getDetail = (id) => {\n  return request({\n    url: '/api/entity/detail',\n    method: 'get',\n    params: { id },\n  })\n}\n\nexport const add = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const update = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const remove = (ids) => {\n  return request({\n    url: '/api/entity/remove',\n    method: 'post',\n    params: { ids },\n  })\n}\n```\n\n### Option 配置文件模板\n\n```javascript\n// @/option/module/entity.js\nexport default (safe) => {\n  // safe => vue 的 this 对象 (Options API)\n  return {\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    border: true,\n    stripe: true,\n    column: [\n      {\n        label: \"名称\",\n        prop: \"name\",\n        search: true,\n        rules: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n      },\n    ],\n  }\n}\n```\n\n---\n\n## 配置动态切换\n\n### 初始化加载\n\n```vue\n<avue-crud v-if=\"option.column\" :option=\"option\">\n\nconst option = ref({})\nonMounted(() => {\n  // 从服务器加载配置\n  getConfig().then(res => {\n    option.value = res.data\n  })\n})\n```\n\n### 动态切换配置\n\n```javascript\nconst changeOption = (type) => {\n  if (type === 'simple') {\n    option.value = simpleOption\n  } else {\n    option.value = fullOption\n  }\n}\n```\n\n---\n\n## 空状态自定义\n\n```javascript\noption: {\n  emptyText: '暂无数据',  // 空状态文字\n}\n```\n\n```vue\n<!-- 自定义空状态 -->\n<template #empty>\n  <div class=\"custom-empty\">\n    <img src=\"empty.png\" />\n    <p>暂无数据，请添加</p>\n  </div>\n</template>\n```\n\n---\n\n## 深层数据绑定\n\n```javascript\n// 数据结构: { id: 1, info: { address: '北京' } }\ncolumn: [\n  {\n    label: '地址',\n    prop: 'address',      // prop 仍然需要\n    bind: 'info.address', // 绑定深层路径\n  },\n]\n```\n\n---\n\n## 行复制\n\n通过 `rowCopy` 方法复制已有行数据到新增弹窗：\n\n```vue\n<template #menu=\"{ row, index, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowCopy(row)\">复制</el-button>\n</template>\n```\n\n需设置 `copyBtn: true` 在 option 中也可直接显示内置复制按钮。\n\n---\n\n## 高级筛选 (filterBtn)\n\n通过 `filterBtn: true` 启用高级筛选对话框，支持多条件组合查询：\n\n```javascript\noption: {\n  filterBtn: true,  // 显示筛选按钮\n}\n```\n\n### 筛选操作符\n\n| 符号 | 含义 |\n|-----|------|\n| = | 等于 |\n| ≠ | 不等于 |\n| like | 模糊匹配 |\n| > | 大于 |\n| ≥ | 大于等于 |\n| < | 小于 |\n| ≤ | 小于等于 |\n| ∈ | 包含于 |\n\n### 筛选事件\n\n```vue\n<avue-crud @filter=\"handleFilter\">\n\nconst handleFilter = (filterData) => {\n  // filterData 格式: [[prop, operator, value], ...]\n  // 示例: [['name', 'like', '张'], ['age', '>', 18]]\n  console.log('筛选条件:', filterData)\n}\n```\n\n---\n\n## 列管理器 (columnBtn)\n\n通过 `columnBtn: true`（默认开启）显示列管理按钮，可动态控制列的显隐、固定、过滤和排序：\n\n```javascript\noption: {\n  columnBtn: true,  // 显示列管理按钮（默认true）\n}\n```\n\n列管理器支持以下操作：\n- **显隐** - 控制列是否在表格中显示\n- **固定** - 设置列固定在左侧或右侧\n- **过滤** - 启用列的筛选功能\n- **排序** - 启用列的排序功能\n- **拖拽排序** - 拖拽调整列的显示顺序\n\n---\n\n## Excel 导出对话框\n\n当设置 `excelBtn: true` 时，点击导出按钮会打开导出配置对话框：\n\n### 导出选项\n\n| 选项 | 说明 |\n|-----|------|\n| 导出范围 | 导出全部数据 / 仅导出选中行 |\n| 包含表头 | 是否包含列标题行 |\n| 包含数据 | 是否包含数据行 |\n| 包含合计 | 是否包含合计/汇总行 |\n| 选择列 | 选择要导出的列 |\n\n### 编程式导出（跳过对话框）\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 编程式导入\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.xlsx(file).then(({ header, results }) => {\n  console.log('表头:', header)\n  console.log('数据:', results)\n})\n```\n\n---\n\n## 权限控制\n\n### 对象模式\n\n```javascript\n// 通过 permission 对象控制按钮权限\nconst permissionList = {\n  addBtn: true,\n  editBtn: true,\n  delBtn: false,\n  viewBtn: true,\n}\n```\n\n### 函数模式（行级权限）\n\n```javascript\n// 通过函数实现行级权限控制\nconst permission = (key, row, index) => {\n  // key: 按钮标识 (addBtn/editBtn/delBtn/viewBtn)\n  // row: 当前行数据\n  // index: 当前行索引\n  if (key === 'editBtn') {\n    return row.status !== 'locked'\n  }\n  return true\n}\n```\n\n---\n\n## 自定义按钮\n\n```vue\n<!-- 隐藏默认按钮，使用自定义 -->\noption: { addBtn: false, editBtn: false, delBtn: false, viewBtn: false }\n\n<!-- 自定义新增按钮 -->\n<template #menu-left>\n  <el-button type=\"primary\" @click=\"$refs.crud.rowAdd()\">自定义新增</el-button>\n</template>\n\n<!-- 自定义操作按钮 -->\n<template #menu=\"{ row, index, type, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowEdit(row, index)\">编辑</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowView(row, index)\">查看</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowDel(row, index)\">删除</el-button>\n</template>\n\n<!-- 自定义弹窗按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\">自定义保存</el-button>\n  <el-button v-if=\"type === 'edit'\">自定义更新</el-button>\n</template>\n```\n",".claude/skills/avue-design/references/crud-form-dialog.md":"# avue-crud 弹窗表单配置参考\n\n## 弹窗基本配置\n\noption 中的弹窗属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogTop | number | 25 | 弹窗距顶部 |\n| dialogDrag | boolean | false | 可拖拽 |\n| dialogFullscreen | boolean | false | 全屏 |\n| dialogCustomClass | string | - | 自定义class |\n| dialogEscape | boolean | true | ESC关闭 |\n| dialogClickModal | boolean | false | 点击遮罩关闭 |\n| dialogCloseBtn | boolean | true | 关闭按钮 |\n| dialogModal | boolean | true | 遮罩层 |\n| dialogMenuPosition | string | 'right' | 按钮位置: left/center/right |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n\n## 打开/关闭回调\n\n```vue\n<avue-crud :before-open=\"beforeOpen\" :before-close=\"beforeClose\">\n\n<!-- Composition API -->\nconst beforeOpen = (done, type, loading) => {\n  // type: 'add' | 'edit' | 'view'\n  if (['edit', 'view'].includes(type)) {\n    // 加载详情数据\n    loading()  // 显示loading (3.5.2+)\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n      done()   // 完成加载\n    })\n  } else {\n    done()\n  }\n}\n\nconst beforeClose = (done, type) => {\n  done()\n}\n```\n\n## 表单验证规则\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    rules: [\n      { required: true, message: '请输入名称', trigger: 'blur' },\n      { min: 2, max: 20, message: '长度在2到20之间', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '邮箱',\n    prop: 'email',\n    rules: [\n      { required: true, message: '请输入邮箱', trigger: 'blur' },\n      { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '年龄',\n    prop: 'age',\n    type: 'number',\n    rules: [{\n      required: true,\n      validator: (rule, value, callback) => {\n        if (value < 18) {\n          callback(new Error('年龄不能小于18岁'))\n        } else {\n          callback()\n        }\n      },\n      trigger: 'blur',\n    }],\n  },\n]\n```\n\n验证规则基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n## 字段显示/禁用控制\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    // 全局控制\n    display: true,       // 是否显示\n    disabled: false,     // 是否禁用\n    // 按操作类型控制\n    addDisplay: true,    // 新增时显示\n    editDisplay: true,   // 编辑时显示\n    viewDisplay: true,   // 查看时显示\n    addDisabled: false,  // 新增时禁用\n    editDisabled: true,  // 编辑时禁用（如ID不可编辑）\n    viewDisabled: false,\n  },\n]\n```\n\n## 表单默认值\n\n```javascript\ncolumn: [\n  {\n    label: '状态',\n    prop: 'status',\n    type: 'select',\n    value: 1,  // 新增时默认值\n    dicData: [\n      { label: '启用', value: 1 },\n      { label: '禁用', value: 0 },\n    ],\n  },\n]\n```\n\n## 字段联动控制 (control)\n\n```javascript\ncolumn: [\n  {\n    label: '类型',\n    prop: 'type',\n    type: 'select',\n    dicData: [\n      { label: '文章', value: 1 },\n      { label: '链接', value: 2 },\n    ],\n    control: (val, form) => {\n      if (val === 1) {\n        return {\n          content: { display: true },\n          url: { display: false },\n        }\n      } else {\n        return {\n          content: { display: false },\n          url: { display: true },\n        }\n      }\n    },\n  },\n  { label: '内容', prop: 'content', type: 'textarea' },\n  { label: '链接', prop: 'url', display: false },\n]\n```\n\n3.5.0+ 支持 Promise：\n```javascript\ncontrol: async (val, form) => {\n  const res = await getConfig(val)\n  return { subType: { dicData: res.data } }\n}\n```\n\n## 表单分组 (group)\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,  // 默认展开\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name' },\n        { label: '编码', prop: 'code' },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', type: 'textarea' },\n      ],\n    },\n  ],\n  // 使用标签页显示分组\n  tabs: true,\n  // column 也要配置（表格列用）\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n    { label: '描述', prop: 'description' },\n  ],\n}\n```\n\n## 表单标签宽度\n\n```javascript\noption: {\n  labelWidth: 120,        // 全局标签宽度\n  labelPosition: 'right', // 全局标签位置: left/right/top\n}\n\n// 也可在 column 中单独设置\n{\n  label: '很长的标签名称',\n  prop: 'longLabel',\n  labelWidth: 180,\n}\n```\n\n## 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤字典翻译字段（$开头的字段）\n  filterNull: true,  // 过滤空值字段\n}\n```\n\n## 防止重复提交\n\n```javascript\n// row-save 和 row-update 中的 done 和 loading 参数：\n// - done(): 关闭表单弹窗\n// - loading(): 关闭loading但保持表单打开（用于接口报错时）\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    ElMessage.success('操作成功')\n    done()     // 关闭弹窗\n    onLoad()\n  }).catch(() => {\n    loading()  // 关闭loading，保持弹窗\n  })\n}\n```\n\n## 表单字段排序\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', order: 1 },\n  { label: '编码', prop: 'code', order: 3 },  // order大的排前面\n  { label: '状态', prop: 'status', order: 2 },\n]\n```\n\n## 获取字段组件引用\n\n```javascript\nconst ref = crudRef.value.getPropRef('name')\n// ref 即为该字段对应的组件实例\n```\n\n## 连续新增（不关闭弹窗）\n\n通过不调用 `done()` 来保持弹窗打开，实现连续新增。\n\n## 自定义表单插槽\n\n```vue\n<!-- 自定义表单字段 -->\n<template #name-form=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"form.name\" />\n</template>\n\n<!-- 自定义表单标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义错误提示 -->\n<template #name-error=\"{ error }\">\n  <span style=\"color: orange\">{{ error }}</span>\n</template>\n\n<!-- 自定义描述 (3.6.4+) -->\n<template #name-desc=\"{ column }\">\n  <span>这是一段描述文字</span>\n</template>\n\n<!-- 自定义表单按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\" type=\"primary\">自定义保存</el-button>\n</template>\n```\n\n## 权限控制\n\n```javascript\n// 对象形式\n<avue-crud :permission=\"{ addBtn: true, editBtn: true, delBtn: false }\">\n\n// 函数形式（行级控制）\n<avue-crud :permission=\"getPermission\">\n\nconst getPermission = (key, row, index) => {\n  // key: 'addBtn' | 'editBtn' | 'delBtn' | 'viewBtn' | 'menu'\n  if (key === 'editBtn' && row?.status === 1) {\n    return false  // 已启用的不允许编辑\n  }\n  return true\n}\n```\n",".claude/skills/avue-design/references/crud-search.md":"# avue-crud 搜索配置参考\n\n## 基本用法\n\n在 column 中设置 `search: true` 开启搜索字段：\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    search: true,\n    searchSpan: 6,\n  },\n  {\n    label: '日期',\n    prop: 'date',\n    type: 'daterange',\n    search: true,\n    searchRange: true,\n    searchSpan: 8,\n  },\n]\n```\n\n## 搜索变量\n\nComposition API 使用 `v-model:search` 自动同步：\n```vue\n<avue-crud v-model:search=\"search\" @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nconst search = ref({})\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\nconst searchReset = () => {\n  onLoad()\n}\n```\n\nOptions API 使用 query 变量手动同步：\n```vue\n<avue-crud @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nsearchChange(params, done) {\n  this.query = params\n  this.page.currentPage = 1\n  this.onLoad(this.page, params)\n  done()\n},\nsearchReset() {\n  this.query = {}\n  this.onLoad(this.page)\n},\n```\n\n## 搜索栏配置\n\n### 全局搜索属性（option 级别）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| searchShow | boolean | true | 首次加载显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留字段数 |\n| searchMenuPosition | string | 'center' | 按钮位置: left/center/right |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示文字 |\n| searchMenuSpan | number | - | 按钮栅格（单独一行时设24） |\n| searchSpan | number | 8 | 默认搜索栅格 |\n| searchGutter | number | - | 默认搜索间距 |\n| searchLabelWidth | number | 80 | 默认搜索标签宽度 |\n| searchLabelPosition | string | 'left' | 默认搜索标签位置 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n\n### 列级搜索属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | any | - | 搜索默认值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索排序 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索类型（覆盖type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 可清空 |\n| searchMultiple | boolean | false | 多选 |\n| searchTags | boolean | false | 标签显示 |\n| searchLabelPosition | string | 'left' | 标签位置 |\n| searchRules | array | - | 验证规则 |\n\n## 搜索字段类型覆盖\n\n可以用 searchType 让搜索框使用不同于表格列的类型：\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  type: 'date',           // 表格/表单中是日期选择\n  search: true,\n  searchType: 'daterange', // 搜索中是日期范围\n  searchRange: true,\n}\n```\n\n## 搜索折叠\n\n```javascript\noption: {\n  searchIcon: true,    // 显示折叠按钮\n  searchIndex: 2,      // 折叠时保留2个字段\n}\n```\n\n## 搜索级联联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/province',\n    cascader: ['city'],\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/city?province={{key}}',\n  },\n]\n```\n\n## 自定义搜索插槽\n\n```vue\n<!-- 自定义搜索菜单 -->\n<template #searchMenu=\"{ row, search, reset }\">\n  <el-button type=\"primary\" @click=\"search\">搜索</el-button>\n  <el-button @click=\"reset\">重置</el-button>\n  <el-button type=\"warning\" @click=\"handleExport\">导出</el-button>\n</template>\n\n<!-- 自定义搜索字段 -->\n<template #name-search=\"{ search }\">\n  <el-input v-model=\"search.name\" placeholder=\"自定义搜索\" />\n</template>\n```\n\n## 日期快捷按钮\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  dateBtn: true,  // 显示日期快捷按钮\n}\n```\n",".claude/skills/avue-design/references/data-display.md":"# Avue 数据展示组件参考\n\n## 组件列表\n\n| 组件 | 说明 |\n|-----|------|\n| avue-data-display | 数据展示卡片（核心组件） |\n| avue-data-pay | 支付数据 |\n| avue-data-tabs | 标签数据 |\n| avue-data-box | 数据盒子 |\n| avue-data-card | 数据卡片 |\n| avue-data-cardtext | 文字卡片 |\n| avue-data-icons | 图标数据 |\n| avue-data-operatext | 操作文字 |\n| avue-data-progress | 进度数据 |\n| avue-data-rotate | 旋转数据 |\n| avue-data-imgtext | 图文数据 |\n| avue-data-panel | 面板 |\n| avue-data-price | 价格 |\n| avue-data-countdown | 倒计时 |\n| avue-data-dashboard | 仪表盘 |\n| avue-data-list | 数据列表 |\n| avue-data-notice | 公告 |\n| avue-data-product | 商品卡片 |\n| avue-data-profile | 人员档案 |\n| avue-data-rank | 排行榜 |\n| avue-data-statistic | 统计值 |\n| avue-data-task | 任务列表 |\n| avue-data-weather | 天气卡片 |\n\n## avue-data-display（核心组件）\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| animation | boolean | true | 是否动画 |\n| decimals | number | 0 | 小数位数 |\n| span | string | '8' | 栅格数 |\n| data | array | - | 数据数组 |\n\n### 数据结构\n\n```javascript\nconst data = [\n  {\n    title: '今日访问',\n    count: 1234,\n    icon: 'el-icon-view',\n    color: '#40c9c6',\n    decimals: 0,\n  },\n  {\n    title: '销售额',\n    count: 56789.50,\n    icon: 'el-icon-money',\n    color: '#36a3f7',\n    decimals: 2,\n  },\n]\n```\n\n### 使用示例\n\n```vue\n<avue-data-display :data=\"displayData\" :option=\"{ decimals: 0, animation: true }\" />\n```\n\n## 通用使用模式\n\n大部分数据展示组件遵循相同模式：\n\n```vue\n<component :data=\"data\" :option=\"option\">\n  <!-- 可选插槽自定义 -->\n</component>\n```\n\n各组件的具体配置请参考对应的示例页面：\n- `data/data0` ~ `data/data12` - 基础数据组件示例\n- `data/countdown` - 倒计时\n- `data/dashboard` - 仪表盘\n- `data/list` - 数据列表\n- `data/notice` - 公告\n- `data/product` - 商品卡片\n- `data/profile` - 人员档案\n- `data/rank` - 排行榜\n- `data/statistic` - 统计值\n- `data/task` - 任务列表\n- `data/weather` - 天气卡片\n",".claude/skills/avue-design/references/form-advanced.md":"# avue-form 高级功能参考\n\n## 目录\n1. [字典配置](#字典配置)\n2. [验证规则](#验证规则)\n3. [动态子表单](#动态子表单)\n4. [文件上传](#文件上传)\n5. [字段联动](#字段联动)\n6. [数据格式](#数据格式)\n7. [自定义插槽](#自定义插槽)\n8. [Mock数据](#mock数据)\n\n---\n\n## 字典配置\n\n### 安装配置\n\n字典组件需要 axios：\n```javascript\nimport axios from 'axios'\napp.use(Avue, { axios })\n```\n\n### 本地字典\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n}\n```\n\n3.5.0+ 支持函数和 Promise：\n```javascript\n{\n  dicData: () => {\n    return [{ label: 'A', value: 1 }]\n  },\n  // 或\n  dicData: async () => {\n    const res = await fetchOptions()\n    return res.data\n  },\n}\n```\n\n### 远程字典\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'select',\n  dicUrl: '/api/dept/list',\n  dicMethod: 'GET',      // 请求方法，默认GET\n  dicQuery: { status: 1 }, // 请求参数\n  dicHeaders: {},          // 请求头\n  props: {\n    label: 'name',\n    value: 'id',\n    children: 'children',\n    res: 'data.records',  // 响应数据层级\n  },\n}\n```\n\n### 字典格式化\n\n```javascript\n{\n  dicUrl: '/api/dict/list',\n  dicFormatter: (res) => {\n    // 自定义处理响应数据\n    return res.data.map(item => ({\n      label: item.dictValue,\n      value: item.dictKey,\n    }))\n  },\n}\n```\n\n### 字典联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city', 'district'],  // 指定子级字段\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}',  // {{key}} = 父级选中值\n    cascader: ['district'],\n  },\n  {\n    label: '区县',\n    prop: 'district',\n    type: 'select',\n    dicUrl: '/api/district?city={{key}}',\n  },\n]\n```\n\n### 动态更新字典\n\n```javascript\n// 更新指定字段字典\nformRef.value.updateDic('type', [\n  { label: '新选项A', value: 'a' },\n  { label: '新选项B', value: 'b' },\n])\n\n// 重新加载所有字典\nformRef.value.dicInit()\n```\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 值存储为字符串\n  separator: ',',         // 分隔符，默认逗号\n  // 实际存储: \"1,2,3\"\n  // 组件显示: [1, 2, 3]\n}\n\n{\n  label: '年龄',\n  prop: 'age',\n  dataType: 'number',    // 强制转数字\n}\n```\n\n---\n\n## 验证规则\n\n基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n### 基本验证\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  rules: [\n    { required: true, message: '请输入名称', trigger: 'blur' },\n    { min: 2, max: 20, message: '长度在2-20之间', trigger: 'blur' },\n  ],\n}\n```\n\n### 类型验证\n\n```javascript\nrules: [\n  { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n  { type: 'url', message: '请输入正确的URL', trigger: 'blur' },\n  { type: 'number', message: '请输入数字', trigger: 'blur' },\n]\n```\n\n### 自定义验证\n\n```javascript\n{\n  label: '密码',\n  prop: 'password',\n  rules: [{\n    required: true,\n    validator: (rule, value, callback) => {\n      if (!value) {\n        callback(new Error('请输入密码'))\n      } else if (value.length < 6) {\n        callback(new Error('密码长度不能少于6位'))\n      } else {\n        callback()\n      }\n    },\n    trigger: 'blur',\n  }],\n}\n```\n\n### 表单验证方法\n\n```javascript\n// 验证整个表单\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败\n    console.log('错误字段:', fields)\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'email'], (errorMsg) => {\n  if (!errorMsg) console.log('验证通过')\n})\n\n// 清除验证\nformRef.value.clearValidate()            // 清除所有\nformRef.value.clearValidate(['name'])    // 清除指定字段\n\n// 重置表单\nformRef.value.resetFields()\n```\n\n### 搜索验证 (CRUD)\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  searchRules: [{ required: true, message: '请选择日期', trigger: 'change' }],\n}\n```\n\n---\n\n## 动态子表单\n\n### 表格模式\n\n```javascript\n{\n  label: '订单明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    index: true,\n    column: [\n      {\n        label: '商品名称',\n        prop: 'productName',\n        rules: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],\n      },\n      {\n        label: '数量',\n        prop: 'quantity',\n        type: 'number',\n        min: 1,\n      },\n      {\n        label: '单价',\n        prop: 'price',\n        type: 'number',\n        precision: 2,\n      },\n    ],\n  },\n}\n```\n\n### 表单模式\n\n```javascript\n{\n  label: '联系方式',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  limit: 5,  // 最多5条\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 8 },\n      { label: '电话', prop: 'phone', span: 8 },\n      { label: '职位', prop: 'position', span: 8 },\n    ],\n  },\n}\n```\n\n### 自定义增删\n\n```javascript\n{\n  type: 'dynamic',\n  rowAdd: (done) => {\n    // 自定义新增逻辑\n    done({ name: '默认值', quantity: 1 })\n  },\n  rowDel: (row, done) => {\n    ElMessageBox.confirm('确定删除?').then(() => done())\n  },\n}\n```\n\n### 父子联动\n\n动态子表单支持与父表单字段联动。\n\n---\n\n## 文件上传\n\n### 基本上传\n\n```javascript\n{\n  label: '附件',\n  prop: 'file',\n  type: 'upload',\n  action: '/api/upload',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n    fileName: 'file',\n  },\n}\n```\n\n### 上传回调\n\n```vue\n<avue-form\n  :upload-before=\"uploadBefore\"\n  :upload-after=\"uploadAfter\"\n  :upload-delete=\"uploadDelete\"\n  :upload-preview=\"uploadPreview\"\n  :upload-error=\"uploadError\"\n  :upload-exceed=\"uploadExceed\"\n>\n\nconst uploadBefore = (file, done, loading) => {\n  // 验证文件\n  if (file.size > 10 * 1024 * 1024) {\n    ElMessage.error('文件不能超过10MB')\n    loading()  // 中断上传\n    return\n  }\n  done(file)  // 继续上传\n}\n\nconst uploadAfter = (res, done) => {\n  // 上传完成\n  done()\n}\n\nconst uploadDelete = (file, column) => {\n  // 返回 false 或 rejected Promise 阻止删除\n  return new Promise((resolve, reject) => {\n    ElMessageBox.confirm('确定删除?')\n      .then(() => resolve())\n      .catch(() => reject())\n  })\n}\n\nconst uploadPreview = (file, column, done) => {\n  // 自定义预览\n  window.open(file.url)\n}\n\nconst uploadError = (error, column) => {\n  ElMessage.error('上传失败')\n}\n\nconst uploadExceed = (limit, files, fileList, column) => {\n  ElMessage.warning(`最多上传${limit}个文件`)\n}\n```\n\n### 图片水印\n\n```javascript\n{\n  type: 'upload',\n  canvasOption: {\n    text: '公司名称',\n    fontFamily: 'microsoft yahei',\n    color: 'rgba(0,0,0,0.3)',\n    fontSize: 20,\n    opacity: 50,\n    bottom: 20,\n    right: 20,\n    ratio: 0.8,\n  },\n}\n```\n\n### 云存储上传\n\n全局配置见 `global-config.md`。\n\n```javascript\n// 阿里云OSS\n{ type: 'upload', oss: 'ali' }\n\n// 七牛云\n{ type: 'upload', oss: 'qiniu' }\n\n// 腾讯COS\n{ type: 'upload', oss: 'cos' }\n```\n\n### 数据格式\n\n```javascript\n// 字符串格式（逗号分隔URL）\n{ type: 'upload', dataType: 'string' }\n\n// 对象格式\n{\n  type: 'upload',\n  dataType: 'object',\n  props: { label: 'name', value: 'url' },\n}\n\n// JSON字符串格式\n{ type: 'upload', dataType: 'json' }\n```\n\n---\n\n## 字段联动\n\n### control 属性\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'radio',\n  dicData: [\n    { label: '个人', value: 1 },\n    { label: '企业', value: 2 },\n  ],\n  control: (val, form) => {\n    if (val === 1) {\n      return {\n        idCard: { display: true, rules: [{ required: true }] },\n        companyName: { display: false },\n        businessLicense: { display: false },\n      }\n    } else {\n      return {\n        idCard: { display: false },\n        companyName: { display: true, rules: [{ required: true }] },\n        businessLicense: { display: true },\n      }\n    }\n  },\n},\n{ label: '身份证', prop: 'idCard' },\n{ label: '企业名称', prop: 'companyName', display: false },\n{ label: '营业执照', prop: 'businessLicense', type: 'upload', display: false },\n```\n\n### control 返回值可控制的属性\n\n可以动态控制字段的以下属性：\n- `display` - 显示/隐藏\n- `disabled` - 启用/禁用\n- `rules` - 验证规则\n- `dicData` - 字典数据\n- `label` - 标签文字\n- 其他 column 属性\n\n### Promise 支持 (3.5.0+)\n\n```javascript\ncontrol: async (val, form) => {\n  const res = await getSubOptions(val)\n  return {\n    subType: {\n      dicData: res.data,\n      display: true,\n    },\n  }\n}\n```\n\n### 获取组件引用\n\n```javascript\nconst ref = formRef.value.getPropRef('tableProp')\n// 可用于操作 input-table 等复杂组件\n```\n\n---\n\n## 数据格式\n\n### 深层数据绑定\n\n```javascript\n// 数据: { id: 1, info: { name: '张三', address: { city: '北京' } } }\ncolumn: [\n  {\n    label: '姓名',\n    prop: 'name',\n    bind: 'info.name',\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    bind: 'info.address.city',\n  },\n]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤 $xxx 字典翻译字段\n  filterNull: true,  // 过滤空值字段\n  filterParams: ['id'], // 不被清空的字段（默认包含 rowKey）\n}\n```\n\n---\n\n## 自定义插槽\n\n### 字段内容插槽\n\n```vue\n<avue-form v-model=\"form\" :option=\"option\">\n  <!-- 自定义字段 -->\n  <template #name=\"{ value, column, dic, size, disabled }\">\n    <el-input v-model=\"form.name\" :disabled=\"disabled\" />\n  </template>\n\n  <!-- 自定义标签 -->\n  <template #name-label=\"{ column }\">\n    <span style=\"color: red\">*</span> {{ column.label }}\n  </template>\n\n  <!-- 自定义错误 -->\n  <template #name-error=\"{ error }\">\n    <span style=\"color: orange\">{{ error }}</span>\n  </template>\n\n  <!-- 自定义描述 (3.6.4+) -->\n  <template #name-desc=\"{ column }\">\n    <el-text type=\"info\">请输入真实姓名</el-text>\n  </template>\n\n  <!-- 自定义按钮 -->\n  <template #menu-form>\n    <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n    <el-button @click=\"handleDraft\">保存草稿</el-button>\n  </template>\n</avue-form>\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-switch', {\n      modelValue: form.value.status,\n      'onUpdate:modelValue': (val) => { form.value.status = val },\n    })\n  },\n}\n```\n\n### 第三方组件集成\n\n```vue\n<template #editor=\"{ value, column }\">\n  <vue-editor v-model=\"form.content\" />\n</template>\n```\n\n### className 样式控制\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  className: 'highlight-field',\n}\n```\n\n```css\n.highlight-field {\n  background: #fef0f0;\n  border-left: 3px solid #f56c6c;\n}\n```\n\n---\n\n## Mock数据\n\n需引入 Mock.js：\n```html\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n\n```javascript\noption: {\n  mockBtn: true,  // 显示Mock按钮\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      mock: { type: 'cname' },  // 中文姓名\n    },\n    {\n      label: '邮箱',\n      prop: 'email',\n      mock: { type: 'email' },\n    },\n    {\n      label: '地址',\n      prop: 'address',\n      mock: { type: 'county', prefix: true },\n    },\n  ],\n}\n\n// 或整体 mock 函数\noption: {\n  mock: () => ({\n    name: '张三',\n    email: 'zhangsan@test.com',\n    address: '北京市朝阳区',\n  }),\n}\n```\n",".claude/skills/avue-design/references/form-core.md":"# avue-form 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表单级属性](#option-表单级属性)\n3. [布局配置](#布局配置)\n4. [按钮配置](#按钮配置)\n5. [Column 通用属性](#column-通用属性)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n---\n\n## 组件属性\n\n在 `<avue-form>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| option | object | - | 表单配置项 |\n| upload-before | function(file, done, loading) | - | 上传前回调 |\n| upload-after | function(res, done) | - | 上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 预览文件回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小 (3.4.4+) |\n| v-model:status | boolean | false | 表单提交状态（禁用状态双向绑定） |\n\n---\n\n## Option 表单级属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 字段配置数组 |\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置: left/right/top |\n| labelSuffix | string | ':' | 标签后缀 |\n| enter | boolean | false | 回车提交 |\n| group | array | - | 分组配置 |\n| tabs | boolean | false | 分组标签页显示 |\n| detail | boolean | false | 详情/只读模式 |\n| disabled | boolean | false | 全部禁用 |\n| readonly | boolean | false | 全部只读 |\n| formWidth | string | - | 表单容器宽度 |\n| tabsActive | number | 0 | 默认激活标签页索引 |\n| tabsType | string | - | 标签页类型(同 el-tabs type) |\n| tabsVerifyAll | boolean | false | 提交时验证所有标签页（默认仅当前页） |\n| filterDic | boolean | false | 过滤 $ 开头的字典翻译字段 |\n| filterNull | boolean | false | 过滤空值字段 |\n| filterParams | array | - | 重置时保留的字段 |\n\n---\n\n## 布局配置\n\n### 栅格布局\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 字段占用栅格数 (24栅格) |\n| gutter | number | 0 | 栅格间距 |\n| offset | number | 0 | 栅格左偏移 |\n| push | number | 0 | 栅格右移动 |\n| pull | number | 0 | 栅格左移动 |\n| row | boolean | false | 是否新起一行 |\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', span: 12 },\n  { label: '编码', prop: 'code', span: 12 },\n  { label: '备注', prop: 'remark', span: 24, row: true },\n]\n```\n\n### 分组显示\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name', span: 12 },\n        { label: '编码', prop: 'code', span: 12 },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', span: 24 },\n      ],\n    },\n  ],\n}\n```\n\n#### Group 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 分组标题 |\n| prop | string | - | 分组标识 |\n| icon | string | - | 分组图标 |\n| collapse | boolean | true | 是否可折叠 |\n| arrow | boolean | true | 是否显示折叠箭头 |\n| display | boolean | true | 是否显示该分组 |\n| header | boolean | true | 是否显示分组头部 |\n| column | array | - | 分组内的字段配置 |\n\n### 标签页显示\n\n```javascript\noption: {\n  tabs: true,\n  // tabsVerifyAll: true, // 提交时验证所有标签页\n  group: [\n    {\n      label: '基本信息',\n      prop: 'tab1',\n      column: [...],\n    },\n    {\n      label: '扩展信息',\n      prop: 'tab2',\n      column: [...],\n    },\n  ],\n}\n```\n\n### 帮助文字\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    tip: '请输入唯一标识名称',      // 字段右侧帮助文字\n    tipPlacement: 'bottom',          // 帮助文字方向\n    labelTip: '该字段用于系统唯一标识', // 标签帮助文字\n    labelTipPlacement: 'bottom',\n  },\n]\n```\n\n### className 样式\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    className: 'custom-field-class',\n  },\n]\n```\n\n---\n\n## 按钮配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menuBtn | boolean | true | 显示按钮区 |\n| menuSpan | number | 24 | 按钮栅格数 |\n| menuPosition | string | 'center' | 按钮位置: left/center/right |\n| submitBtn | boolean | true | 显示提交按钮 |\n| submitText | string | '提交' | 提交按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyText | string | '清空' | 清空按钮文字 |\n| printBtn | boolean | false | 显示打印按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮（需引入Mock.js） |\n| mockText | string | 'Mock' | Mock按钮文字 |\n| printText | string | '打印' | 打印按钮文字 |\n| submitIcon | string | - | 提交按钮图标 |\n| emptyIcon | string | - | 清空按钮图标 |\n\n---\n\n## Column 通用属性\n\n每个字段的通用配置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 标签文字 |\n| prop | string | - | 字段属性名 |\n| type | string | 'input' | 字段类型 |\n| value | any | - | 默认值 |\n| bind | string | - | 深层数据绑定 |\n| disabled | boolean | false | 禁用 |\n| display | boolean | true | 显示 |\n| order | number | - | 排序，数字越大越靠前 |\n| size | string | 'small' | 组件尺寸 |\n| separator | string | ',' | 选项分隔符 (string dataType) |\n| rules | array | - | 验证规则 |\n\n### 栅格属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 栅格数 |\n| gutter | number | 0 | 间距 |\n| offset | number | 0 | 偏移 |\n| push | number | 0 | 右移 |\n| pull | number | 0 | 左移 |\n| row | boolean | false | 换行 |\n\n### 标签属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 内容帮助方向 |\n| labelTip | string | - | 标签帮助文字 |\n| labelTipPlacement | string | 'top' | 标签帮助方向 |\n\n### 联动控制\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [{ label: 'A', value: 1 }, { label: 'B', value: 2 }],\n  control: (val, form) => {\n    return {\n      subField: {\n        display: val === 1,\n        disabled: val === 2,\n        dicData: val === 1 ? listA : listB,\n      }\n    }\n  },\n}\n```\n\n### 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-tag', {}, '自定义内容')\n  },\n}\n```\n\n---\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| submit | (form, done) | 表单提交回调。done() 关闭loading |\n| reset-change | - | 表单清空回调 |\n| change | (form) | 表单数据变化时触发 |\n| tab-click | (tab, event) | 标签页点击时触发 |\n| error | (error) | 表单验证失败时触发 |\n| mock-change | (form) | Mock 数据生成后触发 |\n\n```javascript\n// Composition API\nconst handleSubmit = (form, done) => {\n  saveData(form).then(() => {\n    ElMessage.success('保存成功')\n    done()  // 关闭loading\n  }).catch(() => {\n    done()\n  })\n}\n\nconst handleReset = () => {\n  // 清空后的逻辑\n}\n```\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| submit | - | 触发表单提交 |\n| resetForm | - | 重置表单 |\n| updateDic | (prop, dic) | 更新指定字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | callback(boolean, object) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置表单到初始值 |\n| clearValidate | (props) | 清除验证结果 |\n| show | - | 禁用表单（显示loading状态） |\n| hide | - | 启用表单（隐藏loading状态） |\n| scrollToField | (prop) | 滚动到指定字段 |\n| getField | (prop) | 获取字段实例 |\n\n```javascript\n// 手动验证\nconst formRef = ref(null)\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败，fields 包含错误信息\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'code'], (errorMsg) => {\n  if (!errorMsg) {\n    // 验证通过\n  }\n})\n```\n\n---\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义字段内容 |\n| `{prop}-label` | 自定义字段标签 |\n| `{prop}-error` | 自定义错误提示 |\n| `{prop}-desc` | 自定义描述 (3.6.4+) |\n| menu-form | 自定义按钮区域 |\n| menu-form-before | 按钮区域前内容 (3.4.3+) |\n\n```vue\n<!-- 自定义字段 -->\n<template #name=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"formData.name\" />\n</template>\n\n<!-- 自定义标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义按钮 -->\n<template #menu-form>\n  <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n  <el-button @click=\"handleCancel\">取消</el-button>\n</template>\n```\n\n---\n\n## 独立表单使用示例\n\n### Composition API\n\n```vue\n<template>\n  <avue-form ref=\"formRef\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script setup>\nimport { ref, reactive } from 'vue'\nimport { ElMessage } from 'element-plus'\n\nconst formRef = ref(null)\nconst formData = ref({})\n\nconst option = reactive({\n  labelWidth: 120,\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      span: 12,\n      rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n    },\n    {\n      label: '性别',\n      prop: 'gender',\n      type: 'radio',\n      span: 12,\n      dicData: [\n        { label: '男', value: 1 },\n        { label: '女', value: 2 },\n      ],\n    },\n  ],\n})\n\nconst handleSubmit = (form, done) => {\n  console.log('提交数据:', form)\n  done()\n}\n</script>\n```\n\n### Options API\n\n```vue\n<template>\n  <avue-form ref=\"form\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      formData: {},\n      option: {\n        labelWidth: 120,\n        column: [\n          {\n            label: '姓名',\n            prop: 'name',\n            span: 12,\n            rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n          },\n          {\n            label: '性别',\n            prop: 'gender',\n            type: 'radio',\n            span: 12,\n            dicData: [\n              { label: '男', value: 1 },\n              { label: '女', value: 2 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  methods: {\n    handleSubmit(form, done) {\n      console.log('提交数据:', form)\n      done()\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 数据类型与过滤\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 存储为逗号分隔字符串\n  separator: ',',         // 分隔符，默认逗号\n}\n// 值: \"1,2,3\" 而不是 [1, 2, 3]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,  // 过滤 $ 开头的字典翻译字段\n  filterNull: true, // 过滤空值字段\n}\n```\n",".claude/skills/avue-design/references/form-field-types.md":"# avue-form 字段类型完整参考\n\n本文档列出 avue-form / avue-crud 中所有可用的字段类型及其完整配置属性。\n\n## 目录\n\n1. [input - 文本输入](#input---文本输入)\n2. [number - 数字输入](#number---数字输入)\n3. [select - 下拉选择](#select---下拉选择)\n4. [cascader - 级联选择](#cascader---级联选择)\n5. [checkbox - 多选框](#checkbox---多选框)\n6. [radio - 单选框](#radio---单选框)\n7. [date - 日期选择](#date---日期选择)\n8. [time - 时间选择](#time---时间选择)\n9. [switch - 开关](#switch---开关)\n10. [upload - 文件上传](#upload---文件上传)\n11. [tree - 树选择](#tree---树选择)\n12. [input-tag - 标签输入](#input-tag---标签输入)\n13. [mention - 提及](#mention---提及)\n14. [input-table - 表格选择](#input-table---表格选择)\n15. [color - 颜色选择](#color---颜色选择)\n16. [icon - 图标选择](#icon---图标选择)\n17. [map - 地图选择](#map---地图选择)\n18. [rate - 评分](#rate---评分)\n19. [slider - 滑块](#slider---滑块)\n20. [array/img/url - 数组输入](#arrayimgurl---数组输入)\n21. [dynamic - 动态子表单](#dynamic---动态子表单)\n22. [title - 标题分隔](#title---标题分隔)\n23. [input-cron - Cron表达式](#input-cron---cron表达式)\n\n---\n\n## 通用属性（所有类型共享）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '请输入/请选择'+label | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| readonly | boolean | false | 只读 |\n| disabled | boolean | false | 禁用 |\n| change | function({column, value}) | - | 值变化事件 |\n| click | function({column, value}) | - | 点击事件 |\n| focus | function({column, value}) | - | 聚焦事件 |\n| blur | function({column, value}) | - | 失焦事件 |\n\n---\n\n## input - 文本输入\n\n`type: 'input'`（默认类型，可省略）\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| showWordLimit | boolean | false | 显示字数统计 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prependClick | function | - | 前置文字点击 |\n| appendClick | function | - | 后置文字点击 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| showPassword | boolean | true | 密码切换图标 |\n| autocomplete | string | 'off' | HTML autocomplete 属性 |\n| formatters | function | - | 格式化函数 (3.7.0+) |\n| parser | function | - | 解析函数（formatters 的逆操作）(3.7.0+) |\n| target | string | '_blank' | 链接目标 (url类型时有效) |\n\n### textarea 子类型\n\n```javascript\n{ type: 'textarea', rows: 4, minRows: 2, maxRows: 8 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rows | number | 2 | 文本域行数 |\n| minRows | number | 2 | 最小行数 |\n| maxRows | number | 6 | 最大行数 |\n\n### password 子类型\n\n```javascript\n{ type: 'password', showPassword: true }\n```\n\n### phone 子类型\n\n```javascript\n{ type: 'phone', defaultPhoneCode: '+86' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultPhoneCode | string | '+86' | 默认区号 |\n| phoneCodeOptions | array | - | 区号选项 |\n| phoneMaxLength | number | 11 | 手机号最大长度 |\n| phoneCodePlaceholder | string | '区号' | 区号占位 |\n| phonePlaceholder | string | '请输入手机号' | 手机号占位 |\n\n### currency 子类型\n\n```javascript\n{ type: 'currency', currency: 'CNY', currencyPrecision: 2 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currency | string | 'CNY' | 货币类型 |\n| currencySymbolCustom | string | - | 自定义货币符号 |\n| currencyUnit | string | - | 货币单位 |\n| currencyPrecision | number | 2 | 小数位数 |\n| currencyPlaceholder | string | '请输入金额' | 占位 |\n| currencyMin | number | - | 最小值 |\n| currencyMax | number | - | 最大值 |\n\n### bankCard 子类型\n\n```javascript\n{ type: 'bankCard', showBankCardType: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| bankCardSeparator | string | ' ' | 分隔符 |\n| bankCardSegment | number | 4 | 分段长度 |\n| bankCardMaxDigits | number | 19 | 最大长度 |\n| bankCardPlaceholder | string | '请输入银行卡号' | 占位 |\n| showBankCardType | boolean | true | 显示卡类型 |\n\n### idCard 子类型\n\n```javascript\n{ type: 'idCard', showIdCardInfo: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| idCardPlaceholder | string | '请输入身份证号' | 占位 |\n| showIdCardInfo | boolean | true | 显示身份信息 |\n| idCardRealtimeValidate | boolean | true | 实时验证 |\n\n### email 子类型\n\n```javascript\n{ type: 'email', showEmailSuffix: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| emailPlaceholder | string | '请输入邮箱' | 占位 |\n| emailSuffixes | array | ['@qq.com','@163.com',...] | 邮箱后缀建议 |\n| showEmailSuffix | boolean | true | 显示建议 |\n\n### code 子类型\n\n```javascript\n{ type: 'code', codeLength: 6 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| codePlaceholder | string | - | 占位 |\n| codeLength | number | 6 | 验证码长度 |\n| codeSeparator | string | - | 分隔符 |\n\n### plate 子类型（车牌号）\n\n```javascript\n{ type: 'plate', defaultPlateProvince: '京' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| platePlaceholder | string | '请输入车牌号' | 占位 |\n| defaultPlateProvince | string | '京' | 默认省份 |\n| plateProvinceList | array | ['京','津','沪',...] | 省份列表 |\n\n### ip 子类型\n\n```javascript\n{ type: 'ip', ipVersion: 4 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| ipPlaceholder | string | '请输入IP地址' | 占位 |\n| ipVersion | number | 4 | IP版本 |\n\n### mac 子类型\n\n```javascript\n{ type: 'mac', macSeparator: ':' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| macPlaceholder | string | '请输入MAC地址' | 占位 |\n| macSeparator | string | ':' | 分隔符 |\n\n### uscc 子类型（统一社会信用代码）\n\n```javascript\n{ type: 'uscc' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| usccPlaceholder | string | '请输入统一社会信用代码' | 占位 |\n| usccRealtimeValidate | boolean | true | 实时验证 |\n\n### search 子类型\n\n```javascript\n{ type: 'search' }\n```\n\n带搜索按钮的输入框，点击搜索按钮触发事件。属性同基础 input。\n\n---\n\n## number - 数字输入\n\n`type: 'number'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | -Infinity | 最小值 |\n| max | number | Infinity | 最大值 |\n| step | number | 1 | 步长 |\n| stepStrictly | boolean | false | 只能输入步长倍数 |\n| precision | number | 2 | 小数精度 |\n| controls | boolean | true | 显示控制按钮 |\n| controlsPosition | string | 'top' | 按钮位置: top/right |\n| suffix | string | - | 后缀文字 (3.5.6+) |\n| prefix | string | - | 前缀文字 (3.5.6+) |\n| suffixClick | function | - | 后缀点击 (3.5.6+) |\n| prefixClick | function | - | 前缀点击 (3.5.6+) |\n| align | string | 'left' | 文本对齐 (3.7.2+) |\n| disabledScientific | boolean | false | 禁用科学计数法 (3.7.2+) |\n\n```javascript\n{\n  label: '价格',\n  prop: 'price',\n  type: 'number',\n  min: 0,\n  max: 99999,\n  step: 0.01,\n  precision: 2,\n  controlsPosition: 'right',\n}\n```\n\n---\n\n## select - 下拉选择\n\n`type: 'select'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| limit | number | - | 多选最大数量 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠标签tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| allowCreate | boolean | false | 允许创建新条目 |\n| filterable | boolean | false | 可搜索 |\n| remote | boolean | false | 远程搜索 |\n| defaultFirstOption | boolean | false | 回车选中第一个 |\n| loadingText | string | '加载中' | 加载文字 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n| virtualize | boolean | false | 虚拟DOM (大数据) |\n| group | boolean | false | 分组显示 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| cascader | array | - | 级联子字段prop |\n| cascaderIndex | number | - | 级联默认选项索引 |\n| emptyValues | array | - | 空值配置 (3.4.4+) |\n| valueOnClear | any | - | 清空时的值 (3.4.4+) |\n| removeTag | function({value, column}) | - | 移除标签事件 (3.7.3+) |\n| all | boolean | false | 显示\"全选\"选项 |\n| noMatchText | string | '无匹配数据' | 无匹配时文字 |\n| noDataText | string | '无数据' | 无数据时文字 |\n\n```javascript\n// 基本用法\n{\n  label: '状态',\n  prop: 'status',\n  type: 'select',\n  dicData: [\n    { label: '启用', value: 1 },\n    { label: '禁用', value: 0 },\n  ],\n}\n\n// 远程搜索\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  remote: true,\n  filterable: true,\n  dicUrl: '/api/user/search?name={{key}}',\n  props: { label: 'name', value: 'id' },\n}\n\n// 自定义模板\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  slot: true,  // 使用 userId + Type 插槽\n}\n\n// 分组\n{\n  label: '城市',\n  prop: 'city',\n  type: 'select',\n  group: true,\n  dicData: [\n    {\n      label: '热门城市',\n      children: [\n        { label: '北京', value: 'bj' },\n        { label: '上海', value: 'sh' },\n      ],\n    },\n  ],\n}\n```\n\n---\n\n## cascader - 级联选择\n\n`type: 'cascader'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAllLevels | boolean | true | 显示完整路径 |\n| tags | boolean | false | 标签显示 |\n| separator | string | '/' | 分隔符 |\n| filterable | boolean | false | 可搜索 |\n| filterMethod | function | - | 自定义搜索逻辑 |\n| expandTrigger | string | 'hover' | 展开触发方式: hover/click |\n| multiple | boolean | false | 多选 |\n| collapseTags | boolean | false | 折叠标签 |\n| collapseTagsTooltip | boolean | false | 折叠tooltip |\n| maxCollapseTags | number | - | 最大显示标签数 |\n| checkStrictly | boolean | false | 父子节点独立 |\n| emitPath | boolean | true | 返回完整路径数组 |\n| lazy | boolean | false | 懒加载 |\n| lazyLoad | function(node, resolve) | - | 懒加载函数 |\n| effect | string | 'dark' | tooltip主题 (3.7.2+) |\n| showCheckedStrategy | string | 'SHOW_ALL' | 选中项回填策略 (3.7.2+) |\n| popperClass | string | - | 下拉框class |\n\n```javascript\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  dicData: [\n    {\n      label: '北京',\n      value: 'bj',\n      children: [\n        { label: '朝阳区', value: 'cy' },\n        { label: '海淀区', value: 'hd' },\n      ],\n    },\n  ],\n}\n\n// 懒加载\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  lazy: true,\n  lazyLoad: (node, resolve) => {\n    const { level, value } = node\n    getAreaChildren(value, level).then(res => {\n      resolve(res.data.map(item => ({\n        label: item.name,\n        value: item.id,\n        leaf: level >= 2,  // 标记叶子节点\n      })))\n    })\n  },\n}\n```\n\n---\n\n## checkbox - 多选框\n\n`type: 'checkbox'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| all | boolean | false | 全选按钮 |\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n| min | number | - | 最少选择数 |\n| max | number | - | 最多选择数 |\n| fill | string | '#409eff' | 选中颜色 |\n| textColor | string | '#ffffff' | 选中文字颜色 |\n\n```javascript\n{\n  label: '爱好',\n  prop: 'hobbies',\n  type: 'checkbox',\n  dicData: [\n    { label: '读书', value: 1 },\n    { label: '运动', value: 2 },\n    { label: '旅行', value: 3 },\n  ],\n  all: true,\n  min: 1,\n  max: 3,\n}\n```\n\n---\n\n## radio - 单选框\n\n`type: 'radio'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n\n```javascript\n{\n  label: '性别',\n  prop: 'gender',\n  type: 'radio',\n  dicData: [\n    { label: '男', value: 1 },\n    { label: '女', value: 2 },\n  ],\n}\n```\n\n---\n\n## date - 日期选择\n\n`type: 'date'`\n\n可用 type 值：`date` | `datetime` | `daterange` | `datetimerange` | `week` | `month` | `year` | `dates` | `monthrange` | `yearrange`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultTime | string[] | - | 范围默认时间 |\n| defaultValue | Date | - | 默认日期 |\n| pickerOptions | object | - | 日期选项 |\n| popperClass | string | - | 下拉框class |\n| prefix-icon | string | - | 前缀图标 |\n| cellClassName | function | - | 自定义单元格class |\n| showNow | boolean | true | 显示\"此刻\"按钮 (3.6.0+) |\n| disabledHours | function | - | 禁用小时 (3.8.2+) |\n| disabledMinutes | function | - | 禁用分钟 (3.8.2+) |\n| disabledSeconds | function | - | 禁用秒 (3.8.2+) |\n| disabledDate | function(date) | - | 禁用日期判断函数 |\n| shortcuts | array/function | - | 快捷日期选项 |\n| editable | boolean | true | 输入框可编辑 |\n| unlinkPanels | boolean | false | 范围选择时取消面板联动 |\n\n**日期格式表：**\n\n| 格式 | 含义 | 示例 |\n|-----|------|------|\n| YYYY | 年 | 2024 |\n| MM | 月 | 01 |\n| DD | 日 | 02 |\n| HH | 时(24h) | 03 |\n| mm | 分 | 04 |\n| ss | 秒 | 05 |\n| X | JS时间戳 | 1483326245000 |\n\n```javascript\n// 日期\n{ type: 'date', format: 'YYYY-MM-DD', valueFormat: 'YYYY-MM-DD' }\n\n// 日期时间\n{ type: 'datetime', format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'YYYY-MM-DD HH:mm:ss' }\n\n// 日期范围\n{ type: 'daterange', startPlaceholder: '开始日期', endPlaceholder: '结束日期' }\n\n// 带默认时间的范围\n{\n  type: 'datetimerange',\n  defaultTime: ['00:00:00', '23:59:59'],\n}\n```\n\n---\n\n## time - 时间选择\n\n`type: 'time'` | `'timerange'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultValue | Date | - | 默认值 |\n| pickerOptions | object | - | 时间选项 |\n| popperClass | string | - | 下拉框class |\n| arrowControl | boolean | false | 箭头控制时间 |\n| editable | boolean | true | 输入框可编辑 |\n\n```javascript\n// 基本时间选择\n{ type: 'time', format: 'HH:mm:ss', valueFormat: 'HH:mm:ss' }\n\n// 固定时间点\n{\n  type: 'time',\n  pickerOptions: {\n    start: '08:00',\n    end: '18:00',\n    step: '00:30',\n  },\n}\n```\n\n> **TimeSelect 模式**：当配置了 `start`、`end`、`step`、`maxTime`、`minTime` 中的任一属性时，组件自动切换为 TimeSelect（固定时间点选择）模式。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | string | - | 开始时间 (TimeSelect) |\n| end | string | - | 结束时间 (TimeSelect) |\n| step | string | - | 时间步长 (TimeSelect) |\n| maxTime | string | - | 最大可选时间 (TimeSelect) |\n| minTime | string | - | 最小可选时间 (TimeSelect) |\n\n```javascript\n// TimeSelect 固定时间点选择\n{\n  type: 'time',\n  start: '08:00',\n  end: '18:00',\n  step: '00:30',\n  maxTime: '17:30',\n}\n```\n\n---\n\n## switch - 开关\n\n`type: 'switch'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| len | number | 40 | 开关宽度 |\n| activeColor | string | '#409EFF' | 打开背景色 |\n| inactiveColor | string | '#C0CCDA' | 关闭背景色 |\n| activeIcon | string | - | 打开图标 (3.2.20+) |\n| inactiveIcon | string | - | 关闭图标 (3.2.20+) |\n| activeActionIcon | string | - | 打开动作图标 (3.2.20+) |\n| inactiveActionIcon | string | - | 关闭动作图标 (3.2.20+) |\n| inlinePrompt | boolean | false | 只显示首字符 (3.2.20+) |\n| beforeChange | function(done) | - | 切换前钩子 (3.2.20+) |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  type: 'switch',\n  dicData: [\n    { label: '关闭', value: 0 },\n    { label: '开启', value: 1 },\n  ],\n}\n```\n\n---\n\n## upload - 文件上传\n\n`type: 'upload'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| action | string | **必填** | 上传地址 |\n| accept | string/array | - | 文件类型限制 |\n| fileSize | number | - | 文件大小限制(KB) |\n| limit | number | - | 最大文件数 |\n| multiple | boolean | false | 多文件上传 |\n| directory | boolean | false | 文件夹上传 (3.8.0+) |\n| listType | string | 'text' | 文件列表类型: text/picture/picture-card |\n| showFileList | boolean | true | 显示文件列表 |\n| fileType | string | 'img' | 文件类型: img/audio/video |\n| fileText | string | '点击上传' | 上传按钮文字 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| data | object | - | 额外参数 |\n| headers | object | - | 请求头 |\n| httpRequest | function(file, column) | - | 自定义上传逻辑 |\n| canvasOption | object | - | 图片水印配置 |\n| propsHttp | object | - | 响应结构配置 |\n| dataType | string | - | 数据类型: string/object/json |\n| loadText | string | - | 加载文字 |\n| tip | string | - | 提示文字 |\n| dragFile | boolean | false | 拖拽上传区域（区别于 drag 拖拽排序）|\n| oss | string | - | 云存储类型: ali/qiniu/cos |\n| paramsList | array | - | 额外参数列表 |\n| cropperOption | object | - | 图片裁剪配置 |\n\n### propsHttp 响应配置\n\n| 属性 | 类型 | 说明 |\n|-----|------|------|\n| home | string | 图片根路径 |\n| res | string | 响应层级 |\n| url | string | 图片地址属性 |\n| name | string | 图片名称属性 |\n| fileName | string | 上传文件名（默认'file'） |\n\n### canvasOption 水印配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | 'avuejs.com' | 水印文字 |\n| fontFamily | string | 'microsoft yahei' | 字体 |\n| color | string | '#999' | 颜色 |\n| fontSize | number | 16 | 字号 |\n| opacity | number | 100 | 透明度 |\n| bottom | number | 10 | 底部距离 |\n| right | number | 10 | 右侧距离 |\n| ratio | number | 1 | 压缩比(0-1) |\n\n```javascript\n{\n  label: '附件',\n  prop: 'files',\n  type: 'upload',\n  action: '/api/upload',\n  listType: 'picture-card',\n  multiple: true,\n  limit: 5,\n  fileSize: 10240,\n  accept: 'image/*',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n  },\n  tip: '支持jpg/png格式，单文件不超过10MB',\n}\n```\n\n---\n\n## tree - 树选择\n\n`type: 'tree'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| checkStrictly | boolean | false | 父子独立 |\n| parent | boolean | true | 父节点可选 |\n| accordion | boolean | false | 手风琴模式 |\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| expandOnClickNode | boolean | false | 点击展开 |\n| checkOnClickLeaf | boolean | false | 点击叶子勾选 (3.6.3+) |\n| defaultExpandedKeys | array | - | 默认展开keys |\n| defaultCheckedKeys | array | - | 默认勾选keys |\n| filterable | boolean | false | 可筛选 |\n| filterText | string | '请输入搜索关键字' | 筛选占位 |\n| iconClass | string | - | 自定义图标 |\n| leafOnly | boolean | true | 仅叶子节点 |\n| lazy | boolean | false | 懒加载 |\n| treeLoad | function | - | 加载函数 |\n| cacheData | array | - | 懒加载缓存 (3.2.20+) |\n| nodeClick | function | - | 节点点击回调 |\n| checked | function | - | 勾选回调 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'tree',\n  dicUrl: '/api/dept/tree',\n  props: { label: 'name', value: 'id' },\n  defaultExpandAll: true,\n  checkStrictly: true,\n}\n```\n\n---\n\n## input-tag - 标签输入 (3.6.0+)\n\n`type: 'input-tag'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| drag | boolean | false | 拖拽排序 |\n| size | string | - | 尺寸: large/small |\n| min | number | -Infinity | 最小标签数 |\n| max | number | Infinity | 最大标签数 |\n| prefix | string | - | 前置内容 |\n| prefixClick | function | - | 前置内容点击回调 |\n| suffix | string | - | 后置内容 |\n| suffixClick | function | - | 后置内容点击回调 |\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'input-tag',\n  // 按回车添加标签\n}\n```\n\n---\n\n## mention - 提及 (3.6.0+)\n\n`type: 'mention'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | - | 最大提及数 |\n| remote | boolean | false | 远程搜索 |\n| maxlength | number | - | 最大长度 |\n| minlength | number | - | 最小长度 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| split | string | ',' | 提及分隔符 |\n| prefix | string | '@' | 触发前缀 |\n| whole | boolean | false | 仅匹配完整提及 |\n| checkIsWhole | function | - | 自定义完整匹配检测 |\n| prependClick | function | - | 前置点击回调 |\n| appendClick | function | - | 后置点击回调 |\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  dicData: [\n    { label: '张三', value: 'zhangsan' },\n    { label: '李四', value: 'lisi' },\n  ],\n}\n\n// 远程搜索\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  remote: true,\n  dicUrl: '/api/user/search?name={{key}}',\n}\n```\n\n---\n\n## input-table - 表格选择\n\n`type: 'input-table'`\n\n内部使用 avue-crud 组件，配置参考 crud 文档。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| formatter | function | - | 数据格式化 |\n| onLoad | function | - | 加载/查询/分页方法 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| multiple | boolean | false | 多选 |\n| children | object | - | 表格 CRUD 配置（支持所有 avue-crud option 属性） |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n\n```javascript\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'input-table',\n  formatter: (row) => row.name,\n  onLoad: ({ page, value, data }, callback) => {\n    getList(page.currentPage, page.pageSize, data).then(res => {\n      callback({\n        total: res.data.total,\n        data: res.data.records,\n      })\n    })\n  },\n  children: {\n    border: true,\n    column: [\n      { label: '姓名', prop: 'name', search: true },\n      { label: '手机号', prop: 'phone' },\n    ],\n  },\n}\n```\n\n---\n\n## color - 颜色选择\n\n`type: 'color'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAlpha | boolean | true | 显示透明度 |\n| colorFormat | string | 'rgb' | 颜色格式: hsl/hsv/hex/rgb |\n| popperClass | string | - | 下拉框class |\n| predefine | array | - | 预定义颜色 |\n\n```javascript\n{\n  label: '主题色',\n  prop: 'themeColor',\n  type: 'color',\n  colorFormat: 'hex',\n  predefine: ['#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff'],\n}\n```\n\n---\n\n## icon - 图标选择\n\n`type: 'icon'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| iconList | array | - | 图标数组 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n```javascript\n{\n  label: '图标',\n  prop: 'icon',\n  type: 'icon',\n  iconList: [\n    { label: '首页', value: 'el-icon-s-home' },\n    { label: '设置', value: 'el-icon-setting' },\n  ],\n}\n```\n\niconList 支持分组结构：\n```javascript\niconList: [\n  {\n    label: 'Element Plus',\n    list: [\n      { label: 'Plus', value: 'el-icon-plus' },\n      { label: 'Edit', value: 'el-icon-edit' },\n    ]\n  },\n  {\n    label: 'Font Awesome',\n    list: [\n      { label: 'Home', value: 'fa fa-home' },\n    ]\n  }\n]\n```\n\n需引入图标库：\n```html\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n```\n\n---\n\n## map - 地图选择\n\n`type: 'map'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| params | object | - | 高德地图初始化参数 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| mapChange | function | - | 地图变化回调 |\n| rows | number | - | 文本域行数 |\n| minRows | number | 1 | 最小行数 |\n| maxRows | number | - | 最大行数 |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n需引入高德地图SDK（见 global-config.md）。\n\n```javascript\n{\n  label: '位置',\n  prop: 'location',\n  type: 'map',\n  params: { zoom: 15, center: [116.397428, 39.90923] },\n}\n```\n\n> 值格式为数组：`[经度, 纬度, 格式化地址]`，需引入高德地图 SDK。\n\n---\n\n## rate - 评分\n\n`type: 'rate'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | 5 | 最大分数 |\n| allowHalf | boolean | false | 允许半星 |\n| lowThreshold | number | 2 | 低分界限 |\n| highThreshold | number | 4 | 高分界限 |\n| colors | array/object | ['#F7BA2A','#F7BA2A','#F7BA2A'] | 颜色 |\n| voidColor | string | '#C6D1DE' | 未选颜色 |\n| disabledVoidColor | string | '#EFF2F7' | 禁用未选颜色 |\n| iconClasses | array/object | - | 图标class |\n| voidIconClass | string | 'el-icon-star-off' | 未选图标 |\n| showText | boolean | false | 显示文字 |\n| showScore | boolean | false | 显示分数 |\n| textColor | string | '#1F2D3D' | 文字颜色 |\n| texts | array | ['极差','失望','一般','满意','惊喜'] | 文字列表 |\n| scoreTemplate | string | '{value}' | 分数模板 |\n\n```javascript\n{\n  label: '评分',\n  prop: 'score',\n  type: 'rate',\n  max: 5,\n  allowHalf: true,\n  showText: true,\n  texts: ['极差', '失望', '一般', '满意', '惊喜'],\n}\n```\n\n---\n\n## slider - 滑块\n\n`type: 'slider'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | 0 | 最小值 |\n| max | number | 100 | 最大值 |\n| step | number | 1 | 步长 |\n| showInput | boolean | false | 显示输入框 |\n| showInputControls | boolean | true | 输入框控制按钮 |\n| showStops | boolean | false | 显示间断点 |\n| showTooltip | boolean | true | 显示tooltip |\n| formatTooltip | function | - | tooltip格式化 |\n| tooltipClass | string | - | tooltip class |\n| range | boolean | false | 范围选择 |\n| vertical | boolean | false | 垂直模式 |\n| height | string | - | 垂直模式高度 |\n| marks | object | - | 标记 |\n\n```javascript\n{\n  label: '进度',\n  prop: 'progress',\n  type: 'slider',\n  min: 0,\n  max: 100,\n  step: 10,\n  showStops: true,\n  marks: { 0: '0%', 50: '50%', 100: '100%' },\n}\n```\n\n---\n\n## array/img/url - 数组输入\n\n`type: 'array'` | `'img'` | `'url'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| alone | boolean | false | 单条模式 |\n| limit | number | - | 最大条数 |\n| fileType | string | 'img' | 文件类型 (img类型): img/audio/video |\n\n```javascript\n// 数组输入\n{ label: '标签', prop: 'tags', type: 'array', limit: 5 }\n\n// 图片输入\n{ label: '图片', prop: 'images', type: 'img', limit: 3, fileType: 'img' }\n\n// 链接输入\n{ label: '链接', prop: 'links', type: 'url', limit: 5 }\n```\n\n---\n\n## dynamic - 动态子表单\n\n`type: 'dynamic'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| type | string | 'crud' | 显示类型: crud/form |\n| limit | number | - | 最大行数 |\n| children | object | - | 子表配置 |\n| index | boolean | true | 显示序号 |\n| max | number | - | 最大行数 |\n\n### children 配置属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| type | string | 'crud' | 子表类型: crud/form |\n| column | array | - | 子表字段配置 |\n| index | boolean | true | 显示序号 |\n| indexLabel | string | '#' | 序号标题 |\n| addBtn | boolean | true | 显示新增按钮 |\n| delBtn | boolean | true | 显示删除按钮 |\n| viewBtn | boolean | true | 显示查看按钮 |\n| align | string | - | 内容对齐方式 |\n| headerAlign | string | - | 表头对齐方式 |\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| selectionChange | function | - | 选择变化回调 |\n| sortableChange | function | - | 拖拽排序回调 |\n\n```javascript\n// 表格模式（默认）\n{\n  label: '明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    column: [\n      { label: '商品', prop: 'product' },\n      { label: '数量', prop: 'quantity', type: 'number' },\n      { label: '单价', prop: 'price', type: 'number', precision: 2 },\n    ],\n  },\n}\n\n// 表单模式\n{\n  label: '联系人',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 12 },\n      { label: '电话', prop: 'phone', span: 12 },\n    ],\n  },\n}\n```\n\n---\n\n## title - 标题分隔\n\n`type: 'title'`\n\n用于表单中的分组标题，无输入功能。\n\n```javascript\n{\n  label: '基本信息',\n  prop: 'title1',\n  type: 'title',\n  span: 24,\n}\n```\n\n---\n\n## input-cron - Cron表达式 (3.8.2+)\n\n`type: 'input-cron'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '点击配置Cron表达式' | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| dialogWidth | string | '700px' | 弹窗宽度 |\n\n```javascript\n{\n  label: '定时任务',\n  prop: 'cron',\n  type: 'input-cron',\n  span: 24,\n}\n```\n",".claude/skills/avue-design/references/global-config.md":"# Avue 全局配置参考\n\n## 安装依赖\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n**必须依赖：**\n- `element-plus` - UI组件库\n- `@element-plus/icons-vue` - 图标库\n\n**可选依赖：**\n- `axios` - 字典组件和上传组件需要\n\n## 完整安装示例\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\n\n// 注册所有图标\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\n\napp.use(ElementPlus)\napp.use(Avue)\n```\n\n## 带 axios 的安装（字典/上传组件必需）\n\n```javascript\nimport axios from 'axios'\nconst app = createApp({})\napp.use(Avue, { axios })\n```\n\n## 全局配置选项\n\n```javascript\napp.use(AVUE, {\n  size: 'small',           // 组件默认尺寸: 'small' | 'mini' | 'medium'\n  crudOption: {},           // Crud组件全局默认配置\n  formOption: {},           // Form组件全局默认配置\n  appendToBody: true,       // 弹窗是否追加到body\n  modalAppendToBody: true,  // 遮罩层是否追加到body\n  cos: {},                  // 腾讯云COS配置\n  qiniu: {},                // 七牛云配置\n  ali: {},                  // 阿里云OSS配置\n  canvas: {},               // 全局水印配置\n})\n```\n\n### Crud 全局配置示例\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    border: true,\n    stripe: true,\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    menuWidth: 200,\n    searchLabelWidth: 110,\n    height: 'auto',\n  }\n})\n```\n\n### Form 全局配置示例\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    labelWidth: 120,\n    labelPosition: 'right',\n    size: 'small',\n  }\n})\n```\n\n## 云存储配置\n\n### 阿里云 OSS\n\n```javascript\napp.use(AVUE, {\n  ali: {\n    region: 'oss-cn-beijing',\n    endpoint: 'oss-cn-beijing.aliyuncs.com',\n    stsToken: '',\n    accessKeyId: 'your-access-key-id',\n    accessKeySecret: 'your-access-key-secret',\n    bucket: 'your-bucket-name',\n  }\n})\n```\n\n### 七牛云\n\n```javascript\n// 需引入 CryptoJS：\n// <script src=\"https://cdn.staticfile.net/crypto-js/4.0.0/crypto-js.min.js\"></script>\napp.use(AVUE, {\n  qiniu: {\n    AK: 'your-access-key',\n    SK: 'your-secret-key',\n    scope: 'your-scope',\n    url: 'https://upload.qiniup.com',\n    deadline: 1,\n  }\n})\n```\n\n### 腾讯云 COS\n\n```javascript\n// 需引入 cos-js-sdk-v5：\n// <script src=\"https://cdn.staticfile.net/cos-js-sdk-v5/1.3.2/cos-js-sdk-v5.min.js\"></script>\napp.use(AVUE, {\n  cos: {\n    SecretId: 'your-secret-id',\n    SecretKey: 'your-secret-key',\n    Bucket: 'your-bucket',\n    Region: 'ap-beijing',\n  }\n})\n```\n\n## 全局水印配置\n\n```javascript\napp.use(AVUE, {\n  canvas: {\n    text: 'avuejs.com',        // 水印文字\n    fontFamily: 'microsoft yahei', // 字体\n    color: '#999',              // 颜色\n    fontSize: 16,               // 字号\n    opacity: 100,               // 透明度\n    bottom: 10,                 // 底部距离\n    right: 10,                  // 右侧距离\n    ratio: 1,                   // 压缩比 0-1\n  }\n})\n```\n\n## 国际化\n\n```javascript\nimport Avue from '@smallwei/avue'\nimport enLocale from '@smallwei/avue/lib/locale/lang/en'\nimport zhLocale from '@smallwei/avue/lib/locale/lang/zh'\n\n// 使用英文\napp.use(Avue, { locale: enLocale })\n\n// 配合 vue-i18n\nconst messages = {\n  en: { ...enLocale },\n  zh: { ...zhLocale },\n}\n```\n\n支持的语言：`zh`（简体中文）、`en`（英文）。\n\n## 外部库引入（按需）\n\n```html\n<!-- Excel 导入导出 -->\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n\n<!-- 拖拽排序 -->\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n\n<!-- 高德地图 -->\n<script>\n  window._AMapSecurityConfig = { securityJsCode: 'xxxxx' }\n</script>\n<script src=\"https://webapi.amap.com/maps?v=2.0&key=xxxxxxxx&plugin=AMap.PlaceSearch,AMap.Geocoder\"></script>\n<script src=\"https://webapi.amap.com/ui/1.1/main.js?v=1.0.11\"></script>\n\n<!-- 图标库 -->\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n\n<!-- 流程图 -->\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n\n<!-- 截图 -->\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n\n<!-- PDF导出 -->\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n\n<!-- Mock数据 -->\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n",".claude/skills/avue-design/references/other-components.md":"# Avue 其他组件参考\n\n## 目录\n1. [Card 卡片](#card-卡片)\n2. [DialogForm 弹窗表单](#dialogform-弹窗表单)\n3. [Tabs 选项卡](#tabs-选项卡)\n4. [Search 搜索](#search-搜索)\n5. [Calendar 日历](#calendar-日历)\n6. [Flow 流程图](#flow-流程图)\n7. [Login 登录](#login-登录)\n8. [Article 文章](#article-文章)\n9. [Chat 聊天](#chat-聊天)\n10. [CountUp 数字动画](#countup-数字动画)\n11. [Clipboard 剪切板](#clipboard-剪切板)\n12. [Draggable 拖拽](#draggable-拖拽)\n13. [TextEllipsis 文本省略](#textellipsis-文本省略)\n14. [Sign 电子签名](#sign-电子签名)\n15. [Print 打印](#print-打印)\n16. [Export 导入导出](#export-导入导出)\n17. [ImagePreview 图片预览](#imagepreview-图片预览)\n18. [Contextmenu 右键菜单](#contextmenu-右键菜单)\n19. [Video 摄像头](#video-摄像头)\n20. [Verify 验证码](#verify-验证码)\n21. [Screenshot 截图](#screenshot-截图)\n22. [License 授权书](#license-授权书)\n\n---\n\n## Card 卡片\n\n```vue\n<avue-card :option=\"option\" :data=\"data\" @row-click=\"rowClick\" @row-add=\"rowAdd\">\n  <template #menu=\"{ row, index }\">\n    <el-button @click=\"handleEdit(row)\">编辑</el-button>\n  </template>\n</avue-card>\n```\n\n### Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| span | number | 8 | 栅格数 |\n| gutter | number | 20 | 间距 |\n\n### Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| title | string | 'title' | 标题属性 |\n| img | string | 'img' | 头像属性 |\n| info | string | 'info' | 副标题属性 |\n\n### 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-add | - | 新增 |\n| row-click | (row, index) | 行点击 |\n\n---\n\n## DialogForm 弹窗表单\n\n全局API方式调用 (3.4.1+)：\n\n```javascript\nimport { $DialogForm } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\n\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"确认\",\n    column: [\n      { label: \"姓名\", prop: \"name\", span: 24 },\n    ],\n  },\n})\n\n// 3.5.2+ 主动关闭\ndialog.close()\n```\n\n也可作为组件使用，参考 `default/dialog-form/index` 示例。\n\n---\n\n## Tabs 选项卡\n\n```vue\n<avue-tabs :option=\"option\" @change=\"tabChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Search 搜索\n\n```vue\n<avue-search v-model=\"searchForm\" :option=\"option\" @change=\"searchChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Calendar 日历\n\n```vue\n<avue-calendar :option=\"option\" :data=\"calendarData\" />\n```\n\n---\n\n## Flow 流程图\n\n需引入 jsPlumb：\n```html\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n```\n\n```vue\n<avue-flow :option=\"option\" :width=\"800\" :height=\"600\" @click=\"nodeClick\" />\n```\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| width | number | - | 画布宽度 |\n| height | number | - | 画布高度 |\n| option | object | - | 配置 |\n\n### 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| nodeAdd | (name) | 添加节点 |\n| deleteNode | (id) | 删除节点 |\n\n---\n\n## Login 登录\n\n```vue\n<avue-login :option=\"option\" @submit=\"handleLogin\" />\n```\n\n---\n\n## Article 文章\n\n```vue\n<avue-article :id=\"container\" :offset-top=\"0\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | 'window' | 指定父元素 |\n| offset-top | number | 0 | 触发偏移量 |\n\n---\n\n## Chat 聊天\n\n```vue\n<avue-chat :option=\"option\" />\n```\n\n支持图片、视频、文件、地图等消息类型。\n\n---\n\n## CountUp 数字动画\n\n```vue\n<avue-count-up :start=\"0\" :end=\"12345\" :decimals=\"0\" :duration=\"2\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | number | 0 | 起始数字 |\n| end | number | - | 结束数字 |\n| decimals | number | 0 | 小数位数 |\n| duration | number | 2 | 动画时长(秒) |\n| options | object | - | CountUp.js配置 |\n| callback | function | - | 开始回调 |\n\n---\n\n## Clipboard 剪切板\n\n```vue\n<avue-clipboard :text=\"copyText\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 要复制的文字 |\n\n---\n\n## Draggable 拖拽\n\n```vue\n<avue-draggable :width=\"200\" :height=\"100\" :top=\"50\" :left=\"100\" :disabled=\"false\">\n  <div>可拖拽内容</div>\n</avue-draggable>\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| disabled | boolean | false | 禁用拖拽 |\n| width | string | - | 宽度 |\n| height | string | - | 高度 |\n| top | number | 0 | X位置 |\n| left | number | 0 | Y位置 |\n| z-index | number | 1 | 层级 |\n\n### 事件\n\n| 事件名 | 说明 |\n|-------|------|\n| focus | 获取焦点 |\n| blur | 失去焦点 |\n\n---\n\n## TextEllipsis 文本省略\n\n```vue\n<avue-text-ellipsis :text=\"longText\" :height=\"60\" :is-limit-height=\"true\" :use-tooltip=\"true\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 文本内容 |\n| width | number | - | 宽度限制 |\n| height | number | - | 高度限制 |\n| is-limit-height | boolean | true | 启用高度限制 |\n| use-tooltip | boolean | false | 使用tooltip |\n| placement | string | - | tooltip方向 |\n\n---\n\n## Sign 电子签名\n\n```vue\n<avue-sign />\n```\n\n兼容移动端和PC端。\n\n---\n\n## Print 打印\n\n```vue\n<avue-print :id=\"printId\" :html=\"htmlContent\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | - | DOM元素ID |\n| html | string | - | HTML代码片段 |\n\n---\n\n## Export 导入导出\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 导出\n$Export.excel({\n  title: '导出标题',\n  columns: [{ label: '姓名', prop: 'name' }],\n  data: [{ name: '张三' }],\n})\n```\n\n---\n\n## ImagePreview 图片预览\n\n```javascript\nimport { $ImagePreview } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\nconst images = [\n  { thumbUrl: '缩略图.jpg', url: '原图.jpg' },\n]\n$ImagePreview(appContext)(images, 0)\n```\n\n---\n\n## Contextmenu 右键菜单\n\n```vue\n<div v-contextmenu=\"menuOptions\">\n  右键点击此区域\n</div>\n```\n\n---\n\n## Video 摄像头\n\n```vue\n<avue-video ref=\"video\" />\n```\n\n支持 start/end 方法控制录制。\n\n---\n\n## Verify 验证码\n\n```vue\n<avue-verify />\n```\n\n---\n\n## Screenshot 截图\n\n需引入 html2canvas：\n```html\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n```\n\n---\n\n## License 授权书\n\n需引入 jsPDF (用于PDF导出)：\n```html\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n```\n",".claude/skills/avue-design/references/tree-component.md":"# avue-tree 树组件参考\n\n## 基本用法\n\n```vue\n<avue-tree\n  :option=\"option\"\n  :data=\"treeData\"\n  @node-click=\"nodeClick\"\n  @save=\"handleSave\"\n  @update=\"handleUpdate\"\n  @del=\"handleDel\"\n/>\n```\n\n## Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| virtualize | boolean | false | 虚拟DOM模式 |\n| dialogWidth | string | '50%' | 弹窗宽度 |\n| formOption | object | - | 自定义表单（参考avue-form） |\n| menu | boolean | true | 显示菜单栏 |\n| addBtn | boolean | true | 新增按钮 |\n| editBtn | boolean | true | 编辑按钮 |\n| delBtn | boolean | true | 删除按钮 |\n| filter | boolean | true | 显示搜索框 |\n| props | object | - | 配置选项 |\n| lazy | boolean | false | 懒加载 |\n\n## Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 节点名称属性 |\n| labelText | string | '名称' | 弹窗新增名称 |\n| children | string | 'children' | 子节点属性 |\n| value | string | 'value' | 节点值属性和nodeKey |\n\n## 数据结构\n\n```javascript\nconst treeData = ref([\n  {\n    label: '一级节点',\n    value: '1',\n    children: [\n      { label: '二级节点A', value: '1-1' },\n      { label: '二级节点B', value: '1-2' },\n    ],\n  },\n])\n```\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| save | (parent, data, done, loading) | 新增回调 |\n| update | (parent, data, done, loading) | 编辑回调 |\n| del | (data, done) | 删除回调 |\n| before-open | (done, type) | 打开前回调 |\n| before-close | (done, type) | 关闭前回调 |\n| node-click | (data, node, component) | 节点点击 |\n| check-change | (data, checked, hasCheckedChildren) | 勾选变化 |\n\n## 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| filter | (value) | 筛选树节点 |\n| updateKeyChildren | (key, data) | 更新子节点 |\n| getCheckedNodes | (leafOnly, includeHalfChecked) | 获取勾选节点 |\n| setCheckedNodes | (nodes) | 设置勾选节点 |\n| getCheckedKeys | (leafOnly) | 获取勾选keys |\n| setCheckedKeys | (keys, leafOnly) | 设置勾选keys |\n| setChecked | (key/data, checked, deep) | 设置勾选状态 |\n| getHalfCheckedNodes | - | 获取半选节点 |\n| getHalfCheckedKeys | - | 获取半选keys |\n| getCurrentKey | - | 获取当前选中key |\n| getCurrentNode | - | 获取当前选中数据 |\n| setCurrentKey | (key) | 设置当前选中 |\n| setCurrentNode | (node) | 设置当前选中 |\n| getNode | (data) | 获取节点 |\n| remove | (data) | 删除节点 |\n| append | (data, parentNode) | 追加子节点 |\n| insertBefore | (data, refNode) | 在前面插入 |\n| insertAfter | (data, refNode) | 在后面插入 |\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu | 按钮插槽 |\n| add-btn | 新增按钮插槽 |\n| default | 节点内容插槽 |\n\n## 示例\n\n### 多选模式\n\n```vue\n<avue-tree :option=\"{ ...option, multiple: true }\" :data=\"treeData\" @check-change=\"checkChange\" />\n```\n\n### 懒加载\n\n```vue\n<avue-tree :option=\"option\" :data=\"treeData\">\n\nconst option = reactive({\n  lazy: true,\n  props: { label: 'name', value: 'id' },\n})\n\n// 在 treeLoad 中处理加载逻辑\n```\n\n### 拖拽\n\n参考 `default/tree/drag` 示例。\n\n### 权限控制\n\n参考 `default/tree/permission` 示例。\n\n### 虚拟模式\n\n```javascript\noption: { virtualize: true }\n// 适用于大数据量场景\n```\n",".claude/skills/avue-design/references/utility-apis.md":"# Avue 全局工具函数参考\n\n## $DialogForm - 弹窗表单\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $DialogForm } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\n\n// 打开弹窗表单\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"完成\",\n    column: [\n      {\n        label: \"姓名\",\n        prop: \"name\",\n        span: 24,\n        rules: [{ required: true, message: \"请输入姓名\", trigger: \"blur\" }],\n      },\n    ],\n  },\n})\n\n// 关闭弹窗\ndialog.close()\n```\n\n3.5.2+ 版本支持 `close()` 方法主动关闭。\n\n## $Clipboard - 复制剪切板\n\n```javascript\nimport { $Clipboard } from \"@smallwei/avue\"\n\n$Clipboard({ text: \"要复制的文本\" })\n  .then(() => ElMessage.success(\"复制成功\"))\n  .catch(() => ElMessage.error(\"复制失败\"))\n```\n\n## $ImagePreview - 图片预览\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $ImagePreview } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\nconst datas = [\n  { thumbUrl: \"缩略图URL\", url: \"大图URL\" },\n  { thumbUrl: \"缩略图URL2\", url: \"大图URL2\" },\n]\n\n$ImagePreview(appContext)(datas, 0)  // 第二个参数为起始索引\n```\n\n## $Print - 打印\n\n```javascript\nimport { $Print } from \"@smallwei/avue\"\n\n// 传入 DOM 元素的 id 或 class\n$Print(\"#test\")\n```\n\n## $Export - Excel 导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 需引入 FileSaver.js 和 xlsx\n$Export.excel({\n  title: \"文档标题\",\n  columns: [\n    { label: \"标题\", prop: \"title\" },\n    { label: \"内容\", prop: \"content\" },\n  ],\n  data: [\n    { title: \"测试1\", content: \"内容1\" },\n    { title: \"测试2\", content: \"内容2\" },\n  ],\n})\n```\n\n## $Log - 彩色日志\n\n```javascript\nimport { $Log } from \"@smallwei/avue\"\n\n// 内置颜色: default(#35495E), primary(#3488ff), success(#43B883), warning(#e6a23c), danger(#f56c6c)\n$Log.capsule(\"标题\", \"内容\", \"primary\")\n$Log.primary(\"内容\")\n$Log.success(\"内容\")\n$Log.warning(\"内容\")\n$Log.danger(\"内容\")\n```\n\n## findObject - 查找列配置对象\n\n```javascript\nimport { findObject } from \"@smallwei/avue\"\n\nconst option = { column: [{ label: '名称', prop: 'name' }] }\nconst nameColumn = findObject(option.column, 'name')\n// 返回: { label: '名称', prop: 'name' }\n```\n\n## findNode - 在数组中查找对象\n\n```javascript\nimport { findNode } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst obj = findNode(list, \"sex\", \"prop\")  // 返回 {prop: 'sex'}\n```\n\n## findArray - 查找对象在数组中的索引\n\n```javascript\nimport { findArray } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst index = findArray(list, \"sex\", \"prop\")  // 返回 1\n```\n\n## downFile - 文件下载\n\n```javascript\nimport { downFile } from \"@smallwei/avue\"\n\ndownFile(\"https://example.com/file.pdf\", \"document.pdf\")\n```\n\n## randomId - 生成随机ID\n\n```javascript\nimport { randomId } from \"@smallwei/avue\"\n\nconst id = randomId()  // 返回随机字符串\n```\n\n## loadScript - 动态加载脚本\n\n```javascript\nimport { loadScript } from \"@smallwei/avue\"\n\n// 加载 JS\nloadScript(\"js\", \"https://cdn.example.com/lib.js\").then(() => {\n  // JS 加载完成\n})\n\n// 加载 CSS\nloadScript(\"css\", \"https://cdn.example.com/style.css\").then(() => {\n  // CSS 加载完成\n})\n```\n\n## deepClone - 深拷贝\n\n```javascript\nimport { deepClone } from \"@smallwei/avue\"\n\nconst original = { name: \"张三\", info: { age: 18 } }\nconst cloned = deepClone(original)\n```\n\n## setPx - 设置像素值\n\n```javascript\nimport { setPx } from \"@smallwei/avue\"\n\nsetPx(23)       // '23px'\nsetPx(\"100%\")   // '100%'\nsetPx(\"23px\")   // '23px'\n```\n\n## validatenull - 判断空值\n\n```javascript\nimport { validatenull } from \"@smallwei/avue\"\n\nvalidatenull({})         // true\nvalidatenull([])         // true\nvalidatenull(\"\")         // true\nvalidatenull(undefined)  // true\nvalidatenull(null)       // true\nvalidatenull(\"hello\")    // false\n```\n\n## validData - 数据验证并提供默认值\n\n```javascript\nimport { validData } from \"@smallwei/avue\"\n\nvalidData(undefined, \"默认值\")     // '默认值'\nvalidData({}, { name: 11 })        // { name: 11 }\nvalidData(true, false)             // true (非空返回原值)\n```\n",".claude/skills/blade-commit/README.md":"# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 快速开始\n\n```bash\n# 简单模式（默认）— 单行 gitmoji + 描述\n/blade-commit\n\n# 详细模式 — gitmoji + conventional commit + 变更列表\n/blade-commit -d\n\n# 自定义信息 — 自动补全 gitmoji 格式\n/blade-commit -m \"新增接口权限模块\"\n```\n\n## 两种模式\n\n### 简单模式（默认）\n\n单行格式，适用于日常小改动：\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n```\n\n### 详细模式（`-d`）\n\n多行格式，适用于涉及多文件的较大提交：\n\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n```\n\n## 常用 Gitmoji\n\n| Gitmoji | 图标 | 场景 |\n|---|---|---|\n| `:sparkles:` | ✨ | 新增功能 |\n| `:bug:` | 🐛 | 修复 Bug |\n| `:zap:` | ⚡ | 性能优化 |\n| `:recycle:` | ♻️ | 重构 |\n| `:memo:` | 📝 | 文档变更 |\n| `:wrench:` | 🔧 | 配置变更 |\n| `:fire:` | 🔥 | 移除代码 |\n| `:tada:` | 🎉 | 发布版本 |\n| `:white_check_mark:` | ✅ | 测试 |\n| `:lock:` | 🔒 | 安全修复 |\n\n完整对照表见 SKILL.md。\n\n## 安全保障\n\n- **只做 commit**，禁止 push、pull 等任何远程操作\n- **零署名**，不添加任何 AI/工具署名\n- **逐文件暂存**，不使用 `git add .`，排除敏感文件\n- **自动适配**，根据项目历史提交记录匹配语言和风格\n",".claude/skills/blade-commit/SKILL.md":"---\nname: blade-commit\ndescription: SpringBlade 通用 Git 提交工具。基于 Gitmoji 规范生成提交信息。当用户要求提交代码、commit 变更、生成 commit message、或使用 /blade-commit 时触发。默认强制简单模式（gitmoji + 一句话描述），仅当显式传入 --d 或 --detail 参数时才启用详细模式（gitmoji + conventional commit + 变更列表）。只执行 commit，绝不 push 或执行其他远程操作。确保每次提交都带有 gitmoji 图标。\n---\n\n# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 铁律\n\n- **只做 commit**：禁止执行 `git push`、`git pull`、`git rebase`、`git merge`、`git reset --hard` 或任何非暂存/提交的 git 操作\n- **零署名**：提交信息中不得出现 `Powered by`、`Created by`、`Co-authored-by`、`Generated by` 或任何形式的工具/AI 署名\n- **必须有 Gitmoji**：每条提交信息必须以 gitmoji 代码开头（如 `:sparkles:`）\n\n## 使用方式\n\n- `/blade-commit` — 简单模式（默认，强制单行）\n- `/blade-commit --d` 或 `/blade-commit --detail` — 详细模式（多行带变更列表）\n- `/blade-commit -m \"自定义信息\"` — 使用用户指定的信息，自动补全 gitmoji 格式\n\n## 模式判定规则\n\n**无论变更多大、涉及多少文件，只要用户没有显式传入 `--d` 或 `--detail` 参数，就必须使用简单模式。** 不要因为 diff 内容多就自动升级为详细模式——模式选择权在用户手中，而非由变更规模决定。\n\n## 两种模式\n\n### 简单模式（默认，强制）\n\n单行格式：`:<gitmoji>: <简要描述>`\n\n一句话说清楚改了什么。无论改动大小，只要没有 `--d` / `--detail` 参数，一律使用此格式。\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n:recycle: 重构租户删除逻辑，迁移至 TenantService\n:memo: 补充 OSS 接入文档\n:wrench: 调整 application-dev.yml 数据源配置\n:tada: x.x.x.RELEASE\n```\n\n### 详细模式（需显式传入 `--d` 或 `--detail`）\n\n多行格式，适用于涉及多文件、多项改动的较大提交：\n\n```\n:<gitmoji>: <type>(<scope>): <标题摘要>\n\n- <变更点1>\n- <变更点2>\n- ...\n```\n\n标题行由 gitmoji + conventional commit 类型 + 作用域 + 摘要组成，空一行后用 bullet points 逐条列出具体变更。\n\n**示例 1 — 新功能：**\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\n```\n\n**示例 2 — Bug 修复：**\n```\n:bug: fix(auth): 修复登录 IP 锁定后刷新令牌仍可通过的问题\n\n- RefreshTokenGranter 增加 IP 锁定状态校验\n- LoginAttemptService 补充刷新场景下的失败计数复位逻辑\n- 新增单元测试覆盖刷新 Token 的 IP 锁定分支\n```\n\n**示例 3 — 重构：**\n```\n:recycle: refactor(resource): 重构 OssEndpoint 按职责拆分 Controller\n\n- 拆分文件上传、文件下载、文件删除为独立 Controller\n- 抽取 OssService 处理跨厂商统一逻辑\n- 原 OssEndpoint 保留兼容入口，内部委派至新 Controller\n```\n\n**示例 4 — 性能优化：**\n```\n:zap: perf(system): 优化字典批量查询性能\n\n- DictCache 改用 Caffeine 二级缓存，减少 Redis 往返\n- DictMapper 补充 code + dict_key 联合索引提示\n- 调整 DictService 的 getList 为按租户分片查询\n```\n\n## Gitmoji 对照表\n\n根据变更内容选择最匹配的 gitmoji：\n\n| Gitmoji | 图标 | type | 场景 |\n|---|---|---|---|\n| `:sparkles:` | ✨ | feat | 新增功能、新特性 |\n| `:bug:` | 🐛 | fix | 修复 Bug |\n| `:zap:` | ⚡ | perf | 性能优化、改进提升 |\n| `:recycle:` | ♻️ | refactor | 重构（不改变外部行为） |\n| `:memo:` | 📝 | docs | 文档新增或变更 |\n| `:wrench:` | 🔧 | chore | 配置变更、杂项 |\n| `:white_check_mark:` | ✅ | test | 添加或修改测试 |\n| `:tada:` | 🎉 | release | 发布新版本、里程碑 |\n| `:fire:` | 🔥 | remove | 移除代码或文件 |\n| `:lipstick:` | 💄 | style | UI / 样式调整 |\n| `:ambulance:` | 🚑 | hotfix | 紧急修复 |\n| `:construction:` | 🚧 | wip | 开发进行中 |\n| `:arrow_up:` | ⬆️ | deps | 升级依赖版本 |\n| `:arrow_down:` | ⬇️ | deps | 降级依赖版本 |\n| `:lock:` | 🔒 | security | 安全相关修复 |\n| `:truck:` | 🚚 | move | 移动 / 重命名文件 |\n| `:art:` | 🎨 | style | 改进代码结构 / 格式化 |\n| `:package:` | 📦 | build | 构建系统变更 |\n| `:rewind:` | ⏪ | revert | 回退变更 |\n| `:heavy_plus_sign:` | ➕ | deps | 添加依赖 |\n| `:heavy_minus_sign:` | ➖ | deps | 移除依赖 |\n\n## 执行流程\n\n### 第一步：收集信息\n\n并行执行：\n- `git status` — 查看工作区状态\n- `git diff --staged` — 已暂存的变更\n- `git diff` — 未暂存的变更\n- `git log --oneline -5` — 最近提交记录，确认当前项目的提交风格\n\n### 第二步：适配项目风格\n\n阅读 `git log` 结果，判断当前项目的提交语言（中文/英文）和习惯。提交信息的语言应与项目历史保持一致：\n- 如果历史提交以中文为主，使用中文\n- 如果历史提交以英文为主，使用英文\n- 如果混合使用，默认中文\n\n### 第三步：分析变更\n\n阅读 diff 内容，判断：\n1. **变更类型**：新功能？Bug修复？重构？文档？配置？性能优化？\n2. **影响范围（scope）**：涉及哪个模块或功能（如 `system`、`auth`、`desk`、`resource`、`develop`、`gateway`、`tool` 等）\n3. **核心改动**：最关键的变更是什么\n4. 如果变更混合了多种类型，选最主要的类型\n\n### 第四步：暂存文件\n\n- 使用 `git add <具体文件路径>` 逐个添加，不要用 `git add .` 或 `git add -A`\n- 排除不该提交的文件：`.env`、密钥文件、IDE 配置（`.idea/`、`.vscode/`）、编译产物（`target/`、`dist/`）、系统文件（`.DS_Store`）\n- **前端工程排除规则**：如果项目根目录存在 `package.json`、`vite.config.*`、`vue.config.*` 等前端工程标识文件，则自动将 `website.js`（通常位于 `src/config/website.js`）从暂存列表中排除，即使该文件有变更也不提交。如果 `website.js` 已被暂存，执行 `git reset HEAD <website.js路径>` 将其移出暂存区\n- 如果用户已经手动暂存过文件，跳过此步（但仍需检查并移除已暂存的 `website.js`）\n\n### 第五步：生成提交信息并提交\n\n根据模式选择对应格式，使用 HEREDOC 执行 commit：\n\n**简单模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: 新增角色授权接口\nEOF\n)\"\n```\n\n**详细模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\nEOF\n)\"\n```\n\n### 第六步：确认结果\n\n执行 `git status` 确认提交成功，向用户展示提交结果。\n\n## 写好提交信息的要点\n\n- **简单模式**：一句话说清楚\"做了什么\"，精炼但不含糊，80 字符以内为佳\n- **详细模式**：\n  - 标题行概括全局，不超过 72 字符\n  - bullet points 按重要性排序：先新增，后优化，再修复\n  - 每个 bullet 要具体，提到文件名或模块名让人知道改了哪里\n  - 不要超过 8 条 bullet，太多说明应该拆分 commit\n- **选对 gitmoji**：看对照表选最贴合的。不确定时 `:sparkles:` 适用于大多数新功能，`:zap:` 适用于优化改进\n- 如果用户通过 `-m` 指定了信息，保留用户原意，只在格式上对齐规范（补 gitmoji、调整格式）\n",".claude/skills/blade-design/README.md":"# blade-design - SpringBlade 全栈代码生成器\n\n基于 SpringBlade 开源版框架规范的智能代码生成 Skill，覆盖后端、前端、数据库三层，支持 Boot 单体和 Cloud 微服务两种架构模式。\n\n## 功能概览\n\n```\nblade-design\n├── 后端子技能 (backend)\n│   ├── Entity / DTO / VO / Excel    ← 数据模型层\n│   ├── Mapper / Mapper.xml          ← 数据访问层\n│   ├── Service / ServiceImpl        ← 业务逻辑层\n│   ├── Controller                   ← 接口控制层\n│   ├── Wrapper                      ← 数据转换层\n│   └── Feign Client (Cloud)         ← 微服务通信层\n│\n├── 前端子技能 (frontend)\n│   ├── API 模块                      ← Axios 请求封装\n│   ├── Option 配置                   ← Avue CRUD 列/表单配置\n│   └── Vue 页面                      ← Options API / Composition API\n│\n└── 数据库子技能 (database)\n    ├── MySQL 建表 SQL                ← 开源版官方支持方言\n    └── 菜单 SQL                      ← 权限菜单数据\n```\n\n## 支持的架构模式\n\n### Boot 单体架构\n- 所有代码在同一模块内\n- 包名：`org.springblade.modules.{module}`\n- Controller 路由含服务名前缀：`AppConstant.APPLICATION_XXX_NAME + \"/path\"`\n- 无 Feign Client\n\n### Cloud 微服务架构\n- API 模块（契约）与 Service 模块（实现）分离\n- 包名：`org.springblade.{module}`\n- Controller 路由无前缀（Gateway 自动路由）\n- 含 Feign Client 接口 + 实现\n\n## 支持的实体模式\n\n| 模式 | 基类 | 适用场景 | 审计字段 | 租户隔离 |\n|------|------|---------|---------|---------|\n| **tenant** | `TenantEntity` | 多租户业务表 | 自动继承 | 有 |\n| **base** | `BaseEntity` | 跨租户业务表 | 自动继承 | 无 |\n| **raw** | `Serializable` | 系统 / 关系表 | 手动定义 | 无 |\n\n## 支持的数据库\n\nSpringBlade 开源版**仅官方支持 MySQL**，建表脚本固定输出 MySQL 方言。\n\n| 方言 | 引号 | 大小写 | 注释方式 | 引擎 |\n|------|------|--------|---------|------|\n| MySQL | `` ` `` | 小写 | `COMMENT` 内联 | InnoDB + utf8mb4 |\n\n## 支持的前端风格\n\n| 风格 | 说明 | 适用场景 |\n|------|------|---------|\n| **Options API** | Vue 3 传统选项式写法 | 与现有代码保持一致 |\n| **Composition API** | Vue 3 组合式 `<script setup>` | 新功能开发推荐 |\n\n## 支持的模板类型\n\n| 类型 | 说明 | 适用场景 |\n|------|------|---------|\n| **crud** | 标准增删改查 | 通用业务管理 |\n| **tree** | 树形结构 CRUD | 分类、部门、菜单等层级数据 |\n| **sub** | 主子表 CRUD | 订单-订单项、合同-条款等主从关系 |\n\n## 使用方式\n\n### 快速开始\n```\n帮我生成一个订单管理模块的代码\n```\n\n### 指定参数\n```\n帮我生成一个产品管理的 CRUD：\n- 模块名：product\n- 实体名：Product\n- 架构：boot\n- 字段：\n  - productName (String) 产品名称，必填，搜索\n  - category (Integer) 产品分类，字典 product_category\n  - price (BigDecimal) 单价\n  - description (String/text) 产品描述\n  - status (Integer) 上架状态\n```\n\n### 仅生成部分\n```\n帮我生成 Order 的建表语句（MySQL）\n帮我生成前端页面，用 Composition API\n只需要后端代码，Cloud 架构\n```\n\n## 文件结构\n\n```\nblade-design/\n├── SKILL.md                  ← 主技能文件（路由、配置、工作流）\n├── README.md                 ← 本文档\n└── references/\n    ├── backend.md            ← 后端代码模板（Entity/DTO/VO/Mapper/Service/Controller/Wrapper/Feign）\n    ├── frontend.md           ← 前端代码模板（API/Option/Vue 页面）\n    └── database.md           ← MySQL 建表模板 + 菜单 SQL\n```\n\n## Boot vs Cloud 核心差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| 基础包名 | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| Controller 路由 | `AppConstant.APPLICATION_XXX_NAME + \"/path\"` | `\"/path\"` |\n| Entity 位置 | 同模块 | API 模块 |\n| 服务间调用 | 直接注入 | Feign Client |\n| Feign Client | 无 | 接口在 API 模块，实现在 Service 模块 |\n| POM 结构 | 单一 pom.xml | API + Service 各一个 |\n| 服务注册 | 不需要 | Nacos |\n| 网关路由 | 不需要 | Spring Cloud Gateway |\n\n## 代码规范要点\n\n1. Long 类型 ID 必须 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n2. Date 字段同时添加 `@DateTimeFormat` 和 `@JsonFormat`\n3. 逻辑删除字段 `isDeleted` 添加 `@TableLogic`（基类自动处理）\n4. Swagger 使用 OpenAPI 3 注解（`@Tag`/`@Operation`/`@Schema`/`@Parameter`），控制器类级标注无参 `@ApiOrder` 排序\n5. 响应统一使用 `R<T>` 包装\n6. Wrapper 中通过 `DictCache.getValue()` 翻译字典字段\n7. 前端权限标识格式：`{modelCode}_{action}`\n8. 表名统一 `blade_` 前缀\n9. 主键使用雪花算法（ASSIGN_ID）\n10. 所有表包含标准审计字段\n11. Entity 类名不带 `Entity` 后缀（如 `User` 而非 `UserEntity`）\n12. 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`，开源版仅支持角色级权限\n",".claude/skills/blade-design/SKILL.md":"---\nname: blade-design\ndescription: SpringBlade 全栈代码生成器。根据用户提供的模块名、实体名和字段列表，自动生成符合 SpringBlade 开源版框架规范的后端代码（Boot 单体/Cloud 微服务）、前端页面（Saber Options API/Composition API）和 MySQL 建表语句。当用户说\"帮我生成代码\"、\"创建一个新模块\"、\"生成 CRUD\"、\"新建实体\"、\"建表语句\"、\"生成前端页面\"、\"生成后端接口\"、\"生成 SpringBlade 代码\"、\"写一个 xxx 管理功能\"、\"帮我设计 xxx 表\"、\"blade-design\"等时触发。即使用户只是描述了业务字段或给出了表结构，也应触发此技能来生成对应的 SpringBlade 规范代码。\n---\n\n# SpringBlade 全栈代码生成器 (blade-design)\n\n根据用户需求生成符合 SpringBlade 开源版框架规范的完整 CRUD 代码，覆盖后端、前端、数据库三层。生成的代码与 SpringBlade 现有模块（如 Notice、Dict、Post、User）风格完全一致。\n\n## 信息收集协议\n\n在生成代码前，必须确认以下信息。如用户未提供，主动询问：\n\n### 必需信息\n| 参数 | 说明 | 示例 |\n|------|------|------|\n| **模块名** | 业务模块标识（英文小写） | `desk`、`system`、`order` |\n| **实体名** | 实体类名（PascalCase） | `Notice`、`Order`、`Product` |\n| **中文名** | 业务描述 | `通知公告`、`订单管理` |\n| **字段列表** | 业务字段（不含审计字段） | 见下方格式 |\n\n### 可选信息（有默认值）\n| 参数 | 默认值 | 可选值 |\n|------|--------|--------|\n| **架构类型** | `boot` | `boot`（单体）、`cloud`（微服务） |\n| **实体模式** | `tenant` | `tenant`（租户隔离）、`base`（无租户）、`raw`（原生 Serializable） |\n| **前端风格** | `options` | `options`（Options API）、`composition`（Composition API） |\n| **模板类型** | `crud` | `crud`（基础增删改查）、`tree`（树形结构）、`sub`（主子表） |\n| **是否含 Wrapper** | `true` | `true`、`false` |\n| **生成范围** | `all` | `all`、`backend`、`frontend`、`database` |\n| **服务名（Cloud）** | `blade-{模块名}` | 微服务名称 |\n| **作者** | `BladeX` | 代码注释中的作者 |\n\n> SpringBlade 开源版仅官方支持 MySQL，生成的建表脚本固定为 MySQL 方言。\n\n### 字段定义格式\n\n用户可以用表格或简洁文本描述字段，你需要解析为以下结构：\n\n| 属性 | 说明 |\n|------|------|\n| `name` | Java 属性名（camelCase） |\n| `column` | 数据库列名（snake_case） |\n| `type` | Java 类型：`String`、`Integer`、`Long`、`Date`、`BigDecimal`、`Double` |\n| `jdbcType` | 数据库类型：`varchar(n)`、`int`、`bigint`、`datetime`、`text`、`decimal(m,n)` |\n| `comment` | 中文字段说明 |\n| `required` | 是否必填（默认 false） |\n| `query` | 是否为查询字段（默认 false） |\n| `queryType` | 查询方式：`EQ`、`LIKE`、`BETWEEN`（默认 EQ） |\n| `list` | 是否在列表显示（默认 true） |\n| `form` | 是否在表单显示（默认 true） |\n| `component` | 前端组件：`input`、`textarea`、`select`、`radio`、`checkbox`、`switch`、`date`、`datetime`、`number`、`upload`（默认 input） |\n| `dictCode` | 字典编码（用于 select/radio 类型） |\n\n## Boot vs Cloud 核心架构差异\n\n生成代码时根据架构类型自动调整以下差异点：\n\n### 模块结构\n\n**Boot（单体应用）**：所有代码在同一模块内\n```\nsrc/main/java/org/springblade/modules/{module}/\n├── controller/\n├── service/ + impl/\n├── mapper/\n├── wrapper/\n├── entity/ + dto/ + vo/\n└── excel/\n```\n\n**Cloud（微服务）**：API 与实现分离为两个 Maven 模块\n```\nblade-service-api/blade-{module}-api/          ← 契约层\n└── org/springblade/{module}/\n    ├── entity/ + dto/ + vo/\n    └── feign/I{Name}Client.java               ← Feign 接口\n\nblade-service/blade-{module}/                  ← 实现层\n└── org/springblade/{module}/\n    ├── controller/\n    ├── service/ + impl/\n    ├── mapper/\n    ├── wrapper/\n    └── feign/{Name}Client.java                ← Feign 实现\n```\n\n### 关键差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| **基础包名** | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| **Controller 路由** | `@RequestMapping(AppConstant.APPLICATION_XXX_NAME + \"/{path}\")` | `@RequestMapping(\"/{path}\")` |\n| **服务间调用** | 直接注入 Service | Feign Client 远程调用 |\n| **Entity/VO 位置** | 与 Service 同模块 | 独立 API 模块中 |\n| **Feign Client** | 无 | API 模块：接口定义；Service 模块：实现类 |\n| **POM 依赖** | 单一 pom.xml | API 和 Service 各自的 pom.xml |\n| **服务注册** | 不需要 | Nacos 服务注册与发现 |\n| **Gateway 路由** | 不需要 | Gateway 自动按服务名路由 |\n\n### Boot 路由规则\nBoot 模式下 Controller 需要手动添加服务名前缀，因为没有 Gateway 路由：\n```java\n// Boot: 完整路径 /desk/notice/detail\n@RequestMapping(AppConstant.APPLICATION_DESK_NAME + \"/notice\")\n\n// Cloud: Gateway 自动添加 /blade-desk 前缀，只需 /notice/detail\n@RequestMapping(\"/notice\")\n```\n\n## 实体继承模式\n\n根据业务场景选择合适的实体基类：\n\n### 模式一：TenantEntity（默认，租户隔离业务表）\n- 继承 `TenantEntity`（`org.springblade.core.mp.base.TenantEntity`），自动拥有 `tenantId` + 全套审计字段\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 删除使用 `deleteLogic()`（逻辑删除）\n- 适用：User、Post 等绝大多数多租户业务表\n\n### 模式二：BaseEntity（无租户业务表）\n- 继承 `BaseEntity`（`org.springblade.core.mp.base.BaseEntity`），拥有审计字段但无 `tenantId`\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 适用：Tenant、Notice 等跨租户业务表\n- 也可通过在 `application.yml`（Boot）或 Nacos `blade.yaml`（Cloud）的 `blade.tenant.tables` 中配置表名实现字段隔离，不强制继承 TenantEntity\n\n### 模式三：Raw Serializable（原生模式）\n- 实现 `Serializable`，所有字段手动定义\n- Service 继承 `IService<Entity>` / `ServiceImpl<Mapper, Entity>`\n- 不继承 SpringBlade 基类，使用 MyBatis-Plus 原生 CRUD\n- 删除使用 `removeBatchByIds()`\n- 适用：Menu、Role、Dept、Dict、RoleMenu 等跨租户系统/关系表\n\n## 代码生成工作流\n\n### Step 1: 收集信息\n确认所有必需参数和可选参数，构建完整的生成上下文。\n\n### Step 2: 生成代码\n根据用户选择的生成范围，读取对应的参考文件并生成代码：\n\n| 生成范围 | 参考文件 | 生成产物 |\n|----------|----------|----------|\n| **后端** | 读取 `references/backend.md` | Entity、DTO、VO、Mapper、Mapper.xml、Service、ServiceImpl、Controller、Wrapper、Excel、Feign（Cloud） |\n| **前端** | 读取 `references/frontend.md` | API 模块、Option 配置、Vue 页面 |\n| **数据库** | 读取 `references/database.md` | MySQL 建表 SQL、菜单 SQL |\n\n### Step 3: 输出代码\n按文件维度逐个输出，每个文件包含：\n1. 完整文件路径（相对于项目根目录）\n2. 完整可运行的代码内容\n3. 必要的说明注释\n\n## 输出文件清单\n\n### 后端产物（Boot）\n```\nsrc/main/java/org/springblade/modules/{module}/entity/{Name}.java\nsrc/main/java/org/springblade/modules/{module}/dto/{Name}DTO.java\nsrc/main/java/org/springblade/modules/{module}/vo/{Name}VO.java\nsrc/main/java/org/springblade/modules/{module}/excel/{Name}Excel.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.xml\nsrc/main/java/org/springblade/modules/{module}/service/I{Name}Service.java\nsrc/main/java/org/springblade/modules/{module}/service/impl/{Name}ServiceImpl.java\nsrc/main/java/org/springblade/modules/{module}/controller/{Name}Controller.java\nsrc/main/java/org/springblade/modules/{module}/wrapper/{Name}Wrapper.java\n```\n\n### 后端产物（Cloud）\n```\nblade-service-api/blade-{module}-api/.../entity/{Name}.java\nblade-service-api/blade-{module}-api/.../dto/{Name}DTO.java\nblade-service-api/blade-{module}-api/.../vo/{Name}VO.java\nblade-service-api/blade-{module}-api/.../feign/I{Name}Client.java\nblade-service/blade-{module}/.../excel/{Name}Excel.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.xml\nblade-service/blade-{module}/.../service/I{Name}Service.java\nblade-service/blade-{module}/.../service/impl/{Name}ServiceImpl.java\nblade-service/blade-{module}/.../controller/{Name}Controller.java\nblade-service/blade-{module}/.../wrapper/{Name}Wrapper.java\nblade-service/blade-{module}/.../feign/{Name}Client.java\n```\n\n### 前端产物\n```\nsrc/api/{module}/{modelCode}.js\nsrc/option/{module}/{modelCode}.js\nsrc/views/{module}/{modelCode}.vue\n```\n\n### 数据库产物\n```\n{name}.mysql.sql              -- MySQL 建表语句\n{name}.menu.sql               -- 菜单数据 SQL\n```\n\n## 命名约定\n\n| 维度 | 规则 | 示例（实体名：OrderItem） |\n|------|------|--------------------------|\n| 实体类 | `{Name}` | `OrderItem` |\n| VO 类 | `{Name}VO` | `OrderItemVO` |\n| DTO 类 | `{Name}DTO` | `OrderItemDTO` |\n| Excel 类 | `{Name}Excel` | `OrderItemExcel` |\n| Mapper 接口 | `{Name}Mapper` | `OrderItemMapper` |\n| Mapper XML | `{Name}Mapper.xml` | `OrderItemMapper.xml` |\n| Service 接口 | `I{Name}Service` | `IOrderItemService` |\n| Service 实现 | `{Name}ServiceImpl` | `OrderItemServiceImpl` |\n| Controller | `{Name}Controller` | `OrderItemController` |\n| Wrapper | `{Name}Wrapper` | `OrderItemWrapper` |\n| Feign 接口 | `I{Name}Client` | `IOrderItemClient` |\n| Feign 实现 | `{Name}Client` | `OrderItemClient` |\n| 数据库表名 | `blade_{snake_case}` | `blade_order_item` |\n| API 路径 | `/{modelCode}` | `/order-item` |\n| 前端文件 | `{modelCode}` | `orderItem` |\n\n> SpringBlade 开源版 Entity 统一**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。请严格遵循此命名。\n\n## 重要规范\n\n1. **所有 Long 类型的 ID 字段**必须添加 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n2. **日期字段**必须同时添加 `@DateTimeFormat` 和 `@JsonFormat`，pattern 统一使用 `DateUtil.PATTERN_DATETIME`\n3. **逻辑删除字段** `isDeleted` 必须添加 `@TableLogic` 注解（继承 BaseEntity / TenantEntity 时由基类自动处理）\n4. **Swagger 文档**使用 OpenAPI 3 注解：`@Tag`、`@Operation`、`@Schema`、`@Parameter`\n5. **接口排序**：控制器类级标注无参 `@ApiOrder`（置于 `@Tag` 上方），分组内接口按源码声明顺序展示；个别接口需调整顺序时才在方法上标 `@ApiOrder(n)`\n6. **响应包装**统一使用 `R<T>` 类\n7. **Wrapper 的 entityVO() 方法**中通过 `DictCache.getValue()` 翻译字典字段\n8. **Controller 的 remove 方法**：TenantEntity / BaseEntity 用 `deleteLogic()`，Raw 模式用 `removeBatchByIds()`\n9. **权限控制**：使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 做角色级控制（SpringBlade 开源版仅支持角色权限）\n10. **前端权限标识**格式：`{modelCode}_{action}`，如 `notice_add`、`notice_edit`、`notice_delete`、`notice_view`\n\n## 参考文件索引\n\n| 生成任务 | 参考文件 | 说明 |\n|----------|----------|------|\n| 后端代码 | `references/backend.md` | 完整的后端各层代码模板，含 Boot/Cloud 变体 |\n| 前端代码 | `references/frontend.md` | API、Option 配置、Vue 页面模板（Options/Composition API） |\n| 数据库 SQL | `references/database.md` | MySQL 建表模板、字段类型映射与菜单 SQL |\n",".claude/skills/blade-design/references/backend.md":"# 后端代码生成参考\n\n本文档包含 SpringBlade 开源版后端各层的完整代码模板。所有模板均来源于 SpringBlade 开源工程真实业务代码（参考 `modules/system`、`modules/desk` 及 Cloud 的 `blade-service/blade-system` 等）。\n\n## 目录\n- [一、Entity 实体类](#一entity-实体类)\n- [二、DTO 数据传输对象](#二dto-数据传输对象)\n- [三、VO 视图对象](#三vo-视图对象)\n- [四、Excel 导出类](#四excel-导出类)\n- [五、Mapper 接口](#五mapper-接口)\n- [六、Mapper XML](#六mapper-xml)\n- [七、Service 接口](#七service-接口)\n- [八、ServiceImpl 实现](#八serviceimpl-实现)\n- [九、Controller 控制器](#九controller-控制器)\n- [十、Wrapper 数据转换器](#十wrapper-数据转换器)\n- [十一、Feign Client 接口（Cloud）](#十一feign-client-接口cloud)\n- [十二、Feign Client 实现（Cloud）](#十二feign-client-实现cloud)\n\n---\n\n## 一、Entity 实体类\n\n> 开源版 Entity 命名**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。\n\n### 模式A：TenantEntity（多租户业务表，默认）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.TenantEntity;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends TenantEntity {\n\n    /**\n     * {字段注释}\n     */\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 字段类型注解模式 ---\n\n    // Long 类型字段（防止前端精度丢失）\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"关联ID\")\n    private Long refId;\n\n    // Date 类型字段\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"发布时间\")\n    private Date releaseTime;\n\n    // Integer 字典字段（配合 Wrapper 翻译）\n    @Schema(description = \"分类\")\n    private Integer category;\n\n    // String 普通文本\n    @Schema(description = \"标题\")\n    private String title;\n\n    // String 大文本\n    @Schema(description = \"内容\")\n    private String content;\n}\n```\n\n**TenantEntity 自动继承的字段**（不需要在子类中声明）：\n- `id` (Long) - 主键，@TableId(type = IdType.ASSIGN_ID)，雪花 ID\n- `tenantId` (String) - 租户 ID\n- `createUser` (Long) - 创建人\n- `createDept` (Long) - 创建部门\n- `createTime` (Date) - 创建时间\n- `updateUser` (Long) - 更新人\n- `updateTime` (Date) - 更新时间\n- `status` (Integer) - 业务状态，默认 1\n- `isDeleted` (Integer) - 逻辑删除标记，@TableLogic，默认 0\n\n### 模式B：BaseEntity（跨租户业务表）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends BaseEntity {\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n}\n```\n\n**BaseEntity 继承字段**：与 TenantEntity 相同但不含 `tenantId`。\n\n### 模式C：Raw Serializable（原生模式）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@Schema(description = \"{中文名}\")\npublic class {Name} implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 主键id\n     */\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键id\")\n    @TableId(value = \"id\", type = IdType.ASSIGN_ID)\n    private Long id;\n\n    // --- 业务字段 ---\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 审计字段（手动定义） ---\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建人\", hidden = true)\n    private Long createUser;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建部门\", hidden = true)\n    private Long createDept;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"创建时间\", hidden = true)\n    private Date createTime;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"更新人\", hidden = true)\n    private Long updateUser;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"更新时间\", hidden = true)\n    private Date updateTime;\n\n    @Schema(description = \"业务状态\", hidden = true)\n    private Integer status;\n\n    @TableLogic\n    @Schema(description = \"是否已删除\", hidden = true)\n    private Integer isDeleted;\n}\n```\n\n### Cloud 版本 Entity 差异\n\nCloud 中 Entity 放在 API 模块，包名不同：\n```java\n// Boot\npackage org.springblade.modules.{module}.entity;\n\n// Cloud\npackage org.springblade.{module}.entity;\n```\n\n其余代码完全相同。\n\n---\n\n## 二、DTO 数据传输对象\n\n### 标准 DTO（继承 Entity）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}DTO extends {Name} {\n}\n```\n\n大多数情况下 DTO 为空类，仅作为扩展点。如有特定传输需求可添加字段。\n\n### 精简 DTO（仅特定字段）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\npublic class {Name}DTO implements Serializable {\n\n    @Schema(description = \"特定字段\")\n    private String specificField;\n}\n```\n\n---\n\n## 三、VO 视图对象\n\n### 标准 VO（简单扩展）\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}视图对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} {\n\n    /**\n     * 分类名称（由 Wrapper 通过字典翻译填充）\n     */\n    @Schema(description = \"分类名称\")\n    private String categoryName;\n}\n```\n\n对于每个需要字典翻译的字段，在 VO 中添加对应的 `{字段名}Name` 字段。\n\n### 树形 VO（实现 INode 接口）\n\n用于 Menu、Dict、Dept 等树形结构：\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.tool.node.INode;\nimport org.springblade.modules.{module}.entity.{Name};\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {中文名}视图对象（树形）\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} implements INode<{Name}VO> {\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键ID\")\n    private Long id;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"父节点ID\")\n    private Long parentId;\n\n    @JsonInclude(JsonInclude.Include.NON_EMPTY)\n    @Schema(description = \"子节点\")\n    private List<{Name}VO> children;\n\n    @Schema(description = \"是否有子节点\")\n    private Boolean hasChildren;\n\n    @Schema(description = \"上级名称\")\n    private String parentName;\n\n    @Override\n    public List<{Name}VO> getChildren() {\n        if (this.children == null) {\n            this.children = new ArrayList<>();\n        }\n        return this.children;\n    }\n}\n```\n\n---\n\n## 四、Excel 导出类\n\n```java\npackage org.springblade.modules.{module}.excel;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.idev.excel.annotation.write.style.ColumnWidth;\nimport cn.idev.excel.annotation.write.style.ContentRowHeight;\nimport cn.idev.excel.annotation.write.style.HeadRowHeight;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}导出对象\n *\n * @author {author}\n */\n@Data\n@ColumnWidth(25)\n@HeadRowHeight(20)\n@ContentRowHeight(18)\npublic class {Name}Excel implements Serializable {\n\n    @ExcelProperty(\"{字段中文名}\")\n    private {Type} {fieldName};\n\n    // 不导出审计字段（createUser、createTime 等）\n}\n```\n\n> 开源版使用 FastExcel（`cn.idev.excel.*`，由 `blade-starter-excel` 引入）。\n\n---\n\n## 五、Mapper 接口\n\n```java\npackage org.springblade.modules.{module}.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.excel.{Name}Excel;\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\nimport java.util.List;\n\n/**\n * {中文名} Mapper 接口\n *\n * @author {author}\n */\npublic interface {Name}Mapper extends BaseMapper<{Name}> {\n\n    /**\n     * 自定义分页查询\n     */\n    List<{Name}VO> select{Name}Page(IPage page, {Name}VO {modelCode});\n\n    /**\n     * 导出数据查询\n     */\n    List<{Name}Excel> export{Name}(@Param(\"ew\") Wrapper<{Name}> queryWrapper);\n}\n```\n\n**树形模板追加方法**：\n```java\n    /**\n     * 树形结构查询\n     */\n    List<{Name}VO> tree(String tenantId);\n```\n\n---\n\n## 六、Mapper XML\n\n### 标准 CRUD\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"org.springblade.modules.{module}.mapper.{Name}Mapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"{modelCode}ResultMap\" type=\"org.springblade.modules.{module}.entity.{Name}\">\n        <result column=\"id\" property=\"id\"/>\n        <!-- 每个业务字段一行映射 -->\n        <result column=\"{column_name}\" property=\"{fieldName}\"/>\n        <!-- 审计字段映射（TenantEntity 模式需要） -->\n        <result column=\"create_user\" property=\"createUser\"/>\n        <result column=\"create_dept\" property=\"createDept\"/>\n        <result column=\"create_time\" property=\"createTime\"/>\n        <result column=\"update_user\" property=\"updateUser\"/>\n        <result column=\"update_time\" property=\"updateTime\"/>\n        <result column=\"status\" property=\"status\"/>\n        <result column=\"is_deleted\" property=\"isDeleted\"/>\n    </resultMap>\n\n    <!-- 自定义分页查询 -->\n    <select id=\"select{Name}Page\" resultMap=\"{modelCode}ResultMap\">\n        SELECT\n            t.*,\n            d.dict_value AS categoryName\n        FROM blade_{table_name} t\n        LEFT JOIN (SELECT * FROM blade_dict WHERE code = '{dictCode}') d\n            ON t.category = d.dict_key\n        WHERE t.is_deleted = 0\n        <if test=\"{modelCode}.tenantId != null and {modelCode}.tenantId != ''\">\n            AND t.tenant_id = #{{{modelCode}.tenantId}}\n        </if>\n        <!-- 动态查询条件 -->\n        <if test=\"{modelCode}.{queryField} != null and {modelCode}.{queryField} != ''\">\n            AND t.{query_column} LIKE CONCAT('%', #{{{modelCode}.{queryField}}}, '%')\n        </if>\n        <if test=\"{modelCode}.{eqField} != null\">\n            AND t.{eq_column} = #{{{modelCode}.{eqField}}}\n        </if>\n    </select>\n\n    <!-- 导出查询 -->\n    <select id=\"export{Name}\" resultType=\"org.springblade.modules.{module}.excel.{Name}Excel\">\n        SELECT * FROM blade_{table_name}\n        ${ew.customSqlSegment}\n    </select>\n\n</mapper>\n```\n\n**注意事项**：\n- 如果有字典关联查询，在 select 中 LEFT JOIN blade_dict 表\n- 动态条件中 `LIKE` 用于模糊查询字段，`=` 用于精确查询字段\n- TenantEntity 模式需要 `tenant_id` 过滤条件\n- resultMap 中每个数据库列名（snake_case）映射到 Java 属性（camelCase）\n\n---\n\n## 七、Service 接口\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends BaseService<{Name}> {\n\n    /**\n     * 自定义分页\n     */\n    IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode});\n}\n```\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends IService<{Name}> {\n}\n```\n\n---\n\n## 八、ServiceImpl 实现\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends BaseServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n\n    @Override\n    public IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode}) {\n        {modelCode}.setTenantId(AuthUtil.getTenantId());\n        return page.setRecords(baseMapper.select{Name}Page(page, {modelCode}));\n    }\n}\n```\n\n**注意**：BaseEntity 模式下不需要 `setTenantId()` 那行。\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends ServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n}\n```\n\n---\n\n## 九、Controller 控制器\n\n### Boot 版本\n\n```java\npackage org.springblade.modules.{module}.controller;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springblade.modules.{module}.wrapper.{Name}Wrapper;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.Map;\n\n/**\n * {中文名} 控制器\n *\n * @author {author}\n */\n@RestController\n@AllArgsConstructor\n@RequestMapping(AppConstant.APPLICATION_{MODULE}_NAME + \"/{modelCode}\")\n@ApiOrder\n@Tag(name = \"{中文名}\", description = \"{中文名}接口\")\npublic class {Name}Controller extends BladeController {\n\n    private final I{Name}Service {modelCode}Service;\n\n    /**\n     * 详情\n     */\n    @GetMapping(\"/detail\")\n    @Operation(summary = \"详情\", description = \"传入{modelCode}\")\n    public R<{Name}VO> detail({Name} {modelCode}) {\n        {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n        return R.data({Name}Wrapper.build().entityVO(detail));\n    }\n\n    /**\n     * 分页列表\n     */\n    @GetMapping(\"/list\")\n    @Operation(summary = \"分页\", description = \"传入{modelCode}\")\n    @PreAuth(RoleConstant.HAS_ROLE_ADMIN)\n    public R<IPage<{Name}VO>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n        IPage<{Name}> pages = {modelCode}Service.page(\n            Condition.getPage(query),\n            Condition.getQueryWrapper({modelCode}, {Name}.class)\n        );\n        return R.data({Name}Wrapper.build().pageVO(pages));\n    }\n\n    /**\n     * 自定义分页\n     */\n    @GetMapping(\"/page\")\n    @Operation(summary = \"自定义分页\", description = \"传入{modelCode}\")\n    public R<IPage<{Name}VO>> page({Name}VO {modelCode}, Query query) {\n        IPage<{Name}VO> pages = {modelCode}Service.select{Name}Page(Condition.getPage(query), {modelCode});\n        return R.data(pages);\n    }\n\n    /**\n     * 新增\n     */\n    @PostMapping(\"/save\")\n    @Operation(summary = \"新增\", description = \"传入{modelCode}\")\n    public R save(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.save({modelCode}));\n    }\n\n    /**\n     * 修改\n     */\n    @PostMapping(\"/update\")\n    @Operation(summary = \"修改\", description = \"传入{modelCode}\")\n    public R update(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.updateById({modelCode}));\n    }\n\n    /**\n     * 新增或修改\n     */\n    @PostMapping(\"/submit\")\n    @Operation(summary = \"新增或修改\", description = \"传入{modelCode}\")\n    public R submit(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.saveOrUpdate({modelCode}));\n    }\n\n    /**\n     * 删除\n     */\n    @PostMapping(\"/remove\")\n    @Operation(summary = \"逻辑删除\", description = \"传入ids\")\n    public R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n        return R.status({modelCode}Service.deleteLogic(Func.toLongList(ids)));\n    }\n}\n```\n\n### Cloud 版本差异\n\nCloud 版本仅以下几处不同：\n\n```java\n// 1. 包名不同\npackage org.springblade.{module}.controller;\n\n// 2. import 路径不同（Entity/VO/Service 都在 org.springblade.{module} 下）\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.vo.{Name}VO;\nimport org.springblade.{module}.service.I{Name}Service;\n\n// 3. RequestMapping 不需要服务名前缀\n@RequestMapping(\"/{modelCode}\")\n```\n\n### 无 Wrapper 版本\n\n如不使用 Wrapper，detail 和 list 方法简化为：\n\n```java\n@GetMapping(\"/detail\")\npublic R<{Name}> detail({Name} {modelCode}) {\n    {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n    return R.data(detail);\n}\n\n@GetMapping(\"/list\")\npublic R<IPage<{Name}>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n    IPage<{Name}> pages = {modelCode}Service.page(\n        Condition.getPage(query),\n        Condition.getQueryWrapper({modelCode}, {Name}.class)\n    );\n    return R.data(pages);\n}\n```\n\n### Raw Serializable 模式的 remove 方法\n\n```java\n@PostMapping(\"/remove\")\n@Operation(summary = \"删除\", description = \"传入ids\")\npublic R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n    return R.status({modelCode}Service.removeBatchByIds(Func.toLongList(ids)));\n}\n```\n\n### 权限注解使用规则\n\nSpringBlade 开源版仅支持角色级权限：\n\n| 场景 | 写法 |\n|------|------|\n| 仅管理员可访问（类级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在类上 |\n| 仅管理员可访问（方法级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在方法上 |\n| 隐藏 Swagger 文档 | `@Hidden`（来自 OpenAPI 3） |\n| 全公开 | 不加任何权限注解 |\n\n### 导出接口\n\n```java\n@GetMapping(\"/export-{modelCode}\")\n@Operation(summary = \"导出数据\", description = \"传入{modelCode}\")\n@PreAuth(RoleConstant.HAS_ROLE_ADMIN)\npublic void export{Name}(@RequestParam Map<String, Object> {modelCode}, HttpServletResponse response) {\n    QueryWrapper<{Name}> queryWrapper = Condition.getQueryWrapper({modelCode}, {Name}.class);\n    List<{Name}Excel> list = {modelCode}Service.export{Name}(queryWrapper);\n    ExcelUtil.export(response, \"{中文名}数据\", \"{中文名}数据表\", list, {Name}Excel.class);\n}\n```\n\n---\n\n## 十、Wrapper 数据转换器\n\n```java\npackage org.springblade.modules.{module}.wrapper;\n\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.modules.system.cache.DictCache;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名}包装类，返回视图层所需的字段\n *\n * @author {author}\n */\npublic class {Name}Wrapper extends BaseEntityWrapper<{Name}, {Name}VO> {\n\n    public static {Name}Wrapper build() {\n        return new {Name}Wrapper();\n    }\n\n    @Override\n    public {Name}VO entityVO({Name} {modelCode}) {\n        {Name}VO {modelCode}VO = BeanUtil.copyProperties({modelCode}, {Name}VO.class);\n        // 字典翻译：将字典键值转换为可读名称\n        String categoryName = DictCache.getValue(\"{dictCode}\", {modelCode}.getCategory());\n        {modelCode}VO.setCategoryName(categoryName);\n        return {modelCode}VO;\n    }\n}\n```\n\n**常见翻译模式**：\n\n```java\n// 1. 字典翻译\nString categoryName = DictCache.getValue(\"{dictCode}\", entity.getCategory());\n\n// 2. 关联实体名称（通过缓存）\nUser user = UserCache.getUser(entity.getUserId());\nif (Func.isNotEmpty(user)) {\n    vo.setUserName(user.getAccount());\n}\n\n// 3. 父节点名称（树形结构）\nif (Func.equals(entity.getParentId(), BladeConstant.TOP_PARENT_ID)) {\n    vo.setParentName(BladeConstant.TOP_PARENT_NAME);\n} else {\n    {Name} parent = {modelCode}Service.getById(entity.getParentId());\n    vo.setParentName(parent.getName());\n}\n```\n\n**树形 Wrapper 追加方法**：\n\n```java\npublic List<{Name}VO> listNodeVO(List<{Name}> list) {\n    List<{Name}VO> collect = list.stream()\n        .map(entity -> BeanUtil.copyProperties(entity, {Name}VO.class))\n        .collect(Collectors.toList());\n    return ForestNodeMerger.merge(collect);\n}\n```\n\n---\n\n## 十一、Feign Client 接口（Cloud）\n\n放在 API 模块中（`blade-service-api/blade-{module}-api`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.{module}.entity.{Name};\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n/**\n * {中文名} Feign 接口\n *\n * @author {author}\n */\n@FeignClient(value = AppConstant.APPLICATION_{MODULE}_NAME)\npublic interface I{Name}Client {\n\n    String API_PREFIX = \"/feign/client/{modelCode}\";\n    String TOP = API_PREFIX + \"/top\";\n\n    /**\n     * 获取分页数据\n     */\n    @GetMapping(TOP)\n    BladePage<{Name}> top(@RequestParam(\"current\") Integer current, @RequestParam(\"size\") Integer size);\n}\n```\n\n---\n\n## 十二、Feign Client 实现（Cloud）\n\n放在 Service 模块中（`blade-service/blade-{module}`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Hidden;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.service.I{Name}Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * {中文名} Feign 实现\n *\n * @author {author}\n */\n@Hidden\n@RestController\n@AllArgsConstructor\npublic class {Name}Client implements I{Name}Client {\n\n    private final I{Name}Service service;\n\n    @Override\n    @GetMapping(TOP)\n    public BladePage<{Name}> top(Integer current, Integer size) {\n        Query query = new Query();\n        query.setCurrent(current);\n        query.setSize(size);\n        IPage<{Name}> page = service.page(Condition.getPage(query));\n        return BladePage.of(page);\n    }\n}\n```\n\n**Feign 实现要点**：\n- `@Hidden`：对 Swagger 文档隐藏（内部接口）\n- `@RestController`：作为 HTTP 端点暴露\n- 路径常量复用接口中定义的常量\n\n---\n\n## 附：完整 import 参考\n\n### Entity 常用 import\n```java\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;      // BaseEntity\nimport org.springblade.core.mp.base.TenantEntity;   // TenantEntity\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n```\n\n### Controller 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;      // Boot 路由前缀使用\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springframework.web.bind.annotation.*;\n```\n\n### Service 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springframework.stereotype.Service;\n```\n\n### Wrapper 常用 import\n```java\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.constant.BladeConstant;\nimport org.springblade.core.tool.node.ForestNodeMerger;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.system.cache.DictCache;        // 字典缓存（Boot 路径）\n```\n",".claude/skills/blade-design/references/database.md":"# 数据库建表语句生成参考\n\n本文档包含 SpringBlade 开源版 MySQL 建表模板与菜单数据 SQL。所有模板均来源于开源工程 `doc/sql/` 下的真实 SQL 脚本。\n\n> SpringBlade 开源版仅官方支持 **MySQL**，生成的建表脚本固定为 MySQL 方言。\n\n## 目录\n- [一、通用审计字段](#一通用审计字段)\n- [二、字段类型映射表](#二字段类型映射表)\n- [三、MySQL 建表模板](#三mysql-建表模板)\n- [四、菜单 SQL 生成](#四菜单-sql-生成)\n\n---\n\n## 一、通用审计字段\n\n所有继承 `TenantEntity` 或 `BaseEntity` 的业务表都包含以下固定审计字段：\n\n| 字段名 | 说明 | Java 类型 | 是否可空 | 默认值 |\n|--------|------|-----------|----------|--------|\n| `id` | 主键（雪花 ID） | Long | NOT NULL | 无 |\n| `tenant_id` | 租户 ID（仅 TenantEntity） | String | NULL | '000000' |\n| `create_user` | 创建人 | Long | NULL | NULL |\n| `create_dept` | 创建部门 | Long | NULL | NULL |\n| `create_time` | 创建时间 | Date | NULL | NULL |\n| `update_user` | 修改人 | Long | NULL | NULL |\n| `update_time` | 修改时间 | Date | NULL | NULL |\n| `status` | 业务状态 | Integer | NULL | 1 |\n| `is_deleted` | 逻辑删除标记 | Integer | NULL | 0 |\n\n- **BaseEntity 模式**不包含 `tenant_id` 字段\n- **Raw Serializable 模式**手动定义全部字段（结构相同）\n\n---\n\n## 二、字段类型映射表\n\n### Java 类型 → MySQL 类型映射\n\n| Java 类型 | MySQL 类型 | 说明 |\n|-----------|-----------|------|\n| `String` (短) | `varchar(n)` | `n` 为字符数，通常 `varchar(255)` 以内 |\n| `String` (长) | `text` | 超过 255 字符或富文本 |\n| `Integer` | `int` | 32 位整型 |\n| `Long` | `bigint` | 64 位整型（雪花 ID 必须用） |\n| `Double` | `double` | 双精度浮点 |\n| `BigDecimal` | `decimal(m,n)` | 金额、精确数值 |\n| `Date` | `datetime` | 日期 + 时间 |\n| `Boolean` | `tinyint(1)` | 0 = false，1 = true |\n\n### 审计字段各字段定义（MySQL）\n\n| 字段 | MySQL 定义 |\n|------|-----------|\n| id | `bigint NOT NULL` |\n| tenant_id | `varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000'` |\n| create_user | `bigint NULL DEFAULT NULL` |\n| create_dept | `bigint NULL DEFAULT NULL` |\n| create_time | `datetime NULL DEFAULT NULL` |\n| update_user | `bigint NULL DEFAULT NULL` |\n| update_time | `datetime NULL DEFAULT NULL` |\n| status | `int NULL DEFAULT 1` |\n| is_deleted | `int NULL DEFAULT 0` |\n\n---\n\n## 三、MySQL 建表模板\n\n### TenantEntity 模式（多租户业务表）\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for blade_{table_name}\n-- ----------------------------\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  `tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### BaseEntity 模式（跨租户业务表）\n\n与 TenantEntity 模板一致，**仅删除 `tenant_id` 这一行**：\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### Raw Serializable 模式（系统 / 关系表）\n\n结构同 BaseEntity 模板（同样无 `tenant_id`）。差异在于 Java 侧手动定义全部字段，数据库层面建表语句本身不变。\n\n### 索引建议\n\n对于查询频繁的字段：\n\n```sql\n-- 普通索引\nCREATE INDEX idx_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 唯一索引\nCREATE UNIQUE INDEX uk_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 复合索引（多字段）\nCREATE INDEX idx_{table_name}_{col1}_{col2} ON blade_{table_name} ({col1}, {col2});\n```\n\n**命名约定**：\n- 唯一索引前缀 `uk_`\n- 普通索引前缀 `idx_`\n- 字段名用下划线拼接\n\n### MySQL 约定\n\n- 列名使用反引号 `` ` `` 包裹\n- 注释直接用 `COMMENT` 关键字写在列定义后\n- 表注释在 `CREATE TABLE` 末尾\n- 字符集固定 `utf8mb4`，排序规则 `utf8mb4_general_ci`\n- 存储引擎 `InnoDB`\n- 主键索引类型 `USING BTREE`\n- 租户 ID 列 `varchar(12)` 默认 `'000000'`\n\n---\n\n## 四、菜单 SQL 生成\n\n每个新模块需要插入 5 条菜单记录：1 个主菜单 + 4 个操作按钮（新增、修改、删除、查看）。\n\n### MySQL 菜单 SQL\n\n```sql\n-- ----------------------------\n-- {中文名}菜单数据\n-- ----------------------------\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({menuId}, {parentMenuId}, '{modelCode}', '{中文名}', 'menu', '/{module}/{modelCode}', NULL, 1, 1, 0, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({addMenuId}, {menuId}, '{modelCode}_add', '新增', 'add', '/{module}/{modelCode}/add', 'plus', 1, 2, 1, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({editMenuId}, {menuId}, '{modelCode}_edit', '修改', 'edit', '/{module}/{modelCode}/edit', 'form', 2, 2, 2, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({removeMenuId}, {menuId}, '{modelCode}_delete', '删除', 'delete', '/{module}/{modelCode}/delete', 'delete', 3, 2, 3, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({viewMenuId}, {menuId}, '{modelCode}_view', '查看', 'view', '/{module}/{modelCode}/view', 'eye-open', 4, 2, 2, 1, NULL, 0);\n```\n\n### 菜单字段说明\n\n| 字段 | 说明 | 值 |\n|------|------|-----|\n| `id` | 主菜单 ID | 生成雪花 ID 或自定义（如 1123598812738675301） |\n| `parent_id` | 父菜单 ID | 由用户指定或放在顶级（0） |\n| `code` | 权限编码 | 主菜单: `{modelCode}`，按钮: `{modelCode}_{action}` |\n| `name` | 显示名称 | 主菜单: `{中文名}`，按钮: 新增/修改/删除/查看 |\n| `alias` | 别名 | 主菜单: `menu`，按钮: add/edit/delete/view |\n| `path` | 路由路径 | `/{module}/{modelCode}` |\n| `source` | 图标 | 按钮: plus/form/delete/eye-open |\n| `sort` | 排序 | 按钮从 1 到 4 |\n| `category` | 类别 | 1=菜单，2=按钮 |\n| `action` | 操作类型 | 0=无，1=新增，2=修改，3=删除 |\n| `is_open` | 是否打开 | 1=是 |\n| `is_deleted` | 是否删除 | 0=否 |\n\n---\n\n## 附：无租户表模式速查\n\n如果实体不需要 `tenant_id`（BaseEntity 或 Raw 模式），从建表模板中直接移除 `tenant_id` 行即可，其余审计字段保持不变。\n",".claude/skills/blade-design/references/frontend.md":"# 前端代码生成参考\n\n本文档包含 SpringBlade 开源版 Saber 前端各层的完整代码模板。基于 Vue 3 + Element Plus + Avue 技术栈，支持 Options API 和 Composition API 两种风格。\n\n> 前端 API 路径在 Boot 单体与 Cloud 微服务下**完全一致**，都走 `/blade-{module}/...`。Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 前缀对齐该路径，Cloud 则由 Gateway 按服务名 `blade-{module}` 自动路由。\n\n## 目录\n- [一、API 层](#一api-层)\n- [二、Option 配置层](#二option-配置层)\n- [三、Vue 页面 - Options API](#三vue-页面---options-api)\n- [四、Vue 页面 - Composition API](#四vue-页面---composition-api)\n- [五、树形结构模式](#五树形结构模式)\n- [六、主子表模式](#六主子表模式)\n\n---\n\n## 一、API 层\n\n文件位置：`src/api/{module}/{modelCode}.js`\n\n### 标准 CRUD API\n\n```javascript\nimport request from '@/axios';\n\n/**\n * 获取{中文名}列表（分页）\n */\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n    },\n  });\n};\n\n/**\n * 获取{中文名}详情\n */\nexport const getDetail = (id) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/detail',\n    method: 'get',\n    params: {\n      id,\n    },\n  });\n};\n\n/**\n * 删除{中文名}\n */\nexport const remove = (ids) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/remove',\n    method: 'post',\n    params: {\n      ids,\n    },\n  });\n};\n\n/**\n * 新增{中文名}\n */\nexport const add = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\n/**\n * 修改{中文名}\n */\nexport const update = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n```\n\n**注意事项**：\n- 新增和修改共用 `/submit` 接口（后端 `saveOrUpdate`）\n- `remove` 的 `ids` 是逗号分隔的字符串\n- GET 请求用 `params`，POST 请求用 `data`（请求体）\n- 如需加密传输，添加 `cryptoToken: true` 和 `cryptoData: true`\n\n### 树形模式追加\n\n```javascript\n/**\n * 获取{中文名}树形结构\n */\nexport const getTree = (tenantId) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/tree',\n    method: 'get',\n    params: {\n      tenantId,\n    },\n  });\n};\n```\n\n### 导出接口\n\n```javascript\nimport { exportBlob } from '@/api/common';\n\n/**\n * 导出{中文名}数据\n */\nexport const exportData = (params) => {\n  return exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}`, params);\n};\n```\n\n---\n\n## 二、Option 配置层\n\n文件位置：`src/option/{module}/{modelCode}.js`\n\n### 标准 CRUD 配置\n\n```javascript\nexport const tableOption = {\n  height: 'auto',\n  calcHeight: 30,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  viewBtn: true,\n  dialogClickModal: false,\n  column: [\n    // --- 文本输入框 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      search: true,                    // 是否为搜索字段\n      rules: [\n        {\n          required: true,\n          message: '请输入{字段中文名}',\n          trigger: 'blur',\n        },\n      ],\n    },\n\n    // --- 数字输入 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'number',\n      align: 'right',\n      width: 100,\n    },\n\n    // --- 下拉选择（远程字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      search: true,\n      dicUrl: '/blade-system/dict/dictionary?code={dictCode}',\n      props: {\n        label: 'dictValue',\n        value: 'dictKey',\n      },\n      rules: [\n        {\n          required: true,\n          message: '请选择{字段中文名}',\n          trigger: 'change',\n        },\n      ],\n    },\n\n    // --- 下拉选择（本地字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 开关 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'switch',\n      align: 'center',\n      width: 100,\n      dicData: [\n        { label: '否', value: 0 },\n        { label: '是', value: 1 },\n      ],\n      value: 0,\n    },\n\n    // --- 单选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'radio',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n      value: 1,\n    },\n\n    // --- 多选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'checkbox',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 日期选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'date',\n      format: 'YYYY-MM-DD',\n      valueFormat: 'YYYY-MM-DD',\n    },\n\n    // --- 日期时间选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetime',\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 日期范围搜索 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetimerange',\n      searchRange: true,\n      search: true,\n      hide: true,                      // 列表不显示\n      display: false,                  // 表单不显示\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 多行文本 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'textarea',\n      span: 24,                        // 占满整行\n      hide: true,                      // 列表不显示\n      minRows: 3,\n    },\n\n    // --- 富文本编辑器 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      component: 'avue-ueditor',\n      options: {\n        action: '/blade-resource/oss/endpoint/put-file',\n        props: {\n          res: 'data',\n          url: 'link',\n        },\n      },\n      span: 24,\n      hide: true,\n      minRows: 6,\n    },\n\n    // --- 仅列表显示，表单不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      addDisplay: false,\n      editDisplay: false,\n    },\n\n    // --- 仅表单显示，列表不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      hide: true,\n    },\n\n    // --- 自定义插槽 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      slot: true,                      // 启用自定义渲染插槽\n    },\n\n    // --- 备注（隐藏在列表） ---\n    {\n      label: '备注',\n      prop: 'remark',\n      hide: true,\n    },\n  ],\n};\n```\n\n### 分组表单配置\n\n用于复杂表单（如用户管理），将字段分组到多个 Tab 页：\n\n```javascript\nexport const tableOption = (safe) => {\n  // 自定义校验器（使用闭包访问组件实例）\n  const validatePass = (rule, value, callback) => {\n    if (value === '') {\n      callback(new Error('请输入密码'));\n    } else {\n      callback();\n    }\n  };\n\n  return {\n    height: 'auto',\n    calcHeight: 150,\n    tip: false,\n    searchShow: true,\n    searchMenuSpan: 6,\n    border: true,\n    index: true,\n    selection: true,\n    viewBtn: true,\n    dialogType: 'drawer',\n    dialogWidth: 1000,\n    dialogClickModal: false,\n\n    // 列表使用 column\n    column: [\n      { label: '字段A', prop: 'fieldA', search: true },\n      { label: '字段B', prop: 'fieldB' },\n    ],\n\n    // 表单使用 group（分组标签页）\n    group: [\n      {\n        label: '基础信息',\n        prop: 'baseInfo',\n        icon: 'el-icon-user-solid',\n        column: [\n          { label: '字段A', prop: 'fieldA', rules: [{ required: true, message: '请输入', trigger: 'blur' }] },\n          { label: '字段B', prop: 'fieldB', type: 'select', dicData: [] },\n        ],\n      },\n      {\n        label: '详细信息',\n        prop: 'detailInfo',\n        icon: 'el-icon-s-order',\n        column: [\n          { label: '字段C', prop: 'fieldC' },\n          { label: '字段D', prop: 'fieldD', type: 'textarea', span: 24 },\n        ],\n      },\n    ],\n  };\n};\n```\n\n### 列属性速查\n\n| 属性 | 类型 | 说明 |\n|------|------|------|\n| `label` | String | 列标题/表单标签 |\n| `prop` | String | 字段名 |\n| `type` | String | 组件类型：input/select/radio/checkbox/switch/date/datetime/textarea/number/tree/upload |\n| `search` | Boolean | 是否为搜索字段 |\n| `hide` | Boolean | 列表中隐藏 |\n| `display` | Boolean | 表单中隐藏（false=不显示） |\n| `addDisplay` | Boolean | 新增表单中隐藏 |\n| `editDisplay` | Boolean | 编辑表单中隐藏 |\n| `addDisabled` | Boolean | 新增时禁用 |\n| `editDisabled` | Boolean | 编辑时禁用 |\n| `span` | Number | 表单栅格宽度（24=整行） |\n| `slot` | Boolean | 启用自定义插槽 |\n| `rules` | Array | 校验规则 |\n| `dicUrl` | String | 远程字典 URL |\n| `dicData` | Array | 本地字典数据 |\n| `props` | Object | 字典映射 { label, value } |\n| `value` | Any | 默认值 |\n| `width` | Number | 列宽度 |\n| `align` | String | 对齐方式：left/center/right |\n| `overHidden` | Boolean | 超长文本省略显示 |\n| `component` | String | 自定义组件名 |\n| `searchRange` | Boolean | 搜索使用范围选择 |\n| `format` | String | 显示格式 |\n| `valueFormat` | String | 值格式 |\n| `minRows` | Number | textarea 最小行数 |\n\n---\n\n## 三、Vue 页面 - Options API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"$refs.crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n\n      <!-- 自定义列插槽示例 -->\n      <template #{slotField}=\"{ row }\">\n        <el-tag>{{ row.{slotField} }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\nimport { mapGetters } from 'vuex';\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      option: tableOption,\n      data: [],\n    };\n  },\n\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: this.validData(this.permission.{modelCode}_add, false),\n        viewBtn: this.validData(this.permission.{modelCode}_view, false),\n        delBtn: this.validData(this.permission.{modelCode}_delete, false),\n        editBtn: this.validData(this.permission.{modelCode}_edit, false),\n      };\n    },\n    ids() {\n      let ids = [];\n      this.selectionList.forEach(ele => {\n        ids.push(ele.id);\n      });\n      return ids.join(',');\n    },\n  },\n\n  methods: {\n    rowSave(row, done, loading) {\n      add(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdate(row, index, done, loading) {\n      update(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(row.id);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n      });\n    },\n\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据');\n        return;\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(this.ids);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n        this.$refs.crud.toggleSelection();\n      });\n    },\n\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data;\n        });\n      }\n      done();\n    },\n\n    searchChange(params, done) {\n      this.query = params;\n      this.page.currentPage = 1;\n      this.onLoad(this.page, params);\n      done();\n    },\n\n    searchReset() {\n      this.query = {};\n      this.onLoad(this.page);\n    },\n\n    selectionChange(list) {\n      this.selectionList = list;\n    },\n\n    selectionClear() {\n      this.selectionList = [];\n      this.$refs.crud.toggleSelection();\n    },\n\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage;\n    },\n\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize;\n    },\n\n    refreshChange() {\n      this.onLoad(this.page, this.query);\n    },\n\n    onLoad(page, params = {}) {\n      this.loading = true;\n      getList(\n        page.currentPage,\n        page.pageSize,\n        Object.assign(params, this.query)\n      ).then(res => {\n        const data = res.data.data;\n        this.page.total = data.total;\n        this.data = data.records;\n        this.loading = false;\n        this.selectionClear();\n      });\n    },\n  },\n};\n</script>\n```\n\n---\n\n## 四、Vue 页面 - Composition API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed } from 'vue';\nimport { useStore } from 'vuex';\nimport { ElMessage, ElMessageBox } from 'element-plus';\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\n\nconst store = useStore();\nconst crud = ref(null);\n\n// 数据\nconst form = ref({});\nconst query = ref({});\nconst loading = ref(true);\nconst data = ref([]);\nconst selectionList = ref([]);\nconst option = reactive(tableOption);\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n});\n\n// 权限\nconst permission = computed(() => store.getters.permission);\nconst permissionList = computed(() => {\n  const validData = (value, defaultVal = false) => (value ? true : defaultVal);\n  return {\n    addBtn: validData(permission.value.{modelCode}_add),\n    viewBtn: validData(permission.value.{modelCode}_view),\n    delBtn: validData(permission.value.{modelCode}_delete),\n    editBtn: validData(permission.value.{modelCode}_edit),\n  };\n});\n\nconst ids = computed(() => {\n  return selectionList.value.map(ele => ele.id).join(',');\n});\n\n// 方法\nconst rowSave = (row, done, loading) => {\n  add(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(row.id);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n  });\n};\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据');\n    return;\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(ids.value);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n    crud.value.toggleSelection();\n  });\n};\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data;\n    });\n  }\n  done();\n};\n\nconst searchChange = (params, done) => {\n  query.value = params;\n  page.value.currentPage = 1;\n  onLoad(page.value, params);\n  done();\n};\n\nconst searchReset = () => {\n  query.value = {};\n  onLoad(page.value);\n};\n\nconst selectionChange = (list) => {\n  selectionList.value = list;\n};\n\nconst currentChange = (currentPage) => {\n  page.value.currentPage = currentPage;\n};\n\nconst sizeChange = (pageSize) => {\n  page.value.pageSize = pageSize;\n};\n\nconst refreshChange = () => {\n  onLoad(page.value, query.value);\n};\n\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(\n    page.currentPage,\n    page.pageSize,\n    Object.assign(params, query.value)\n  ).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n    selectionList.value = [];\n  });\n};\n</script>\n```\n\n### Options API vs Composition API 转换对照\n\n| Options API | Composition API |\n|-------------|-----------------|\n| `data() { return { form: {} } }` | `const form = ref({})` |\n| `computed: { ids() { ... } }` | `const ids = computed(() => { ... })` |\n| `methods: { rowSave() { ... } }` | `const rowSave = () => { ... }` |\n| `this.form` | `form.value`（模板中直接用 `form`） |\n| `this.$refs.crud` | `crud.value` |\n| `this.$message.success(...)` | `ElMessage.success(...)` |\n| `this.$confirm(...)` | `ElMessageBox.confirm(...)` |\n| `...mapGetters(['permission'])` | `const permission = computed(() => store.getters.permission)` |\n| `mounted() { ... }` | `onMounted(() => { ... })` |\n| `watch: { ... }` | `watch(source, callback)` |\n\n---\n\n## 五、树形结构模式\n\n### Option 配置（追加树形父节点字段）\n\n```javascript\n// 在 column 中添加树形父节点字段\n{\n  label: '上级{中文名}',\n  prop: 'parentId',\n  type: 'tree',\n  dicData: [],                    // 动态加载\n  hide: true,\n  value: 0,                       // 默认值为顶级\n  props: {\n    label: 'title',\n    value: 'value',\n    children: 'children',\n  },\n},\n```\n\n### Vue 页面（树形数据加载）\n\n在 `onLoad` 之后加载树形数据并更新列配置：\n\n```javascript\n// Options API\nmethods: {\n  onLoad(page, params = {}) {\n    this.loading = true;\n    getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n      const data = res.data.data;\n      this.page.total = data.total;\n      this.data = data.records;\n      this.loading = false;\n\n      // 加载树形数据\n      getTree().then(treeRes => {\n        const column = this.findObject(this.option.column, 'parentId');\n        column.dicData = treeRes.data.data;\n      });\n    });\n  },\n},\n\n// Composition API\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(page.currentPage, page.pageSize, Object.assign(params, query.value)).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n\n    getTree().then(treeRes => {\n      const column = findObject(option.column, 'parentId');\n      column.dicData = treeRes.data.data;\n    });\n  });\n};\n```\n\n### 表格树形展示\n\n在 avue-crud 模板中使用树形属性：\n\n```vue\n<avue-crud\n  :option=\"option\"\n  :data=\"data\"\n  row-key=\"id\"\n  :tree-props=\"{ children: 'children', hasChildren: 'hasChildren' }\"\n>\n```\n\n---\n\n## 六、主子表模式\n\n### API 层（主表 + 子表）\n\n```javascript\n// 主表 API（同标准 CRUD）\nexport const getList = (current, size, params) => { ... };\nexport const add = (row) => { ... };\nexport const update = (row) => { ... };\nexport const remove = (ids) => { ... };\n\n// 子表 API\nexport const getSubList = (current, size, {fkFieldName}, params) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n      {fkFieldName},\n    },\n  });\n};\n\nexport const addSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const updateSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const removeSub = (ids) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/remove',\n    method: 'post',\n    params: { ids },\n  });\n};\n```\n\n### Vue 页面（主子表 Drawer 模式）\n\n```vue\n<template>\n  <basic-container>\n    <!-- 主表 -->\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu=\"{ row }\">\n        <el-button text type=\"primary\" @click=\"openSub(row)\">子表</el-button>\n      </template>\n    </avue-crud>\n\n    <!-- 子表抽屉 -->\n    <el-drawer\n      title=\"{子表中文名}\"\n      v-model=\"subBox\"\n      size=\"60%\"\n      append-to-body\n    >\n      <avue-crud\n        :option=\"subOption\"\n        :table-loading=\"subLoading\"\n        :data=\"subData\"\n        :page=\"subPage\"\n        ref=\"crudSub\"\n        v-model=\"subForm\"\n        @row-del=\"rowDelSub\"\n        @row-update=\"rowUpdateSub\"\n        @row-save=\"rowSaveSub\"\n        @on-load=\"onLoadSub\"\n      />\n    </el-drawer>\n  </basic-container>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      // 主表数据\n      form: {},\n      data: [],\n      loading: true,\n      page: { pageSize: 10, currentPage: 1, total: 0 },\n      option: tableOption,\n\n      // 子表数据\n      subBox: false,\n      subForm: {},\n      subData: [],\n      subLoading: true,\n      subPage: { pageSize: 10, currentPage: 1, total: 0 },\n      subOption: subTableOption,\n      mainId: '',\n    };\n  },\n\n  methods: {\n    openSub(row) {\n      this.mainId = row.id;\n      this.subBox = true;\n      this.onLoadSub(this.subPage);\n    },\n\n    rowSaveSub(row, done, loading) {\n      row.{fkFieldName} = this.mainId;\n      addSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdateSub(row, index, done, loading) {\n      updateSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDelSub(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return removeSub(row.id);\n      }).then(() => {\n        this.onLoadSub(this.subPage);\n        this.$message.success('操作成功!');\n      });\n    },\n\n    onLoadSub(page, params = {}) {\n      this.subLoading = true;\n      getSubList(\n        page.currentPage,\n        page.pageSize,\n        this.mainId,\n        params\n      ).then(res => {\n        const data = res.data.data;\n        this.subPage.total = data.total;\n        this.subData = data.records;\n        this.subLoading = false;\n      });\n    },\n\n    // ... 主表方法同标准 CRUD\n  },\n};\n</script>\n```\n\n---\n\n## 附：常用工具函数\n\n### 数组与逗号字符串互转\n\n后端用逗号字符串存储多选值（如 deptId=\"1,2,3\"），前端需要转换：\n\n```javascript\nimport func from '@/utils/func';\n\n// 提交时：数组 → 逗号字符串\nrow.deptId = func.join(row.deptId);\n\n// 回显时：逗号字符串 → 数组\nthis.form.deptId = func.split(this.form.deptId);\n```\n\n### 导出功能\n\n```javascript\nimport NProgress from 'nprogress';\nimport { exportBlob } from '@/api/common';\nimport { downloadXls } from '@/utils/util';\n\nconst handleExport = () => {\n  NProgress.start();\n  exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}?${qs.stringify(this.query)}`).then(res => {\n    downloadXls(res.data, `{中文名}数据${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);\n    NProgress.done();\n  });\n};\n```\n\n### 辅助方法 findObject / findColumn\n\n```javascript\n// 在 option 的 column 中查找指定 prop 的列\nconst column = this.findObject(this.option.column, 'fieldName');\n\n// 在 group 中查找\nconst column = this.findColumn(this.option.group, 'fieldName');\n```\n\n这两个是全局挂载的工具方法（在 `main.js` 中注册）。\n",".claude/skills/blade-plan/README.md":"# Blade Plan\n\nSpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为结构化任务，逐个执行，全程无辅助文件。\n\n## 核心理念\n\n**先想清楚，再动手写。** 面对涉及多个文件的改动，不急着写代码，而是先理清思路、拆好任务、确认方案，再逐个执行。用最小的流程开销，获得结构化开发的核心收益。\n\n```\n需求 → 分析规划 → 确认 → 逐任务执行 → 完成总结\n```\n\n## 快速开始\n\n```bash\n# 启动规划执行 — 展示方案后等待确认\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n\n# 自动模式 — 展示方案后直接执行，不等待确认\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n```\n\n## 工作流程\n\n**交互模式（默认）：** 展示规划方案后暂停等待确认，审批通过后开始逐任务执行。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析与规划                                              │\n  │  理解需求 → 扫描代码 → 制定方案 → 拆解任务                 │\n  │       ↓ 用户确认                                            │\n  │                                                             │\n  │  🔨 逐任务执行（TaskCreate/TaskUpdate 追踪进度）            │\n  │  Task 1 ✅ → Task 2 ✅ → Task 3 🔄 → Task 4 ⏳ → ...      │\n  │       ↓                                                     │\n  │  ✅ 完成总结                                                │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n**自动模式（--auto）：** 展示规划后直接开始执行，不等待确认。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析规划  →  🔨 逐任务执行  →  ✅ 完成总结             │\n  │  （展示方案后直接执行，不暂停）                             │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n## 适用场景\n\n| 场景 | 适合 | 不适合 |\n|------|------|--------|\n| 新增中等功能模块（如限流注解、审计日志） | ✅ | |\n| 跨多个文件的 Bug 修复 | ✅ | |\n| 模块级重构（如拆分策略类） | ✅ | |\n| 新增 Starter 模块 | ✅ | |\n| 现有功能增强（添加新维度、新策略） | ✅ | |\n| 单文件简单修改 | | ❌ 直接对话即可 |\n| 快速问答或查询 | | ❌ 直接对话即可 |\n| 从零开发完整业务系统 | | ❌ 建议拆成多次会话分阶段推进 |\n| 需要先独立设计数据模型或跨服务契约 | | ❌ 先单独设计再进入 Plan |\n\n## 复杂度分层\n\n| 复杂度 | 文件变更量 | 建议方式 |\n|--------|-----------|---------|\n| 低 | 1~5 个文件 | 直接对话 |\n| **中** | **5~15 个文件** | **Blade Plan** |\n| 高 | 15+ 个文件 | 拆成多次会话，每次走一次 `/blade-plan` |\n\n## 特性\n\n- **零文件开销**：不生成任何辅助文件，规划在对话中展示，进度由内置任务系统追踪\n- **上下文锚定**：每个任务执行前重读相关代码，防止偏离\n- **构建验证**：每组任务完成后自动执行编译检查\n- **技能协同**：自动识别可借助 `/blade-design`、`/avue-design` 加速的任务\n- **智能降级**：过于简单时直接执行，复杂度过高时建议拆成多次会话\n- **SpringBlade 体系适配**：内置 Boot / Cloud 分层最佳实践，同时可适配其他技术栈\n\n## 文件结构\n\n```\nblade-plan/             # Skill 定义\n├── README.md           # 本文件\n└── SKILL.md            # Skill 主指令（全部内容，无 references 目录）\n```\n\n运行时不产生任何文件，所有状态由 Claude Code 内置任务系统管理。\n",".claude/skills/blade-plan/SKILL.md":"---\nname: blade-plan\ndescription: >\n  SpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为「分析规划 → 逐任务执行 → 完成总结」\n  三步流程，用 Claude Code 内置任务系统追踪进度，全程无辅助文件。适用于新增中等功能模块、修复跨模块 Bug、\n  中等规模重构、添加新 Starter 等涉及 5~15 个文件变更的开发任务。\n  当用户说\"帮我做一个XX功能\"、\"这个改动涉及几个文件\"、\"帮我重构一下XX\"、\"帮我加个XX\"、\n  \"plan\"、\"规划一下再做\"、\"先想清楚再写\"、\"这个稍微有点复杂\"等时触发。也可直接使用 /blade-plan 调用。\n  不适用于简单单文件修改（直接对话即可），也不适用于需要完整架构设计的复杂系统（建议分阶段拆解为多次会话）。\n  即使用户没有说\"plan\"，只要需求涉及多文件改动或需要一定设计思考，也应主动建议使用本工作流。\n---\n\n# Blade Plan — 轻量规划执行工作流\n\n先想清楚，再动手写。用最小的流程开销，获得结构化开发的核心收益。\n\n## 定位\n\nBlade Plan 面向**中等复杂度**的开发任务，在\"直接对话\"和\"分阶段设计\"之间提供一条中间路径：\n\n| 复杂度 | 方式 | 文件变更量 | 典型场景 |\n|--------|------|-----------|---------|\n| 低 | 直接对话 | 1~5 个文件 | 单个 bug 修复、配置调整、工具方法添加 |\n| **中** | **Blade Plan** | **5~15 个文件** | **新增中等功能、跨模块重构、复杂 bug、新 Starter** |\n| 高 | 分阶段执行 | 15+ 个文件 | 从零开发完整模块、复杂系统设计、大型架构重构 |\n\n判断标准不是绝对的文件数，而是：需求是否清晰到可以直接拆任务？改动是否能在一次会话内完成？如果两个答案都是\"是\"，Blade Plan 就是合适的选择。\n\n## 核心理念\n\n- **先规划后执行**：先把思路理清、任务拆好，再逐个动手——这能避免写到一半发现方向错了\n- **上下文锚定**：每个任务执行前重读相关代码，防止在对话累积中逐渐偏离\n- **最小变更**：只做计划中的事，不夹带\"顺手优化\"\n- **构建验证**：编译不过的代码等于没写\n- **零文件开销**：不生成任何辅助文件（无 spec.json、tasks.json、design.md 等），所有规划直接在对话中呈现，进度由 Claude Code 内置 TaskCreate / TaskUpdate 追踪\n\n## 使用方式\n\n```\n/blade-plan <需求描述>           # 启动规划执行（展示方案后等待确认）\n/blade-plan --auto <需求描述>    # 启动规划执行（自动模式，展示方案后直接执行）\n```\n\n**示例：**\n\n```\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n/blade-plan 修复多租户场景下动态数据源切换时连接池泄漏问题\n```\n\n## 路由逻辑\n\n收到用户输入后：\n\n1. **带 `--auto` 参数**：提取需求描述 → 自动模式（规划后直接执行，不等确认）\n2. **带需求描述**：→ 交互模式（规划后等待用户确认）\n3. **无参数**：提示输入需求描述\n\n---\n\n## 工作流\n\n```\n用户需求 → [分析与规划] → 确认 → [逐任务执行] → 完成总结\n                              ↑\n                         自动模式跳过\n```\n\n三个步骤，一次确认，全程用 Claude Code 内置任务系统追踪进度。\n\n---\n\n### 步骤一：分析与规划\n\n**目标**：理解需求，摸清现状，制定可执行的任务清单。\n\n**执行动作：**\n\n1. **理解需求**：\n   - 解析用户的需求描述，提炼核心功能点\n   - 识别隐含需求和边界条件\n   - 如有歧义，先向用户澄清关键问题再继续\n\n2. **扫描现状**：\n   - 阅读 CLAUDE.md 了解项目规范（如有）\n   - 扫描相关模块现有代码，理解代码结构和风格\n   - 搜索是否有可复用的类、工具方法或已有实现\n   - 评估改动对现有功能的影响范围\n\n3. **制定方案**：\n   - 确定技术实现策略（用什么方式解决问题）\n   - 识别需要新增和修改的文件\n   - 将改动拆解为有序的任务列表（按依赖关系排序）\n   - 任务粒度：一个任务对应一个逻辑完整的改动单元（通常 1~3 个文件）\n\n4. **展示规划**，格式如下：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  📋 Blade Plan\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n需求：{一句话概述}\n\n方案：\n  {2~4 句话描述技术方案的核心思路，\n   包括关键的技术选型和架构决策}\n\n任务清单（共 X 个）：\n  1. {任务标题}  — {涉及的文件或模块}\n  2. {任务标题}  — {涉及的文件或模块}\n  3. {任务标题}  — {涉及的文件或模块}\n  ...\n\n影响范围：\n  新增 X 个文件 | 修改 Y 个文件\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n5. **等待确认或自动推进**：\n   - **交互模式**：追加 `请确认方案后开始执行，或提出修改意见。` 并等待用户回复\n   - **自动模式**：展示规划后直接进入步骤二\n   - 用户说\"可以\"/\"没问题\"/\"开始\"等视为确认通过\n   - 用户提出修改意见则调整方案后重新展示\n\n6. **创建任务**：确认后，使用 **TaskCreate** 为每个任务创建跟踪条目，便于用户实时看到进度\n\n---\n\n### 步骤二：逐任务执行\n\n**目标**：按计划逐个完成任务，保持纪律性。\n\n**对每个任务执行以下循环：**\n\n1. **TaskUpdate** → 标记当前任务为 `in_progress`\n\n2. **加载上下文**（上下文锚定）：\n   - 重新阅读目标文件及其关联代码——即使刚才读过，也要再看一眼\n   - 查看前序任务的产出文件，确保接续一致\n   - 模仿同模块现有代码的风格和模式\n\n3. **执行编码**：\n   - 严格遵循项目 CLAUDE.md 编码规范（如有）\n   - 只做当前任务描述的事，不附带额外改动\n   - 执行前判断是否可借助其他 Blade 技能加速（见「技能协同」）\n\n4. **TaskUpdate** → 标记当前任务为 `completed`\n\n5. **继续下一个任务**\n\n> 为什么要逐任务而不是一口气全写完？因为每个任务完成后的代码状态是下一个任务的输入。逐步推进让每一步都建立在稳固的基础上，也让用户能实时看到进度。\n\n#### 构建检查点\n\n每完成一组逻辑相关的任务（或每 3~4 个任务），执行构建验证：\n\n- **Maven 项目**：`mvn clean compile -DskipTests`\n- **Gradle 项目**：`gradle compileJava`\n- **Node.js 项目**：`npm run build` 或 `tsc --noEmit`\n- **其他项目**：根据项目构建配置自动识别\n\n构建失败则立即修复后再继续，不跳过，不拖延。\n\n#### 技能协同\n\n执行任务时，识别任务类型并借助对应技能提升效率：\n\n| 任务特征 | 推荐技能 | 说明 |\n|---------|---------|------|\n| 后端 CRUD 全套（Entity → Mapper → Service → Controller） | `/blade-design` | 生成骨架代码，再根据实际需求补充业务逻辑 |\n| Avue 组件前端页面 | `/avue-design` | 生成配置化的 Avue 前端代码 |\n\n**协同原则**：技能生成的代码是起点而非终点——生成后必须根据实际需求调整，并通过构建验证。\n\n> **关于代码提交**：`/blade-commit` 绝不自动执行。完成总结后将提交作为后续建议告知用户，由用户自行决定何时提交。仅当用户明确要求\"帮我提交\"时才执行。\n\n---\n\n### 步骤三：完成总结\n\n所有任务完成后，输出总结报告：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  ✅ Blade Plan 执行完毕\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n任务: X/X 完成\n\n新增文件:\n  + path/to/NewFile.java\n  + path/to/AnotherFile.java\n\n修改文件:\n  ~ path/to/ExistingFile.java\n\n后续建议:\n  1. 功能测试（交由用户执行）\n  2. 确认无误后可使用 /blade-commit 提交代码\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n---\n\n## 关键原则\n\n### 上下文锚定\n\n每个任务执行前，重新阅读相关代码。哪怕觉得\"刚才看过了\"，也要再扫一眼。中等任务的对话虽不如大型任务长，但 5~6 个任务执行下来，上下文已经积累了不少内容，记忆会产生微妙偏差。花几秒重读比花几分钟修偏划算得多。\n\n### 遵守项目规范\n\n先看同模块现有代码怎么写的，再动手。代码风格、命名习惯、继承关系、分层结构——模仿现有代码是最可靠的规范来源。如有 CLAUDE.md，严格遵循其中的编码规范。在 SpringBlade 体系中，Boot 和 Cloud 共享统一分层：Controller → Service → Mapper → Wrapper → Entity，差异仅在包路径和路由前缀（Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 手动拼前缀，Cloud 由网关按服务名自动路由）。\n\n### 最小变更原则\n\n每个任务只做计划中描述的事。发现\"顺手可以优化\"的地方，记下来在总结中告诉用户，但不自行改动。未计划的改动是 bug 的温床。\n\n### 复用优先\n\n编写新代码前，先在项目中搜索是否已有可复用的工具类、基类或配置。尤其在 SpringBlade 体系中，`blade-core-tool` 和各 Starter 模块提供了大量基础设施，重复造轮子不仅浪费时间，还会引入风格不一致。\n\n### 构建验证\n\n代码变更后主动执行构建验证。编译不过就立即修，不要想着\"后面一起修\"——错误会像雪球一样越滚越大。\n\n---\n\n## 何时降级为直接对话\n\n如果分析后发现：\n\n- 只涉及 1~5 个文件的简单改动\n- 不需要拆解任务，直接改就行\n\n主动提示：`这个改动比较简单，不需要走规划流程，直接帮你改。` 然后直接执行。\n\n## 何时建议分阶段拆解\n\n如果在规划阶段发现以下信号，建议用户把需求拆成多次会话分阶段推进：\n\n- 任务数量超过 15 个，工作量超出一次会话的合理范围\n- 涉及复杂的数据库表结构设计（多表关联、索引策略），需要先单独设计数据模型\n- 需要跨多个服务的 API 协调，应当先固定契约再逐服务落地\n- 需求本身还不清晰，应先通过对话澄清、对齐理解后再进入 Plan 流程\n\n主动提示：`这个需求的复杂度较高，建议先拆成几个独立阶段（如：设计数据模型 → 落地后端 → 补全前端），每个阶段单独走一次 /blade-plan 会更稳。`\n","CLAUDE.md":"# CLAUDE.md\n\n欢迎来到 SpringBlade 开源微服务版（master 分支）。本文档汇总了微服务拓扑、API/Service 分层、Feign 调用、网关与配置中心等常见约定，帮助 Claude Code 快速上手，产出与项目风格一致的代码。\n\n作为 AI 助手参与本项目开发时，你必须：\n\n- 动手前先看一看目标服务及其 API 模块的现有实现，模仿它的结构与风格通常是最快的路径\n- 需要外部知识时，优先查阅 Spring Boot、Spring Cloud、MyBatis-Plus、Nacos 等官方文档，而不是凭记忆作答\n- 需求不够清晰时，先复述已知信息、抛出关键澄清问题，再动手\n- 面对跨服务或跨模块改动，拆成可验证的小步骤逐步推进比一气呵成更稳妥\n\n> 所有开发内容必须建立在深度思考过的基础之上，禁止机械生成与错误填充。\n> 如果你已了解所有规范，请在用户第一次对话时说明：\"我已充分了解 SpringBlade 开源微服务版开发规范。\"\n\n---\n\n## 1. 项目概述\n\nSpringBlade 是 BladeX 平台的**开源微服务版本**（master 分支），基于 Spring Cloud + Spring Boot 构建，由 Nacos 做服务发现与配置中心，Sentinel 做熔断限流，Spring Cloud Gateway 做统一入口，依赖 blade-tool 核心库。\n\n---\n\n## 2. 项目架构\n\n```\nSpringBlade/                            # Maven 父工程（pom 聚合）\n├── blade-auth/                         # 认证授权服务（多 granter：password/refresh/social/captcha）\n├── blade-gateway/                      # Spring Cloud Gateway 网关（WebFlux + JWT 鉴权过滤器）\n├── blade-common/                       # 公共模块（常量、启动器、工具、缓存键）\n├── blade-ops/                          # 运维服务父模块\n│   ├── blade-admin/                    #   Spring Boot Admin 监控\n│   ├── blade-develop/                  #   代码生成（Velocity 模板）\n│   ├── blade-report/                   #   报表服务（UReport2）\n│   ├── blade-resource/                 #   资源 / OSS 管理\n│   ├── blade-seata-order/              #   Seata 分布式事务演示：订单\n│   └── blade-seata-storage/            #   Seata 分布式事务演示：库存\n├── blade-service/                      # 业务服务父模块\n│   ├── blade-system/                   #   系统管理（用户/角色/菜单/字典/租户/部门/岗位）\n│   ├── blade-desk/                     #   工作台（通知公告）\n│   ├── blade-log/                      #   日志服务（API/Error/Usual 日志落库）\n│   └── blade-demo/                     #   业务演示模块\n├── blade-service-api/                  # 业务契约父模块（Entity / VO / DTO / Feign）\n│   ├── blade-system-api/               #   ISysClient（部门、租户、客户端信息）\n│   ├── blade-user-api/                 #   IUserClient（用户核心信息）\n│   ├── blade-desk-api/                 #   桌面 API 契约\n│   ├── blade-dict-api/                 #   IDictClient（字典翻译）\n│   ├── blade-scope-api/                #   数据权限契约\n│   └── blade-demo-api/                 #   演示模块契约\n├── doc/\n│   ├── nacos/                          # Nacos 配置脚本（blade.yaml、各环境 datasource）\n│   └── sql/                            # 多数据库建表 & 升级脚本\n├── script/                             # Docker / FatJar / 服务脚本\n└── pom.xml                             # 父 POM（blade-tool 依赖管理）\n```\n\n> **与开源 Boot 版的关键区别**：Cloud 为多模块 Maven 工程，存在 **API/Service 分离**，跨服务调用通过 Feign，Controller 路由**不带** `AppConstant` 前缀（网关按服务名路由）。\n\n### 2.1 分层架构\n\n每个业务域遵循 **API 模块 + Service 模块**双层划分：\n\n| 层次 | 包路径 | 所在模块 |\n| --- | --- | --- |\n| Controller | `controller/` | Service 模块（`blade-service/*` 或 `blade-ops/*`） |\n| Service / ServiceImpl | `service/` + `service/impl/` | Service 模块 |\n| Mapper（接口 + XML 同包） | `mapper/` | Service 模块 |\n| Wrapper | `wrapper/`（Entity → VO 转换） | Service 模块 |\n| Excel 导入导出 | `excel/` | Service 模块 |\n| Feign 实现类 | `feign/` | Service 模块（实现对应 API 模块的 Feign 接口） |\n| Entity | `entity/` | **API 模块（`blade-*-api`）** |\n| VO / DTO | `vo/` `dto/` | **API 模块** |\n| Feign Client + Fallback | `feign/` | **API 模块** |\n\n### 2.2 API / Service 分离原则\n\n- **API 模块**（`blade-*-api`）：定义 Entity、VO、DTO、Feign 接口 + Fallback，供其他服务依赖；**不含**具体业务逻辑\n- **Service 模块**（`blade-service/*`、`blade-ops/*`）：实现 Controller、ServiceImpl、Mapper、Wrapper 与 Feign 接口的服务端实现\n- 跨服务调用**必须**通过 Feign Client，禁止直接依赖其他 Service 模块\n- 公共常量与工具放在 `blade-common`\n\n---\n\n## 3. 技术栈\n\n| 类别 | 技术 |\n| --- | --- |\n| JDK | Java 21 |\n| 基础框架 | Spring Boot / Spring Cloud / Spring Cloud Alibaba / Maven |\n| 核心库 | blade-core-bom、blade-core-cloud、blade-core-boot |\n| 注册中心 / 配置中心 | Nacos |\n| 熔断限流 | Sentinel（`spring-cloud-starter-alibaba-sentinel`） |\n| 分布式事务 | Seata（`blade-starter-transaction`，见 `blade-seata-order`、`blade-seata-storage`） |\n| RPC | OpenFeign（与 Sentinel 集成） |\n| 网关 | Spring Cloud Gateway（WebFlux） |\n| ORM | MyBatis-Plus + dynamic-datasource-spring-boot4-starter |\n| 安全 | blade-core-secure（JWT + 可选 AES 加密 + SM2 国密登录） |\n| 缓存 | BladeRedis（Protostuff 序列化，响应式 Gateway 侧用 Reactive Redis） |\n| 数据权限 | blade-starter-datascope（DataScopeHandler 行级过滤） |\n| 监控 | Spring Boot Admin（`blade-admin`）+ Actuator |\n| 文档 | springdoc（各服务 OpenAPI 3 原生 UI） |\n| 报表 | UReport2（`blade-report`） |\n| 代码生成 | blade-starter-develop + Apache Velocity（`blade-develop`） |\n| 验证码 | easy-captcha |\n| JWT | jjwt-impl + jjwt-jackson |\n| 数据库 | MySQL / PostgreSQL / Oracle / SQL Server / 达梦 / 人大金仓 / 崖山 |\n\n---\n\n## 4. 开发规范\n\n### 4.1 编写新功能前\n\n1. 先阅读目标服务中已有的类，理解其结构、命名和风格\n2. 标准参考模块：`blade-service/blade-system`（Service 最完整）、`blade-service-api/blade-system-api`（API 契约最完整）、`blade-service/blade-desk` + `blade-desk-api`（最简样板）\n3. 主动模仿现有代码风格，包括缩进（Java 用 Tab，YAML/JSON 用 2 空格）、注解顺序、Javadoc 格式\n4. 新建 Java 源文件必须包含 Apache 2.0 许可证头部和 Javadoc 类注释（`@author Chill`）\n\n### 4.2 编写完成后\n\n1. 使用 `mvn clean compile -DskipTests` 编译验证（父工程根目录执行会编译所有子模块），编译不通过必须修复\n2. 引入模块依赖前先检查循环依赖；**跨服务依赖必须经由 `blade-*-api` + Feign**，禁止 `blade-service/A` 直接依赖 `blade-service/B`\n3. 编译通过后将测试交由用户执行，**不得自行执行任何测试**（包括 `mvn test`、启动 Nacos/服务、访问接口）\n4. 除非用户明确要求，不应撰写示例或额外文档\n\n### 4.3 代码生成\n\n当需要生成 CRUD 全套代码（Entity、VO、Service、Controller、Wrapper、Mapper、Feign、建表语句等）时，优先使用 **`/blade-design`** skill。该 skill 可根据模块名、实体名和字段列表，自动生成符合 BladeX 框架规范的后端代码和多数据库建表语句，确保生成结果与项目风格完全一致。\n\n本工程亦内置 `blade-ops/blade-develop` 代码生成服务（Velocity 模板），可在运行时通过后台界面生成。\n\n---\n\n## 5. 命名规范\n\n### 5.1 包名\n\n统一前缀 `org.springblade`，按业务域划分：`system`、`system.user`、`desk`、`auth`、`gateway`、`develop`、`report`、`resource`、`common` 等。API 与 Service 两侧使用相同业务域包名，彼此仅通过模块边界隔离。\n\n### 5.2 类名\n\n| 类型 | 规则 | 示例 |\n| --- | --- | --- |\n| Entity | 直接类名 | `Post`、`User`、`Notice` |\n| VO / DTO | `XxxVO` / `XxxDTO` | `MenuVO`、`RoleDTO` |\n| Service 接口 | `IXxxService` | `IParamService`、`IUserService` |\n| Service 实现 | `XxxServiceImpl` | `ParamServiceImpl` |\n| Controller | `XxxController` | `ParamController` |\n| Mapper | `XxxMapper` | `ParamMapper` |\n| Wrapper | `XxxWrapper` | `UserWrapper` |\n| Feign 接口 / 降级 | `IXxxClient` / `IXxxClientFallback` | `ISysClient` / `ISysClientFallback`、`IUserClient` / `IUserClientFallback` |\n| 表名 | `blade_` + 下划线 | `blade_user`、`blade_post` |\n\n### 5.3 变量命名\n\n- 必须具有明确语义：`Exception exception`（✅）`Exception e`（❌）；`List<Post> postList`（✅）`List<Post> list`（❌）\n- 冲突时提升语义：`Cache cache; Cache postCache`（✅）`Cache cache1; Cache cache2`（❌）\n\n---\n\n## 6. 编码规范\n\n### 6.1 注解顺序\n\n- **Controller 类**：`@RestController` → Lombok（`@AllArgsConstructor`） → `@RequestMapping(\"/resource\")` → `@Tag`\n- **Controller 方法**：HTTP 方法注解（`@GetMapping`/`@PostMapping`）→ `@Operation` → `@PreAuth`（如需角色控制）\n- **Feign 接口**：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- **Feign Fallback**：`@Component` → 实现对应接口\n- **Entity 类**：`@Data` → `@TableName` → `@EqualsAndHashCode(callSuper = true)`（继承 BaseEntity/TenantEntity 时必须） → `@Schema`（字段级使用）\n- **VO 类**：`@Data` → `@EqualsAndHashCode(callSuper = true)` → `@Schema`\n\n### 6.2 Entity-Service 继承体系（核心）\n\nEntity 基类的选择**直接决定** Service 和 ServiceImpl 的继承方式，三者必须配套：\n\n| 场景 | Entity | Service 接口 | ServiceImpl | 示例 |\n| --- | --- | --- | --- | --- |\n| **多租户业务表** | `extends TenantEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `User`、`Post`、`Notice` |\n| **非租户业务表** | `extends BaseEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `Tenant` |\n| **轻量级/关系表** | `implements Serializable` | `extends IService<T>` | `extends ServiceImpl<M, T>` | `RoleMenu`、`Dict`、`Role` |\n\n- `TenantEntity` 继承自 `BaseEntity` 并额外包含 `tenantId`\n- `BaseEntity` 包含：`id`、`createUser`、`createDept`、`createTime`、`updateUser`、`updateTime`、`status`、`isDeleted`\n- `BaseService`/`BaseServiceImpl` 是 BladeX 增强版，提供 `deleteLogic(List<Long>)` 等方法，**要求 Entity 继承 BaseEntity 或 TenantEntity**\n- `IService`/`ServiceImpl` 是 MyBatis-Plus 原生版，用于 `implements Serializable` 的轻量实体，删除用 `removeByIds()`\n\n### 6.3 Controller 约定\n\n- 视需要继承 `BladeController`（获取 `getUser()` 等便捷方法），使用 `@AllArgsConstructor` 注入依赖\n- **路由直接使用资源路径**（如 `@RequestMapping(\"/param\")`），网关按服务名路由，**无需** `AppConstant` 前缀（与 Boot 版区别点）\n- 统一返回 `R<T>` 响应体（`R.data(...)` / `R.status(...)` / `R.success(...)`）\n- Entity → VO 转换通过 `XxxWrapper.build().entityVO(entity)` / `.pageVO(page)` / `.listVO(list)`\n- 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（角色级）；**本版本不支持** 菜单级 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 写法，也不含 ApiScopeHandler 接口权限\n- 需要跳过 Swagger 展示时使用 `@Hidden`\n\n### 6.4 Feign Client（Cloud 特有）\n\n- **接口定义在 API 模块** 的 `feign/` 包中，由所有调用方共享依赖\n- 注解签名：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- 路径常量定义在接口内部：`String API_PREFIX = \"/xxx\"`；方法路径使用 `API_PREFIX + \"/yyy\"` 拼接\n- 返回类型统一使用 `R<T>`（除非明确只返回原始对象，如 `ISysClient#getDept` 返回 `Dept`）\n- **降级类**：与接口同包，命名 `IXxxClientFallback`，标注 `@Component` 并实现全部方法，方法返回 `null` 或安全缺省值\n- Feign 实现（服务端）位于对应 Service 模块的 `feign/` 包，`@RestController` 实现接口并复用内部 Service\n- Feign + Sentinel 默认开启（`feign.sentinel.enabled=true`）\n\n### 6.5 依赖注入\n\n- Controller 使用 `@AllArgsConstructor` + `private` 字段\n- Service 需要额外依赖时使用 `@AllArgsConstructor` 或 `@RequiredArgsConstructor` + `private final` 字段\n- Feign 客户端通过构造器注入，Spring Cloud 自动代理\n\n### 6.6 Lombok\n\n禁止手写 getter/setter，统一使用 `@Data`、`@EqualsAndHashCode(callSuper = true)`、`@AllArgsConstructor`、`@RequiredArgsConstructor`、`@Slf4j`、`@SneakyThrows` 等\n\n### 6.7 Java 21 特性\n\n- **基线 JDK 21 LTS**，优先采用已稳定的现代语言特性提升可读性：`switch` 表达式与模式匹配（含 Record Patterns）、`instanceof` 模式匹配、Text Blocks（承载多行 SQL / JSON 字面量）、`@Serial`（标注 `serialVersionUID` 与序列化方法）\n- 实体 / VO 沿用 Lombok（`@Data` 等），**不改用 record**——需保留可变字段与无参构造以适配 MyBatis-Plus 映射\n- **禁止 `var`**，所有变量显式声明类型；优先使用 Stream API，Lambda 保持简洁\n- 虚拟线程（Virtual Threads）属运行期能力，由 Spring Boot 4 配置 `spring.threads.virtual.enabled` 按需开启，非编码约定\n\n### 6.8 MyBatis-Plus\n\n- 简单查询使用 `LambdaQueryWrapper` 或 `Wrappers.<T>query()`\n- 复杂查询写在 Mapper XML 中，Mapper XML 与接口**同包**放置（位于 `src/main/java` 下，由 `pom.xml` `<resources>` 声明打包）\n- 分页统一使用 `Condition.getPage(query)` + `Condition.getQueryWrapper(param, Entity.class)`\n- 禁止 JDBC 直连查询\n- 主键使用雪花算法 `IdType.ASSIGN_ID`，Long 类型主键必须标注 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n- 多数据源切换通过 dynamic-datasource 的 `@DS(\"...\")` 注解，数据源定义在 Nacos 配置\n\n### 6.9 日志\n\n- 使用 `@Slf4j`，占位符传参 `log.info(\"user {} login\", userId)`（禁止字符串拼接）\n- 包含关键业务标识，异常必须携带堆栈 `log.error(\"...\", e)`，禁止打印敏感信息（密码、Token、手机号等）\n- API 调用日志、错误日志、业务日志由 `blade-service/blade-log` 异步落库，事件通过 `AsyncEventDispatcher` 发布\n\n---\n\n## 7. 数据库规范\n\n- **表名前缀**：`blade_`（如 `blade_user`、`blade_role_menu`）\n- **业务表必含字段**：`id`（BIGINT）、`tenant_id`（VARCHAR，多租户表）、`create_user`、`create_dept`、`create_time`、`update_user`、`update_time`、`status`、`is_deleted`\n- **主键策略**：雪花算法（`IdType.ASSIGN_ID`），Long 字段用 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n- **逻辑删除**：`is_deleted`（INT），`0` = 未删除，`1` = 已删除\n- **索引命名**：唯一索引 `uk_` 前缀，普通索引 `idx_` 前缀\n- **多数据库**：支持 7 种数据库，SQL 脚本位于 `doc/sql/`（全量建表 + 版本升级脚本），修改表结构时需同步所有脚本\n\n---\n\n## 8. 多租户与安全\n\n### 8.1 多租户\n\n- 默认**字段隔离模式**（`tenant_id`），MP 内置方法自动注入\n- 租户表通过 Nacos `blade.yaml` 中 `blade.tenant.tables` 配置声明\n- 自定义 SQL 需手动过滤：`AuthUtil.getTenantId()` 或 `SecureUtil.getTenantId()`\n- **本版本不支持** `@TenantDS` / `@NonDS` 数据源切换注解；如需物理隔离，可结合 dynamic-datasource 的 `@DS` 配合业务逻辑实现\n\n### 8.2 权限体系\n\n- **角色权限**：`@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（或自定义 SpEL 表达式）\n- **数据权限**：`blade-starter-datascope` DataScopeHandler，行级过滤，通过 `RoleScope` 配置\n- **本版本不含**：接口权限（ApiScopeHandler）、菜单级权限表达式 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 通配放行\n- 接口放行请在 Nacos `blade.secure.skip-url` 中配置 URL 通配，或在 `blade-gateway` 的 `AuthFilter` 白名单中配置\n\n### 8.3 登录与 Token\n\n- 登录密码使用 **SM2 国密**加解密（`blade.auth.public-key` / `blade.auth.private-key`，由 `blade-auth` 模块的 `Sm2KeyGenerator` 生成）\n- Token 使用 JWT 签名（`blade.token.sign-key`）+ 可选 AES 加密（`blade.token.aes-key`）\n- 多 Granter 支持（`blade-auth/granter/`）：`PasswordTokenGranter`、`CaptchaTokenGranter`、`RefreshTokenGranter`、`SocialTokenGranter`\n- **网关统一鉴权**：`blade-gateway/filter/AuthFilter` 作为 `GlobalFilter` 验证 JWT，下游服务通过 `exchange attributes` 透传用户上下文\n- 获取当前用户：`SecureUtil.getUser()` / `SecureUtil.getUserId()` / `SecureUtil.getTenantId()` / `SecureUtil.isAdministrator()`\n\n### 8.4 分布式事务\n\n- 由 `blade-starter-transaction`（Seata）提供支持，参考 `blade-ops/blade-seata-order` + `blade-ops/blade-seata-storage` 双服务示例\n- 需要跨服务事务的场景标注 `@GlobalTransactional`\n\n---\n\n## 9. 缓存规范\n\n- `CacheUtil` 统一操作，常量命名在 `blade-common/cache/CacheNames`\n- 数据变更后清除缓存：`CacheUtil.clear(CACHE_NAME, Boolean.FALSE)`\n- 字典翻译：推荐跨服务通过 `IDictClient.getValue(code, dictKey)`（Feign）；服务本地可用 `DictCache.getValue(...)`\n- Redis 序列化使用 Protostuff（默认配置），键分布式作用域；Gateway 侧使用 Reactive Redis\n\n---\n\n## 10. 开源许可头部\n\n所有新建的 Java 源文件必须在文件顶部包含以下 Apache 2.0 许可头部：\n\n```java\n/**\n * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n```\n\n> **重要**：本工程为开源版本，**不要**写入 BladeX 商业许可证头部。若需要迁移为商业版本，请以商业项目代码样例为准。\n\n---\n\n## 11. 构建与部署\n\n### 11.1 Maven 构建\n\n```bash\nmvn clean compile -DskipTests                      # 根目录执行：编译所有子模块\nmvn clean package -DskipTests                      # 打包所有服务为 FatJar\nmvn clean package -DskipTests -pl blade-service/blade-system -am  # 只打包 system 服务及其依赖模块\n```\n\n- 父 POM 使用 `${revision}` 占位符管理版本，由 `flatten-maven-plugin` 在 `process-resources` 阶段展开\n- 各服务打包产物通过 `directory-maven-plugin` 汇总复制到根目录 `target/`\n- `finalName` 使用 `${project.name}`，产物命名与 artifactId 一致\n\n### 11.2 Nacos 配置\n\n- 配置文件位于 `doc/nacos/`：`blade.yaml`（公共配置）+ `blade-dev.yaml`/`blade-test.yaml`/`blade-prod.yaml`（环境差异）\n- 数据源、密钥、Swagger、Sentinel、Feign 等共享参数统一在 Nacos 配置\n- 各服务 `src/main/resources/application-{env}.yml` 仅保留 `server.port` 与 `spring.datasource.url` 等少量本地覆盖\n\n### 11.3 Docker 部署\n\n- `script/docker/docker-compose.yml` 提供一体化编排：Nacos + Sentinel + Redis + Nginx + 全部微服务\n- 默认网段 `blade_net`，Nacos `172.30.0.48`，Sentinel `172.30.0.58`\n- Harbor 镜像推送通过父 POM 的 `docker.registry.url` 属性控制\n\n### 11.4 必备环境变量\n\n```bash\nBLADE_OAUTH2_PUBLIC_KEY    # SM2 公钥\nBLADE_OAUTH2_PRIVATE_KEY   # SM2 私钥\nBLADE_TOKEN_SIGN_KEY       # JWT 签名密钥\nBLADE_TOKEN_CRYPTO_KEY     # Token AES 加密密钥\n```\n\n### 11.5 服务端口（默认）\n\n| 服务 | 端口 |\n| --- | --- |\n| blade-gateway | 80 |\n| blade-auth | 8100 |\n| blade-admin | 7002 |\n| blade-system | 8106 |\n| blade-desk | 8103 |\n| blade-log | 8102 |\n| blade-develop | 8108 |\n| blade-report | 8109 |\n| blade-resource | 8110 |\n\n> 实际端口以 `src/main/resources/application-{env}.yml` 中 `server.port` 为准。\n\n---\n\n## 12. Git 提交规范\n\n当需要提交代码时，优先使用 **`/blade-commit`** skill，它会自动分析变更内容并生成符合项目规范的 Gitmoji 提交信息。\n\n采用 **Gitmoji** 风格，中文描述。格式：`:<gitmoji>: 简要描述`\n\n| Gitmoji | 场景 | 示例 |\n| --- | --- | --- |\n| `:sparkles:` | 新增功能 | `:sparkles: 登录认证新增IP锁定逻辑` |\n| `:zap:` | 优化重构 | `:zap: 优化字典查询排序逻辑` |\n| `:bug:` | 修复缺陷 | `:bug: 修复用户查询未过滤已删除数据` |\n| `:tada:` | 版本发布 | `:tada: x.x.x.RELEASE` |\n| `:recycle:` | 代码重构 | `:recycle: 重构租户删除逻辑` |\n\n---\n\n## 13. 框架组件速查\n\n| 组件 | 用途 |\n| --- | --- |\n| `R<T>` | 统一 API 响应（`org.springblade.core.tool.api.R`） |\n| `BladeController` | Controller 基类（`org.springblade.core.boot.ctrl`） |\n| `BladeCloudApplication` | Cloud 服务启动注解（`blade-auth`、业务服务使用） |\n| `TenantEntity` / `BaseEntity` | 实体基类（`org.springblade.core.mp.base`） |\n| `BaseService` / `BaseServiceImpl` | BladeX 增强 Service（含 `deleteLogic`） |\n| `BaseEntityWrapper` | Entity→VO 转换基类 |\n| `BladeUser` | 当前登录用户（含租户信息） |\n| `Condition` / `Query` | 查询条件构建 / 分页参数 |\n| `CacheUtil` / `DictCache` | 缓存工具 / 字典缓存 |\n| `SecureUtil` / `AuthUtil` | 获取当前用户/租户信息 |\n| `BladeRedis` | Redis 操作封装 |\n| `Func` / `BeanUtil` / `StringUtil` | 通用工具类 |\n| `ServiceException` | 业务异常 |\n| `@PreAuth` | 角色权限注解（`RoleConstant.HAS_ROLE_ADMIN`） |\n| `@FeignClient` + `AppConstant.APPLICATION_XXX_NAME` | 服务间调用 |\n| `ISysClient` / `IUserClient` / `IDictClient` | 常用 Feign 契约 |\n| `@GlobalTransactional` | Seata 分布式事务 |\n| `@Operation` / `@Tag` / `@Schema` | springdoc / OpenAPI 3 文档注解 |\n| `LauncherConstant` / `AppConstant` / `CommonConstant` | 启动常量 / 应用名 / 公共常量 |\n\n---\n\n## 14. 风格一致性\n\n1. 风格不确定时，优先查阅现有服务并模仿当前业务域的实现方式\n2. 新增业务域时，**先建 API 模块再建 Service 模块**；API 模块只放契约，不引入 Service 依赖\n3. 新服务参考 `blade-service/blade-system` + `blade-service-api/blade-system-api`（最完整） 或 `blade-service/blade-desk` + `blade-desk-api`（最简样板）\n4. 现有服务或契约已满足需求时，禁止自写替代实现；跨服务使用数据请走 Feign，不要重复落库\n5. 与用户交互全程使用**中文**，代码注释和 Javadoc 亦使用中文\n6. 本工程为开源版，**不要**引用商业版特有能力（Flowable、PowerJob、`@TenantDS`、`@BladeView`、接口权限、`blade-ops-api`、`blade-plugin` 等）；如确实需要，请先向用户确认是否切换到商业版代码库\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\n欢迎来到 SpringBlade 开源微服务版（master 分支）。本文档汇总了微服务拓扑、API/Service 分层、Feign 调用、网关与配置中心等常见约定，帮助 Claude Code 快速上手，产出与项目风格一致的代码。\n\n作为 AI 助手参与本项目开发时，你必须：\n\n- 动手前先看一看目标服务及其 API 模块的现有实现，模仿它的结构与风格通常是最快的路径\n- 需要外部知识时，优先查阅 Spring Boot、Spring Cloud、MyBatis-Plus、Nacos 等官方文档，而不是凭记忆作答\n- 需求不够清晰时，先复述已知信息、抛出关键澄清问题，再动手\n- 面对跨服务或跨模块改动，拆成可验证的小步骤逐步推进比一气呵成更稳妥\n\n> 所有开发内容必须建立在深度思考过的基础之上，禁止机械生成与错误填充。\n> 如果你已了解所有规范，请在用户第一次对话时说明：\"我已充分了解 SpringBlade 开源微服务版开发规范。\"\n\n---\n\n## 1. 项目概述\n\nSpringBlade 是 BladeX 平台的**开源微服务版本**（master 分支），基于 Spring Cloud + Spring Boot 构建，由 Nacos 做服务发现与配置中心，Sentinel 做熔断限流，Spring Cloud Gateway 做统一入口，依赖 blade-tool 核心库。\n\n---\n\n## 2. 项目架构\n\n```\nSpringBlade/                            # Maven 父工程（pom 聚合）\n├── blade-auth/                         # 认证授权服务（多 granter：password/refresh/social/captcha）\n├── blade-gateway/                      # Spring Cloud Gateway 网关（WebFlux + JWT 鉴权过滤器）\n├── blade-common/                       # 公共模块（常量、启动器、工具、缓存键）\n├── blade-ops/                          # 运维服务父模块\n│   ├── blade-admin/                    #   Spring Boot Admin 监控\n│   ├── blade-develop/                  #   代码生成（Velocity 模板）\n│   ├── blade-report/                   #   报表服务（UReport2）\n│   ├── blade-resource/                 #   资源 / OSS 管理\n│   ├── blade-seata-order/              #   Seata 分布式事务演示：订单\n│   └── blade-seata-storage/            #   Seata 分布式事务演示：库存\n├── blade-service/                      # 业务服务父模块\n│   ├── blade-system/                   #   系统管理（用户/角色/菜单/字典/租户/部门/岗位）\n│   ├── blade-desk/                     #   工作台（通知公告）\n│   ├── blade-log/                      #   日志服务（API/Error/Usual 日志落库）\n│   └── blade-demo/                     #   业务演示模块\n├── blade-service-api/                  # 业务契约父模块（Entity / VO / DTO / Feign）\n│   ├── blade-system-api/               #   ISysClient（部门、租户、客户端信息）\n│   ├── blade-user-api/                 #   IUserClient（用户核心信息）\n│   ├── blade-desk-api/                 #   桌面 API 契约\n│   ├── blade-dict-api/                 #   IDictClient（字典翻译）\n│   ├── blade-scope-api/                #   数据权限契约\n│   └── blade-demo-api/                 #   演示模块契约\n├── doc/\n│   ├── nacos/                          # Nacos 配置脚本（blade.yaml、各环境 datasource）\n│   └── sql/                            # 多数据库建表 & 升级脚本\n├── script/                             # Docker / FatJar / 服务脚本\n└── pom.xml                             # 父 POM（blade-tool 依赖管理）\n```\n\n> **与开源 Boot 版的关键区别**：Cloud 为多模块 Maven 工程，存在 **API/Service 分离**，跨服务调用通过 Feign，Controller 路由**不带** `AppConstant` 前缀（网关按服务名路由）。\n\n### 2.1 分层架构\n\n每个业务域遵循 **API 模块 + Service 模块**双层划分：\n\n| 层次 | 包路径 | 所在模块 |\n| --- | --- | --- |\n| Controller | `controller/` | Service 模块（`blade-service/*` 或 `blade-ops/*`） |\n| Service / ServiceImpl | `service/` + `service/impl/` | Service 模块 |\n| Mapper（接口 + XML 同包） | `mapper/` | Service 模块 |\n| Wrapper | `wrapper/`（Entity → VO 转换） | Service 模块 |\n| Excel 导入导出 | `excel/` | Service 模块 |\n| Feign 实现类 | `feign/` | Service 模块（实现对应 API 模块的 Feign 接口） |\n| Entity | `entity/` | **API 模块（`blade-*-api`）** |\n| VO / DTO | `vo/` `dto/` | **API 模块** |\n| Feign Client + Fallback | `feign/` | **API 模块** |\n\n### 2.2 API / Service 分离原则\n\n- **API 模块**（`blade-*-api`）：定义 Entity、VO、DTO、Feign 接口 + Fallback，供其他服务依赖；**不含**具体业务逻辑\n- **Service 模块**（`blade-service/*`、`blade-ops/*`）：实现 Controller、ServiceImpl、Mapper、Wrapper 与 Feign 接口的服务端实现\n- 跨服务调用**必须**通过 Feign Client，禁止直接依赖其他 Service 模块\n- 公共常量与工具放在 `blade-common`\n\n---\n\n## 3. 技术栈\n\n| 类别 | 技术 |\n| --- | --- |\n| JDK | Java 21 |\n| 基础框架 | Spring Boot / Spring Cloud / Spring Cloud Alibaba / Maven |\n| 核心库 | blade-core-bom、blade-core-cloud、blade-core-boot |\n| 注册中心 / 配置中心 | Nacos |\n| 熔断限流 | Sentinel（`spring-cloud-starter-alibaba-sentinel`） |\n| 分布式事务 | Seata（`blade-starter-transaction`，见 `blade-seata-order`、`blade-seata-storage`） |\n| RPC | OpenFeign（与 Sentinel 集成） |\n| 网关 | Spring Cloud Gateway（WebFlux） |\n| ORM | MyBatis-Plus + dynamic-datasource-spring-boot4-starter |\n| 安全 | blade-core-secure（JWT + 可选 AES 加密 + SM2 国密登录） |\n| 缓存 | BladeRedis（Protostuff 序列化，响应式 Gateway 侧用 Reactive Redis） |\n| 数据权限 | blade-starter-datascope（DataScopeHandler 行级过滤） |\n| 监控 | Spring Boot Admin（`blade-admin`）+ Actuator |\n| 文档 | springdoc（各服务 OpenAPI 3 原生 UI） |\n| 报表 | UReport2（`blade-report`） |\n| 代码生成 | blade-starter-develop + Apache Velocity（`blade-develop`） |\n| 验证码 | easy-captcha |\n| JWT | jjwt-impl + jjwt-jackson |\n| 数据库 | MySQL / PostgreSQL / Oracle / SQL Server / 达梦 / 人大金仓 / 崖山 |\n\n---\n\n## 4. 开发规范\n\n### 4.1 编写新功能前\n\n1. 先阅读目标服务中已有的类，理解其结构、命名和风格\n2. 标准参考模块：`blade-service/blade-system`（Service 最完整）、`blade-service-api/blade-system-api`（API 契约最完整）、`blade-service/blade-desk` + `blade-desk-api`（最简样板）\n3. 主动模仿现有代码风格，包括缩进（Java 用 Tab，YAML/JSON 用 2 空格）、注解顺序、Javadoc 格式\n4. 新建 Java 源文件必须包含 Apache 2.0 许可证头部和 Javadoc 类注释（`@author Chill`）\n\n### 4.2 编写完成后\n\n1. 使用 `mvn clean compile -DskipTests` 编译验证（父工程根目录执行会编译所有子模块），编译不通过必须修复\n2. 引入模块依赖前先检查循环依赖；**跨服务依赖必须经由 `blade-*-api` + Feign**，禁止 `blade-service/A` 直接依赖 `blade-service/B`\n3. 编译通过后将测试交由用户执行，**不得自行执行任何测试**（包括 `mvn test`、启动 Nacos/服务、访问接口）\n4. 除非用户明确要求，不应撰写示例或额外文档\n\n### 4.3 代码生成\n\n当需要生成 CRUD 全套代码（Entity、VO、Service、Controller、Wrapper、Mapper、Feign、建表语句等）时，优先使用 **`/blade-design`** skill。该 skill 可根据模块名、实体名和字段列表，自动生成符合 BladeX 框架规范的后端代码和多数据库建表语句，确保生成结果与项目风格完全一致。\n\n本工程亦内置 `blade-ops/blade-develop` 代码生成服务（Velocity 模板），可在运行时通过后台界面生成。\n\n---\n\n## 5. 命名规范\n\n### 5.1 包名\n\n统一前缀 `org.springblade`，按业务域划分：`system`、`system.user`、`desk`、`auth`、`gateway`、`develop`、`report`、`resource`、`common` 等。API 与 Service 两侧使用相同业务域包名，彼此仅通过模块边界隔离。\n\n### 5.2 类名\n\n| 类型 | 规则 | 示例 |\n| --- | --- | --- |\n| Entity | 直接类名 | `Post`、`User`、`Notice` |\n| VO / DTO | `XxxVO` / `XxxDTO` | `MenuVO`、`RoleDTO` |\n| Service 接口 | `IXxxService` | `IParamService`、`IUserService` |\n| Service 实现 | `XxxServiceImpl` | `ParamServiceImpl` |\n| Controller | `XxxController` | `ParamController` |\n| Mapper | `XxxMapper` | `ParamMapper` |\n| Wrapper | `XxxWrapper` | `UserWrapper` |\n| Feign 接口 / 降级 | `IXxxClient` / `IXxxClientFallback` | `ISysClient` / `ISysClientFallback`、`IUserClient` / `IUserClientFallback` |\n| 表名 | `blade_` + 下划线 | `blade_user`、`blade_post` |\n\n### 5.3 变量命名\n\n- 必须具有明确语义：`Exception exception`（✅）`Exception e`（❌）；`List<Post> postList`（✅）`List<Post> list`（❌）\n- 冲突时提升语义：`Cache cache; Cache postCache`（✅）`Cache cache1; Cache cache2`（❌）\n\n---\n\n## 6. 编码规范\n\n### 6.1 注解顺序\n\n- **Controller 类**：`@RestController` → Lombok（`@AllArgsConstructor`） → `@RequestMapping(\"/resource\")` → `@Tag`\n- **Controller 方法**：HTTP 方法注解（`@GetMapping`/`@PostMapping`）→ `@Operation` → `@PreAuth`（如需角色控制）\n- **Feign 接口**：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- **Feign Fallback**：`@Component` → 实现对应接口\n- **Entity 类**：`@Data` → `@TableName` → `@EqualsAndHashCode(callSuper = true)`（继承 BaseEntity/TenantEntity 时必须） → `@Schema`（字段级使用）\n- **VO 类**：`@Data` → `@EqualsAndHashCode(callSuper = true)` → `@Schema`\n\n### 6.2 Entity-Service 继承体系（核心）\n\nEntity 基类的选择**直接决定** Service 和 ServiceImpl 的继承方式，三者必须配套：\n\n| 场景 | Entity | Service 接口 | ServiceImpl | 示例 |\n| --- | --- | --- | --- | --- |\n| **多租户业务表** | `extends TenantEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `User`、`Post`、`Notice` |\n| **非租户业务表** | `extends BaseEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `Tenant` |\n| **轻量级/关系表** | `implements Serializable` | `extends IService<T>` | `extends ServiceImpl<M, T>` | `RoleMenu`、`Dict`、`Role` |\n\n- `TenantEntity` 继承自 `BaseEntity` 并额外包含 `tenantId`\n- `BaseEntity` 包含：`id`、`createUser`、`createDept`、`createTime`、`updateUser`、`updateTime`、`status`、`isDeleted`\n- `BaseService`/`BaseServiceImpl` 是 BladeX 增强版，提供 `deleteLogic(List<Long>)` 等方法，**要求 Entity 继承 BaseEntity 或 TenantEntity**\n- `IService`/`ServiceImpl` 是 MyBatis-Plus 原生版，用于 `implements Serializable` 的轻量实体，删除用 `removeByIds()`\n\n### 6.3 Controller 约定\n\n- 视需要继承 `BladeController`（获取 `getUser()` 等便捷方法），使用 `@AllArgsConstructor` 注入依赖\n- **路由直接使用资源路径**（如 `@RequestMapping(\"/param\")`），网关按服务名路由，**无需** `AppConstant` 前缀（与 Boot 版区别点）\n- 统一返回 `R<T>` 响应体（`R.data(...)` / `R.status(...)` / `R.success(...)`）\n- Entity → VO 转换通过 `XxxWrapper.build().entityVO(entity)` / `.pageVO(page)` / `.listVO(list)`\n- 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（角色级）；**本版本不支持** 菜单级 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 写法，也不含 ApiScopeHandler 接口权限\n- 需要跳过 Swagger 展示时使用 `@Hidden`\n\n### 6.4 Feign Client（Cloud 特有）\n\n- **接口定义在 API 模块** 的 `feign/` 包中，由所有调用方共享依赖\n- 注解签名：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- 路径常量定义在接口内部：`String API_PREFIX = \"/xxx\"`；方法路径使用 `API_PREFIX + \"/yyy\"` 拼接\n- 返回类型统一使用 `R<T>`（除非明确只返回原始对象，如 `ISysClient#getDept` 返回 `Dept`）\n- **降级类**：与接口同包，命名 `IXxxClientFallback`，标注 `@Component` 并实现全部方法，方法返回 `null` 或安全缺省值\n- Feign 实现（服务端）位于对应 Service 模块的 `feign/` 包，`@RestController` 实现接口并复用内部 Service\n- Feign + Sentinel 默认开启（`feign.sentinel.enabled=true`）\n\n### 6.5 依赖注入\n\n- Controller 使用 `@AllArgsConstructor` + `private` 字段\n- Service 需要额外依赖时使用 `@AllArgsConstructor` 或 `@RequiredArgsConstructor` + `private final` 字段\n- Feign 客户端通过构造器注入，Spring Cloud 自动代理\n\n### 6.6 Lombok\n\n禁止手写 getter/setter，统一使用 `@Data`、`@EqualsAndHashCode(callSuper = true)`、`@AllArgsConstructor`、`@RequiredArgsConstructor`、`@Slf4j`、`@SneakyThrows` 等\n\n### 6.7 Java 21 特性\n\n- **基线 JDK 21 LTS**，优先采用已稳定的现代语言特性提升可读性：`switch` 表达式与模式匹配（含 Record Patterns）、`instanceof` 模式匹配、Text Blocks（承载多行 SQL / JSON 字面量）、`@Serial`（标注 `serialVersionUID` 与序列化方法）\n- 实体 / VO 沿用 Lombok（`@Data` 等），**不改用 record**——需保留可变字段与无参构造以适配 MyBatis-Plus 映射\n- **禁止 `var`**，所有变量显式声明类型；优先使用 Stream API，Lambda 保持简洁\n- 虚拟线程（Virtual Threads）属运行期能力，由 Spring Boot 4 配置 `spring.threads.virtual.enabled` 按需开启，非编码约定\n\n### 6.8 MyBatis-Plus\n\n- 简单查询使用 `LambdaQueryWrapper` 或 `Wrappers.<T>query()`\n- 复杂查询写在 Mapper XML 中，Mapper XML 与接口**同包**放置（位于 `src/main/java` 下，由 `pom.xml` `<resources>` 声明打包）\n- 分页统一使用 `Condition.getPage(query)` + `Condition.getQueryWrapper(param, Entity.class)`\n- 禁止 JDBC 直连查询\n- 主键使用雪花算法 `IdType.ASSIGN_ID`，Long 类型主键必须标注 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n- 多数据源切换通过 dynamic-datasource 的 `@DS(\"...\")` 注解，数据源定义在 Nacos 配置\n\n### 6.9 日志\n\n- 使用 `@Slf4j`，占位符传参 `log.info(\"user {} login\", userId)`（禁止字符串拼接）\n- 包含关键业务标识，异常必须携带堆栈 `log.error(\"...\", e)`，禁止打印敏感信息（密码、Token、手机号等）\n- API 调用日志、错误日志、业务日志由 `blade-service/blade-log` 异步落库，事件通过 `AsyncEventDispatcher` 发布\n\n---\n\n## 7. 数据库规范\n\n- **表名前缀**：`blade_`（如 `blade_user`、`blade_role_menu`）\n- **业务表必含字段**：`id`（BIGINT）、`tenant_id`（VARCHAR，多租户表）、`create_user`、`create_dept`、`create_time`、`update_user`、`update_time`、`status`、`is_deleted`\n- **主键策略**：雪花算法（`IdType.ASSIGN_ID`），Long 字段用 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n- **逻辑删除**：`is_deleted`（INT），`0` = 未删除，`1` = 已删除\n- **索引命名**：唯一索引 `uk_` 前缀，普通索引 `idx_` 前缀\n- **多数据库**：支持 7 种数据库，SQL 脚本位于 `doc/sql/`（全量建表 + 版本升级脚本），修改表结构时需同步所有脚本\n\n---\n\n## 8. 多租户与安全\n\n### 8.1 多租户\n\n- 默认**字段隔离模式**（`tenant_id`），MP 内置方法自动注入\n- 租户表通过 Nacos `blade.yaml` 中 `blade.tenant.tables` 配置声明\n- 自定义 SQL 需手动过滤：`AuthUtil.getTenantId()` 或 `SecureUtil.getTenantId()`\n- **本版本不支持** `@TenantDS` / `@NonDS` 数据源切换注解；如需物理隔离，可结合 dynamic-datasource 的 `@DS` 配合业务逻辑实现\n\n### 8.2 权限体系\n\n- **角色权限**：`@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（或自定义 SpEL 表达式）\n- **数据权限**：`blade-starter-datascope` DataScopeHandler，行级过滤，通过 `RoleScope` 配置\n- **本版本不含**：接口权限（ApiScopeHandler）、菜单级权限表达式 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 通配放行\n- 接口放行请在 Nacos `blade.secure.skip-url` 中配置 URL 通配，或在 `blade-gateway` 的 `AuthFilter` 白名单中配置\n\n### 8.3 登录与 Token\n\n- 登录密码使用 **SM2 国密**加解密（`blade.auth.public-key` / `blade.auth.private-key`，由 `blade-auth` 模块的 `Sm2KeyGenerator` 生成）\n- Token 使用 JWT 签名（`blade.token.sign-key`）+ 可选 AES 加密（`blade.token.aes-key`）\n- 多 Granter 支持（`blade-auth/granter/`）：`PasswordTokenGranter`、`CaptchaTokenGranter`、`RefreshTokenGranter`、`SocialTokenGranter`\n- **网关统一鉴权**：`blade-gateway/filter/AuthFilter` 作为 `GlobalFilter` 验证 JWT，下游服务通过 `exchange attributes` 透传用户上下文\n- 获取当前用户：`SecureUtil.getUser()` / `SecureUtil.getUserId()` / `SecureUtil.getTenantId()` / `SecureUtil.isAdministrator()`\n\n### 8.4 分布式事务\n\n- 由 `blade-starter-transaction`（Seata）提供支持，参考 `blade-ops/blade-seata-order` + `blade-ops/blade-seata-storage` 双服务示例\n- 需要跨服务事务的场景标注 `@GlobalTransactional`\n\n---\n\n## 9. 缓存规范\n\n- `CacheUtil` 统一操作，常量命名在 `blade-common/cache/CacheNames`\n- 数据变更后清除缓存：`CacheUtil.clear(CACHE_NAME, Boolean.FALSE)`\n- 字典翻译：推荐跨服务通过 `IDictClient.getValue(code, dictKey)`（Feign）；服务本地可用 `DictCache.getValue(...)`\n- Redis 序列化使用 Protostuff（默认配置），键分布式作用域；Gateway 侧使用 Reactive Redis\n\n---\n\n## 10. 开源许可头部\n\n所有新建的 Java 源文件必须在文件顶部包含以下 Apache 2.0 许可头部：\n\n```java\n/**\n * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n```\n\n> **重要**：本工程为开源版本，**不要**写入 BladeX 商业许可证头部。若需要迁移为商业版本，请以商业项目代码样例为准。\n\n---\n\n## 11. 构建与部署\n\n### 11.1 Maven 构建\n\n```bash\nmvn clean compile -DskipTests                      # 根目录执行：编译所有子模块\nmvn clean package -DskipTests                      # 打包所有服务为 FatJar\nmvn clean package -DskipTests -pl blade-service/blade-system -am  # 只打包 system 服务及其依赖模块\n```\n\n- 父 POM 使用 `${revision}` 占位符管理版本，由 `flatten-maven-plugin` 在 `process-resources` 阶段展开\n- 各服务打包产物通过 `directory-maven-plugin` 汇总复制到根目录 `target/`\n- `finalName` 使用 `${project.name}`，产物命名与 artifactId 一致\n\n### 11.2 Nacos 配置\n\n- 配置文件位于 `doc/nacos/`：`blade.yaml`（公共配置）+ `blade-dev.yaml`/`blade-test.yaml`/`blade-prod.yaml`（环境差异）\n- 数据源、密钥、Swagger、Sentinel、Feign 等共享参数统一在 Nacos 配置\n- 各服务 `src/main/resources/application-{env}.yml` 仅保留 `server.port` 与 `spring.datasource.url` 等少量本地覆盖\n\n### 11.3 Docker 部署\n\n- `script/docker/docker-compose.yml` 提供一体化编排：Nacos + Sentinel + Redis + Nginx + 全部微服务\n- 默认网段 `blade_net`，Nacos `172.30.0.48`，Sentinel `172.30.0.58`\n- Harbor 镜像推送通过父 POM 的 `docker.registry.url` 属性控制\n\n### 11.4 必备环境变量\n\n```bash\nBLADE_OAUTH2_PUBLIC_KEY    # SM2 公钥\nBLADE_OAUTH2_PRIVATE_KEY   # SM2 私钥\nBLADE_TOKEN_SIGN_KEY       # JWT 签名密钥\nBLADE_TOKEN_CRYPTO_KEY     # Token AES 加密密钥\n```\n\n### 11.5 服务端口（默认）\n\n| 服务 | 端口 |\n| --- | --- |\n| blade-gateway | 80 |\n| blade-auth | 8100 |\n| blade-admin | 7002 |\n| blade-system | 8106 |\n| blade-desk | 8103 |\n| blade-log | 8102 |\n| blade-develop | 8108 |\n| blade-report | 8109 |\n| blade-resource | 8110 |\n\n> 实际端口以 `src/main/resources/application-{env}.yml` 中 `server.port` 为准。\n\n---\n\n## 12. Git 提交规范\n\n当需要提交代码时，优先使用 **`/blade-commit`** skill，它会自动分析变更内容并生成符合项目规范的 Gitmoji 提交信息。\n\n采用 **Gitmoji** 风格，中文描述。格式：`:<gitmoji>: 简要描述`\n\n| Gitmoji | 场景 | 示例 |\n| --- | --- | --- |\n| `:sparkles:` | 新增功能 | `:sparkles: 登录认证新增IP锁定逻辑` |\n| `:zap:` | 优化重构 | `:zap: 优化字典查询排序逻辑` |\n| `:bug:` | 修复缺陷 | `:bug: 修复用户查询未过滤已删除数据` |\n| `:tada:` | 版本发布 | `:tada: x.x.x.RELEASE` |\n| `:recycle:` | 代码重构 | `:recycle: 重构租户删除逻辑` |\n\n---\n\n## 13. 框架组件速查\n\n| 组件 | 用途 |\n| --- | --- |\n| `R<T>` | 统一 API 响应（`org.springblade.core.tool.api.R`） |\n| `BladeController` | Controller 基类（`org.springblade.core.boot.ctrl`） |\n| `BladeCloudApplication` | Cloud 服务启动注解（`blade-auth`、业务服务使用） |\n| `TenantEntity` / `BaseEntity` | 实体基类（`org.springblade.core.mp.base`） |\n| `BaseService` / `BaseServiceImpl` | BladeX 增强 Service（含 `deleteLogic`） |\n| `BaseEntityWrapper` | Entity→VO 转换基类 |\n| `BladeUser` | 当前登录用户（含租户信息） |\n| `Condition` / `Query` | 查询条件构建 / 分页参数 |\n| `CacheUtil` / `DictCache` | 缓存工具 / 字典缓存 |\n| `SecureUtil` / `AuthUtil` | 获取当前用户/租户信息 |\n| `BladeRedis` | Redis 操作封装 |\n| `Func` / `BeanUtil` / `StringUtil` | 通用工具类 |\n| `ServiceException` | 业务异常 |\n| `@PreAuth` | 角色权限注解（`RoleConstant.HAS_ROLE_ADMIN`） |\n| `@FeignClient` + `AppConstant.APPLICATION_XXX_NAME` | 服务间调用 |\n| `ISysClient` / `IUserClient` / `IDictClient` | 常用 Feign 契约 |\n| `@GlobalTransactional` | Seata 分布式事务 |\n| `@Operation` / `@Tag` / `@Schema` | springdoc / OpenAPI 3 文档注解 |\n| `LauncherConstant` / `AppConstant` / `CommonConstant` | 启动常量 / 应用名 / 公共常量 |\n\n---\n\n## 14. 风格一致性\n\n1. 风格不确定时，优先查阅现有服务并模仿当前业务域的实现方式\n2. 新增业务域时，**先建 API 模块再建 Service 模块**；API 模块只放契约，不引入 Service 依赖\n3. 新服务参考 `blade-service/blade-system` + `blade-service-api/blade-system-api`（最完整） 或 `blade-service/blade-desk` + `blade-desk-api`（最简样板）\n4. 现有服务或契约已满足需求时，禁止自写替代实现；跨服务使用数据请走 Feign，不要重复落库\n5. 与用户交互全程使用**中文**，代码注释和 Javadoc 亦使用中文\n6. 本工程为开源版，**不要**引用商业版特有能力（Flowable、PowerJob、`@TenantDS`、`@BladeView`、接口权限、`blade-ops-api`、`blade-plugin` 等）；如确实需要，请先向用户确认是否切换到商业版代码库\n","isInternal":false,"tokens":7799,"sizeBytes":23630},{"name":"README.md","path":".claude/skills/avue-design/README.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/README.md","title":"Avue-design Skill","category":"anthropic-skill","format":"markdown","content":"# avue-design Skill 集合\n\nAvue 组件代码生成与配置指南，基于 Avue v3.8.2 (Vue3 + Element-Plus) 文档构建。\n\n## 概述\n\n本 Skill 集合为 Claude Code 提供完整的 Avue 组件库知识，使其能够根据用户需求精准生成 Avue 组件代码。覆盖 CRUD 表格、动态表单、树组件、数据展示等全部 Avue 组件，支持 Composition API 和 Options API 两种代码风格。\n\n## 目录结构\n\n```\navue-design/\n├── SKILL.md                           # 主入口 - 概览、路由、速查表\n├── README.md                          # 本文件\n└── references/                        # 参考文档（按需加载）\n    │\n    │── 基础配置 ───────────────────────\n    ├── global-config.md               # 安装、全局配置、国际化、云存储\n    ├── api-style-guide.md             # Composition API vs Options API 完整模板\n    ├── utility-apis.md                # 16个全局工具函数 ($DialogForm, $Export 等)\n    │\n    │── avue-crud 表格组件 ─────────────\n    ├── crud-core.md                   # 组件属性、Option配置、按钮、弹窗、分页、事件、方法、插槽\n    ├── crud-column.md                 # 列配置、字典、Props映射、排序过滤、格式化渲染\n    ├── crud-search.md                 # 搜索栏配置、搜索字段属性、级联搜索\n    ├── crud-form-dialog.md            # 弹窗表单配置、验证规则、字段控制、分组、权限\n    ├── crud-features.md               # 树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽、插件模式\n    │\n    │── avue-form 表单组件 ─────────────\n    ├── form-core.md                   # 组件属性、布局、按钮、Column通用属性、事件、方法、插槽\n    ├── form-field-types.md            # 全部27+字段类型详细配置（input含13子类型、select、date等）\n    ├── form-advanced.md               # 字典、验证、动态子表单、文件上传(含云存储)、字段联动、Mock\n    │\n    │── 其他组件 ─────────────────────\n    ├── tree-component.md              # avue-tree 树组件完整API\n    ├── data-display.md                # 23个数据展示组件\n    └── other-components.md            # Card、DialogForm、Flow、Login、Chat等22个独立组件\n```\n\n## 模块说明\n\n### 1. 基础配置（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `global-config.md` | npm安装、Element-Plus集成、全局crudOption/formOption、阿里云/七牛/腾讯COS云存储配置、水印配置、国际化、外部库CDN |\n| `api-style-guide.md` | Composition API 和 Options API 两种风格的完整 CRUD 页面模板，包含详细的差异对照表和转换规则 |\n| `utility-apis.md` | 全部16个Avue工具函数：$DialogForm、$Clipboard、$ImagePreview、$Print、$Export、$Log、findObject、deepClone等 |\n\n### 2. avue-crud 表格组件（5个文件）\n\n这是 Avue 最核心的组件，文档最为丰富。\n\n| 文件 | 内容 |\n|-----|------|\n| `crud-core.md` | 组件属性（21个）、Option表格级属性（索引/选择/展开/行/菜单/网格/工具栏共80+属性）、按钮配置（25+）、弹窗配置（19个）、分页（8个）、事件（35+）、方法（40+含行内编辑/滚动/搜索/视图方法组）、插槽（22+） |\n| `crud-column.md` | 列通用属性（20+含hide/cell/detail控制）、字典配置（7个属性+Props映射）、字典联动、显示控制、多级表头、排序过滤、formatter/render/html格式化、合计行、合并行列、搜索字段属性（20+含searchDisabled/searchControl/renderSearch）、表单字段属性（20+） |\n| `crud-search.md` | 搜索变量绑定（双API风格）、全局搜索属性（15+）、列级搜索属性（14个）、类型覆盖、折叠、级联联动、自定义插槽 |\n| `crud-form-dialog.md` | 弹窗属性（15个）、beforeOpen/beforeClose回调、验证规则(async-validator)、字段display/disabled控制、默认值、control联动、分组/标签页、数据过滤、权限控制 |\n| `crud-features.md` | 树表(基本/懒加载)、虚拟滚动(3.7.0+)、卡片网格模式(3.4.0+)、行编辑(单击/双击)、Excel导入导出(含对话框选项)、行/列拖拽排序、合计行、父子表、行复制、高级筛选(8种操作符)、列管理器、权限控制(对象/行级函数)、CRUD插件模式(Mixin)、API/Option文件模板、动态配置切换 |\n\n### 3. avue-form 表单组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `form-core.md` | 组件属性（9个含v-model:status）、Option属性（20个含formWidth/tabsActive/tabsType/tabsVerifyAll/filterDic/filterNull）、布局（栅格/分组/标签页/帮助文字）、Group属性（8个含arrow/display/header）、按钮配置（12个）、Column通用属性（25+）、事件（6个含change/tab-click/error/mock-change）、方法（12个含show/hide/scrollToField/getField）、插槽（6个）、两种API风格完整示例 |\n| `form-field-types.md` | 全部27+字段类型完整配置：input(含14子类型:password/textarea/search/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc + formatters/parser)、number、select(含all/noMatchText)、cascader、checkbox、radio、date(含disabledDate/shortcuts/unlinkPanels)、time(含arrowControl + TimeSelect自动切换)、switch、upload(含dragFile/oss/cropperOption)、tree、input-tag(含min/max/prefix/suffix)、mention(含split/prefix/whole)、input-table(含children/dialogWidth)、color、icon(含dialogWidth/分组iconList)、map(含mapChange/dialogWidth/值格式说明)、rate、slider、array/img/url、dynamic(含max + 完整children配置12属性)、title、input-cron |\n| `form-advanced.md` | 字典配置(本地/远程/格式化/联动/更新)、验证规则(基本/类型/自定义/方法)、动态子表单(表格/表单模式)、文件上传(回调/水印/云存储/数据格式)、字段联动(control/Promise)、数据格式(深层绑定/过滤)、自定义插槽、Mock数据 |\n\n### 4. 其他组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `tree-component.md` | avue-tree完整API：Option属性（12个）、Props属性（4个）、事件（7个）、方法（18个）、插槽（3个）、多选/懒加载/拖拽/虚拟模式示例 |\n| `data-display.md` | 23个数据展示组件列表、avue-data-display核心组件属性和数据结构 |\n| `other-components.md` | 22个独立组件：Card、DialogForm、Tabs、Search、Calendar、Flow、Login、Article、Chat、CountUp、Clipboard、Draggable、TextEllipsis、Sign、Print、Export、ImagePreview、Contextmenu、Video、Verify、Screenshot、License |\n\n## 工作原理\n\n1. **主入口 SKILL.md**（< 200行）始终加载到上下文中，提供：\n   - 快速安装指南\n   - 参考文件导航表（根据任务类型指引读取哪个文件）\n   - 代码生成核心规则\n   - 字段类型速查表\n\n2. **参考文件** 按需加载，当用户需要特定组件的配置时才读取对应文件：\n   - 需要创建 CRUD 页面 → 读取 `crud-core.md` + `crud-column.md`\n   - 需要配置搜索 → 读取 `crud-search.md`\n   - 需要特定字段类型 → 读取 `form-field-types.md`\n   - 需要文件上传 → 读取 `form-advanced.md`\n\n3. **双API风格** 支持：\n   - Composition API（推荐）：`<script setup>` + `ref()` + 箭头函数\n   - Options API：`export default` + `data()` + `methods`\n\n## 数据来源\n\n基于以下 Avue 官方文档与源码完整解析：\n\n- `crud/` 目录 32 个文档（CRUD表格组件）\n- `form/` 目录 38 个文档（表单组件）\n- `component/` 目录 20 个文档（独立组件）\n- `default/` 目录 25 个文档（默认组件）\n- `data/` 目录 23 个文档（数据展示组件）\n- `docs/` 目录 10 个文档（全局配置、API、安装等）\n- Saber3 项目参考文件（Composition API vs Options API 对照）\n- Avue v3.8.2 完整源码审计（packages/element-plus/ 全部组件 + packages/core/ 核心模块 + src/ 工具库）\n\n总计 **6,000+ 行** 参考文档，覆盖 Avue v3.8.2 全部组件配置。\n\n## 版本信息\n\n- Avue 版本：v3.8.2 (2026-03-27)\n- Vue 版本：Vue 3\n- UI 框架：Element-Plus\n- 创建日期：2026-04-05\n","isInternal":false,"tokens":2645,"sizeBytes":8474},{"name":"api-style-guide.md","path":".claude/skills/avue-design/references/api-style-guide.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/api-style-guide.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Avue 代码风格指南：Composition API vs Options API\n\n生成 Avue 代码时，需根据用户偏好选择以下两种风格之一。默认使用 Composition API。\n\n## 目录\n\n1. [Composition API 完整模板](#composition-api-完整模板)\n2. [Options API 完整模板](#options-api-完整模板)\n3. [关键差异对照表](#关键差异对照表)\n4. [转换规则](#转换规则)\n\n---\n\n## Composition API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <!-- 自定义左侧按钮 -->\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <!-- 自定义列内容 -->\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed, onMounted } from 'vue'\nimport { useStore } from 'vuex'\nimport { ElMessage, ElMessageBox } from 'element-plus'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\n// ============ 响应式数据 ============\nconst crud = ref(null)\nconst form = ref({})\nconst search = ref({})\nconst loading = ref(true)\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n})\nconst selectionList = ref([])\nconst data = ref([])\n\n// ============ Vuex ============\nconst store = useStore()\nconst permission = computed(() => store.getters.permission)\n\n// ============ 计算属性 ============\nconst permissionList = computed(() => ({\n  addBtn: !!permission.value.module_add,\n  viewBtn: !!permission.value.module_view,\n  delBtn: !!permission.value.module_delete,\n  editBtn: !!permission.value.module_edit,\n}))\n\nconst ids = computed(() =>\n  selectionList.value.map(ele => ele.id).join(',')\n)\n\n// ============ Option 配置 ============\nconst option = reactive({\n  height: 'auto',\n  calcHeight: 32,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  dialogClickModal: false,\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      search: true,\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      search: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n})\n\n// ============ 生命周期 ============\nonMounted(() => {\n  // 初始化逻辑\n})\n\n// ============ 方法 ============\nconst onLoad = () => {\n  loading.value = true\n  getList(page.value.currentPage, page.value.pageSize, Object.assign({}, search.value)).then(res => {\n    const resData = res.data.data\n    page.value.total = resData.total\n    data.value = resData.records\n    loading.value = false\n    selectionClear()\n  })\n}\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(row.id))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据')\n    return\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(ids.value))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n    })\n  }\n  done()\n}\n\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\n\nconst searchReset = () => {\n  onLoad()\n}\n\nconst selectionChange = (list) => {\n  selectionList.value = list\n}\n\nconst selectionClear = () => {\n  selectionList.value = []\n  crud.value?.toggleSelection()\n}\n\nconst refreshChange = () => {\n  onLoad()\n}\n</script>\n```\n\n---\n\n## Options API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      data: [],\n      option: {\n        height: 'auto',\n        calcHeight: 32,\n        tip: false,\n        searchShow: true,\n        searchMenuSpan: 6,\n        border: true,\n        index: true,\n        selection: true,\n        dialogClickModal: false,\n        column: [\n          {\n            label: '名称',\n            prop: 'name',\n            search: true,\n            rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n          },\n          {\n            label: '状态',\n            prop: 'status',\n            type: 'select',\n            search: true,\n            dicData: [\n              { label: '启用', value: 1 },\n              { label: '禁用', value: 0 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: !!this.permission.module_add,\n        viewBtn: !!this.permission.module_view,\n        delBtn: !!this.permission.module_delete,\n        editBtn: !!this.permission.module_edit,\n      }\n    },\n    ids() {\n      return this.selectionList.map(ele => ele.id).join(',')\n    },\n  },\n  methods: {\n    onLoad(page, params = {}) {\n      this.loading = true\n      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n        const data = res.data.data\n        this.page.total = data.total\n        this.data = data.records\n        this.loading = false\n        this.selectionClear()\n      })\n    },\n    rowSave(row, done, loading) {\n      add(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowUpdate(row, index, done, loading) {\n      update(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(row.id))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n        })\n    },\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据')\n        return\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(this.ids))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n          this.$refs.crud.toggleSelection()\n        })\n    },\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data\n        })\n      }\n      done()\n    },\n    searchChange(params, done) {\n      this.query = params\n      this.page.currentPage = 1\n      this.onLoad(this.page, params)\n      done()\n    },\n    searchReset() {\n      this.query = {}\n      this.onLoad(this.page)\n    },\n    selectionChange(list) {\n      this.selectionList = list\n    },\n    selectionClear() {\n      this.selectionList = []\n      this.$refs.crud.toggleSelection()\n    },\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage\n    },\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize\n    },\n    refreshChange() {\n      this.onLoad(this.page)\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 关键差异对照表\n\n| 特性 | Composition API | Options API |\n|-----|----------------|-------------|\n| **脚本标签** | `<script setup>` | `<script> export default {}` |\n| **数据声明** | `const x = ref(val)` / `reactive({})` | `data() { return { x: val } }` |\n| **脚本中访问数据** | `x.value` | `this.x` |\n| **模板中访问数据** | `{{ x }}` (自动解包) | `{{ x }}` |\n| **计算属性** | `const x = computed(() => ...)` | `computed: { x() { ... } }` |\n| **方法** | `const fn = () => {}` | `methods: { fn() {} }` |\n| **生命周期** | `onMounted(() => {})` | `mounted() {}` |\n| **Vuex** | `const store = useStore()` | `...mapGetters([...])` |\n| **消息提示** | `ElMessage.success(...)` | `this.$message(...)` |\n| **确认框** | `ElMessageBox.confirm(...)` | `this.$confirm(...)` |\n| **组件引用** | `const crud = ref(null)` | `this.$refs.crud` |\n| **搜索参数** | `v-model:search=\"search\"` 自动同步 | 手动赋值 `this.query = params` |\n| **分页事件** | `v-model:page` 自动处理 | 需要 `currentChange`/`sizeChange` |\n\n## 转换规则\n\n从 Composition API 转换为 Options API：\n1. `ref()` 声明 → 移入 `data()` return\n2. 移除所有 `.value` 访问\n3. 箭头函数 → `methods` 对象方法\n4. `computed(() => ...)` → `computed` 对象\n5. `ElMessage` / `ElMessageBox` → `this.$message` / `this.$confirm`\n6. `useStore()` → `mapGetters`\n7. `onMounted()` → `mounted()`\n8. 添加 `currentChange` / `sizeChange` 事件处理\n\n从 Options API 转换为 Composition API：\n1. `data()` 中的属性 → `ref()` 或 `reactive()`\n2. 添加 `.value` 访问\n3. `methods` 中的方法 → 顶层箭头函数\n4. `this.$xxx` → 导入对应模块\n5. `v-model:search=\"search\"` 替代 `query` 变量\n6. 移除 `currentChange` / `sizeChange`（由 v-model:page 处理）\n","isInternal":false,"tokens":3073,"sizeBytes":11608},{"name":"crud-column.md","path":".claude/skills/avue-design/references/crud-column.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/crud-column.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-crud 列配置参考\n\n## 目录\n1. [列通用属性](#列通用属性)\n2. [字典配置](#字典配置)\n3. [显示控制](#显示控制)\n4. [排序与过滤](#排序与过滤)\n5. [格式化与渲染](#格式化与渲染)\n6. [搜索字段属性](#搜索字段属性)\n7. [表单字段属性](#表单字段属性)\n\n---\n\n## 列通用属性\n\ncolumn 数组中每个对象的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 列标题 |\n| prop | string | - | 列属性名 |\n| type | string | - | 字段类型 (input/select/date等，详见form-field-types) |\n| width | string | - | 列宽度 |\n| minWidth | string | - | 列最小宽度 |\n| fixed | boolean/string | - | 固定列: true/left/right |\n| align | string | 'left' | 对齐方式: left/center/right |\n| headerAlign | string | 'left' | 表头对齐方式 |\n| className | string | - | 单元格自定义class |\n| labelClassName | string | - | 表头自定义class |\n| resizable | boolean | true | 列宽可拖拽 |\n| slot | boolean | - | 启用具名插槽（插槽名=prop） |\n| bind | string | - | 深层数据绑定，如 'info.address' |\n| value | - | - | 列默认值 |\n| order | number | - | 字段排序，数字越大越靠前 |\n| gridRow | boolean | false | 网格模式新起一行 |\n| hide | boolean | false | 隐藏列（不在表格中显示） |\n| cell | boolean | false | 该列可行内编辑 |\n| addDetail | boolean | false | 新增时详情模式 |\n| editDetail | boolean | false | 编辑时详情模式 |\n| detail | boolean | false | 全局详情模式 |\n\n### 主键配置\n\n```javascript\noption: {\n  rowKey: 'id',           // 主键字段名，默认 'id'\n  rowParentKey: 'parentId' // 父级主键(树表用)\n}\n```\n\n### 对象格式列配置 (3.2.15+)\n\ncolumn 可以用对象格式替代数组，以 prop 为 key：\n\n```javascript\n// 数组格式\ncolumn: [\n  { label: '名称', prop: 'name' },\n  { label: '状态', prop: 'status' }\n]\n\n// 对象格式\ncolumn: {\n  name: { label: '名称' },\n  status: { label: '状态' }\n}\n```\n\n---\n\n## 字典配置\n\n### 字典属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| dicData | array/function/promise | - | 本地字典数据 (3.5.0+ 支持函数/Promise) |\n| dicUrl | string | - | 远程字典URL |\n| dicFlag | boolean | false | 打开表单时重新请求字典 |\n| dicQuery | object | - | 字典请求查询参数 |\n| dicHeaders | object | - | 字典请求头参数 |\n| dicFormatter | function(res) | (res)=>res | 字典返回数据格式化 |\n| dicMethod | string | 'GET' | 字典请求方法 |\n\n### Props 字段映射\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 选项标签属性 |\n| value | string | 'value' | 选项值属性 |\n| children | string | 'children' | 子选项属性 |\n| disabled | string | 'disabled' | 禁用属性 |\n| res | string | - | 响应数据层级，如 'data.data' |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  // 本地字典\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n  // 或远程字典\n  dicUrl: '/api/dict/type',\n  props: {\n    label: 'name',\n    value: 'code',\n    res: 'data.records',\n  },\n}\n```\n\n### 字典联动\n\n通过 `cascader` 属性实现级联联动：\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city'],  // 指定子级字段prop\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}', // {{key}} 为父级选中值\n  },\n]\n```\n\n### 更新字典\n\n```javascript\n// 更新指定字段的字典\ncrudRef.value.updateDic('prop', [{ label: '新选项', value: 1 }])\n// 重新加载所有字典\ncrudRef.value.dicInit()\n```\n\n---\n\n## 显示控制\n\n### 溢出省略\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| overHidden | boolean/object | false | 内容溢出显示tooltip |\n| showOverflowTooltip | boolean/object | false | 同上 |\n| ellipsis | boolean | false | 文本省略 (3.6.4+) |\n\n### 多级表头\n\n通过 `children` 嵌套实现：\n\n```javascript\ncolumn: [\n  {\n    label: '用户信息',\n    children: [\n      { label: '姓名', prop: 'name' },\n      { label: '年龄', prop: 'age' },\n    ]\n  }\n]\n```\n\n### 表头固定\n\n```javascript\noption: {\n  height: '500px',  // 设置高度后表头自动固定\n  showHeader: true,  // 默认true\n}\n```\n\n---\n\n## 排序与过滤\n\n### 排序\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| sortable | boolean/string | - | 排序: true(本地)/custom(远程) |\n| defaultSort | object | - | 默认排序: {prop, order} |\n| columnSort | boolean | - | 列拖拽排序 |\n\n```javascript\n// 远程排序\n{\n  label: '日期',\n  prop: 'date',\n  sortable: 'custom',  // 需监听 sort-change 事件\n}\n\n// 默认排序\noption: {\n  defaultSort: { prop: 'date', order: 'descending' }\n}\n```\n\n### 过滤\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| filters | boolean/array | false | 数据过滤 |\n| filterMethod | function | - | 自定义过滤逻辑 |\n| filter-multiple | boolean | true | 多选过滤 |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  filters: true,  // 自动从 dicData 生成\n  // 或手动指定\n  filters: [\n    { text: '启用', value: 1 },\n    { text: '禁用', value: 0 },\n  ],\n}\n```\n\n---\n\n## 格式化与渲染\n\n### formatter 格式化\n\n```javascript\n{\n  label: '金额',\n  prop: 'amount',\n  formatter: (row, value, label, column) => {\n    return '¥' + value.toFixed(2)\n  },\n}\n```\n\n### tooltipFormatter (3.6.3+)\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  overHidden: true,\n  tooltipFormatter: (row, value, label, column) => {\n    return `详细内容: ${value}`\n  },\n}\n```\n\n### html 属性\n\n```javascript\n{\n  label: '内容',\n  prop: 'content',\n  html: true,  // 支持HTML翻译\n}\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  // 列区域渲染\n  render: (h, { row, index, dic }) => {\n    return h('el-tag', { type: row.status === 1 ? 'success' : 'danger' }, row.statusName)\n  },\n  // 表单区域渲染\n  renderForm: (h, { column, $index, dic }) => {\n    return h('el-input', { modelValue: form.value[column.prop] })\n  },\n  // 表头渲染\n  renderHeader: (h, { column }) => {\n    return h('span', { style: 'color: red' }, column.label)\n  },\n}\n```\n\n### 合计行\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },     // 求和\n    { name: 'price', type: 'average' },   // 平均值\n    { name: 'count', type: 'count' },     // 计数\n  ],\n}\n\n// 或自定义合计\n// 通过 summary-method 属性传入函数\n```\n\n### 合并行列\n\n```javascript\n// 通过 span-method 属性设置\n<avue-crud :span-method=\"spanMethod\">\n\nconst spanMethod = ({ row, column, rowIndex, columnIndex }) => {\n  if (rowIndex === 0 && columnIndex === 0) {\n    return { rowspan: 2, colspan: 1 }\n  }\n}\n```\n\n---\n\n## 搜索字段属性\n\n在 column 中通过 `search: true` 启用搜索，以下为搜索相关属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | string | - | 搜索初始值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索字段排序 |\n| searchGutter | number | - | 搜索字段间距 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索字段类型（覆盖 type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 搜索可清空 |\n| searchMultiple | boolean | false | 搜索多选 |\n| searchTags | boolean | false | 搜索标签显示 |\n| searchLabelPosition | string | 'left' | 搜索标签位置 |\n| searchRules | array | - | 搜索验证规则 |\n| searchDisabled | boolean | false | 搜索字段禁用 |\n| searchReadonly | boolean | false | 搜索字段只读 |\n| searchIndex | number | - | 搜索栏初始显示时包含此字段 |\n| searchControl | function | - | 搜索字段联动控制 |\n| renderSearch | function | - | 搜索区域自定义渲染函数 (3.4.2+) |\n\n### 搜索栏全局配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| searchBtnIcon | string | - | 搜索按钮图标 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| emptyBtnIcon | string | - | 清空按钮图标 |\n| searchShow | boolean | true | 初始显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留数量 |\n| searchMenuPosition | string | 'center' | 搜索按钮位置 |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示内容 |\n| searchTipPlacement | string | 'bottom' | 提示方向 |\n| searchMenuSpan | number | - | 搜索按钮栅格数 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索默认栅格数 |\n| searchGutter | number | - | 搜索默认间距 |\n| searchLabelWidth | number | 80 | 搜索默认标签宽度 |\n| searchLabelPosition | string | 'left' | 搜索默认标签位置 |\n\n---\n\n## 表单字段属性\n\ncolumn 中控制字段在表单（新增/编辑/查看弹窗）中表现的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| display | boolean | true | 在表单中显示 |\n| disabled | boolean | false | 在表单中禁用 |\n| addDisabled | boolean | false | 新增时禁用 |\n| editDisabled | boolean | false | 编辑时禁用 |\n| viewDisabled | boolean | false | 查看时禁用 |\n| addDisplay | boolean | true | 新增时显示 |\n| editDisplay | boolean | true | 编辑时显示 |\n| viewDisplay | boolean | true | 查看时显示 |\n| span | number | 12 | 表单栅格数 |\n| gutter | number | 0 | 表单间距 |\n| offset | number | 0 | 表单偏移 |\n| row | boolean | false | 表单换行 |\n| labelWidth | string | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| rules | array | - | 验证规则 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 帮助文字方向 |\n| labelTip | string | - | 标题帮助文字 |\n| labelTipPlacement | string | 'top' | 标题帮助文字方向 |\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n| render | function | - | 列区域渲染 (3.4.2+) |\n| renderForm | function | - | 表单区域渲染 (3.4.2+) |\n| renderHeader | function | - | 表头渲染 (3.4.2+) |\n","isInternal":false,"tokens":3459,"sizeBytes":10817},{"name":"crud-core.md","path":".claude/skills/avue-design/references/crud-core.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/crud-core.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-crud 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表格级属性](#option-表格级属性)\n3. [按钮配置](#按钮配置)\n4. [弹窗配置](#弹窗配置)\n5. [分页配置](#分页配置)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n获取表格原生引用：\n```javascript\nconst crudRef = ref(null)\n// 访问 el-table 实例\ncrudRef.value.$refs.table\n```\n\n---\n\n## 组件属性\n\n在 `<avue-crud>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| data | array | - | 表格显示数据 |\n| option | object | - | 组件配置项 |\n| before-open | function(done, type, loading) | - | 打开弹窗前回调，type: add/edit/view |\n| before-close | function(done, type) | - | 关闭弹窗前回调 |\n| permission | function/object | - | 按钮权限控制（支持行级） |\n| v-model:search | object | - | 搜索变量双向绑定 |\n| v-model:page | object | - | 分页变量双向绑定 |\n| cell-class-name | function/string | - | 单元格 className 回调 |\n| cell-style | function/object | - | 单元格样式回调 |\n| header-cell-class-name | function/string | - | 表头单元格 className |\n| header-row-class-name | function/string | - | 表头行 className |\n| header-row-style | function/object | - | 表头行样式 |\n| header-cell-style | function/object | - | 表头单元格样式 |\n| row-class-name | function/string | - | 行 className 回调 |\n| row-style | function/object | - | 行样式回调 |\n| span-method | function | - | 合并行或列 |\n| summary-method | function | - | 自定义合计计算 |\n| table-loading | boolean | - | 加载状态 |\n| upload-before | function(file, done, loading) | - | 上传组件上传前回调 |\n| upload-after | function(res, done) | - | 上传组件上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 上传预览回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制回调 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小回调 (3.4.4+) |\n| disabled | boolean | false | 禁用所有交互元素 |\n| width | number/string | - | 表格宽度 |\n| height | number/string | - | 表格高度 |\n\n---\n\n## Option 表格级属性\n\n在 `option` 对象中设置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 列配置数组 |\n| height | string | - | 表格高度，设 'auto' 为自适应窗口 |\n| maxHeight | string | - | 表格最大高度 |\n| title | string | - | 表格标题 |\n| border | boolean | false | 纵向边框 |\n| stripe | boolean | false | 斑马纹 |\n| fit | boolean | true | 列宽自适应 |\n| showHeader | boolean | true | 显示表头 |\n| header | boolean | false | 隐藏表格头部操作栏 |\n| emptyText | string | '暂无数据' | 空数据提示文字 |\n| tooltipEffect | string | 'dark' | tooltip 主题: dark/light |\n| tooltipOptions | object | - | tooltip 配置项 |\n| scrollbarAlwaysOn | boolean | true | 始终显示滚动条 (3.6.0+) |\n\n### 索引列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| index | boolean | false | 显示序号 |\n| indexLabel | string | '#' | 序号列标题 |\n| indexWidth | number | 50 | 序号列宽度 |\n| indexFixed | boolean/string | true | 序号列固定: true/left/right |\n| indexClassName | string | - | 序号列单元格自定义class |\n| indexLabelClassName | string | - | 序号列表头自定义class |\n\n### 选择列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| selection | boolean | false | 显示多选框 |\n| selectable | function | - | 判断行是否可选 |\n| selectionWidth | number | 50 | 选择列宽度 |\n| selectionFixed | boolean/string | true | 选择列固定 |\n| selectionClassName | string | - | 选择列单元格class |\n| selectionLabelClassName | string | - | 选择列表头class |\n| reserveSelection | boolean | false | 翻页保留选择 |\n\n### 展开行\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| expand | boolean | false | 显示展开 |\n| expandWidth | number | 50 | 展开列宽度 |\n| expandFixed | boolean/string | true | 展开列固定 |\n| expandClassName | string | - | 展开列单元格class |\n| expandLabelClassName | string | - | 展开列表头class |\n| defaultExpandAll | boolean | false | 默认展开所有行 |\n| expandRowKeys | array | - | 展开行的keys |\n\n### 行配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowKey | string | 'id' | 行数据主键 |\n| rowParentKey | string | 'parentId' | 父行主键(树表用) |\n| highlightCurrentRow | boolean | false | 高亮当前行 |\n| defaultSort | object | - | 默认排序 {prop, order: 'ascending'/'descending'} |\n| showOverflowTooltip | boolean/object | false | 溢出提示 |\n| showSummary | boolean | false | 显示合计行 |\n| lazy | boolean | false | 懒加载子节点 |\n\n### 菜单列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menu | boolean | true | 显示操作菜单 |\n| menuWidth | number | 220 | 菜单列宽度 |\n| menuTitle | string | '操作' | 菜单列标题 |\n| menuFixed | boolean/string | true | 菜单列固定 |\n| menuType | string | 'text' | 菜单按钮样式: button/icon/text/menu |\n| menuHeaderAlign | string | 'center' | 菜单表头对齐 |\n| menuAlign | string | 'center' | 菜单按钮对齐 |\n| menuClassName | string | - | 菜单单元格class |\n| menuLabelClassName | string | - | 菜单表头class |\n\n### 卡片/网格模式 (3.4.0+)\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| grid | boolean | false | 启用网格模式 |\n| gridBtn | boolean | true | 显示模式切换按钮 |\n| gridSpan | number | 8 | 网格栅格数 |\n| gridBackgroundImage | string | - | 网格背景图片 |\n| gridBackground | string/function | - | 网格背景色 |\n\n### 工具栏与功能开关\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addRowBtn | boolean | false | 显示新增行内编辑按钮 |\n| cellBtn | boolean | false | 显示行内编辑操作按钮 |\n| dateBtn | boolean | false | 显示日期范围选择按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮 |\n| selectClearBtn | boolean | true | 显示清空选中按钮 |\n| tip | boolean | true | 显示选中项提示 |\n| page | boolean | true | 显示分页 |\n| searchShow | boolean | true | 搜索栏默认显示 |\n| searchIcon | boolean | true | 搜索栏展开/收起图标 |\n| searchIndex | number | - | 搜索栏初始显示字段数（超出折叠） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchSpan | number | 6 | 搜索栅格数 |\n| calcHeight | number | 300 | 表格自适应高度偏移量 |\n| menuXsWidth | number | 100 | 移动端菜单列宽度 |\n| gridXsSpan | number | 12 | 移动端网格栅格数 |\n| headerAlign | string | 'left' | 表头默认对齐方式 |\n| width | string | '100%' | 表格宽度 |\n| formFullscreen | boolean | false | 弹窗显示全屏切换按钮 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| filterMultiple | boolean | true | 过滤器默认多选 |\n\n---\n\n## 按钮配置\n\n### 操作按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| addBtnText | string | '新增' | 新增按钮文字 |\n| addBtnIcon | string | - | 新增按钮图标 |\n| editBtn | boolean | true | 修改按钮 |\n| editBtnText | string | '修改' | 修改按钮文字 |\n| editBtnIcon | string | - | 修改按钮图标 |\n| delBtn | boolean | true | 删除按钮 |\n| delBtnText | string | '删除' | 删除按钮文字 |\n| delBtnIcon | string | - | 删除按钮图标 |\n| viewBtn | boolean | false | 查看按钮 |\n| viewBtnText | string | '查看' | 查看按钮文字 |\n| viewBtnIcon | string | - | 查看按钮图标 |\n| copyBtn | boolean | false | 复制按钮 |\n\n### 弹窗按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| updateBtn | boolean | true | 弹窗修改按钮 |\n| updateBtnText | string | '修改' | 修改按钮文字 |\n| updateBtnIcon | string | - | 修改按钮图标 |\n| saveBtn | boolean | true | 弹窗保存按钮 |\n| saveBtnText | string | '保存' | 保存按钮文字 |\n| saveBtnIcon | string | - | 保存按钮图标 |\n| cancelBtn | boolean | true | 弹窗取消按钮 |\n| cancelBtnText | string | '取消' | 取消按钮文字 |\n| cancelBtnIcon | string | - | 取消按钮图标 |\n\n### 工具栏按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| printBtn | boolean | false | 打印按钮 |\n| excelBtn | boolean | false | 导出按钮 |\n| filterBtn | boolean | false | 筛选按钮 |\n| refreshBtn | boolean | true | 刷新按钮 |\n| columnBtn | boolean | true | 列操作按钮 |\n| searchShowBtn | boolean | true | 搜索显隐按钮 |\n| addRowBtn | boolean | false | 新增行内编辑按钮 |\n| dateBtn | boolean | false | 日期范围选择按钮 |\n| gridBtn | boolean | true | 网格/表格切换按钮 |\n\n### 按钮文字图标规则\n\n按钮名 + `Text` = 按钮文字，按钮名 + `Icon` = 按钮图标。设置图标为 `' '`（空格）可禁用图标。\n\n---\n\n## 弹窗配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogDrag | boolean | false | 弹窗可拖拽 |\n| dialogFullscreen | boolean | false | 弹窗全屏 |\n| dialogCustomClass | string | - | 弹窗自定义class |\n| dialogEscape | boolean | true | ESC关闭弹窗 |\n| dialogClickModal | boolean | false | 点击遮罩关闭弹窗 |\n| dialogCloseBtn | boolean | true | 显示关闭按钮 |\n| dialogModal | boolean | true | 显示遮罩层 |\n| dialogMenuPosition | string | 'right' | 弹窗按钮位置 |\n| dialogTop | number | 25 | 弹窗距顶部距离 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| headerClass | string | - | 弹窗头部自定义class (3.7.0+) |\n| bodyClass | string | - | 弹窗内容区自定义class (3.7.0+) |\n| footerClass | string | - | 弹窗底部自定义class (3.7.0+) |\n\n---\n\n## 分页配置\n\n通过 `v-model:page` 绑定分页对象：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currentPage | number | 1 | 当前页码 |\n| pageSize | number | 20 | 每页条数 |\n| total | number | 0 | 总条数 |\n| pagerCount | number | 7 | 页码按钮数量 |\n| background | boolean | true | 分页按钮背景 |\n| layout | string | 'prev, pager, next, jumper, ->, total' | 分页组件布局 |\n| pageSizes | number[] | [10, 20, 30, 40, 50, 100] | 每页条数选项 |\n| simplePage | boolean | false | 只有一页时隐藏分页 |\n\n---\n\n## 事件\n\n### 数据操作事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-save | (row, done, loading) | 新增数据后触发。done() 关闭表单，loading() 保持表单 |\n| row-update | (row, index, done, loading) | 修改数据后触发 |\n| row-del | (row, index) | 删除行触发 |\n| refresh-change | - | 点击刷新按钮后触发 |\n\n### 搜索事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| search-change | (form, done) | 搜索后触发 |\n| search-reset | - | 清空搜索回调 |\n| date-change | (date) | 日期选择回调 |\n\n### 选择事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| select | (selection, row) | 用户勾选 |\n| select-all | (selection) | 用户全选 |\n| selection-change | (selection) | 选择项变化 |\n| current-row-change | (currentRow, oldCurrentRow) | 当前行变化 |\n| selection-clear | (selection) | 清空选中时触发 |\n\n### 行/单元格事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| cell-mouse-enter | (row, column, cell, event) | 单元格hover进入 |\n| cell-mouse-leave | (row, column, cell, event) | 单元格hover离开 |\n| cell-click | (row, column, cell, event) | 单元格点击 |\n| cell-dblclick | (row, column, cell, event) | 单元格双击 |\n| row-click | (row, column, event) | 行点击 |\n| row-contextmenu | (row, column, event) | 行右击 |\n| row-dblclick | (row, column, event) | 行双击 |\n\n### 表头事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| header-click | (column, event) | 表头点击 |\n| header-contextmenu | (column, event) | 表头右击 |\n| header-dragend | (newWidth, oldWidth, column, event) | 列宽改变 |\n\n### 排序/过滤事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| sort-change | ({ column, prop, order }) | 排序变化 |\n| filter-change | (filters) | 过滤变化 |\n| sortable-change | ({ newIndex, oldIndex }) | 行拖拽排序 |\n| column-sortable-change | ({ newIndex, oldIndex }) | 列拖拽排序 (3.5.2+) |\n\n### 其他事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| on-load | (page) | 页面加载/分页变化时触发 |\n| tree-load | (row, treeNode, resolve) | 加载子节点 (lazy=true) |\n| expand-change | (row, expandedRows/expanded) | 行展开/收起 |\n| grid-status-change | (status) | 网格/表格模式切换 (3.4.7+) |\n| scroll | ({scrollLeft, scrollTop}) | 表格滚动 (3.6.0+) |\n| tab-click | (tab, event) | 弹窗标签页点击 |\n| error | (error) | 表单验证失败 |\n| filter | (filterData) | 高级筛选应用 |\n| date-change | (date) | 日期范围选择回调 |\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowAdd | - | 打开新增弹窗 |\n| rowEdit | (row, index) | 打开编辑弹窗 |\n| rowView | (row, index) | 打开查看弹窗 |\n| updateDic | (prop, dic) | 更新字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | (valid, done, msg) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置所有字段 |\n| clearValidate | (props) | 清除验证结果 |\n| clearSelection | - | 清除多选 |\n| toggleSelection | (array) | 切换选择 |\n| toggleAllSelection | - | 切换全选 |\n| toggleRowExpansion | (row, expanded) | 切换行展开 |\n| setCurrentRow | (row) | 设置当前行 |\n| clearSort | - | 清除排序 |\n| clearFilter | (columnKey) | 清除过滤 |\n| doLayout | - | 重新布局表格 |\n| refreshTable | - | 重新初始化表格 |\n| sort | (prop, order) | 手动排序 |\n| updateKeyChildren | (key, data) | 更新懒加载子节点 (3.6.0+) |\n\n### 行内编辑方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowCopy | (row) | 复制行数据到新增弹窗 |\n| rowCellAdd | (row) | 新增一行行内编辑数据 |\n| rowCell | (row, index) | 切换行编辑状态 |\n| rowCellEdit | (row, index) | 进入行编辑模式 |\n| rowCellUpdate | (row, index) | 保存行编辑数据 |\n| rowCancel | (row, index) | 取消行编辑 |\n| closeDialog | (row, index) | 关闭弹窗并更新数据 |\n| validateCellForm | - | 验证所有行内编辑表单 |\n| validateCellField | (index) | 验证指定行的编辑表单 |\n\n### 数据方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| findData | (id) | 通过主键值查找行数据 |\n| getPermission | (key, row, index) | 获取按钮权限 |\n| getTableHeight | - | 重新计算表格高度 |\n\n### 滚动方法 (3.7.0+ 虚拟滚动)\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| scrollTo | (coordinates) | 滚动到指定位置 |\n| scrollToTop | - | 滚动到顶部 |\n| scrollToLeft | - | 滚动到最左侧 |\n| scrollRow | (rowKey) | 滚动到指定行（虚拟滚动模式） |\n| setScrollTop | (top) | 设置垂直滚动位置 |\n| setScrollLeft | (left) | 设置水平滚动位置 |\n\n### 搜索方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| searchReset | - | 重置搜索表单 |\n| searchChange | (form, done) | 手动触发搜索 |\n\n### 视图方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| handleGridShow | - | 切换网格/表格视图 |\n| columns | - | 获取当前列配置 |\n\n---\n\n## 插槽\n\n### 列相关插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义列内容，参数: {row, index, dic, size, label} |\n| `{prop}-header` | 自定义列表头 |\n| `{prop}-search` | 自定义搜索输入 |\n| `{prop}-form` | 自定义表单字段 |\n| `{prop}-label` | 自定义表单标签 |\n| `{prop}-error` | 自定义表单错误提示 |\n| `{prop}-desc` | 自定义表单描述 (3.6.4+) |\n| `{prop}-type` | 自定义字段类型选择器 |\n\n### 区域插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| search | 自定义整个搜索区域 |\n| searchMenu | 自定义搜索菜单 |\n| header | 自定义表格头部 |\n| body | 自定义表格主体 (3.4.3+) |\n| page | 自定义分页 |\n| empty | 自定义空状态 |\n| tip | 自定义选中提示区域 |\n\n### 按钮插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu-left | 自定义菜单左侧 |\n| menu-right | 自定义菜单右侧 |\n| menu | 自定义整个操作菜单，参数: {row, index, type, size} |\n| menu-before | 操作菜单前 (3.4.3+) |\n| menu-btn | 自定义菜单按钮 |\n| menu-btn-before | 菜单按钮前 (3.4.3+) |\n| menu-form | 自定义表单菜单 |\n| menu-form-before | 表单菜单前 (3.4.3+) |\n| menu-header | 菜单表头插槽 |\n","isInternal":false,"tokens":5689,"sizeBytes":17943},{"name":"crud-features.md","path":".claude/skills/avue-design/references/crud-features.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/crud-features.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-crud 高级功能参考\n\n## 目录\n1. [树形表格](#树形表格)\n2. [虚拟滚动](#虚拟滚动)\n3. [卡片/网格模式](#卡片网格模式)\n4. [行编辑（单元格编辑）](#行编辑)\n5. [导入导出](#导入导出)\n6. [拖拽排序](#拖拽排序)\n7. [合计行](#合计行)\n8. [父子表](#父子表)\n9. [加载状态](#加载状态)\n10. [CRUD 插件模式](#crud-插件模式)\n11. [行复制](#行复制)\n12. [高级筛选](#高级筛选-filterbtn)\n13. [列管理器](#列管理器-columnbtn)\n14. [Excel 导出对话框](#excel-导出对话框)\n15. [权限控制](#权限控制)\n\n---\n\n## 树形表格\n\n### 基本树表\n\n```javascript\noption: {\n  rowKey: 'id',\n  rowParentKey: 'parentId',\n  defaultExpandAll: true,  // 默认展开所有\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n  ],\n}\n\n// 数据需要有 id 和 parentId 字段\ndata: [\n  { id: 1, parentId: 0, name: '父节点', code: 'P1' },\n  { id: 2, parentId: 1, name: '子节点', code: 'C1' },\n]\n```\n\n### 懒加载树表\n\n```vue\n<avue-crud :option=\"option\" @tree-load=\"treeLoad\">\n\noption: {\n  rowKey: 'id',\n  lazy: true,\n  column: [\n    { label: '名称', prop: 'name', hasChildren: 'hasChildren' },\n  ],\n}\n\nconst treeLoad = (row, treeNode, resolve) => {\n  getChildren(row.id).then(res => {\n    resolve(res.data.data)\n  })\n}\n```\n\n注意：非懒加载时需去掉 `hasChildren` 字段。\n\n---\n\n## 虚拟滚动 (3.7.0+)\n\n用于大数据量场景的虚拟滚动：\n\n```javascript\noption: {\n  virtualize: true,\n  height: 500,  // 必须为数字\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      width: 200,   // 虚拟滚动时 width 必须为数字\n      fixed: true,   // 列自适应\n    },\n  ],\n}\n```\n\n注意：虚拟滚动模式下 `width` 和 `height` 必须是数字类型。\n\n自适应高度：\n```javascript\noption: {\n  virtualize: true,\n  height: 'auto',  // 自适应\n}\n```\n\n---\n\n## 卡片/网格模式 (3.4.0+)\n\n将表格显示为卡片网格形式：\n\n```javascript\noption: {\n  grid: true,           // 启用网格模式\n  gridBtn: true,        // 显示切换按钮（默认true）\n  gridSpan: 8,          // 每卡片占栅格数\n  gridBackgroundImage: 'url(xxx)',  // 背景图\n  gridBackground: '#f5f5f5',       // 背景色\n}\n\n// 背景色也可以是函数\noption: {\n  gridBackground: (row, index) => {\n    return index % 2 === 0 ? '#f0f9ff' : '#fff'\n  },\n}\n```\n\n自定义卡片插槽：\n```vue\n<template #default=\"{ row, index }\">\n  <div class=\"custom-card\">\n    <h3>{{ row.name }}</h3>\n    <p>{{ row.description }}</p>\n  </div>\n</template>\n```\n\n---\n\n## 行编辑\n\n### 基本行编辑\n\n```javascript\noption: {\n  cellBtn: true,     // 显示行编辑按钮\n  addRowBtn: true,   // 显示行新增按钮\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      cell: true,    // 该列可编辑\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      cell: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n}\n```\n\n### 首次进入编辑模式\n\n```javascript\n// 数据中设置 $cellEdit: true\ndata: [\n  { id: 1, name: 'test', $cellEdit: true },\n]\n```\n\n### 单击/双击编辑\n\n```vue\n<!-- 单击编辑 -->\n<avue-crud @cell-click=\"cellClick\">\n\nconst cellClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n\n<!-- 双击编辑 -->\n<avue-crud @cell-dblclick=\"cellDblClick\">\n\nconst cellDblClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n```\n\n### 保存行编辑\n\n数据必须包含 `rowKey`（默认 id）字段才能保存。\n\n---\n\n## 导入导出\n\n### 导出 Excel\n\n```javascript\noption: {\n  excelBtn: true,  // 显示导出按钮\n}\n```\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n### 编程式导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 导入 Excel\n\n参考 `default/export/import` 示例。\n\n---\n\n## 拖拽排序\n\n### 行拖拽排序\n\n```javascript\noption: {\n  sortable: true,  // 启用行拖拽\n  rowKey: 'id',    // 必须设置主键\n}\n```\n\n需引入 Sortable.js：\n```html\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n```\n\n监听排序事件：\n```vue\n<avue-crud @sortable-change=\"sortableChange\">\n\nconst sortableChange = ({ newIndex, oldIndex }) => {\n  // 处理排序逻辑\n}\n```\n\n### 列拖拽排序 (3.5.2+)\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    columnSort: true,  // 启用列拖拽\n  },\n]\n```\n\n监听事件：\n```vue\n<avue-crud @column-sortable-change=\"columnSortableChange\">\n```\n\n---\n\n## 合计行\n\n### 内置合计\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },      // 求和\n    { name: 'price', type: 'average' },    // 平均值\n    { name: 'count', type: 'count' },      // 计数\n  ],\n}\n```\n\n### 自定义合计\n\n```vue\n<avue-crud :summary-method=\"summaryMethod\">\n\nconst summaryMethod = ({ columns, data }) => {\n  const sums = []\n  columns.forEach((column, index) => {\n    if (index === 0) {\n      sums[index] = '合计'\n      return\n    }\n    const values = data.map(item => Number(item[column.property]))\n    if (!values.every(isNaN)) {\n      sums[index] = values.reduce((a, b) => a + b, 0).toFixed(2)\n    } else {\n      sums[index] = '-'\n    }\n  })\n  return sums\n}\n```\n\n---\n\n## 父子表\n\n```vue\n<avue-crud :option=\"parentOption\" :data=\"parentData\" @row-click=\"parentRowClick\">\n  <!-- 子表通过展开行或独立组件实现 -->\n  <template #expand=\"{ row }\">\n    <avue-crud :option=\"childOption\" :data=\"row.children\" />\n  </template>\n</avue-crud>\n```\n\n或使用独立组件封装子表，通过 `crud-children` 模式。\n\n---\n\n## 加载状态\n\n```vue\n<avue-crud :table-loading=\"loading\">\n\nconst loading = ref(true)\n// 数据加载完成后\nloading.value = false\n```\n\n---\n\n## CRUD 插件模式\n\n### Mixin 模式（Options API）\n\n```javascript\n// @/mixins/crud.js\nexport default (app, option = {}) => {\n  let optionObj = import.meta.glob(`../option/**/**`)[`../option/${option.name}.js`]\n  let apiObj = import.meta.glob(`../api/**/**`)[`../api/${option.name}.js`]\n  // ... mixin 逻辑\n}\n\n// 全局注册\n// main.js\nimport crudCommon from '@/mixins/crud'\nwindow.$crudCommon = crudCommon\n\n// 使用\nexport default window.$crudCommon({\n  data() { return {} },\n  methods: {\n    listBefore() {},\n    listAfter() {},\n    addBefore() { this.form.createUser = 'admin' },\n    addAfter() {},\n    updateBefore() {},\n    updateAfter() {},\n    delBefore() {},\n    delAfter() {},\n  }\n}, {\n  name: 'system/params',\n  list: 'list',\n  update: 'update',\n  add: 'add',\n  del: 'del',\n  rowKey: 'id',\n  pageNumber: 'pageNumber',\n  pageSize: 'pageSize',\n  res: (data) => ({ total: data.total, data: data.records }),\n  total: 'total',\n  data: 'records',\n})\n```\n\n### 绑定辅助（Options API）\n\n```vue\n<avue-crud v-bind=\"bindVal\" v-on=\"onEvent\" v-model=\"form\" v-model:page=\"page\">\n\ncomputed: {\n  bindVal() {\n    return {\n      ref: 'crud',\n      option: this.option,\n      data: this.data,\n      tableLoading: this.loading,\n    }\n  },\n  onEvent() {\n    return {\n      'on-load': this.getList,\n      'row-save': this.rowSave,\n      'row-update': this.rowUpdate,\n      'row-del': this.rowDel,\n      'refresh-change': this.refreshChange,\n      'search-reset': this.searchChange,\n      'search-change': this.searchChange,\n    }\n  },\n}\n```\n\n### API 文件模板\n\n```javascript\n// @/api/module/entity.js\nimport request from '@/router/axios'\n\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/api/entity/list',\n    method: 'get',\n    params: { current, size, ...params },\n  })\n}\n\nexport const getDetail = (id) => {\n  return request({\n    url: '/api/entity/detail',\n    method: 'get',\n    params: { id },\n  })\n}\n\nexport const add = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const update = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const remove = (ids) => {\n  return request({\n    url: '/api/entity/remove',\n    method: 'post',\n    params: { ids },\n  })\n}\n```\n\n### Option 配置文件模板\n\n```javascript\n// @/option/module/entity.js\nexport default (safe) => {\n  // safe => vue 的 this 对象 (Options API)\n  return {\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    border: true,\n    stripe: true,\n    column: [\n      {\n        label: \"名称\",\n        prop: \"name\",\n        search: true,\n        rules: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n      },\n    ],\n  }\n}\n```\n\n---\n\n## 配置动态切换\n\n### 初始化加载\n\n```vue\n<avue-crud v-if=\"option.column\" :option=\"option\">\n\nconst option = ref({})\nonMounted(() => {\n  // 从服务器加载配置\n  getConfig().then(res => {\n    option.value = res.data\n  })\n})\n```\n\n### 动态切换配置\n\n```javascript\nconst changeOption = (type) => {\n  if (type === 'simple') {\n    option.value = simpleOption\n  } else {\n    option.value = fullOption\n  }\n}\n```\n\n---\n\n## 空状态自定义\n\n```javascript\noption: {\n  emptyText: '暂无数据',  // 空状态文字\n}\n```\n\n```vue\n<!-- 自定义空状态 -->\n<template #empty>\n  <div class=\"custom-empty\">\n    <img src=\"empty.png\" />\n    <p>暂无数据，请添加</p>\n  </div>\n</template>\n```\n\n---\n\n## 深层数据绑定\n\n```javascript\n// 数据结构: { id: 1, info: { address: '北京' } }\ncolumn: [\n  {\n    label: '地址',\n    prop: 'address',      // prop 仍然需要\n    bind: 'info.address', // 绑定深层路径\n  },\n]\n```\n\n---\n\n## 行复制\n\n通过 `rowCopy` 方法复制已有行数据到新增弹窗：\n\n```vue\n<template #menu=\"{ row, index, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowCopy(row)\">复制</el-button>\n</template>\n```\n\n需设置 `copyBtn: true` 在 option 中也可直接显示内置复制按钮。\n\n---\n\n## 高级筛选 (filterBtn)\n\n通过 `filterBtn: true` 启用高级筛选对话框，支持多条件组合查询：\n\n```javascript\noption: {\n  filterBtn: true,  // 显示筛选按钮\n}\n```\n\n### 筛选操作符\n\n| 符号 | 含义 |\n|-----|------|\n| = | 等于 |\n| ≠ | 不等于 |\n| like | 模糊匹配 |\n| > | 大于 |\n| ≥ | 大于等于 |\n| < | 小于 |\n| ≤ | 小于等于 |\n| ∈ | 包含于 |\n\n### 筛选事件\n\n```vue\n<avue-crud @filter=\"handleFilter\">\n\nconst handleFilter = (filterData) => {\n  // filterData 格式: [[prop, operator, value], ...]\n  // 示例: [['name', 'like', '张'], ['age', '>', 18]]\n  console.log('筛选条件:', filterData)\n}\n```\n\n---\n\n## 列管理器 (columnBtn)\n\n通过 `columnBtn: true`（默认开启）显示列管理按钮，可动态控制列的显隐、固定、过滤和排序：\n\n```javascript\noption: {\n  columnBtn: true,  // 显示列管理按钮（默认true）\n}\n```\n\n列管理器支持以下操作：\n- **显隐** - 控制列是否在表格中显示\n- **固定** - 设置列固定在左侧或右侧\n- **过滤** - 启用列的筛选功能\n- **排序** - 启用列的排序功能\n- **拖拽排序** - 拖拽调整列的显示顺序\n\n---\n\n## Excel 导出对话框\n\n当设置 `excelBtn: true` 时，点击导出按钮会打开导出配置对话框：\n\n### 导出选项\n\n| 选项 | 说明 |\n|-----|------|\n| 导出范围 | 导出全部数据 / 仅导出选中行 |\n| 包含表头 | 是否包含列标题行 |\n| 包含数据 | 是否包含数据行 |\n| 包含合计 | 是否包含合计/汇总行 |\n| 选择列 | 选择要导出的列 |\n\n### 编程式导出（跳过对话框）\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 编程式导入\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.xlsx(file).then(({ header, results }) => {\n  console.log('表头:', header)\n  console.log('数据:', results)\n})\n```\n\n---\n\n## 权限控制\n\n### 对象模式\n\n```javascript\n// 通过 permission 对象控制按钮权限\nconst permissionList = {\n  addBtn: true,\n  editBtn: true,\n  delBtn: false,\n  viewBtn: true,\n}\n```\n\n### 函数模式（行级权限）\n\n```javascript\n// 通过函数实现行级权限控制\nconst permission = (key, row, index) => {\n  // key: 按钮标识 (addBtn/editBtn/delBtn/viewBtn)\n  // row: 当前行数据\n  // index: 当前行索引\n  if (key === 'editBtn') {\n    return row.status !== 'locked'\n  }\n  return true\n}\n```\n\n---\n\n## 自定义按钮\n\n```vue\n<!-- 隐藏默认按钮，使用自定义 -->\noption: { addBtn: false, editBtn: false, delBtn: false, viewBtn: false }\n\n<!-- 自定义新增按钮 -->\n<template #menu-left>\n  <el-button type=\"primary\" @click=\"$refs.crud.rowAdd()\">自定义新增</el-button>\n</template>\n\n<!-- 自定义操作按钮 -->\n<template #menu=\"{ row, index, type, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowEdit(row, index)\">编辑</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowView(row, index)\">查看</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowDel(row, index)\">删除</el-button>\n</template>\n\n<!-- 自定义弹窗按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\">自定义保存</el-button>\n  <el-button v-if=\"type === 'edit'\">自定义更新</el-button>\n</template>\n```\n","isInternal":false,"tokens":4348,"sizeBytes":13560},{"name":"crud-form-dialog.md","path":".claude/skills/avue-design/references/crud-form-dialog.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/crud-form-dialog.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-crud 弹窗表单配置参考\n\n## 弹窗基本配置\n\noption 中的弹窗属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogTop | number | 25 | 弹窗距顶部 |\n| dialogDrag | boolean | false | 可拖拽 |\n| dialogFullscreen | boolean | false | 全屏 |\n| dialogCustomClass | string | - | 自定义class |\n| dialogEscape | boolean | true | ESC关闭 |\n| dialogClickModal | boolean | false | 点击遮罩关闭 |\n| dialogCloseBtn | boolean | true | 关闭按钮 |\n| dialogModal | boolean | true | 遮罩层 |\n| dialogMenuPosition | string | 'right' | 按钮位置: left/center/right |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n\n## 打开/关闭回调\n\n```vue\n<avue-crud :before-open=\"beforeOpen\" :before-close=\"beforeClose\">\n\n<!-- Composition API -->\nconst beforeOpen = (done, type, loading) => {\n  // type: 'add' | 'edit' | 'view'\n  if (['edit', 'view'].includes(type)) {\n    // 加载详情数据\n    loading()  // 显示loading (3.5.2+)\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n      done()   // 完成加载\n    })\n  } else {\n    done()\n  }\n}\n\nconst beforeClose = (done, type) => {\n  done()\n}\n```\n\n## 表单验证规则\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    rules: [\n      { required: true, message: '请输入名称', trigger: 'blur' },\n      { min: 2, max: 20, message: '长度在2到20之间', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '邮箱',\n    prop: 'email',\n    rules: [\n      { required: true, message: '请输入邮箱', trigger: 'blur' },\n      { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '年龄',\n    prop: 'age',\n    type: 'number',\n    rules: [{\n      required: true,\n      validator: (rule, value, callback) => {\n        if (value < 18) {\n          callback(new Error('年龄不能小于18岁'))\n        } else {\n          callback()\n        }\n      },\n      trigger: 'blur',\n    }],\n  },\n]\n```\n\n验证规则基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n## 字段显示/禁用控制\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    // 全局控制\n    display: true,       // 是否显示\n    disabled: false,     // 是否禁用\n    // 按操作类型控制\n    addDisplay: true,    // 新增时显示\n    editDisplay: true,   // 编辑时显示\n    viewDisplay: true,   // 查看时显示\n    addDisabled: false,  // 新增时禁用\n    editDisabled: true,  // 编辑时禁用（如ID不可编辑）\n    viewDisabled: false,\n  },\n]\n```\n\n## 表单默认值\n\n```javascript\ncolumn: [\n  {\n    label: '状态',\n    prop: 'status',\n    type: 'select',\n    value: 1,  // 新增时默认值\n    dicData: [\n      { label: '启用', value: 1 },\n      { label: '禁用', value: 0 },\n    ],\n  },\n]\n```\n\n## 字段联动控制 (control)\n\n```javascript\ncolumn: [\n  {\n    label: '类型',\n    prop: 'type',\n    type: 'select',\n    dicData: [\n      { label: '文章', value: 1 },\n      { label: '链接', value: 2 },\n    ],\n    control: (val, form) => {\n      if (val === 1) {\n        return {\n          content: { display: true },\n          url: { display: false },\n        }\n      } else {\n        return {\n          content: { display: false },\n          url: { display: true },\n        }\n      }\n    },\n  },\n  { label: '内容', prop: 'content', type: 'textarea' },\n  { label: '链接', prop: 'url', display: false },\n]\n```\n\n3.5.0+ 支持 Promise：\n```javascript\ncontrol: async (val, form) => {\n  const res = await getConfig(val)\n  return { subType: { dicData: res.data } }\n}\n```\n\n## 表单分组 (group)\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,  // 默认展开\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name' },\n        { label: '编码', prop: 'code' },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', type: 'textarea' },\n      ],\n    },\n  ],\n  // 使用标签页显示分组\n  tabs: true,\n  // column 也要配置（表格列用）\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n    { label: '描述', prop: 'description' },\n  ],\n}\n```\n\n## 表单标签宽度\n\n```javascript\noption: {\n  labelWidth: 120,        // 全局标签宽度\n  labelPosition: 'right', // 全局标签位置: left/right/top\n}\n\n// 也可在 column 中单独设置\n{\n  label: '很长的标签名称',\n  prop: 'longLabel',\n  labelWidth: 180,\n}\n```\n\n## 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤字典翻译字段（$开头的字段）\n  filterNull: true,  // 过滤空值字段\n}\n```\n\n## 防止重复提交\n\n```javascript\n// row-save 和 row-update 中的 done 和 loading 参数：\n// - done(): 关闭表单弹窗\n// - loading(): 关闭loading但保持表单打开（用于接口报错时）\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    ElMessage.success('操作成功')\n    done()     // 关闭弹窗\n    onLoad()\n  }).catch(() => {\n    loading()  // 关闭loading，保持弹窗\n  })\n}\n```\n\n## 表单字段排序\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', order: 1 },\n  { label: '编码', prop: 'code', order: 3 },  // order大的排前面\n  { label: '状态', prop: 'status', order: 2 },\n]\n```\n\n## 获取字段组件引用\n\n```javascript\nconst ref = crudRef.value.getPropRef('name')\n// ref 即为该字段对应的组件实例\n```\n\n## 连续新增（不关闭弹窗）\n\n通过不调用 `done()` 来保持弹窗打开，实现连续新增。\n\n## 自定义表单插槽\n\n```vue\n<!-- 自定义表单字段 -->\n<template #name-form=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"form.name\" />\n</template>\n\n<!-- 自定义表单标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义错误提示 -->\n<template #name-error=\"{ error }\">\n  <span style=\"color: orange\">{{ error }}</span>\n</template>\n\n<!-- 自定义描述 (3.6.4+) -->\n<template #name-desc=\"{ column }\">\n  <span>这是一段描述文字</span>\n</template>\n\n<!-- 自定义表单按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\" type=\"primary\">自定义保存</el-button>\n</template>\n```\n\n## 权限控制\n\n```javascript\n// 对象形式\n<avue-crud :permission=\"{ addBtn: true, editBtn: true, delBtn: false }\">\n\n// 函数形式（行级控制）\n<avue-crud :permission=\"getPermission\">\n\nconst getPermission = (key, row, index) => {\n  // key: 'addBtn' | 'editBtn' | 'delBtn' | 'viewBtn' | 'menu'\n  if (key === 'editBtn' && row?.status === 1) {\n    return false  // 已启用的不允许编辑\n  }\n  return true\n}\n```\n","isInternal":false,"tokens":2208,"sizeBytes":7149},{"name":"crud-search.md","path":".claude/skills/avue-design/references/crud-search.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/crud-search.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-crud 搜索配置参考\n\n## 基本用法\n\n在 column 中设置 `search: true` 开启搜索字段：\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    search: true,\n    searchSpan: 6,\n  },\n  {\n    label: '日期',\n    prop: 'date',\n    type: 'daterange',\n    search: true,\n    searchRange: true,\n    searchSpan: 8,\n  },\n]\n```\n\n## 搜索变量\n\nComposition API 使用 `v-model:search` 自动同步：\n```vue\n<avue-crud v-model:search=\"search\" @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nconst search = ref({})\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\nconst searchReset = () => {\n  onLoad()\n}\n```\n\nOptions API 使用 query 变量手动同步：\n```vue\n<avue-crud @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nsearchChange(params, done) {\n  this.query = params\n  this.page.currentPage = 1\n  this.onLoad(this.page, params)\n  done()\n},\nsearchReset() {\n  this.query = {}\n  this.onLoad(this.page)\n},\n```\n\n## 搜索栏配置\n\n### 全局搜索属性（option 级别）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| searchShow | boolean | true | 首次加载显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留字段数 |\n| searchMenuPosition | string | 'center' | 按钮位置: left/center/right |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示文字 |\n| searchMenuSpan | number | - | 按钮栅格（单独一行时设24） |\n| searchSpan | number | 8 | 默认搜索栅格 |\n| searchGutter | number | - | 默认搜索间距 |\n| searchLabelWidth | number | 80 | 默认搜索标签宽度 |\n| searchLabelPosition | string | 'left' | 默认搜索标签位置 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n\n### 列级搜索属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | any | - | 搜索默认值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索排序 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索类型（覆盖type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 可清空 |\n| searchMultiple | boolean | false | 多选 |\n| searchTags | boolean | false | 标签显示 |\n| searchLabelPosition | string | 'left' | 标签位置 |\n| searchRules | array | - | 验证规则 |\n\n## 搜索字段类型覆盖\n\n可以用 searchType 让搜索框使用不同于表格列的类型：\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  type: 'date',           // 表格/表单中是日期选择\n  search: true,\n  searchType: 'daterange', // 搜索中是日期范围\n  searchRange: true,\n}\n```\n\n## 搜索折叠\n\n```javascript\noption: {\n  searchIcon: true,    // 显示折叠按钮\n  searchIndex: 2,      // 折叠时保留2个字段\n}\n```\n\n## 搜索级联联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/province',\n    cascader: ['city'],\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/city?province={{key}}',\n  },\n]\n```\n\n## 自定义搜索插槽\n\n```vue\n<!-- 自定义搜索菜单 -->\n<template #searchMenu=\"{ row, search, reset }\">\n  <el-button type=\"primary\" @click=\"search\">搜索</el-button>\n  <el-button @click=\"reset\">重置</el-button>\n  <el-button type=\"warning\" @click=\"handleExport\">导出</el-button>\n</template>\n\n<!-- 自定义搜索字段 -->\n<template #name-search=\"{ search }\">\n  <el-input v-model=\"search.name\" placeholder=\"自定义搜索\" />\n</template>\n```\n\n## 日期快捷按钮\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  dateBtn: true,  // 显示日期快捷按钮\n}\n```\n","isInternal":false,"tokens":1254,"sizeBytes":4186},{"name":"data-display.md","path":".claude/skills/avue-design/references/data-display.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/data-display.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Avue 数据展示组件参考\n\n## 组件列表\n\n| 组件 | 说明 |\n|-----|------|\n| avue-data-display | 数据展示卡片（核心组件） |\n| avue-data-pay | 支付数据 |\n| avue-data-tabs | 标签数据 |\n| avue-data-box | 数据盒子 |\n| avue-data-card | 数据卡片 |\n| avue-data-cardtext | 文字卡片 |\n| avue-data-icons | 图标数据 |\n| avue-data-operatext | 操作文字 |\n| avue-data-progress | 进度数据 |\n| avue-data-rotate | 旋转数据 |\n| avue-data-imgtext | 图文数据 |\n| avue-data-panel | 面板 |\n| avue-data-price | 价格 |\n| avue-data-countdown | 倒计时 |\n| avue-data-dashboard | 仪表盘 |\n| avue-data-list | 数据列表 |\n| avue-data-notice | 公告 |\n| avue-data-product | 商品卡片 |\n| avue-data-profile | 人员档案 |\n| avue-data-rank | 排行榜 |\n| avue-data-statistic | 统计值 |\n| avue-data-task | 任务列表 |\n| avue-data-weather | 天气卡片 |\n\n## avue-data-display（核心组件）\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| animation | boolean | true | 是否动画 |\n| decimals | number | 0 | 小数位数 |\n| span | string | '8' | 栅格数 |\n| data | array | - | 数据数组 |\n\n### 数据结构\n\n```javascript\nconst data = [\n  {\n    title: '今日访问',\n    count: 1234,\n    icon: 'el-icon-view',\n    color: '#40c9c6',\n    decimals: 0,\n  },\n  {\n    title: '销售额',\n    count: 56789.50,\n    icon: 'el-icon-money',\n    color: '#36a3f7',\n    decimals: 2,\n  },\n]\n```\n\n### 使用示例\n\n```vue\n<avue-data-display :data=\"displayData\" :option=\"{ decimals: 0, animation: true }\" />\n```\n\n## 通用使用模式\n\n大部分数据展示组件遵循相同模式：\n\n```vue\n<component :data=\"data\" :option=\"option\">\n  <!-- 可选插槽自定义 -->\n</component>\n```\n\n各组件的具体配置请参考对应的示例页面：\n- `data/data0` ~ `data/data12` - 基础数据组件示例\n- `data/countdown` - 倒计时\n- `data/dashboard` - 仪表盘\n- `data/list` - 数据列表\n- `data/notice` - 公告\n- `data/product` - 商品卡片\n- `data/profile` - 人员档案\n- `data/rank` - 排行榜\n- `data/statistic` - 统计值\n- `data/task` - 任务列表\n- `data/weather` - 天气卡片\n","isInternal":false,"tokens":749,"sizeBytes":2187},{"name":"form-advanced.md","path":".claude/skills/avue-design/references/form-advanced.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/form-advanced.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-form 高级功能参考\n\n## 目录\n1. [字典配置](#字典配置)\n2. [验证规则](#验证规则)\n3. [动态子表单](#动态子表单)\n4. [文件上传](#文件上传)\n5. [字段联动](#字段联动)\n6. [数据格式](#数据格式)\n7. [自定义插槽](#自定义插槽)\n8. [Mock数据](#mock数据)\n\n---\n\n## 字典配置\n\n### 安装配置\n\n字典组件需要 axios：\n```javascript\nimport axios from 'axios'\napp.use(Avue, { axios })\n```\n\n### 本地字典\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n}\n```\n\n3.5.0+ 支持函数和 Promise：\n```javascript\n{\n  dicData: () => {\n    return [{ label: 'A', value: 1 }]\n  },\n  // 或\n  dicData: async () => {\n    const res = await fetchOptions()\n    return res.data\n  },\n}\n```\n\n### 远程字典\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'select',\n  dicUrl: '/api/dept/list',\n  dicMethod: 'GET',      // 请求方法，默认GET\n  dicQuery: { status: 1 }, // 请求参数\n  dicHeaders: {},          // 请求头\n  props: {\n    label: 'name',\n    value: 'id',\n    children: 'children',\n    res: 'data.records',  // 响应数据层级\n  },\n}\n```\n\n### 字典格式化\n\n```javascript\n{\n  dicUrl: '/api/dict/list',\n  dicFormatter: (res) => {\n    // 自定义处理响应数据\n    return res.data.map(item => ({\n      label: item.dictValue,\n      value: item.dictKey,\n    }))\n  },\n}\n```\n\n### 字典联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city', 'district'],  // 指定子级字段\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}',  // {{key}} = 父级选中值\n    cascader: ['district'],\n  },\n  {\n    label: '区县',\n    prop: 'district',\n    type: 'select',\n    dicUrl: '/api/district?city={{key}}',\n  },\n]\n```\n\n### 动态更新字典\n\n```javascript\n// 更新指定字段字典\nformRef.value.updateDic('type', [\n  { label: '新选项A', value: 'a' },\n  { label: '新选项B', value: 'b' },\n])\n\n// 重新加载所有字典\nformRef.value.dicInit()\n```\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 值存储为字符串\n  separator: ',',         // 分隔符，默认逗号\n  // 实际存储: \"1,2,3\"\n  // 组件显示: [1, 2, 3]\n}\n\n{\n  label: '年龄',\n  prop: 'age',\n  dataType: 'number',    // 强制转数字\n}\n```\n\n---\n\n## 验证规则\n\n基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n### 基本验证\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  rules: [\n    { required: true, message: '请输入名称', trigger: 'blur' },\n    { min: 2, max: 20, message: '长度在2-20之间', trigger: 'blur' },\n  ],\n}\n```\n\n### 类型验证\n\n```javascript\nrules: [\n  { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n  { type: 'url', message: '请输入正确的URL', trigger: 'blur' },\n  { type: 'number', message: '请输入数字', trigger: 'blur' },\n]\n```\n\n### 自定义验证\n\n```javascript\n{\n  label: '密码',\n  prop: 'password',\n  rules: [{\n    required: true,\n    validator: (rule, value, callback) => {\n      if (!value) {\n        callback(new Error('请输入密码'))\n      } else if (value.length < 6) {\n        callback(new Error('密码长度不能少于6位'))\n      } else {\n        callback()\n      }\n    },\n    trigger: 'blur',\n  }],\n}\n```\n\n### 表单验证方法\n\n```javascript\n// 验证整个表单\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败\n    console.log('错误字段:', fields)\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'email'], (errorMsg) => {\n  if (!errorMsg) console.log('验证通过')\n})\n\n// 清除验证\nformRef.value.clearValidate()            // 清除所有\nformRef.value.clearValidate(['name'])    // 清除指定字段\n\n// 重置表单\nformRef.value.resetFields()\n```\n\n### 搜索验证 (CRUD)\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  searchRules: [{ required: true, message: '请选择日期', trigger: 'change' }],\n}\n```\n\n---\n\n## 动态子表单\n\n### 表格模式\n\n```javascript\n{\n  label: '订单明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    index: true,\n    column: [\n      {\n        label: '商品名称',\n        prop: 'productName',\n        rules: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],\n      },\n      {\n        label: '数量',\n        prop: 'quantity',\n        type: 'number',\n        min: 1,\n      },\n      {\n        label: '单价',\n        prop: 'price',\n        type: 'number',\n        precision: 2,\n      },\n    ],\n  },\n}\n```\n\n### 表单模式\n\n```javascript\n{\n  label: '联系方式',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  limit: 5,  // 最多5条\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 8 },\n      { label: '电话', prop: 'phone', span: 8 },\n      { label: '职位', prop: 'position', span: 8 },\n    ],\n  },\n}\n```\n\n### 自定义增删\n\n```javascript\n{\n  type: 'dynamic',\n  rowAdd: (done) => {\n    // 自定义新增逻辑\n    done({ name: '默认值', quantity: 1 })\n  },\n  rowDel: (row, done) => {\n    ElMessageBox.confirm('确定删除?').then(() => done())\n  },\n}\n```\n\n### 父子联动\n\n动态子表单支持与父表单字段联动。\n\n---\n\n## 文件上传\n\n### 基本上传\n\n```javascript\n{\n  label: '附件',\n  prop: 'file',\n  type: 'upload',\n  action: '/api/upload',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n    fileName: 'file',\n  },\n}\n```\n\n### 上传回调\n\n```vue\n<avue-form\n  :upload-before=\"uploadBefore\"\n  :upload-after=\"uploadAfter\"\n  :upload-delete=\"uploadDelete\"\n  :upload-preview=\"uploadPreview\"\n  :upload-error=\"uploadError\"\n  :upload-exceed=\"uploadExceed\"\n>\n\nconst uploadBefore = (file, done, loading) => {\n  // 验证文件\n  if (file.size > 10 * 1024 * 1024) {\n    ElMessage.error('文件不能超过10MB')\n    loading()  // 中断上传\n    return\n  }\n  done(file)  // 继续上传\n}\n\nconst uploadAfter = (res, done) => {\n  // 上传完成\n  done()\n}\n\nconst uploadDelete = (file, column) => {\n  // 返回 false 或 rejected Promise 阻止删除\n  return new Promise((resolve, reject) => {\n    ElMessageBox.confirm('确定删除?')\n      .then(() => resolve())\n      .catch(() => reject())\n  })\n}\n\nconst uploadPreview = (file, column, done) => {\n  // 自定义预览\n  window.open(file.url)\n}\n\nconst uploadError = (error, column) => {\n  ElMessage.error('上传失败')\n}\n\nconst uploadExceed = (limit, files, fileList, column) => {\n  ElMessage.warning(`最多上传${limit}个文件`)\n}\n```\n\n### 图片水印\n\n```javascript\n{\n  type: 'upload',\n  canvasOption: {\n    text: '公司名称',\n    fontFamily: 'microsoft yahei',\n    color: 'rgba(0,0,0,0.3)',\n    fontSize: 20,\n    opacity: 50,\n    bottom: 20,\n    right: 20,\n    ratio: 0.8,\n  },\n}\n```\n\n### 云存储上传\n\n全局配置见 `global-config.md`。\n\n```javascript\n// 阿里云OSS\n{ type: 'upload', oss: 'ali' }\n\n// 七牛云\n{ type: 'upload', oss: 'qiniu' }\n\n// 腾讯COS\n{ type: 'upload', oss: 'cos' }\n```\n\n### 数据格式\n\n```javascript\n// 字符串格式（逗号分隔URL）\n{ type: 'upload', dataType: 'string' }\n\n// 对象格式\n{\n  type: 'upload',\n  dataType: 'object',\n  props: { label: 'name', value: 'url' },\n}\n\n// JSON字符串格式\n{ type: 'upload', dataType: 'json' }\n```\n\n---\n\n## 字段联动\n\n### control 属性\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'radio',\n  dicData: [\n    { label: '个人', value: 1 },\n    { label: '企业', value: 2 },\n  ],\n  control: (val, form) => {\n    if (val === 1) {\n      return {\n        idCard: { display: true, rules: [{ required: true }] },\n        companyName: { display: false },\n        businessLicense: { display: false },\n      }\n    } else {\n      return {\n        idCard: { display: false },\n        companyName: { display: true, rules: [{ required: true }] },\n        businessLicense: { display: true },\n      }\n    }\n  },\n},\n{ label: '身份证', prop: 'idCard' },\n{ label: '企业名称', prop: 'companyName', display: false },\n{ label: '营业执照', prop: 'businessLicense', type: 'upload', display: false },\n```\n\n### control 返回值可控制的属性\n\n可以动态控制字段的以下属性：\n- `display` - 显示/隐藏\n- `disabled` - 启用/禁用\n- `rules` - 验证规则\n- `dicData` - 字典数据\n- `label` - 标签文字\n- 其他 column 属性\n\n### Promise 支持 (3.5.0+)\n\n```javascript\ncontrol: async (val, form) => {\n  const res = await getSubOptions(val)\n  return {\n    subType: {\n      dicData: res.data,\n      display: true,\n    },\n  }\n}\n```\n\n### 获取组件引用\n\n```javascript\nconst ref = formRef.value.getPropRef('tableProp')\n// 可用于操作 input-table 等复杂组件\n```\n\n---\n\n## 数据格式\n\n### 深层数据绑定\n\n```javascript\n// 数据: { id: 1, info: { name: '张三', address: { city: '北京' } } }\ncolumn: [\n  {\n    label: '姓名',\n    prop: 'name',\n    bind: 'info.name',\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    bind: 'info.address.city',\n  },\n]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤 $xxx 字典翻译字段\n  filterNull: true,  // 过滤空值字段\n  filterParams: ['id'], // 不被清空的字段（默认包含 rowKey）\n}\n```\n\n---\n\n## 自定义插槽\n\n### 字段内容插槽\n\n```vue\n<avue-form v-model=\"form\" :option=\"option\">\n  <!-- 自定义字段 -->\n  <template #name=\"{ value, column, dic, size, disabled }\">\n    <el-input v-model=\"form.name\" :disabled=\"disabled\" />\n  </template>\n\n  <!-- 自定义标签 -->\n  <template #name-label=\"{ column }\">\n    <span style=\"color: red\">*</span> {{ column.label }}\n  </template>\n\n  <!-- 自定义错误 -->\n  <template #name-error=\"{ error }\">\n    <span style=\"color: orange\">{{ error }}</span>\n  </template>\n\n  <!-- 自定义描述 (3.6.4+) -->\n  <template #name-desc=\"{ column }\">\n    <el-text type=\"info\">请输入真实姓名</el-text>\n  </template>\n\n  <!-- 自定义按钮 -->\n  <template #menu-form>\n    <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n    <el-button @click=\"handleDraft\">保存草稿</el-button>\n  </template>\n</avue-form>\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-switch', {\n      modelValue: form.value.status,\n      'onUpdate:modelValue': (val) => { form.value.status = val },\n    })\n  },\n}\n```\n\n### 第三方组件集成\n\n```vue\n<template #editor=\"{ value, column }\">\n  <vue-editor v-model=\"form.content\" />\n</template>\n```\n\n### className 样式控制\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  className: 'highlight-field',\n}\n```\n\n```css\n.highlight-field {\n  background: #fef0f0;\n  border-left: 3px solid #f56c6c;\n}\n```\n\n---\n\n## Mock数据\n\n需引入 Mock.js：\n```html\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n\n```javascript\noption: {\n  mockBtn: true,  // 显示Mock按钮\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      mock: { type: 'cname' },  // 中文姓名\n    },\n    {\n      label: '邮箱',\n      prop: 'email',\n      mock: { type: 'email' },\n    },\n    {\n      label: '地址',\n      prop: 'address',\n      mock: { type: 'county', prefix: true },\n    },\n  ],\n}\n\n// 或整体 mock 函数\noption: {\n  mock: () => ({\n    name: '张三',\n    email: 'zhangsan@test.com',\n    address: '北京市朝阳区',\n  }),\n}\n```\n","isInternal":false,"tokens":3670,"sizeBytes":11780},{"name":"form-core.md","path":".claude/skills/avue-design/references/form-core.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/form-core.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-form 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表单级属性](#option-表单级属性)\n3. [布局配置](#布局配置)\n4. [按钮配置](#按钮配置)\n5. [Column 通用属性](#column-通用属性)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n---\n\n## 组件属性\n\n在 `<avue-form>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| option | object | - | 表单配置项 |\n| upload-before | function(file, done, loading) | - | 上传前回调 |\n| upload-after | function(res, done) | - | 上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 预览文件回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小 (3.4.4+) |\n| v-model:status | boolean | false | 表单提交状态（禁用状态双向绑定） |\n\n---\n\n## Option 表单级属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 字段配置数组 |\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置: left/right/top |\n| labelSuffix | string | ':' | 标签后缀 |\n| enter | boolean | false | 回车提交 |\n| group | array | - | 分组配置 |\n| tabs | boolean | false | 分组标签页显示 |\n| detail | boolean | false | 详情/只读模式 |\n| disabled | boolean | false | 全部禁用 |\n| readonly | boolean | false | 全部只读 |\n| formWidth | string | - | 表单容器宽度 |\n| tabsActive | number | 0 | 默认激活标签页索引 |\n| tabsType | string | - | 标签页类型(同 el-tabs type) |\n| tabsVerifyAll | boolean | false | 提交时验证所有标签页（默认仅当前页） |\n| filterDic | boolean | false | 过滤 $ 开头的字典翻译字段 |\n| filterNull | boolean | false | 过滤空值字段 |\n| filterParams | array | - | 重置时保留的字段 |\n\n---\n\n## 布局配置\n\n### 栅格布局\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 字段占用栅格数 (24栅格) |\n| gutter | number | 0 | 栅格间距 |\n| offset | number | 0 | 栅格左偏移 |\n| push | number | 0 | 栅格右移动 |\n| pull | number | 0 | 栅格左移动 |\n| row | boolean | false | 是否新起一行 |\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', span: 12 },\n  { label: '编码', prop: 'code', span: 12 },\n  { label: '备注', prop: 'remark', span: 24, row: true },\n]\n```\n\n### 分组显示\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name', span: 12 },\n        { label: '编码', prop: 'code', span: 12 },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', span: 24 },\n      ],\n    },\n  ],\n}\n```\n\n#### Group 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 分组标题 |\n| prop | string | - | 分组标识 |\n| icon | string | - | 分组图标 |\n| collapse | boolean | true | 是否可折叠 |\n| arrow | boolean | true | 是否显示折叠箭头 |\n| display | boolean | true | 是否显示该分组 |\n| header | boolean | true | 是否显示分组头部 |\n| column | array | - | 分组内的字段配置 |\n\n### 标签页显示\n\n```javascript\noption: {\n  tabs: true,\n  // tabsVerifyAll: true, // 提交时验证所有标签页\n  group: [\n    {\n      label: '基本信息',\n      prop: 'tab1',\n      column: [...],\n    },\n    {\n      label: '扩展信息',\n      prop: 'tab2',\n      column: [...],\n    },\n  ],\n}\n```\n\n### 帮助文字\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    tip: '请输入唯一标识名称',      // 字段右侧帮助文字\n    tipPlacement: 'bottom',          // 帮助文字方向\n    labelTip: '该字段用于系统唯一标识', // 标签帮助文字\n    labelTipPlacement: 'bottom',\n  },\n]\n```\n\n### className 样式\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    className: 'custom-field-class',\n  },\n]\n```\n\n---\n\n## 按钮配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menuBtn | boolean | true | 显示按钮区 |\n| menuSpan | number | 24 | 按钮栅格数 |\n| menuPosition | string | 'center' | 按钮位置: left/center/right |\n| submitBtn | boolean | true | 显示提交按钮 |\n| submitText | string | '提交' | 提交按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyText | string | '清空' | 清空按钮文字 |\n| printBtn | boolean | false | 显示打印按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮（需引入Mock.js） |\n| mockText | string | 'Mock' | Mock按钮文字 |\n| printText | string | '打印' | 打印按钮文字 |\n| submitIcon | string | - | 提交按钮图标 |\n| emptyIcon | string | - | 清空按钮图标 |\n\n---\n\n## Column 通用属性\n\n每个字段的通用配置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 标签文字 |\n| prop | string | - | 字段属性名 |\n| type | string | 'input' | 字段类型 |\n| value | any | - | 默认值 |\n| bind | string | - | 深层数据绑定 |\n| disabled | boolean | false | 禁用 |\n| display | boolean | true | 显示 |\n| order | number | - | 排序，数字越大越靠前 |\n| size | string | 'small' | 组件尺寸 |\n| separator | string | ',' | 选项分隔符 (string dataType) |\n| rules | array | - | 验证规则 |\n\n### 栅格属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 栅格数 |\n| gutter | number | 0 | 间距 |\n| offset | number | 0 | 偏移 |\n| push | number | 0 | 右移 |\n| pull | number | 0 | 左移 |\n| row | boolean | false | 换行 |\n\n### 标签属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 内容帮助方向 |\n| labelTip | string | - | 标签帮助文字 |\n| labelTipPlacement | string | 'top' | 标签帮助方向 |\n\n### 联动控制\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [{ label: 'A', value: 1 }, { label: 'B', value: 2 }],\n  control: (val, form) => {\n    return {\n      subField: {\n        display: val === 1,\n        disabled: val === 2,\n        dicData: val === 1 ? listA : listB,\n      }\n    }\n  },\n}\n```\n\n### 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-tag', {}, '自定义内容')\n  },\n}\n```\n\n---\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| submit | (form, done) | 表单提交回调。done() 关闭loading |\n| reset-change | - | 表单清空回调 |\n| change | (form) | 表单数据变化时触发 |\n| tab-click | (tab, event) | 标签页点击时触发 |\n| error | (error) | 表单验证失败时触发 |\n| mock-change | (form) | Mock 数据生成后触发 |\n\n```javascript\n// Composition API\nconst handleSubmit = (form, done) => {\n  saveData(form).then(() => {\n    ElMessage.success('保存成功')\n    done()  // 关闭loading\n  }).catch(() => {\n    done()\n  })\n}\n\nconst handleReset = () => {\n  // 清空后的逻辑\n}\n```\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| submit | - | 触发表单提交 |\n| resetForm | - | 重置表单 |\n| updateDic | (prop, dic) | 更新指定字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | callback(boolean, object) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置表单到初始值 |\n| clearValidate | (props) | 清除验证结果 |\n| show | - | 禁用表单（显示loading状态） |\n| hide | - | 启用表单（隐藏loading状态） |\n| scrollToField | (prop) | 滚动到指定字段 |\n| getField | (prop) | 获取字段实例 |\n\n```javascript\n// 手动验证\nconst formRef = ref(null)\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败，fields 包含错误信息\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'code'], (errorMsg) => {\n  if (!errorMsg) {\n    // 验证通过\n  }\n})\n```\n\n---\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义字段内容 |\n| `{prop}-label` | 自定义字段标签 |\n| `{prop}-error` | 自定义错误提示 |\n| `{prop}-desc` | 自定义描述 (3.6.4+) |\n| menu-form | 自定义按钮区域 |\n| menu-form-before | 按钮区域前内容 (3.4.3+) |\n\n```vue\n<!-- 自定义字段 -->\n<template #name=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"formData.name\" />\n</template>\n\n<!-- 自定义标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义按钮 -->\n<template #menu-form>\n  <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n  <el-button @click=\"handleCancel\">取消</el-button>\n</template>\n```\n\n---\n\n## 独立表单使用示例\n\n### Composition API\n\n```vue\n<template>\n  <avue-form ref=\"formRef\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script setup>\nimport { ref, reactive } from 'vue'\nimport { ElMessage } from 'element-plus'\n\nconst formRef = ref(null)\nconst formData = ref({})\n\nconst option = reactive({\n  labelWidth: 120,\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      span: 12,\n      rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n    },\n    {\n      label: '性别',\n      prop: 'gender',\n      type: 'radio',\n      span: 12,\n      dicData: [\n        { label: '男', value: 1 },\n        { label: '女', value: 2 },\n      ],\n    },\n  ],\n})\n\nconst handleSubmit = (form, done) => {\n  console.log('提交数据:', form)\n  done()\n}\n</script>\n```\n\n### Options API\n\n```vue\n<template>\n  <avue-form ref=\"form\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      formData: {},\n      option: {\n        labelWidth: 120,\n        column: [\n          {\n            label: '姓名',\n            prop: 'name',\n            span: 12,\n            rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n          },\n          {\n            label: '性别',\n            prop: 'gender',\n            type: 'radio',\n            span: 12,\n            dicData: [\n              { label: '男', value: 1 },\n              { label: '女', value: 2 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  methods: {\n    handleSubmit(form, done) {\n      console.log('提交数据:', form)\n      done()\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 数据类型与过滤\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 存储为逗号分隔字符串\n  separator: ',',         // 分隔符，默认逗号\n}\n// 值: \"1,2,3\" 而不是 [1, 2, 3]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,  // 过滤 $ 开头的字典翻译字段\n  filterNull: true, // 过滤空值字段\n}\n```\n","isInternal":false,"tokens":3749,"sizeBytes":12079},{"name":"form-field-types.md","path":".claude/skills/avue-design/references/form-field-types.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/form-field-types.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-form 字段类型完整参考\n\n本文档列出 avue-form / avue-crud 中所有可用的字段类型及其完整配置属性。\n\n## 目录\n\n1. [input - 文本输入](#input---文本输入)\n2. [number - 数字输入](#number---数字输入)\n3. [select - 下拉选择](#select---下拉选择)\n4. [cascader - 级联选择](#cascader---级联选择)\n5. [checkbox - 多选框](#checkbox---多选框)\n6. [radio - 单选框](#radio---单选框)\n7. [date - 日期选择](#date---日期选择)\n8. [time - 时间选择](#time---时间选择)\n9. [switch - 开关](#switch---开关)\n10. [upload - 文件上传](#upload---文件上传)\n11. [tree - 树选择](#tree---树选择)\n12. [input-tag - 标签输入](#input-tag---标签输入)\n13. [mention - 提及](#mention---提及)\n14. [input-table - 表格选择](#input-table---表格选择)\n15. [color - 颜色选择](#color---颜色选择)\n16. [icon - 图标选择](#icon---图标选择)\n17. [map - 地图选择](#map---地图选择)\n18. [rate - 评分](#rate---评分)\n19. [slider - 滑块](#slider---滑块)\n20. [array/img/url - 数组输入](#arrayimgurl---数组输入)\n21. [dynamic - 动态子表单](#dynamic---动态子表单)\n22. [title - 标题分隔](#title---标题分隔)\n23. [input-cron - Cron表达式](#input-cron---cron表达式)\n\n---\n\n## 通用属性（所有类型共享）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '请输入/请选择'+label | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| readonly | boolean | false | 只读 |\n| disabled | boolean | false | 禁用 |\n| change | function({column, value}) | - | 值变化事件 |\n| click | function({column, value}) | - | 点击事件 |\n| focus | function({column, value}) | - | 聚焦事件 |\n| blur | function({column, value}) | - | 失焦事件 |\n\n---\n\n## input - 文本输入\n\n`type: 'input'`（默认类型，可省略）\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| showWordLimit | boolean | false | 显示字数统计 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prependClick | function | - | 前置文字点击 |\n| appendClick | function | - | 后置文字点击 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| showPassword | boolean | true | 密码切换图标 |\n| autocomplete | string | 'off' | HTML autocomplete 属性 |\n| formatters | function | - | 格式化函数 (3.7.0+) |\n| parser | function | - | 解析函数（formatters 的逆操作）(3.7.0+) |\n| target | string | '_blank' | 链接目标 (url类型时有效) |\n\n### textarea 子类型\n\n```javascript\n{ type: 'textarea', rows: 4, minRows: 2, maxRows: 8 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rows | number | 2 | 文本域行数 |\n| minRows | number | 2 | 最小行数 |\n| maxRows | number | 6 | 最大行数 |\n\n### password 子类型\n\n```javascript\n{ type: 'password', showPassword: true }\n```\n\n### phone 子类型\n\n```javascript\n{ type: 'phone', defaultPhoneCode: '+86' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultPhoneCode | string | '+86' | 默认区号 |\n| phoneCodeOptions | array | - | 区号选项 |\n| phoneMaxLength | number | 11 | 手机号最大长度 |\n| phoneCodePlaceholder | string | '区号' | 区号占位 |\n| phonePlaceholder | string | '请输入手机号' | 手机号占位 |\n\n### currency 子类型\n\n```javascript\n{ type: 'currency', currency: 'CNY', currencyPrecision: 2 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currency | string | 'CNY' | 货币类型 |\n| currencySymbolCustom | string | - | 自定义货币符号 |\n| currencyUnit | string | - | 货币单位 |\n| currencyPrecision | number | 2 | 小数位数 |\n| currencyPlaceholder | string | '请输入金额' | 占位 |\n| currencyMin | number | - | 最小值 |\n| currencyMax | number | - | 最大值 |\n\n### bankCard 子类型\n\n```javascript\n{ type: 'bankCard', showBankCardType: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| bankCardSeparator | string | ' ' | 分隔符 |\n| bankCardSegment | number | 4 | 分段长度 |\n| bankCardMaxDigits | number | 19 | 最大长度 |\n| bankCardPlaceholder | string | '请输入银行卡号' | 占位 |\n| showBankCardType | boolean | true | 显示卡类型 |\n\n### idCard 子类型\n\n```javascript\n{ type: 'idCard', showIdCardInfo: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| idCardPlaceholder | string | '请输入身份证号' | 占位 |\n| showIdCardInfo | boolean | true | 显示身份信息 |\n| idCardRealtimeValidate | boolean | true | 实时验证 |\n\n### email 子类型\n\n```javascript\n{ type: 'email', showEmailSuffix: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| emailPlaceholder | string | '请输入邮箱' | 占位 |\n| emailSuffixes | array | ['@qq.com','@163.com',...] | 邮箱后缀建议 |\n| showEmailSuffix | boolean | true | 显示建议 |\n\n### code 子类型\n\n```javascript\n{ type: 'code', codeLength: 6 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| codePlaceholder | string | - | 占位 |\n| codeLength | number | 6 | 验证码长度 |\n| codeSeparator | string | - | 分隔符 |\n\n### plate 子类型（车牌号）\n\n```javascript\n{ type: 'plate', defaultPlateProvince: '京' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| platePlaceholder | string | '请输入车牌号' | 占位 |\n| defaultPlateProvince | string | '京' | 默认省份 |\n| plateProvinceList | array | ['京','津','沪',...] | 省份列表 |\n\n### ip 子类型\n\n```javascript\n{ type: 'ip', ipVersion: 4 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| ipPlaceholder | string | '请输入IP地址' | 占位 |\n| ipVersion | number | 4 | IP版本 |\n\n### mac 子类型\n\n```javascript\n{ type: 'mac', macSeparator: ':' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| macPlaceholder | string | '请输入MAC地址' | 占位 |\n| macSeparator | string | ':' | 分隔符 |\n\n### uscc 子类型（统一社会信用代码）\n\n```javascript\n{ type: 'uscc' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| usccPlaceholder | string | '请输入统一社会信用代码' | 占位 |\n| usccRealtimeValidate | boolean | true | 实时验证 |\n\n### search 子类型\n\n```javascript\n{ type: 'search' }\n```\n\n带搜索按钮的输入框，点击搜索按钮触发事件。属性同基础 input。\n\n---\n\n## number - 数字输入\n\n`type: 'number'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | -Infinity | 最小值 |\n| max | number | Infinity | 最大值 |\n| step | number | 1 | 步长 |\n| stepStrictly | boolean | false | 只能输入步长倍数 |\n| precision | number | 2 | 小数精度 |\n| controls | boolean | true | 显示控制按钮 |\n| controlsPosition | string | 'top' | 按钮位置: top/right |\n| suffix | string | - | 后缀文字 (3.5.6+) |\n| prefix | string | - | 前缀文字 (3.5.6+) |\n| suffixClick | function | - | 后缀点击 (3.5.6+) |\n| prefixClick | function | - | 前缀点击 (3.5.6+) |\n| align | string | 'left' | 文本对齐 (3.7.2+) |\n| disabledScientific | boolean | false | 禁用科学计数法 (3.7.2+) |\n\n```javascript\n{\n  label: '价格',\n  prop: 'price',\n  type: 'number',\n  min: 0,\n  max: 99999,\n  step: 0.01,\n  precision: 2,\n  controlsPosition: 'right',\n}\n```\n\n---\n\n## select - 下拉选择\n\n`type: 'select'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| limit | number | - | 多选最大数量 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠标签tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| allowCreate | boolean | false | 允许创建新条目 |\n| filterable | boolean | false | 可搜索 |\n| remote | boolean | false | 远程搜索 |\n| defaultFirstOption | boolean | false | 回车选中第一个 |\n| loadingText | string | '加载中' | 加载文字 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n| virtualize | boolean | false | 虚拟DOM (大数据) |\n| group | boolean | false | 分组显示 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| cascader | array | - | 级联子字段prop |\n| cascaderIndex | number | - | 级联默认选项索引 |\n| emptyValues | array | - | 空值配置 (3.4.4+) |\n| valueOnClear | any | - | 清空时的值 (3.4.4+) |\n| removeTag | function({value, column}) | - | 移除标签事件 (3.7.3+) |\n| all | boolean | false | 显示\"全选\"选项 |\n| noMatchText | string | '无匹配数据' | 无匹配时文字 |\n| noDataText | string | '无数据' | 无数据时文字 |\n\n```javascript\n// 基本用法\n{\n  label: '状态',\n  prop: 'status',\n  type: 'select',\n  dicData: [\n    { label: '启用', value: 1 },\n    { label: '禁用', value: 0 },\n  ],\n}\n\n// 远程搜索\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  remote: true,\n  filterable: true,\n  dicUrl: '/api/user/search?name={{key}}',\n  props: { label: 'name', value: 'id' },\n}\n\n// 自定义模板\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  slot: true,  // 使用 userId + Type 插槽\n}\n\n// 分组\n{\n  label: '城市',\n  prop: 'city',\n  type: 'select',\n  group: true,\n  dicData: [\n    {\n      label: '热门城市',\n      children: [\n        { label: '北京', value: 'bj' },\n        { label: '上海', value: 'sh' },\n      ],\n    },\n  ],\n}\n```\n\n---\n\n## cascader - 级联选择\n\n`type: 'cascader'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAllLevels | boolean | true | 显示完整路径 |\n| tags | boolean | false | 标签显示 |\n| separator | string | '/' | 分隔符 |\n| filterable | boolean | false | 可搜索 |\n| filterMethod | function | - | 自定义搜索逻辑 |\n| expandTrigger | string | 'hover' | 展开触发方式: hover/click |\n| multiple | boolean | false | 多选 |\n| collapseTags | boolean | false | 折叠标签 |\n| collapseTagsTooltip | boolean | false | 折叠tooltip |\n| maxCollapseTags | number | - | 最大显示标签数 |\n| checkStrictly | boolean | false | 父子节点独立 |\n| emitPath | boolean | true | 返回完整路径数组 |\n| lazy | boolean | false | 懒加载 |\n| lazyLoad | function(node, resolve) | - | 懒加载函数 |\n| effect | string | 'dark' | tooltip主题 (3.7.2+) |\n| showCheckedStrategy | string | 'SHOW_ALL' | 选中项回填策略 (3.7.2+) |\n| popperClass | string | - | 下拉框class |\n\n```javascript\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  dicData: [\n    {\n      label: '北京',\n      value: 'bj',\n      children: [\n        { label: '朝阳区', value: 'cy' },\n        { label: '海淀区', value: 'hd' },\n      ],\n    },\n  ],\n}\n\n// 懒加载\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  lazy: true,\n  lazyLoad: (node, resolve) => {\n    const { level, value } = node\n    getAreaChildren(value, level).then(res => {\n      resolve(res.data.map(item => ({\n        label: item.name,\n        value: item.id,\n        leaf: level >= 2,  // 标记叶子节点\n      })))\n    })\n  },\n}\n```\n\n---\n\n## checkbox - 多选框\n\n`type: 'checkbox'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| all | boolean | false | 全选按钮 |\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n| min | number | - | 最少选择数 |\n| max | number | - | 最多选择数 |\n| fill | string | '#409eff' | 选中颜色 |\n| textColor | string | '#ffffff' | 选中文字颜色 |\n\n```javascript\n{\n  label: '爱好',\n  prop: 'hobbies',\n  type: 'checkbox',\n  dicData: [\n    { label: '读书', value: 1 },\n    { label: '运动', value: 2 },\n    { label: '旅行', value: 3 },\n  ],\n  all: true,\n  min: 1,\n  max: 3,\n}\n```\n\n---\n\n## radio - 单选框\n\n`type: 'radio'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n\n```javascript\n{\n  label: '性别',\n  prop: 'gender',\n  type: 'radio',\n  dicData: [\n    { label: '男', value: 1 },\n    { label: '女', value: 2 },\n  ],\n}\n```\n\n---\n\n## date - 日期选择\n\n`type: 'date'`\n\n可用 type 值：`date` | `datetime` | `daterange` | `datetimerange` | `week` | `month` | `year` | `dates` | `monthrange` | `yearrange`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultTime | string[] | - | 范围默认时间 |\n| defaultValue | Date | - | 默认日期 |\n| pickerOptions | object | - | 日期选项 |\n| popperClass | string | - | 下拉框class |\n| prefix-icon | string | - | 前缀图标 |\n| cellClassName | function | - | 自定义单元格class |\n| showNow | boolean | true | 显示\"此刻\"按钮 (3.6.0+) |\n| disabledHours | function | - | 禁用小时 (3.8.2+) |\n| disabledMinutes | function | - | 禁用分钟 (3.8.2+) |\n| disabledSeconds | function | - | 禁用秒 (3.8.2+) |\n| disabledDate | function(date) | - | 禁用日期判断函数 |\n| shortcuts | array/function | - | 快捷日期选项 |\n| editable | boolean | true | 输入框可编辑 |\n| unlinkPanels | boolean | false | 范围选择时取消面板联动 |\n\n**日期格式表：**\n\n| 格式 | 含义 | 示例 |\n|-----|------|------|\n| YYYY | 年 | 2024 |\n| MM | 月 | 01 |\n| DD | 日 | 02 |\n| HH | 时(24h) | 03 |\n| mm | 分 | 04 |\n| ss | 秒 | 05 |\n| X | JS时间戳 | 1483326245000 |\n\n```javascript\n// 日期\n{ type: 'date', format: 'YYYY-MM-DD', valueFormat: 'YYYY-MM-DD' }\n\n// 日期时间\n{ type: 'datetime', format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'YYYY-MM-DD HH:mm:ss' }\n\n// 日期范围\n{ type: 'daterange', startPlaceholder: '开始日期', endPlaceholder: '结束日期' }\n\n// 带默认时间的范围\n{\n  type: 'datetimerange',\n  defaultTime: ['00:00:00', '23:59:59'],\n}\n```\n\n---\n\n## time - 时间选择\n\n`type: 'time'` | `'timerange'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultValue | Date | - | 默认值 |\n| pickerOptions | object | - | 时间选项 |\n| popperClass | string | - | 下拉框class |\n| arrowControl | boolean | false | 箭头控制时间 |\n| editable | boolean | true | 输入框可编辑 |\n\n```javascript\n// 基本时间选择\n{ type: 'time', format: 'HH:mm:ss', valueFormat: 'HH:mm:ss' }\n\n// 固定时间点\n{\n  type: 'time',\n  pickerOptions: {\n    start: '08:00',\n    end: '18:00',\n    step: '00:30',\n  },\n}\n```\n\n> **TimeSelect 模式**：当配置了 `start`、`end`、`step`、`maxTime`、`minTime` 中的任一属性时，组件自动切换为 TimeSelect（固定时间点选择）模式。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | string | - | 开始时间 (TimeSelect) |\n| end | string | - | 结束时间 (TimeSelect) |\n| step | string | - | 时间步长 (TimeSelect) |\n| maxTime | string | - | 最大可选时间 (TimeSelect) |\n| minTime | string | - | 最小可选时间 (TimeSelect) |\n\n```javascript\n// TimeSelect 固定时间点选择\n{\n  type: 'time',\n  start: '08:00',\n  end: '18:00',\n  step: '00:30',\n  maxTime: '17:30',\n}\n```\n\n---\n\n## switch - 开关\n\n`type: 'switch'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| len | number | 40 | 开关宽度 |\n| activeColor | string | '#409EFF' | 打开背景色 |\n| inactiveColor | string | '#C0CCDA' | 关闭背景色 |\n| activeIcon | string | - | 打开图标 (3.2.20+) |\n| inactiveIcon | string | - | 关闭图标 (3.2.20+) |\n| activeActionIcon | string | - | 打开动作图标 (3.2.20+) |\n| inactiveActionIcon | string | - | 关闭动作图标 (3.2.20+) |\n| inlinePrompt | boolean | false | 只显示首字符 (3.2.20+) |\n| beforeChange | function(done) | - | 切换前钩子 (3.2.20+) |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  type: 'switch',\n  dicData: [\n    { label: '关闭', value: 0 },\n    { label: '开启', value: 1 },\n  ],\n}\n```\n\n---\n\n## upload - 文件上传\n\n`type: 'upload'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| action | string | **必填** | 上传地址 |\n| accept | string/array | - | 文件类型限制 |\n| fileSize | number | - | 文件大小限制(KB) |\n| limit | number | - | 最大文件数 |\n| multiple | boolean | false | 多文件上传 |\n| directory | boolean | false | 文件夹上传 (3.8.0+) |\n| listType | string | 'text' | 文件列表类型: text/picture/picture-card |\n| showFileList | boolean | true | 显示文件列表 |\n| fileType | string | 'img' | 文件类型: img/audio/video |\n| fileText | string | '点击上传' | 上传按钮文字 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| data | object | - | 额外参数 |\n| headers | object | - | 请求头 |\n| httpRequest | function(file, column) | - | 自定义上传逻辑 |\n| canvasOption | object | - | 图片水印配置 |\n| propsHttp | object | - | 响应结构配置 |\n| dataType | string | - | 数据类型: string/object/json |\n| loadText | string | - | 加载文字 |\n| tip | string | - | 提示文字 |\n| dragFile | boolean | false | 拖拽上传区域（区别于 drag 拖拽排序）|\n| oss | string | - | 云存储类型: ali/qiniu/cos |\n| paramsList | array | - | 额外参数列表 |\n| cropperOption | object | - | 图片裁剪配置 |\n\n### propsHttp 响应配置\n\n| 属性 | 类型 | 说明 |\n|-----|------|------|\n| home | string | 图片根路径 |\n| res | string | 响应层级 |\n| url | string | 图片地址属性 |\n| name | string | 图片名称属性 |\n| fileName | string | 上传文件名（默认'file'） |\n\n### canvasOption 水印配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | 'avuejs.com' | 水印文字 |\n| fontFamily | string | 'microsoft yahei' | 字体 |\n| color | string | '#999' | 颜色 |\n| fontSize | number | 16 | 字号 |\n| opacity | number | 100 | 透明度 |\n| bottom | number | 10 | 底部距离 |\n| right | number | 10 | 右侧距离 |\n| ratio | number | 1 | 压缩比(0-1) |\n\n```javascript\n{\n  label: '附件',\n  prop: 'files',\n  type: 'upload',\n  action: '/api/upload',\n  listType: 'picture-card',\n  multiple: true,\n  limit: 5,\n  fileSize: 10240,\n  accept: 'image/*',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n  },\n  tip: '支持jpg/png格式，单文件不超过10MB',\n}\n```\n\n---\n\n## tree - 树选择\n\n`type: 'tree'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| checkStrictly | boolean | false | 父子独立 |\n| parent | boolean | true | 父节点可选 |\n| accordion | boolean | false | 手风琴模式 |\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| expandOnClickNode | boolean | false | 点击展开 |\n| checkOnClickLeaf | boolean | false | 点击叶子勾选 (3.6.3+) |\n| defaultExpandedKeys | array | - | 默认展开keys |\n| defaultCheckedKeys | array | - | 默认勾选keys |\n| filterable | boolean | false | 可筛选 |\n| filterText | string | '请输入搜索关键字' | 筛选占位 |\n| iconClass | string | - | 自定义图标 |\n| leafOnly | boolean | true | 仅叶子节点 |\n| lazy | boolean | false | 懒加载 |\n| treeLoad | function | - | 加载函数 |\n| cacheData | array | - | 懒加载缓存 (3.2.20+) |\n| nodeClick | function | - | 节点点击回调 |\n| checked | function | - | 勾选回调 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'tree',\n  dicUrl: '/api/dept/tree',\n  props: { label: 'name', value: 'id' },\n  defaultExpandAll: true,\n  checkStrictly: true,\n}\n```\n\n---\n\n## input-tag - 标签输入 (3.6.0+)\n\n`type: 'input-tag'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| drag | boolean | false | 拖拽排序 |\n| size | string | - | 尺寸: large/small |\n| min | number | -Infinity | 最小标签数 |\n| max | number | Infinity | 最大标签数 |\n| prefix | string | - | 前置内容 |\n| prefixClick | function | - | 前置内容点击回调 |\n| suffix | string | - | 后置内容 |\n| suffixClick | function | - | 后置内容点击回调 |\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'input-tag',\n  // 按回车添加标签\n}\n```\n\n---\n\n## mention - 提及 (3.6.0+)\n\n`type: 'mention'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | - | 最大提及数 |\n| remote | boolean | false | 远程搜索 |\n| maxlength | number | - | 最大长度 |\n| minlength | number | - | 最小长度 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| split | string | ',' | 提及分隔符 |\n| prefix | string | '@' | 触发前缀 |\n| whole | boolean | false | 仅匹配完整提及 |\n| checkIsWhole | function | - | 自定义完整匹配检测 |\n| prependClick | function | - | 前置点击回调 |\n| appendClick | function | - | 后置点击回调 |\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  dicData: [\n    { label: '张三', value: 'zhangsan' },\n    { label: '李四', value: 'lisi' },\n  ],\n}\n\n// 远程搜索\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  remote: true,\n  dicUrl: '/api/user/search?name={{key}}',\n}\n```\n\n---\n\n## input-table - 表格选择\n\n`type: 'input-table'`\n\n内部使用 avue-crud 组件，配置参考 crud 文档。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| formatter | function | - | 数据格式化 |\n| onLoad | function | - | 加载/查询/分页方法 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| multiple | boolean | false | 多选 |\n| children | object | - | 表格 CRUD 配置（支持所有 avue-crud option 属性） |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n\n```javascript\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'input-table',\n  formatter: (row) => row.name,\n  onLoad: ({ page, value, data }, callback) => {\n    getList(page.currentPage, page.pageSize, data).then(res => {\n      callback({\n        total: res.data.total,\n        data: res.data.records,\n      })\n    })\n  },\n  children: {\n    border: true,\n    column: [\n      { label: '姓名', prop: 'name', search: true },\n      { label: '手机号', prop: 'phone' },\n    ],\n  },\n}\n```\n\n---\n\n## color - 颜色选择\n\n`type: 'color'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAlpha | boolean | true | 显示透明度 |\n| colorFormat | string | 'rgb' | 颜色格式: hsl/hsv/hex/rgb |\n| popperClass | string | - | 下拉框class |\n| predefine | array | - | 预定义颜色 |\n\n```javascript\n{\n  label: '主题色',\n  prop: 'themeColor',\n  type: 'color',\n  colorFormat: 'hex',\n  predefine: ['#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff'],\n}\n```\n\n---\n\n## icon - 图标选择\n\n`type: 'icon'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| iconList | array | - | 图标数组 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n```javascript\n{\n  label: '图标',\n  prop: 'icon',\n  type: 'icon',\n  iconList: [\n    { label: '首页', value: 'el-icon-s-home' },\n    { label: '设置', value: 'el-icon-setting' },\n  ],\n}\n```\n\niconList 支持分组结构：\n```javascript\niconList: [\n  {\n    label: 'Element Plus',\n    list: [\n      { label: 'Plus', value: 'el-icon-plus' },\n      { label: 'Edit', value: 'el-icon-edit' },\n    ]\n  },\n  {\n    label: 'Font Awesome',\n    list: [\n      { label: 'Home', value: 'fa fa-home' },\n    ]\n  }\n]\n```\n\n需引入图标库：\n```html\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n```\n\n---\n\n## map - 地图选择\n\n`type: 'map'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| params | object | - | 高德地图初始化参数 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| mapChange | function | - | 地图变化回调 |\n| rows | number | - | 文本域行数 |\n| minRows | number | 1 | 最小行数 |\n| maxRows | number | - | 最大行数 |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n需引入高德地图SDK（见 global-config.md）。\n\n```javascript\n{\n  label: '位置',\n  prop: 'location',\n  type: 'map',\n  params: { zoom: 15, center: [116.397428, 39.90923] },\n}\n```\n\n> 值格式为数组：`[经度, 纬度, 格式化地址]`，需引入高德地图 SDK。\n\n---\n\n## rate - 评分\n\n`type: 'rate'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | 5 | 最大分数 |\n| allowHalf | boolean | false | 允许半星 |\n| lowThreshold | number | 2 | 低分界限 |\n| highThreshold | number | 4 | 高分界限 |\n| colors | array/object | ['#F7BA2A','#F7BA2A','#F7BA2A'] | 颜色 |\n| voidColor | string | '#C6D1DE' | 未选颜色 |\n| disabledVoidColor | string | '#EFF2F7' | 禁用未选颜色 |\n| iconClasses | array/object | - | 图标class |\n| voidIconClass | string | 'el-icon-star-off' | 未选图标 |\n| showText | boolean | false | 显示文字 |\n| showScore | boolean | false | 显示分数 |\n| textColor | string | '#1F2D3D' | 文字颜色 |\n| texts | array | ['极差','失望','一般','满意','惊喜'] | 文字列表 |\n| scoreTemplate | string | '{value}' | 分数模板 |\n\n```javascript\n{\n  label: '评分',\n  prop: 'score',\n  type: 'rate',\n  max: 5,\n  allowHalf: true,\n  showText: true,\n  texts: ['极差', '失望', '一般', '满意', '惊喜'],\n}\n```\n\n---\n\n## slider - 滑块\n\n`type: 'slider'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | 0 | 最小值 |\n| max | number | 100 | 最大值 |\n| step | number | 1 | 步长 |\n| showInput | boolean | false | 显示输入框 |\n| showInputControls | boolean | true | 输入框控制按钮 |\n| showStops | boolean | false | 显示间断点 |\n| showTooltip | boolean | true | 显示tooltip |\n| formatTooltip | function | - | tooltip格式化 |\n| tooltipClass | string | - | tooltip class |\n| range | boolean | false | 范围选择 |\n| vertical | boolean | false | 垂直模式 |\n| height | string | - | 垂直模式高度 |\n| marks | object | - | 标记 |\n\n```javascript\n{\n  label: '进度',\n  prop: 'progress',\n  type: 'slider',\n  min: 0,\n  max: 100,\n  step: 10,\n  showStops: true,\n  marks: { 0: '0%', 50: '50%', 100: '100%' },\n}\n```\n\n---\n\n## array/img/url - 数组输入\n\n`type: 'array'` | `'img'` | `'url'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| alone | boolean | false | 单条模式 |\n| limit | number | - | 最大条数 |\n| fileType | string | 'img' | 文件类型 (img类型): img/audio/video |\n\n```javascript\n// 数组输入\n{ label: '标签', prop: 'tags', type: 'array', limit: 5 }\n\n// 图片输入\n{ label: '图片', prop: 'images', type: 'img', limit: 3, fileType: 'img' }\n\n// 链接输入\n{ label: '链接', prop: 'links', type: 'url', limit: 5 }\n```\n\n---\n\n## dynamic - 动态子表单\n\n`type: 'dynamic'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| type | string | 'crud' | 显示类型: crud/form |\n| limit | number | - | 最大行数 |\n| children | object | - | 子表配置 |\n| index | boolean | true | 显示序号 |\n| max | number | - | 最大行数 |\n\n### children 配置属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| type | string | 'crud' | 子表类型: crud/form |\n| column | array | - | 子表字段配置 |\n| index | boolean | true | 显示序号 |\n| indexLabel | string | '#' | 序号标题 |\n| addBtn | boolean | true | 显示新增按钮 |\n| delBtn | boolean | true | 显示删除按钮 |\n| viewBtn | boolean | true | 显示查看按钮 |\n| align | string | - | 内容对齐方式 |\n| headerAlign | string | - | 表头对齐方式 |\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| selectionChange | function | - | 选择变化回调 |\n| sortableChange | function | - | 拖拽排序回调 |\n\n```javascript\n// 表格模式（默认）\n{\n  label: '明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    column: [\n      { label: '商品', prop: 'product' },\n      { label: '数量', prop: 'quantity', type: 'number' },\n      { label: '单价', prop: 'price', type: 'number', precision: 2 },\n    ],\n  },\n}\n\n// 表单模式\n{\n  label: '联系人',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 12 },\n      { label: '电话', prop: 'phone', span: 12 },\n    ],\n  },\n}\n```\n\n---\n\n## title - 标题分隔\n\n`type: 'title'`\n\n用于表单中的分组标题，无输入功能。\n\n```javascript\n{\n  label: '基本信息',\n  prop: 'title1',\n  type: 'title',\n  span: 24,\n}\n```\n\n---\n\n## input-cron - Cron表达式 (3.8.2+)\n\n`type: 'input-cron'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '点击配置Cron表达式' | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| dialogWidth | string | '700px' | 弹窗宽度 |\n\n```javascript\n{\n  label: '定时任务',\n  prop: 'cron',\n  type: 'input-cron',\n  span: 24,\n}\n```\n","isInternal":false,"tokens":9957,"sizeBytes":30819},{"name":"global-config.md","path":".claude/skills/avue-design/references/global-config.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/global-config.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Avue 全局配置参考\n\n## 安装依赖\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n**必须依赖：**\n- `element-plus` - UI组件库\n- `@element-plus/icons-vue` - 图标库\n\n**可选依赖：**\n- `axios` - 字典组件和上传组件需要\n\n## 完整安装示例\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\n\n// 注册所有图标\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\n\napp.use(ElementPlus)\napp.use(Avue)\n```\n\n## 带 axios 的安装（字典/上传组件必需）\n\n```javascript\nimport axios from 'axios'\nconst app = createApp({})\napp.use(Avue, { axios })\n```\n\n## 全局配置选项\n\n```javascript\napp.use(AVUE, {\n  size: 'small',           // 组件默认尺寸: 'small' | 'mini' | 'medium'\n  crudOption: {},           // Crud组件全局默认配置\n  formOption: {},           // Form组件全局默认配置\n  appendToBody: true,       // 弹窗是否追加到body\n  modalAppendToBody: true,  // 遮罩层是否追加到body\n  cos: {},                  // 腾讯云COS配置\n  qiniu: {},                // 七牛云配置\n  ali: {},                  // 阿里云OSS配置\n  canvas: {},               // 全局水印配置\n})\n```\n\n### Crud 全局配置示例\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    border: true,\n    stripe: true,\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    menuWidth: 200,\n    searchLabelWidth: 110,\n    height: 'auto',\n  }\n})\n```\n\n### Form 全局配置示例\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    labelWidth: 120,\n    labelPosition: 'right',\n    size: 'small',\n  }\n})\n```\n\n## 云存储配置\n\n### 阿里云 OSS\n\n```javascript\napp.use(AVUE, {\n  ali: {\n    region: 'oss-cn-beijing',\n    endpoint: 'oss-cn-beijing.aliyuncs.com',\n    stsToken: '',\n    accessKeyId: 'your-access-key-id',\n    accessKeySecret: 'your-access-key-secret',\n    bucket: 'your-bucket-name',\n  }\n})\n```\n\n### 七牛云\n\n```javascript\n// 需引入 CryptoJS：\n// <script src=\"https://cdn.staticfile.net/crypto-js/4.0.0/crypto-js.min.js\"></script>\napp.use(AVUE, {\n  qiniu: {\n    AK: 'your-access-key',\n    SK: 'your-secret-key',\n    scope: 'your-scope',\n    url: 'https://upload.qiniup.com',\n    deadline: 1,\n  }\n})\n```\n\n### 腾讯云 COS\n\n```javascript\n// 需引入 cos-js-sdk-v5：\n// <script src=\"https://cdn.staticfile.net/cos-js-sdk-v5/1.3.2/cos-js-sdk-v5.min.js\"></script>\napp.use(AVUE, {\n  cos: {\n    SecretId: 'your-secret-id',\n    SecretKey: 'your-secret-key',\n    Bucket: 'your-bucket',\n    Region: 'ap-beijing',\n  }\n})\n```\n\n## 全局水印配置\n\n```javascript\napp.use(AVUE, {\n  canvas: {\n    text: 'avuejs.com',        // 水印文字\n    fontFamily: 'microsoft yahei', // 字体\n    color: '#999',              // 颜色\n    fontSize: 16,               // 字号\n    opacity: 100,               // 透明度\n    bottom: 10,                 // 底部距离\n    right: 10,                  // 右侧距离\n    ratio: 1,                   // 压缩比 0-1\n  }\n})\n```\n\n## 国际化\n\n```javascript\nimport Avue from '@smallwei/avue'\nimport enLocale from '@smallwei/avue/lib/locale/lang/en'\nimport zhLocale from '@smallwei/avue/lib/locale/lang/zh'\n\n// 使用英文\napp.use(Avue, { locale: enLocale })\n\n// 配合 vue-i18n\nconst messages = {\n  en: { ...enLocale },\n  zh: { ...zhLocale },\n}\n```\n\n支持的语言：`zh`（简体中文）、`en`（英文）。\n\n## 外部库引入（按需）\n\n```html\n<!-- Excel 导入导出 -->\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n\n<!-- 拖拽排序 -->\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n\n<!-- 高德地图 -->\n<script>\n  window._AMapSecurityConfig = { securityJsCode: 'xxxxx' }\n</script>\n<script src=\"https://webapi.amap.com/maps?v=2.0&key=xxxxxxxx&plugin=AMap.PlaceSearch,AMap.Geocoder\"></script>\n<script src=\"https://webapi.amap.com/ui/1.1/main.js?v=1.0.11\"></script>\n\n<!-- 图标库 -->\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n\n<!-- 流程图 -->\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n\n<!-- 截图 -->\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n\n<!-- PDF导出 -->\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n\n<!-- Mock数据 -->\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n","isInternal":false,"tokens":1536,"sizeBytes":4780},{"name":"other-components.md","path":".claude/skills/avue-design/references/other-components.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/other-components.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Avue 其他组件参考\n\n## 目录\n1. [Card 卡片](#card-卡片)\n2. [DialogForm 弹窗表单](#dialogform-弹窗表单)\n3. [Tabs 选项卡](#tabs-选项卡)\n4. [Search 搜索](#search-搜索)\n5. [Calendar 日历](#calendar-日历)\n6. [Flow 流程图](#flow-流程图)\n7. [Login 登录](#login-登录)\n8. [Article 文章](#article-文章)\n9. [Chat 聊天](#chat-聊天)\n10. [CountUp 数字动画](#countup-数字动画)\n11. [Clipboard 剪切板](#clipboard-剪切板)\n12. [Draggable 拖拽](#draggable-拖拽)\n13. [TextEllipsis 文本省略](#textellipsis-文本省略)\n14. [Sign 电子签名](#sign-电子签名)\n15. [Print 打印](#print-打印)\n16. [Export 导入导出](#export-导入导出)\n17. [ImagePreview 图片预览](#imagepreview-图片预览)\n18. [Contextmenu 右键菜单](#contextmenu-右键菜单)\n19. [Video 摄像头](#video-摄像头)\n20. [Verify 验证码](#verify-验证码)\n21. [Screenshot 截图](#screenshot-截图)\n22. [License 授权书](#license-授权书)\n\n---\n\n## Card 卡片\n\n```vue\n<avue-card :option=\"option\" :data=\"data\" @row-click=\"rowClick\" @row-add=\"rowAdd\">\n  <template #menu=\"{ row, index }\">\n    <el-button @click=\"handleEdit(row)\">编辑</el-button>\n  </template>\n</avue-card>\n```\n\n### Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| span | number | 8 | 栅格数 |\n| gutter | number | 20 | 间距 |\n\n### Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| title | string | 'title' | 标题属性 |\n| img | string | 'img' | 头像属性 |\n| info | string | 'info' | 副标题属性 |\n\n### 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-add | - | 新增 |\n| row-click | (row, index) | 行点击 |\n\n---\n\n## DialogForm 弹窗表单\n\n全局API方式调用 (3.4.1+)：\n\n```javascript\nimport { $DialogForm } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\n\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"确认\",\n    column: [\n      { label: \"姓名\", prop: \"name\", span: 24 },\n    ],\n  },\n})\n\n// 3.5.2+ 主动关闭\ndialog.close()\n```\n\n也可作为组件使用，参考 `default/dialog-form/index` 示例。\n\n---\n\n## Tabs 选项卡\n\n```vue\n<avue-tabs :option=\"option\" @change=\"tabChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Search 搜索\n\n```vue\n<avue-search v-model=\"searchForm\" :option=\"option\" @change=\"searchChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Calendar 日历\n\n```vue\n<avue-calendar :option=\"option\" :data=\"calendarData\" />\n```\n\n---\n\n## Flow 流程图\n\n需引入 jsPlumb：\n```html\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n```\n\n```vue\n<avue-flow :option=\"option\" :width=\"800\" :height=\"600\" @click=\"nodeClick\" />\n```\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| width | number | - | 画布宽度 |\n| height | number | - | 画布高度 |\n| option | object | - | 配置 |\n\n### 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| nodeAdd | (name) | 添加节点 |\n| deleteNode | (id) | 删除节点 |\n\n---\n\n## Login 登录\n\n```vue\n<avue-login :option=\"option\" @submit=\"handleLogin\" />\n```\n\n---\n\n## Article 文章\n\n```vue\n<avue-article :id=\"container\" :offset-top=\"0\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | 'window' | 指定父元素 |\n| offset-top | number | 0 | 触发偏移量 |\n\n---\n\n## Chat 聊天\n\n```vue\n<avue-chat :option=\"option\" />\n```\n\n支持图片、视频、文件、地图等消息类型。\n\n---\n\n## CountUp 数字动画\n\n```vue\n<avue-count-up :start=\"0\" :end=\"12345\" :decimals=\"0\" :duration=\"2\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | number | 0 | 起始数字 |\n| end | number | - | 结束数字 |\n| decimals | number | 0 | 小数位数 |\n| duration | number | 2 | 动画时长(秒) |\n| options | object | - | CountUp.js配置 |\n| callback | function | - | 开始回调 |\n\n---\n\n## Clipboard 剪切板\n\n```vue\n<avue-clipboard :text=\"copyText\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 要复制的文字 |\n\n---\n\n## Draggable 拖拽\n\n```vue\n<avue-draggable :width=\"200\" :height=\"100\" :top=\"50\" :left=\"100\" :disabled=\"false\">\n  <div>可拖拽内容</div>\n</avue-draggable>\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| disabled | boolean | false | 禁用拖拽 |\n| width | string | - | 宽度 |\n| height | string | - | 高度 |\n| top | number | 0 | X位置 |\n| left | number | 0 | Y位置 |\n| z-index | number | 1 | 层级 |\n\n### 事件\n\n| 事件名 | 说明 |\n|-------|------|\n| focus | 获取焦点 |\n| blur | 失去焦点 |\n\n---\n\n## TextEllipsis 文本省略\n\n```vue\n<avue-text-ellipsis :text=\"longText\" :height=\"60\" :is-limit-height=\"true\" :use-tooltip=\"true\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 文本内容 |\n| width | number | - | 宽度限制 |\n| height | number | - | 高度限制 |\n| is-limit-height | boolean | true | 启用高度限制 |\n| use-tooltip | boolean | false | 使用tooltip |\n| placement | string | - | tooltip方向 |\n\n---\n\n## Sign 电子签名\n\n```vue\n<avue-sign />\n```\n\n兼容移动端和PC端。\n\n---\n\n## Print 打印\n\n```vue\n<avue-print :id=\"printId\" :html=\"htmlContent\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | - | DOM元素ID |\n| html | string | - | HTML代码片段 |\n\n---\n\n## Export 导入导出\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 导出\n$Export.excel({\n  title: '导出标题',\n  columns: [{ label: '姓名', prop: 'name' }],\n  data: [{ name: '张三' }],\n})\n```\n\n---\n\n## ImagePreview 图片预览\n\n```javascript\nimport { $ImagePreview } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\nconst images = [\n  { thumbUrl: '缩略图.jpg', url: '原图.jpg' },\n]\n$ImagePreview(appContext)(images, 0)\n```\n\n---\n\n## Contextmenu 右键菜单\n\n```vue\n<div v-contextmenu=\"menuOptions\">\n  右键点击此区域\n</div>\n```\n\n---\n\n## Video 摄像头\n\n```vue\n<avue-video ref=\"video\" />\n```\n\n支持 start/end 方法控制录制。\n\n---\n\n## Verify 验证码\n\n```vue\n<avue-verify />\n```\n\n---\n\n## Screenshot 截图\n\n需引入 html2canvas：\n```html\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n```\n\n---\n\n## License 授权书\n\n需引入 jsPDF (用于PDF导出)：\n```html\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n```\n","isInternal":false,"tokens":2290,"sizeBytes":6931},{"name":"tree-component.md","path":".claude/skills/avue-design/references/tree-component.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/tree-component.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# avue-tree 树组件参考\n\n## 基本用法\n\n```vue\n<avue-tree\n  :option=\"option\"\n  :data=\"treeData\"\n  @node-click=\"nodeClick\"\n  @save=\"handleSave\"\n  @update=\"handleUpdate\"\n  @del=\"handleDel\"\n/>\n```\n\n## Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| virtualize | boolean | false | 虚拟DOM模式 |\n| dialogWidth | string | '50%' | 弹窗宽度 |\n| formOption | object | - | 自定义表单（参考avue-form） |\n| menu | boolean | true | 显示菜单栏 |\n| addBtn | boolean | true | 新增按钮 |\n| editBtn | boolean | true | 编辑按钮 |\n| delBtn | boolean | true | 删除按钮 |\n| filter | boolean | true | 显示搜索框 |\n| props | object | - | 配置选项 |\n| lazy | boolean | false | 懒加载 |\n\n## Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 节点名称属性 |\n| labelText | string | '名称' | 弹窗新增名称 |\n| children | string | 'children' | 子节点属性 |\n| value | string | 'value' | 节点值属性和nodeKey |\n\n## 数据结构\n\n```javascript\nconst treeData = ref([\n  {\n    label: '一级节点',\n    value: '1',\n    children: [\n      { label: '二级节点A', value: '1-1' },\n      { label: '二级节点B', value: '1-2' },\n    ],\n  },\n])\n```\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| save | (parent, data, done, loading) | 新增回调 |\n| update | (parent, data, done, loading) | 编辑回调 |\n| del | (data, done) | 删除回调 |\n| before-open | (done, type) | 打开前回调 |\n| before-close | (done, type) | 关闭前回调 |\n| node-click | (data, node, component) | 节点点击 |\n| check-change | (data, checked, hasCheckedChildren) | 勾选变化 |\n\n## 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| filter | (value) | 筛选树节点 |\n| updateKeyChildren | (key, data) | 更新子节点 |\n| getCheckedNodes | (leafOnly, includeHalfChecked) | 获取勾选节点 |\n| setCheckedNodes | (nodes) | 设置勾选节点 |\n| getCheckedKeys | (leafOnly) | 获取勾选keys |\n| setCheckedKeys | (keys, leafOnly) | 设置勾选keys |\n| setChecked | (key/data, checked, deep) | 设置勾选状态 |\n| getHalfCheckedNodes | - | 获取半选节点 |\n| getHalfCheckedKeys | - | 获取半选keys |\n| getCurrentKey | - | 获取当前选中key |\n| getCurrentNode | - | 获取当前选中数据 |\n| setCurrentKey | (key) | 设置当前选中 |\n| setCurrentNode | (node) | 设置当前选中 |\n| getNode | (data) | 获取节点 |\n| remove | (data) | 删除节点 |\n| append | (data, parentNode) | 追加子节点 |\n| insertBefore | (data, refNode) | 在前面插入 |\n| insertAfter | (data, refNode) | 在后面插入 |\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu | 按钮插槽 |\n| add-btn | 新增按钮插槽 |\n| default | 节点内容插槽 |\n\n## 示例\n\n### 多选模式\n\n```vue\n<avue-tree :option=\"{ ...option, multiple: true }\" :data=\"treeData\" @check-change=\"checkChange\" />\n```\n\n### 懒加载\n\n```vue\n<avue-tree :option=\"option\" :data=\"treeData\">\n\nconst option = reactive({\n  lazy: true,\n  props: { label: 'name', value: 'id' },\n})\n\n// 在 treeLoad 中处理加载逻辑\n```\n\n### 拖拽\n\n参考 `default/tree/drag` 示例。\n\n### 权限控制\n\n参考 `default/tree/permission` 示例。\n\n### 虚拟模式\n\n```javascript\noption: { virtualize: true }\n// 适用于大数据量场景\n```\n","isInternal":false,"tokens":1094,"sizeBytes":3454},{"name":"utility-apis.md","path":".claude/skills/avue-design/references/utility-apis.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/references/utility-apis.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Avue 全局工具函数参考\n\n## $DialogForm - 弹窗表单\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $DialogForm } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\n\n// 打开弹窗表单\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"完成\",\n    column: [\n      {\n        label: \"姓名\",\n        prop: \"name\",\n        span: 24,\n        rules: [{ required: true, message: \"请输入姓名\", trigger: \"blur\" }],\n      },\n    ],\n  },\n})\n\n// 关闭弹窗\ndialog.close()\n```\n\n3.5.2+ 版本支持 `close()` 方法主动关闭。\n\n## $Clipboard - 复制剪切板\n\n```javascript\nimport { $Clipboard } from \"@smallwei/avue\"\n\n$Clipboard({ text: \"要复制的文本\" })\n  .then(() => ElMessage.success(\"复制成功\"))\n  .catch(() => ElMessage.error(\"复制失败\"))\n```\n\n## $ImagePreview - 图片预览\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $ImagePreview } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\nconst datas = [\n  { thumbUrl: \"缩略图URL\", url: \"大图URL\" },\n  { thumbUrl: \"缩略图URL2\", url: \"大图URL2\" },\n]\n\n$ImagePreview(appContext)(datas, 0)  // 第二个参数为起始索引\n```\n\n## $Print - 打印\n\n```javascript\nimport { $Print } from \"@smallwei/avue\"\n\n// 传入 DOM 元素的 id 或 class\n$Print(\"#test\")\n```\n\n## $Export - Excel 导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 需引入 FileSaver.js 和 xlsx\n$Export.excel({\n  title: \"文档标题\",\n  columns: [\n    { label: \"标题\", prop: \"title\" },\n    { label: \"内容\", prop: \"content\" },\n  ],\n  data: [\n    { title: \"测试1\", content: \"内容1\" },\n    { title: \"测试2\", content: \"内容2\" },\n  ],\n})\n```\n\n## $Log - 彩色日志\n\n```javascript\nimport { $Log } from \"@smallwei/avue\"\n\n// 内置颜色: default(#35495E), primary(#3488ff), success(#43B883), warning(#e6a23c), danger(#f56c6c)\n$Log.capsule(\"标题\", \"内容\", \"primary\")\n$Log.primary(\"内容\")\n$Log.success(\"内容\")\n$Log.warning(\"内容\")\n$Log.danger(\"内容\")\n```\n\n## findObject - 查找列配置对象\n\n```javascript\nimport { findObject } from \"@smallwei/avue\"\n\nconst option = { column: [{ label: '名称', prop: 'name' }] }\nconst nameColumn = findObject(option.column, 'name')\n// 返回: { label: '名称', prop: 'name' }\n```\n\n## findNode - 在数组中查找对象\n\n```javascript\nimport { findNode } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst obj = findNode(list, \"sex\", \"prop\")  // 返回 {prop: 'sex'}\n```\n\n## findArray - 查找对象在数组中的索引\n\n```javascript\nimport { findArray } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst index = findArray(list, \"sex\", \"prop\")  // 返回 1\n```\n\n## downFile - 文件下载\n\n```javascript\nimport { downFile } from \"@smallwei/avue\"\n\ndownFile(\"https://example.com/file.pdf\", \"document.pdf\")\n```\n\n## randomId - 生成随机ID\n\n```javascript\nimport { randomId } from \"@smallwei/avue\"\n\nconst id = randomId()  // 返回随机字符串\n```\n\n## loadScript - 动态加载脚本\n\n```javascript\nimport { loadScript } from \"@smallwei/avue\"\n\n// 加载 JS\nloadScript(\"js\", \"https://cdn.example.com/lib.js\").then(() => {\n  // JS 加载完成\n})\n\n// 加载 CSS\nloadScript(\"css\", \"https://cdn.example.com/style.css\").then(() => {\n  // CSS 加载完成\n})\n```\n\n## deepClone - 深拷贝\n\n```javascript\nimport { deepClone } from \"@smallwei/avue\"\n\nconst original = { name: \"张三\", info: { age: 18 } }\nconst cloned = deepClone(original)\n```\n\n## setPx - 设置像素值\n\n```javascript\nimport { setPx } from \"@smallwei/avue\"\n\nsetPx(23)       // '23px'\nsetPx(\"100%\")   // '100%'\nsetPx(\"23px\")   // '23px'\n```\n\n## validatenull - 判断空值\n\n```javascript\nimport { validatenull } from \"@smallwei/avue\"\n\nvalidatenull({})         // true\nvalidatenull([])         // true\nvalidatenull(\"\")         // true\nvalidatenull(undefined)  // true\nvalidatenull(null)       // true\nvalidatenull(\"hello\")    // false\n```\n\n## validData - 数据验证并提供默认值\n\n```javascript\nimport { validData } from \"@smallwei/avue\"\n\nvalidData(undefined, \"默认值\")     // '默认值'\nvalidData({}, { name: 11 })        // { name: 11 }\nvalidData(true, false)             // true (非空返回原值)\n```\n","isInternal":false,"tokens":1305,"sizeBytes":4253},{"name":"SKILL.md","path":".claude/skills/avue-design/SKILL.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/avue-design/SKILL.md","title":"avue-design","category":"anthropic-skill","format":"markdown","content":"---\nname: avue-design\ndescription: \"Avue 组件代码生成与配置指南。当用户需要创建、配置或生成基于 Avue (avue-crud, avue-form, avue-tree 等) 的 Vue 页面代码时使用此 skill。覆盖 CRUD 表格、表单、树组件、数据展示等全部 Avue 组件。支持 Composition API 和 Options API 两种代码风格。当用户提到 avue、crud表格、动态表单、JSON配置表单、低代码表单、avue-crud、avue-form 等关键词时，务必使用此 skill。\"\n---\n\n# Avue Design - 组件代码生成 Skill 集合\n\nAvue 是基于 Vue3 + Element-Plus 的低代码前端框架，通过 JSON 配置生成页面。版本：v3.8.2，MIT 协议。\n\n## 快速安装\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\napp.use(ElementPlus)\napp.use(Avue)\n// 若使用字典或上传组件，需传入 axios：\n// import axios from 'axios'\n// app.use(Avue, { axios })\n```\n\n## 参考文件导航\n\n根据用户需求，读取对应的参考文件获取完整配置信息：\n\n### 基础配置\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/global-config.md` | 全局配置、安装、国际化、云存储 |\n| `references/api-style-guide.md` | 需要生成 Composition API 或 Options API 代码时 |\n| `references/utility-apis.md` | 使用 Avue 全局工具函数 ($DialogForm, $Clipboard 等) |\n\n### avue-crud (CRUD 表格组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/crud-core.md` | CRUD 基础属性、事件、方法、插槽、按钮、弹窗、分页 |\n| `references/crud-column.md` | 列配置、字典、排序、过滤、格式化、渲染 |\n| `references/crud-search.md` | 搜索栏配置、搜索字段属性 |\n| `references/crud-form-dialog.md` | CRUD 弹窗表单配置、验证规则、字段状态控制 |\n| `references/crud-features.md` | 高级功能：树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽排序、权限控制等 |\n\n### avue-form (表单组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/form-core.md` | 表单基础属性、事件、方法、插槽、布局 |\n| `references/form-field-types.md` | 全部字段类型 (input/select/date/upload 等 27+ 种) 的详细配置属性 |\n| `references/form-advanced.md` | 字典配置、验证规则、动态子表单、文件上传(含云存储)、字段联动 |\n\n### 其他组件\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/tree-component.md` | avue-tree 树组件 |\n| `references/data-display.md` | 数据展示组件 (DataDisplay, DataCard 等) |\n| `references/other-components.md` | Card、DialogForm、Calendar、Flow、Login、Tabs 等独立组件 |\n\n## 代码生成核心规则\n\n### 1. 两种 API 风格\n\n生成代码时必须确认用户要用哪种风格（默认 Composition API）。详细模板见 `references/api-style-guide.md`。\n\n**Composition API (推荐)**：使用 `<script setup>`，`ref()`/`reactive()` 声明数据，箭头函数定义方法。\n\n**Options API**：使用 `export default {}`，`data()` 返回数据，`methods` 对象定义方法。\n\n### 2. avue-crud 基本结构\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    />\n  </basic-container>\n</template>\n```\n\n### 3. avue-form 基本结构\n\n```vue\n<template>\n  <avue-form\n    ref=\"form\"\n    v-model=\"formData\"\n    :option=\"option\"\n    @submit=\"handleSubmit\"\n    @reset-change=\"handleReset\"\n  />\n</template>\n```\n\n### 4. option 配置核心\n\noption 是 Avue 组件的核心配置对象，包含：\n- 表格/表单级属性（size, height, border, stripe 等）\n- `column` 数组：每个字段的完整配置（label, prop, type, rules, dicData 等）\n- `group` 数组：分组配置（仅表单）\n\n```javascript\nconst option = {\n  // 表格级配置\n  height: 'auto',\n  border: true,\n  stripe: true,\n  index: true,\n  selection: true,\n  // 列配置\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      type: 'input',        // 字段类型\n      search: true,          // 启用搜索\n      span: 12,              // 表单中占栅格数\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n      // 字典配置\n      dicData: [],           // 本地字典\n      dicUrl: '',            // 远程字典URL\n      props: { label: 'label', value: 'value' },\n    }\n  ]\n}\n```\n\n### 5. 字段类型速查\n\n| 类型 | type 值 | 说明 |\n|-----|---------|------|\n| 文本输入 | input | 支持 password/textarea/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc 子类型 |\n| 数字输入 | number | 数字步进器 |\n| 下拉选择 | select | 单选/多选/远程搜索/分组 |\n| 级联选择 | cascader | 多级联动选择 |\n| 多选框 | checkbox | 多选组 |\n| 单选框 | radio | 单选组 |\n| 日期 | date/datetime/daterange/datetimerange/week/month/year/dates | 日期时间选择 |\n| 时间 | time/timerange | 时间选择 |\n| 开关 | switch | 布尔切换 |\n| 文件上传 | upload | 支持阿里云/七牛/腾讯COS |\n| 数组 | array | 数组输入框 |\n| 图片 | img | 图片输入 |\n| 链接 | url | 超链接输入 |\n| 树选择 | tree | 树型选择器 |\n| 标签输入 | input-tag | 标签输入 (3.6.0+) |\n| 提及 | mention | @提及组件 (3.6.0+) |\n| 表格选择 | input-table | 表格弹窗选择 |\n| 颜色选择 | color | 颜色选择器 |\n| Cron表达式 | input-cron | Cron可视化配置 (3.8.2+) |\n| 图标选择 | icon | 图标选择器 |\n| 地图选择 | map | 高德地图坐标选择 |\n| 评分 | rate | 星级评分 |\n| 滑块 | slider | 滑动条 |\n| 标题 | title | 分组标题 |\n| 动态表单 | dynamic | 子表单/子表格 |\n\n### 6. 关键注意事项\n\n- `column` 可以是数组也可以是对象格式（3.2.15+，用 prop 做 key）\n- 深层数据绑定用 `bind` 属性：`bind: 'info.address'`\n- 字典联动用 `cascader` 属性指定子字段 prop\n- 字段动态控制用 `control` 属性（支持 Promise，3.5.0+）\n- 表单分组用 `group` 配置，标签页显示设 `tabs: true`\n- 所有组件都支持 `render` 函数自定义渲染（3.4.2+）\n- CRUD 弹窗类型用 `dialogType: 'drawer'` 可改为抽屉模式\n- 搜索字段通过在 column 中设 `search: true` 启用\n","frontmatter":{"name":"avue-design","description":"Avue 组件代码生成与配置指南。当用户需要创建、配置或生成基于 Avue (avue-crud, avue-form, avue-tree 等) 的 Vue 页面代码时使用此 skill。覆盖 CRUD 表格、表单、树组件、数据展示等全部 Avue 组件。支持 Composition API 和 Options API 两种代码风格。当用户提到 avue、crud表格、动态表单、JSON配置表单、低代码表单、avue-crud、avue-form 等关键词时，务必使用此 skill。"},"isInternal":false,"tokens":2243,"sizeBytes":7088},{"name":"README.md","path":".claude/skills/blade-commit/README.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-commit/README.md","title":"Blade-commit Skill","category":"anthropic-skill","format":"markdown","content":"# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 快速开始\n\n```bash\n# 简单模式（默认）— 单行 gitmoji + 描述\n/blade-commit\n\n# 详细模式 — gitmoji + conventional commit + 变更列表\n/blade-commit -d\n\n# 自定义信息 — 自动补全 gitmoji 格式\n/blade-commit -m \"新增接口权限模块\"\n```\n\n## 两种模式\n\n### 简单模式（默认）\n\n单行格式，适用于日常小改动：\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n```\n\n### 详细模式（`-d`）\n\n多行格式，适用于涉及多文件的较大提交：\n\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n```\n\n## 常用 Gitmoji\n\n| Gitmoji | 图标 | 场景 |\n|---|---|---|\n| `:sparkles:` | ✨ | 新增功能 |\n| `:bug:` | 🐛 | 修复 Bug |\n| `:zap:` | ⚡ | 性能优化 |\n| `:recycle:` | ♻️ | 重构 |\n| `:memo:` | 📝 | 文档变更 |\n| `:wrench:` | 🔧 | 配置变更 |\n| `:fire:` | 🔥 | 移除代码 |\n| `:tada:` | 🎉 | 发布版本 |\n| `:white_check_mark:` | ✅ | 测试 |\n| `:lock:` | 🔒 | 安全修复 |\n\n完整对照表见 SKILL.md。\n\n## 安全保障\n\n- **只做 commit**，禁止 push、pull 等任何远程操作\n- **零署名**，不添加任何 AI/工具署名\n- **逐文件暂存**，不使用 `git add .`，排除敏感文件\n- **自动适配**，根据项目历史提交记录匹配语言和风格\n","isInternal":false,"tokens":601,"sizeBytes":1643},{"name":"SKILL.md","path":".claude/skills/blade-commit/SKILL.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-commit/SKILL.md","title":"blade-commit","category":"anthropic-skill","format":"markdown","content":"---\nname: blade-commit\ndescription: SpringBlade 通用 Git 提交工具。基于 Gitmoji 规范生成提交信息。当用户要求提交代码、commit 变更、生成 commit message、或使用 /blade-commit 时触发。默认强制简单模式（gitmoji + 一句话描述），仅当显式传入 --d 或 --detail 参数时才启用详细模式（gitmoji + conventional commit + 变更列表）。只执行 commit，绝不 push 或执行其他远程操作。确保每次提交都带有 gitmoji 图标。\n---\n\n# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 铁律\n\n- **只做 commit**：禁止执行 `git push`、`git pull`、`git rebase`、`git merge`、`git reset --hard` 或任何非暂存/提交的 git 操作\n- **零署名**：提交信息中不得出现 `Powered by`、`Created by`、`Co-authored-by`、`Generated by` 或任何形式的工具/AI 署名\n- **必须有 Gitmoji**：每条提交信息必须以 gitmoji 代码开头（如 `:sparkles:`）\n\n## 使用方式\n\n- `/blade-commit` — 简单模式（默认，强制单行）\n- `/blade-commit --d` 或 `/blade-commit --detail` — 详细模式（多行带变更列表）\n- `/blade-commit -m \"自定义信息\"` — 使用用户指定的信息，自动补全 gitmoji 格式\n\n## 模式判定规则\n\n**无论变更多大、涉及多少文件，只要用户没有显式传入 `--d` 或 `--detail` 参数，就必须使用简单模式。** 不要因为 diff 内容多就自动升级为详细模式——模式选择权在用户手中，而非由变更规模决定。\n\n## 两种模式\n\n### 简单模式（默认，强制）\n\n单行格式：`:<gitmoji>: <简要描述>`\n\n一句话说清楚改了什么。无论改动大小，只要没有 `--d` / `--detail` 参数，一律使用此格式。\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n:recycle: 重构租户删除逻辑，迁移至 TenantService\n:memo: 补充 OSS 接入文档\n:wrench: 调整 application-dev.yml 数据源配置\n:tada: x.x.x.RELEASE\n```\n\n### 详细模式（需显式传入 `--d` 或 `--detail`）\n\n多行格式，适用于涉及多文件、多项改动的较大提交：\n\n```\n:<gitmoji>: <type>(<scope>): <标题摘要>\n\n- <变更点1>\n- <变更点2>\n- ...\n```\n\n标题行由 gitmoji + conventional commit 类型 + 作用域 + 摘要组成，空一行后用 bullet points 逐条列出具体变更。\n\n**示例 1 — 新功能：**\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\n```\n\n**示例 2 — Bug 修复：**\n```\n:bug: fix(auth): 修复登录 IP 锁定后刷新令牌仍可通过的问题\n\n- RefreshTokenGranter 增加 IP 锁定状态校验\n- LoginAttemptService 补充刷新场景下的失败计数复位逻辑\n- 新增单元测试覆盖刷新 Token 的 IP 锁定分支\n```\n\n**示例 3 — 重构：**\n```\n:recycle: refactor(resource): 重构 OssEndpoint 按职责拆分 Controller\n\n- 拆分文件上传、文件下载、文件删除为独立 Controller\n- 抽取 OssService 处理跨厂商统一逻辑\n- 原 OssEndpoint 保留兼容入口，内部委派至新 Controller\n```\n\n**示例 4 — 性能优化：**\n```\n:zap: perf(system): 优化字典批量查询性能\n\n- DictCache 改用 Caffeine 二级缓存，减少 Redis 往返\n- DictMapper 补充 code + dict_key 联合索引提示\n- 调整 DictService 的 getList 为按租户分片查询\n```\n\n## Gitmoji 对照表\n\n根据变更内容选择最匹配的 gitmoji：\n\n| Gitmoji | 图标 | type | 场景 |\n|---|---|---|---|\n| `:sparkles:` | ✨ | feat | 新增功能、新特性 |\n| `:bug:` | 🐛 | fix | 修复 Bug |\n| `:zap:` | ⚡ | perf | 性能优化、改进提升 |\n| `:recycle:` | ♻️ | refactor | 重构（不改变外部行为） |\n| `:memo:` | 📝 | docs | 文档新增或变更 |\n| `:wrench:` | 🔧 | chore | 配置变更、杂项 |\n| `:white_check_mark:` | ✅ | test | 添加或修改测试 |\n| `:tada:` | 🎉 | release | 发布新版本、里程碑 |\n| `:fire:` | 🔥 | remove | 移除代码或文件 |\n| `:lipstick:` | 💄 | style | UI / 样式调整 |\n| `:ambulance:` | 🚑 | hotfix | 紧急修复 |\n| `:construction:` | 🚧 | wip | 开发进行中 |\n| `:arrow_up:` | ⬆️ | deps | 升级依赖版本 |\n| `:arrow_down:` | ⬇️ | deps | 降级依赖版本 |\n| `:lock:` | 🔒 | security | 安全相关修复 |\n| `:truck:` | 🚚 | move | 移动 / 重命名文件 |\n| `:art:` | 🎨 | style | 改进代码结构 / 格式化 |\n| `:package:` | 📦 | build | 构建系统变更 |\n| `:rewind:` | ⏪ | revert | 回退变更 |\n| `:heavy_plus_sign:` | ➕ | deps | 添加依赖 |\n| `:heavy_minus_sign:` | ➖ | deps | 移除依赖 |\n\n## 执行流程\n\n### 第一步：收集信息\n\n并行执行：\n- `git status` — 查看工作区状态\n- `git diff --staged` — 已暂存的变更\n- `git diff` — 未暂存的变更\n- `git log --oneline -5` — 最近提交记录，确认当前项目的提交风格\n\n### 第二步：适配项目风格\n\n阅读 `git log` 结果，判断当前项目的提交语言（中文/英文）和习惯。提交信息的语言应与项目历史保持一致：\n- 如果历史提交以中文为主，使用中文\n- 如果历史提交以英文为主，使用英文\n- 如果混合使用，默认中文\n\n### 第三步：分析变更\n\n阅读 diff 内容，判断：\n1. **变更类型**：新功能？Bug修复？重构？文档？配置？性能优化？\n2. **影响范围（scope）**：涉及哪个模块或功能（如 `system`、`auth`、`desk`、`resource`、`develop`、`gateway`、`tool` 等）\n3. **核心改动**：最关键的变更是什么\n4. 如果变更混合了多种类型，选最主要的类型\n\n### 第四步：暂存文件\n\n- 使用 `git add <具体文件路径>` 逐个添加，不要用 `git add .` 或 `git add -A`\n- 排除不该提交的文件：`.env`、密钥文件、IDE 配置（`.idea/`、`.vscode/`）、编译产物（`target/`、`dist/`）、系统文件（`.DS_Store`）\n- **前端工程排除规则**：如果项目根目录存在 `package.json`、`vite.config.*`、`vue.config.*` 等前端工程标识文件，则自动将 `website.js`（通常位于 `src/config/website.js`）从暂存列表中排除，即使该文件有变更也不提交。如果 `website.js` 已被暂存，执行 `git reset HEAD <website.js路径>` 将其移出暂存区\n- 如果用户已经手动暂存过文件，跳过此步（但仍需检查并移除已暂存的 `website.js`）\n\n### 第五步：生成提交信息并提交\n\n根据模式选择对应格式，使用 HEREDOC 执行 commit：\n\n**简单模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: 新增角色授权接口\nEOF\n)\"\n```\n\n**详细模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\nEOF\n)\"\n```\n\n### 第六步：确认结果\n\n执行 `git status` 确认提交成功，向用户展示提交结果。\n\n## 写好提交信息的要点\n\n- **简单模式**：一句话说清楚\"做了什么\"，精炼但不含糊，80 字符以内为佳\n- **详细模式**：\n  - 标题行概括全局，不超过 72 字符\n  - bullet points 按重要性排序：先新增，后优化，再修复\n  - 每个 bullet 要具体，提到文件名或模块名让人知道改了哪里\n  - 不要超过 8 条 bullet，太多说明应该拆分 commit\n- **选对 gitmoji**：看对照表选最贴合的。不确定时 `:sparkles:` 适用于大多数新功能，`:zap:` 适用于优化改进\n- 如果用户通过 `-m` 指定了信息，保留用户原意，只在格式上对齐规范（补 gitmoji、调整格式）\n","frontmatter":{"name":"blade-commit","description":"SpringBlade 通用 Git 提交工具。基于 Gitmoji 规范生成提交信息。当用户要求提交代码、commit 变更、生成 commit message、或使用 /blade-commit 时触发。默认强制简单模式（gitmoji + 一句话描述），仅当显式传入 --d 或 --detail 参数时才启用详细模式（gitmoji + conventional commit + 变更列表）。只执行 commit，绝不 push 或执行其他远程操作。确保每次提交都带有 gitmoji 图标。"},"isInternal":false,"tokens":2794,"sizeBytes":8049},{"name":"README.md","path":".claude/skills/blade-design/README.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-design/README.md","title":"Blade-design Skill","category":"anthropic-skill","format":"markdown","content":"# blade-design - SpringBlade 全栈代码生成器\n\n基于 SpringBlade 开源版框架规范的智能代码生成 Skill，覆盖后端、前端、数据库三层，支持 Boot 单体和 Cloud 微服务两种架构模式。\n\n## 功能概览\n\n```\nblade-design\n├── 后端子技能 (backend)\n│   ├── Entity / DTO / VO / Excel    ← 数据模型层\n│   ├── Mapper / Mapper.xml          ← 数据访问层\n│   ├── Service / ServiceImpl        ← 业务逻辑层\n│   ├── Controller                   ← 接口控制层\n│   ├── Wrapper                      ← 数据转换层\n│   └── Feign Client (Cloud)         ← 微服务通信层\n│\n├── 前端子技能 (frontend)\n│   ├── API 模块                      ← Axios 请求封装\n│   ├── Option 配置                   ← Avue CRUD 列/表单配置\n│   └── Vue 页面                      ← Options API / Composition API\n│\n└── 数据库子技能 (database)\n    ├── MySQL 建表 SQL                ← 开源版官方支持方言\n    └── 菜单 SQL                      ← 权限菜单数据\n```\n\n## 支持的架构模式\n\n### Boot 单体架构\n- 所有代码在同一模块内\n- 包名：`org.springblade.modules.{module}`\n- Controller 路由含服务名前缀：`AppConstant.APPLICATION_XXX_NAME + \"/path\"`\n- 无 Feign Client\n\n### Cloud 微服务架构\n- API 模块（契约）与 Service 模块（实现）分离\n- 包名：`org.springblade.{module}`\n- Controller 路由无前缀（Gateway 自动路由）\n- 含 Feign Client 接口 + 实现\n\n## 支持的实体模式\n\n| 模式 | 基类 | 适用场景 | 审计字段 | 租户隔离 |\n|------|------|---------|---------|---------|\n| **tenant** | `TenantEntity` | 多租户业务表 | 自动继承 | 有 |\n| **base** | `BaseEntity` | 跨租户业务表 | 自动继承 | 无 |\n| **raw** | `Serializable` | 系统 / 关系表 | 手动定义 | 无 |\n\n## 支持的数据库\n\nSpringBlade 开源版**仅官方支持 MySQL**，建表脚本固定输出 MySQL 方言。\n\n| 方言 | 引号 | 大小写 | 注释方式 | 引擎 |\n|------|------|--------|---------|------|\n| MySQL | `` ` `` | 小写 | `COMMENT` 内联 | InnoDB + utf8mb4 |\n\n## 支持的前端风格\n\n| 风格 | 说明 | 适用场景 |\n|------|------|---------|\n| **Options API** | Vue 3 传统选项式写法 | 与现有代码保持一致 |\n| **Composition API** | Vue 3 组合式 `<script setup>` | 新功能开发推荐 |\n\n## 支持的模板类型\n\n| 类型 | 说明 | 适用场景 |\n|------|------|---------|\n| **crud** | 标准增删改查 | 通用业务管理 |\n| **tree** | 树形结构 CRUD | 分类、部门、菜单等层级数据 |\n| **sub** | 主子表 CRUD | 订单-订单项、合同-条款等主从关系 |\n\n## 使用方式\n\n### 快速开始\n```\n帮我生成一个订单管理模块的代码\n```\n\n### 指定参数\n```\n帮我生成一个产品管理的 CRUD：\n- 模块名：product\n- 实体名：Product\n- 架构：boot\n- 字段：\n  - productName (String) 产品名称，必填，搜索\n  - category (Integer) 产品分类，字典 product_category\n  - price (BigDecimal) 单价\n  - description (String/text) 产品描述\n  - status (Integer) 上架状态\n```\n\n### 仅生成部分\n```\n帮我生成 Order 的建表语句（MySQL）\n帮我生成前端页面，用 Composition API\n只需要后端代码，Cloud 架构\n```\n\n## 文件结构\n\n```\nblade-design/\n├── SKILL.md                  ← 主技能文件（路由、配置、工作流）\n├── README.md                 ← 本文档\n└── references/\n    ├── backend.md            ← 后端代码模板（Entity/DTO/VO/Mapper/Service/Controller/Wrapper/Feign）\n    ├── frontend.md           ← 前端代码模板（API/Option/Vue 页面）\n    └── database.md           ← MySQL 建表模板 + 菜单 SQL\n```\n\n## Boot vs Cloud 核心差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| 基础包名 | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| Controller 路由 | `AppConstant.APPLICATION_XXX_NAME + \"/path\"` | `\"/path\"` |\n| Entity 位置 | 同模块 | API 模块 |\n| 服务间调用 | 直接注入 | Feign Client |\n| Feign Client | 无 | 接口在 API 模块，实现在 Service 模块 |\n| POM 结构 | 单一 pom.xml | API + Service 各一个 |\n| 服务注册 | 不需要 | Nacos |\n| 网关路由 | 不需要 | Spring Cloud Gateway |\n\n## 代码规范要点\n\n1. Long 类型 ID 必须 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n2. Date 字段同时添加 `@DateTimeFormat` 和 `@JsonFormat`\n3. 逻辑删除字段 `isDeleted` 添加 `@TableLogic`（基类自动处理）\n4. Swagger 使用 OpenAPI 3 注解（`@Tag`/`@Operation`/`@Schema`/`@Parameter`），控制器类级标注无参 `@ApiOrder` 排序\n5. 响应统一使用 `R<T>` 包装\n6. Wrapper 中通过 `DictCache.getValue()` 翻译字典字段\n7. 前端权限标识格式：`{modelCode}_{action}`\n8. 表名统一 `blade_` 前缀\n9. 主键使用雪花算法（ASSIGN_ID）\n10. 所有表包含标准审计字段\n11. Entity 类名不带 `Entity` 后缀（如 `User` 而非 `UserEntity`）\n12. 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`，开源版仅支持角色级权限\n","isInternal":false,"tokens":1691,"sizeBytes":5269},{"name":"backend.md","path":".claude/skills/blade-design/references/backend.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-design/references/backend.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# 后端代码生成参考\n\n本文档包含 SpringBlade 开源版后端各层的完整代码模板。所有模板均来源于 SpringBlade 开源工程真实业务代码（参考 `modules/system`、`modules/desk` 及 Cloud 的 `blade-service/blade-system` 等）。\n\n## 目录\n- [一、Entity 实体类](#一entity-实体类)\n- [二、DTO 数据传输对象](#二dto-数据传输对象)\n- [三、VO 视图对象](#三vo-视图对象)\n- [四、Excel 导出类](#四excel-导出类)\n- [五、Mapper 接口](#五mapper-接口)\n- [六、Mapper XML](#六mapper-xml)\n- [七、Service 接口](#七service-接口)\n- [八、ServiceImpl 实现](#八serviceimpl-实现)\n- [九、Controller 控制器](#九controller-控制器)\n- [十、Wrapper 数据转换器](#十wrapper-数据转换器)\n- [十一、Feign Client 接口（Cloud）](#十一feign-client-接口cloud)\n- [十二、Feign Client 实现（Cloud）](#十二feign-client-实现cloud)\n\n---\n\n## 一、Entity 实体类\n\n> 开源版 Entity 命名**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。\n\n### 模式A：TenantEntity（多租户业务表，默认）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.TenantEntity;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends TenantEntity {\n\n    /**\n     * {字段注释}\n     */\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 字段类型注解模式 ---\n\n    // Long 类型字段（防止前端精度丢失）\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"关联ID\")\n    private Long refId;\n\n    // Date 类型字段\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"发布时间\")\n    private Date releaseTime;\n\n    // Integer 字典字段（配合 Wrapper 翻译）\n    @Schema(description = \"分类\")\n    private Integer category;\n\n    // String 普通文本\n    @Schema(description = \"标题\")\n    private String title;\n\n    // String 大文本\n    @Schema(description = \"内容\")\n    private String content;\n}\n```\n\n**TenantEntity 自动继承的字段**（不需要在子类中声明）：\n- `id` (Long) - 主键，@TableId(type = IdType.ASSIGN_ID)，雪花 ID\n- `tenantId` (String) - 租户 ID\n- `createUser` (Long) - 创建人\n- `createDept` (Long) - 创建部门\n- `createTime` (Date) - 创建时间\n- `updateUser` (Long) - 更新人\n- `updateTime` (Date) - 更新时间\n- `status` (Integer) - 业务状态，默认 1\n- `isDeleted` (Integer) - 逻辑删除标记，@TableLogic，默认 0\n\n### 模式B：BaseEntity（跨租户业务表）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends BaseEntity {\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n}\n```\n\n**BaseEntity 继承字段**：与 TenantEntity 相同但不含 `tenantId`。\n\n### 模式C：Raw Serializable（原生模式）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@Schema(description = \"{中文名}\")\npublic class {Name} implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 主键id\n     */\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键id\")\n    @TableId(value = \"id\", type = IdType.ASSIGN_ID)\n    private Long id;\n\n    // --- 业务字段 ---\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 审计字段（手动定义） ---\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建人\", hidden = true)\n    private Long createUser;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建部门\", hidden = true)\n    private Long createDept;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"创建时间\", hidden = true)\n    private Date createTime;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"更新人\", hidden = true)\n    private Long updateUser;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"更新时间\", hidden = true)\n    private Date updateTime;\n\n    @Schema(description = \"业务状态\", hidden = true)\n    private Integer status;\n\n    @TableLogic\n    @Schema(description = \"是否已删除\", hidden = true)\n    private Integer isDeleted;\n}\n```\n\n### Cloud 版本 Entity 差异\n\nCloud 中 Entity 放在 API 模块，包名不同：\n```java\n// Boot\npackage org.springblade.modules.{module}.entity;\n\n// Cloud\npackage org.springblade.{module}.entity;\n```\n\n其余代码完全相同。\n\n---\n\n## 二、DTO 数据传输对象\n\n### 标准 DTO（继承 Entity）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}DTO extends {Name} {\n}\n```\n\n大多数情况下 DTO 为空类，仅作为扩展点。如有特定传输需求可添加字段。\n\n### 精简 DTO（仅特定字段）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\npublic class {Name}DTO implements Serializable {\n\n    @Schema(description = \"特定字段\")\n    private String specificField;\n}\n```\n\n---\n\n## 三、VO 视图对象\n\n### 标准 VO（简单扩展）\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}视图对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} {\n\n    /**\n     * 分类名称（由 Wrapper 通过字典翻译填充）\n     */\n    @Schema(description = \"分类名称\")\n    private String categoryName;\n}\n```\n\n对于每个需要字典翻译的字段，在 VO 中添加对应的 `{字段名}Name` 字段。\n\n### 树形 VO（实现 INode 接口）\n\n用于 Menu、Dict、Dept 等树形结构：\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.tool.node.INode;\nimport org.springblade.modules.{module}.entity.{Name};\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {中文名}视图对象（树形）\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} implements INode<{Name}VO> {\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键ID\")\n    private Long id;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"父节点ID\")\n    private Long parentId;\n\n    @JsonInclude(JsonInclude.Include.NON_EMPTY)\n    @Schema(description = \"子节点\")\n    private List<{Name}VO> children;\n\n    @Schema(description = \"是否有子节点\")\n    private Boolean hasChildren;\n\n    @Schema(description = \"上级名称\")\n    private String parentName;\n\n    @Override\n    public List<{Name}VO> getChildren() {\n        if (this.children == null) {\n            this.children = new ArrayList<>();\n        }\n        return this.children;\n    }\n}\n```\n\n---\n\n## 四、Excel 导出类\n\n```java\npackage org.springblade.modules.{module}.excel;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.idev.excel.annotation.write.style.ColumnWidth;\nimport cn.idev.excel.annotation.write.style.ContentRowHeight;\nimport cn.idev.excel.annotation.write.style.HeadRowHeight;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}导出对象\n *\n * @author {author}\n */\n@Data\n@ColumnWidth(25)\n@HeadRowHeight(20)\n@ContentRowHeight(18)\npublic class {Name}Excel implements Serializable {\n\n    @ExcelProperty(\"{字段中文名}\")\n    private {Type} {fieldName};\n\n    // 不导出审计字段（createUser、createTime 等）\n}\n```\n\n> 开源版使用 FastExcel（`cn.idev.excel.*`，由 `blade-starter-excel` 引入）。\n\n---\n\n## 五、Mapper 接口\n\n```java\npackage org.springblade.modules.{module}.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.excel.{Name}Excel;\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\nimport java.util.List;\n\n/**\n * {中文名} Mapper 接口\n *\n * @author {author}\n */\npublic interface {Name}Mapper extends BaseMapper<{Name}> {\n\n    /**\n     * 自定义分页查询\n     */\n    List<{Name}VO> select{Name}Page(IPage page, {Name}VO {modelCode});\n\n    /**\n     * 导出数据查询\n     */\n    List<{Name}Excel> export{Name}(@Param(\"ew\") Wrapper<{Name}> queryWrapper);\n}\n```\n\n**树形模板追加方法**：\n```java\n    /**\n     * 树形结构查询\n     */\n    List<{Name}VO> tree(String tenantId);\n```\n\n---\n\n## 六、Mapper XML\n\n### 标准 CRUD\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"org.springblade.modules.{module}.mapper.{Name}Mapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"{modelCode}ResultMap\" type=\"org.springblade.modules.{module}.entity.{Name}\">\n        <result column=\"id\" property=\"id\"/>\n        <!-- 每个业务字段一行映射 -->\n        <result column=\"{column_name}\" property=\"{fieldName}\"/>\n        <!-- 审计字段映射（TenantEntity 模式需要） -->\n        <result column=\"create_user\" property=\"createUser\"/>\n        <result column=\"create_dept\" property=\"createDept\"/>\n        <result column=\"create_time\" property=\"createTime\"/>\n        <result column=\"update_user\" property=\"updateUser\"/>\n        <result column=\"update_time\" property=\"updateTime\"/>\n        <result column=\"status\" property=\"status\"/>\n        <result column=\"is_deleted\" property=\"isDeleted\"/>\n    </resultMap>\n\n    <!-- 自定义分页查询 -->\n    <select id=\"select{Name}Page\" resultMap=\"{modelCode}ResultMap\">\n        SELECT\n            t.*,\n            d.dict_value AS categoryName\n        FROM blade_{table_name} t\n        LEFT JOIN (SELECT * FROM blade_dict WHERE code = '{dictCode}') d\n            ON t.category = d.dict_key\n        WHERE t.is_deleted = 0\n        <if test=\"{modelCode}.tenantId != null and {modelCode}.tenantId != ''\">\n            AND t.tenant_id = #{{{modelCode}.tenantId}}\n        </if>\n        <!-- 动态查询条件 -->\n        <if test=\"{modelCode}.{queryField} != null and {modelCode}.{queryField} != ''\">\n            AND t.{query_column} LIKE CONCAT('%', #{{{modelCode}.{queryField}}}, '%')\n        </if>\n        <if test=\"{modelCode}.{eqField} != null\">\n            AND t.{eq_column} = #{{{modelCode}.{eqField}}}\n        </if>\n    </select>\n\n    <!-- 导出查询 -->\n    <select id=\"export{Name}\" resultType=\"org.springblade.modules.{module}.excel.{Name}Excel\">\n        SELECT * FROM blade_{table_name}\n        ${ew.customSqlSegment}\n    </select>\n\n</mapper>\n```\n\n**注意事项**：\n- 如果有字典关联查询，在 select 中 LEFT JOIN blade_dict 表\n- 动态条件中 `LIKE` 用于模糊查询字段，`=` 用于精确查询字段\n- TenantEntity 模式需要 `tenant_id` 过滤条件\n- resultMap 中每个数据库列名（snake_case）映射到 Java 属性（camelCase）\n\n---\n\n## 七、Service 接口\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends BaseService<{Name}> {\n\n    /**\n     * 自定义分页\n     */\n    IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode});\n}\n```\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends IService<{Name}> {\n}\n```\n\n---\n\n## 八、ServiceImpl 实现\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends BaseServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n\n    @Override\n    public IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode}) {\n        {modelCode}.setTenantId(AuthUtil.getTenantId());\n        return page.setRecords(baseMapper.select{Name}Page(page, {modelCode}));\n    }\n}\n```\n\n**注意**：BaseEntity 模式下不需要 `setTenantId()` 那行。\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends ServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n}\n```\n\n---\n\n## 九、Controller 控制器\n\n### Boot 版本\n\n```java\npackage org.springblade.modules.{module}.controller;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springblade.modules.{module}.wrapper.{Name}Wrapper;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.Map;\n\n/**\n * {中文名} 控制器\n *\n * @author {author}\n */\n@RestController\n@AllArgsConstructor\n@RequestMapping(AppConstant.APPLICATION_{MODULE}_NAME + \"/{modelCode}\")\n@ApiOrder\n@Tag(name = \"{中文名}\", description = \"{中文名}接口\")\npublic class {Name}Controller extends BladeController {\n\n    private final I{Name}Service {modelCode}Service;\n\n    /**\n     * 详情\n     */\n    @GetMapping(\"/detail\")\n    @Operation(summary = \"详情\", description = \"传入{modelCode}\")\n    public R<{Name}VO> detail({Name} {modelCode}) {\n        {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n        return R.data({Name}Wrapper.build().entityVO(detail));\n    }\n\n    /**\n     * 分页列表\n     */\n    @GetMapping(\"/list\")\n    @Operation(summary = \"分页\", description = \"传入{modelCode}\")\n    @PreAuth(RoleConstant.HAS_ROLE_ADMIN)\n    public R<IPage<{Name}VO>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n        IPage<{Name}> pages = {modelCode}Service.page(\n            Condition.getPage(query),\n            Condition.getQueryWrapper({modelCode}, {Name}.class)\n        );\n        return R.data({Name}Wrapper.build().pageVO(pages));\n    }\n\n    /**\n     * 自定义分页\n     */\n    @GetMapping(\"/page\")\n    @Operation(summary = \"自定义分页\", description = \"传入{modelCode}\")\n    public R<IPage<{Name}VO>> page({Name}VO {modelCode}, Query query) {\n        IPage<{Name}VO> pages = {modelCode}Service.select{Name}Page(Condition.getPage(query), {modelCode});\n        return R.data(pages);\n    }\n\n    /**\n     * 新增\n     */\n    @PostMapping(\"/save\")\n    @Operation(summary = \"新增\", description = \"传入{modelCode}\")\n    public R save(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.save({modelCode}));\n    }\n\n    /**\n     * 修改\n     */\n    @PostMapping(\"/update\")\n    @Operation(summary = \"修改\", description = \"传入{modelCode}\")\n    public R update(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.updateById({modelCode}));\n    }\n\n    /**\n     * 新增或修改\n     */\n    @PostMapping(\"/submit\")\n    @Operation(summary = \"新增或修改\", description = \"传入{modelCode}\")\n    public R submit(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.saveOrUpdate({modelCode}));\n    }\n\n    /**\n     * 删除\n     */\n    @PostMapping(\"/remove\")\n    @Operation(summary = \"逻辑删除\", description = \"传入ids\")\n    public R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n        return R.status({modelCode}Service.deleteLogic(Func.toLongList(ids)));\n    }\n}\n```\n\n### Cloud 版本差异\n\nCloud 版本仅以下几处不同：\n\n```java\n// 1. 包名不同\npackage org.springblade.{module}.controller;\n\n// 2. import 路径不同（Entity/VO/Service 都在 org.springblade.{module} 下）\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.vo.{Name}VO;\nimport org.springblade.{module}.service.I{Name}Service;\n\n// 3. RequestMapping 不需要服务名前缀\n@RequestMapping(\"/{modelCode}\")\n```\n\n### 无 Wrapper 版本\n\n如不使用 Wrapper，detail 和 list 方法简化为：\n\n```java\n@GetMapping(\"/detail\")\npublic R<{Name}> detail({Name} {modelCode}) {\n    {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n    return R.data(detail);\n}\n\n@GetMapping(\"/list\")\npublic R<IPage<{Name}>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n    IPage<{Name}> pages = {modelCode}Service.page(\n        Condition.getPage(query),\n        Condition.getQueryWrapper({modelCode}, {Name}.class)\n    );\n    return R.data(pages);\n}\n```\n\n### Raw Serializable 模式的 remove 方法\n\n```java\n@PostMapping(\"/remove\")\n@Operation(summary = \"删除\", description = \"传入ids\")\npublic R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n    return R.status({modelCode}Service.removeBatchByIds(Func.toLongList(ids)));\n}\n```\n\n### 权限注解使用规则\n\nSpringBlade 开源版仅支持角色级权限：\n\n| 场景 | 写法 |\n|------|------|\n| 仅管理员可访问（类级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在类上 |\n| 仅管理员可访问（方法级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在方法上 |\n| 隐藏 Swagger 文档 | `@Hidden`（来自 OpenAPI 3） |\n| 全公开 | 不加任何权限注解 |\n\n### 导出接口\n\n```java\n@GetMapping(\"/export-{modelCode}\")\n@Operation(summary = \"导出数据\", description = \"传入{modelCode}\")\n@PreAuth(RoleConstant.HAS_ROLE_ADMIN)\npublic void export{Name}(@RequestParam Map<String, Object> {modelCode}, HttpServletResponse response) {\n    QueryWrapper<{Name}> queryWrapper = Condition.getQueryWrapper({modelCode}, {Name}.class);\n    List<{Name}Excel> list = {modelCode}Service.export{Name}(queryWrapper);\n    ExcelUtil.export(response, \"{中文名}数据\", \"{中文名}数据表\", list, {Name}Excel.class);\n}\n```\n\n---\n\n## 十、Wrapper 数据转换器\n\n```java\npackage org.springblade.modules.{module}.wrapper;\n\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.modules.system.cache.DictCache;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名}包装类，返回视图层所需的字段\n *\n * @author {author}\n */\npublic class {Name}Wrapper extends BaseEntityWrapper<{Name}, {Name}VO> {\n\n    public static {Name}Wrapper build() {\n        return new {Name}Wrapper();\n    }\n\n    @Override\n    public {Name}VO entityVO({Name} {modelCode}) {\n        {Name}VO {modelCode}VO = BeanUtil.copyProperties({modelCode}, {Name}VO.class);\n        // 字典翻译：将字典键值转换为可读名称\n        String categoryName = DictCache.getValue(\"{dictCode}\", {modelCode}.getCategory());\n        {modelCode}VO.setCategoryName(categoryName);\n        return {modelCode}VO;\n    }\n}\n```\n\n**常见翻译模式**：\n\n```java\n// 1. 字典翻译\nString categoryName = DictCache.getValue(\"{dictCode}\", entity.getCategory());\n\n// 2. 关联实体名称（通过缓存）\nUser user = UserCache.getUser(entity.getUserId());\nif (Func.isNotEmpty(user)) {\n    vo.setUserName(user.getAccount());\n}\n\n// 3. 父节点名称（树形结构）\nif (Func.equals(entity.getParentId(), BladeConstant.TOP_PARENT_ID)) {\n    vo.setParentName(BladeConstant.TOP_PARENT_NAME);\n} else {\n    {Name} parent = {modelCode}Service.getById(entity.getParentId());\n    vo.setParentName(parent.getName());\n}\n```\n\n**树形 Wrapper 追加方法**：\n\n```java\npublic List<{Name}VO> listNodeVO(List<{Name}> list) {\n    List<{Name}VO> collect = list.stream()\n        .map(entity -> BeanUtil.copyProperties(entity, {Name}VO.class))\n        .collect(Collectors.toList());\n    return ForestNodeMerger.merge(collect);\n}\n```\n\n---\n\n## 十一、Feign Client 接口（Cloud）\n\n放在 API 模块中（`blade-service-api/blade-{module}-api`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.{module}.entity.{Name};\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n/**\n * {中文名} Feign 接口\n *\n * @author {author}\n */\n@FeignClient(value = AppConstant.APPLICATION_{MODULE}_NAME)\npublic interface I{Name}Client {\n\n    String API_PREFIX = \"/feign/client/{modelCode}\";\n    String TOP = API_PREFIX + \"/top\";\n\n    /**\n     * 获取分页数据\n     */\n    @GetMapping(TOP)\n    BladePage<{Name}> top(@RequestParam(\"current\") Integer current, @RequestParam(\"size\") Integer size);\n}\n```\n\n---\n\n## 十二、Feign Client 实现（Cloud）\n\n放在 Service 模块中（`blade-service/blade-{module}`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Hidden;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.service.I{Name}Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * {中文名} Feign 实现\n *\n * @author {author}\n */\n@Hidden\n@RestController\n@AllArgsConstructor\npublic class {Name}Client implements I{Name}Client {\n\n    private final I{Name}Service service;\n\n    @Override\n    @GetMapping(TOP)\n    public BladePage<{Name}> top(Integer current, Integer size) {\n        Query query = new Query();\n        query.setCurrent(current);\n        query.setSize(size);\n        IPage<{Name}> page = service.page(Condition.getPage(query));\n        return BladePage.of(page);\n    }\n}\n```\n\n**Feign 实现要点**：\n- `@Hidden`：对 Swagger 文档隐藏（内部接口）\n- `@RestController`：作为 HTTP 端点暴露\n- 路径常量复用接口中定义的常量\n\n---\n\n## 附：完整 import 参考\n\n### Entity 常用 import\n```java\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;      // BaseEntity\nimport org.springblade.core.mp.base.TenantEntity;   // TenantEntity\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n```\n\n### Controller 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;      // Boot 路由前缀使用\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springframework.web.bind.annotation.*;\n```\n\n### Service 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springframework.stereotype.Service;\n```\n\n### Wrapper 常用 import\n```java\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.constant.BladeConstant;\nimport org.springblade.core.tool.node.ForestNodeMerger;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.system.cache.DictCache;        // 字典缓存（Boot 路径）\n```\n","isInternal":false,"tokens":7090,"sizeBytes":28729},{"name":"database.md","path":".claude/skills/blade-design/references/database.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-design/references/database.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# 数据库建表语句生成参考\n\n本文档包含 SpringBlade 开源版 MySQL 建表模板与菜单数据 SQL。所有模板均来源于开源工程 `doc/sql/` 下的真实 SQL 脚本。\n\n> SpringBlade 开源版仅官方支持 **MySQL**，生成的建表脚本固定为 MySQL 方言。\n\n## 目录\n- [一、通用审计字段](#一通用审计字段)\n- [二、字段类型映射表](#二字段类型映射表)\n- [三、MySQL 建表模板](#三mysql-建表模板)\n- [四、菜单 SQL 生成](#四菜单-sql-生成)\n\n---\n\n## 一、通用审计字段\n\n所有继承 `TenantEntity` 或 `BaseEntity` 的业务表都包含以下固定审计字段：\n\n| 字段名 | 说明 | Java 类型 | 是否可空 | 默认值 |\n|--------|------|-----------|----------|--------|\n| `id` | 主键（雪花 ID） | Long | NOT NULL | 无 |\n| `tenant_id` | 租户 ID（仅 TenantEntity） | String | NULL | '000000' |\n| `create_user` | 创建人 | Long | NULL | NULL |\n| `create_dept` | 创建部门 | Long | NULL | NULL |\n| `create_time` | 创建时间 | Date | NULL | NULL |\n| `update_user` | 修改人 | Long | NULL | NULL |\n| `update_time` | 修改时间 | Date | NULL | NULL |\n| `status` | 业务状态 | Integer | NULL | 1 |\n| `is_deleted` | 逻辑删除标记 | Integer | NULL | 0 |\n\n- **BaseEntity 模式**不包含 `tenant_id` 字段\n- **Raw Serializable 模式**手动定义全部字段（结构相同）\n\n---\n\n## 二、字段类型映射表\n\n### Java 类型 → MySQL 类型映射\n\n| Java 类型 | MySQL 类型 | 说明 |\n|-----------|-----------|------|\n| `String` (短) | `varchar(n)` | `n` 为字符数，通常 `varchar(255)` 以内 |\n| `String` (长) | `text` | 超过 255 字符或富文本 |\n| `Integer` | `int` | 32 位整型 |\n| `Long` | `bigint` | 64 位整型（雪花 ID 必须用） |\n| `Double` | `double` | 双精度浮点 |\n| `BigDecimal` | `decimal(m,n)` | 金额、精确数值 |\n| `Date` | `datetime` | 日期 + 时间 |\n| `Boolean` | `tinyint(1)` | 0 = false，1 = true |\n\n### 审计字段各字段定义（MySQL）\n\n| 字段 | MySQL 定义 |\n|------|-----------|\n| id | `bigint NOT NULL` |\n| tenant_id | `varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000'` |\n| create_user | `bigint NULL DEFAULT NULL` |\n| create_dept | `bigint NULL DEFAULT NULL` |\n| create_time | `datetime NULL DEFAULT NULL` |\n| update_user | `bigint NULL DEFAULT NULL` |\n| update_time | `datetime NULL DEFAULT NULL` |\n| status | `int NULL DEFAULT 1` |\n| is_deleted | `int NULL DEFAULT 0` |\n\n---\n\n## 三、MySQL 建表模板\n\n### TenantEntity 模式（多租户业务表）\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for blade_{table_name}\n-- ----------------------------\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  `tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### BaseEntity 模式（跨租户业务表）\n\n与 TenantEntity 模板一致，**仅删除 `tenant_id` 这一行**：\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### Raw Serializable 模式（系统 / 关系表）\n\n结构同 BaseEntity 模板（同样无 `tenant_id`）。差异在于 Java 侧手动定义全部字段，数据库层面建表语句本身不变。\n\n### 索引建议\n\n对于查询频繁的字段：\n\n```sql\n-- 普通索引\nCREATE INDEX idx_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 唯一索引\nCREATE UNIQUE INDEX uk_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 复合索引（多字段）\nCREATE INDEX idx_{table_name}_{col1}_{col2} ON blade_{table_name} ({col1}, {col2});\n```\n\n**命名约定**：\n- 唯一索引前缀 `uk_`\n- 普通索引前缀 `idx_`\n- 字段名用下划线拼接\n\n### MySQL 约定\n\n- 列名使用反引号 `` ` `` 包裹\n- 注释直接用 `COMMENT` 关键字写在列定义后\n- 表注释在 `CREATE TABLE` 末尾\n- 字符集固定 `utf8mb4`，排序规则 `utf8mb4_general_ci`\n- 存储引擎 `InnoDB`\n- 主键索引类型 `USING BTREE`\n- 租户 ID 列 `varchar(12)` 默认 `'000000'`\n\n---\n\n## 四、菜单 SQL 生成\n\n每个新模块需要插入 5 条菜单记录：1 个主菜单 + 4 个操作按钮（新增、修改、删除、查看）。\n\n### MySQL 菜单 SQL\n\n```sql\n-- ----------------------------\n-- {中文名}菜单数据\n-- ----------------------------\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({menuId}, {parentMenuId}, '{modelCode}', '{中文名}', 'menu', '/{module}/{modelCode}', NULL, 1, 1, 0, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({addMenuId}, {menuId}, '{modelCode}_add', '新增', 'add', '/{module}/{modelCode}/add', 'plus', 1, 2, 1, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({editMenuId}, {menuId}, '{modelCode}_edit', '修改', 'edit', '/{module}/{modelCode}/edit', 'form', 2, 2, 2, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({removeMenuId}, {menuId}, '{modelCode}_delete', '删除', 'delete', '/{module}/{modelCode}/delete', 'delete', 3, 2, 3, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({viewMenuId}, {menuId}, '{modelCode}_view', '查看', 'view', '/{module}/{modelCode}/view', 'eye-open', 4, 2, 2, 1, NULL, 0);\n```\n\n### 菜单字段说明\n\n| 字段 | 说明 | 值 |\n|------|------|-----|\n| `id` | 主菜单 ID | 生成雪花 ID 或自定义（如 1123598812738675301） |\n| `parent_id` | 父菜单 ID | 由用户指定或放在顶级（0） |\n| `code` | 权限编码 | 主菜单: `{modelCode}`，按钮: `{modelCode}_{action}` |\n| `name` | 显示名称 | 主菜单: `{中文名}`，按钮: 新增/修改/删除/查看 |\n| `alias` | 别名 | 主菜单: `menu`，按钮: add/edit/delete/view |\n| `path` | 路由路径 | `/{module}/{modelCode}` |\n| `source` | 图标 | 按钮: plus/form/delete/eye-open |\n| `sort` | 排序 | 按钮从 1 到 4 |\n| `category` | 类别 | 1=菜单，2=按钮 |\n| `action` | 操作类型 | 0=无，1=新增，2=修改，3=删除 |\n| `is_open` | 是否打开 | 1=是 |\n| `is_deleted` | 是否删除 | 0=否 |\n\n---\n\n## 附：无租户表模式速查\n\n如果实体不需要 `tenant_id`（BaseEntity 或 Raw 模式），从建表模板中直接移除 `tenant_id` 行即可，其余审计字段保持不变。\n","isInternal":false,"tokens":2713,"sizeBytes":8448},{"name":"frontend.md","path":".claude/skills/blade-design/references/frontend.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-design/references/frontend.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# 前端代码生成参考\n\n本文档包含 SpringBlade 开源版 Saber 前端各层的完整代码模板。基于 Vue 3 + Element Plus + Avue 技术栈，支持 Options API 和 Composition API 两种风格。\n\n> 前端 API 路径在 Boot 单体与 Cloud 微服务下**完全一致**，都走 `/blade-{module}/...`。Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 前缀对齐该路径，Cloud 则由 Gateway 按服务名 `blade-{module}` 自动路由。\n\n## 目录\n- [一、API 层](#一api-层)\n- [二、Option 配置层](#二option-配置层)\n- [三、Vue 页面 - Options API](#三vue-页面---options-api)\n- [四、Vue 页面 - Composition API](#四vue-页面---composition-api)\n- [五、树形结构模式](#五树形结构模式)\n- [六、主子表模式](#六主子表模式)\n\n---\n\n## 一、API 层\n\n文件位置：`src/api/{module}/{modelCode}.js`\n\n### 标准 CRUD API\n\n```javascript\nimport request from '@/axios';\n\n/**\n * 获取{中文名}列表（分页）\n */\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n    },\n  });\n};\n\n/**\n * 获取{中文名}详情\n */\nexport const getDetail = (id) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/detail',\n    method: 'get',\n    params: {\n      id,\n    },\n  });\n};\n\n/**\n * 删除{中文名}\n */\nexport const remove = (ids) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/remove',\n    method: 'post',\n    params: {\n      ids,\n    },\n  });\n};\n\n/**\n * 新增{中文名}\n */\nexport const add = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\n/**\n * 修改{中文名}\n */\nexport const update = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n```\n\n**注意事项**：\n- 新增和修改共用 `/submit` 接口（后端 `saveOrUpdate`）\n- `remove` 的 `ids` 是逗号分隔的字符串\n- GET 请求用 `params`，POST 请求用 `data`（请求体）\n- 如需加密传输，添加 `cryptoToken: true` 和 `cryptoData: true`\n\n### 树形模式追加\n\n```javascript\n/**\n * 获取{中文名}树形结构\n */\nexport const getTree = (tenantId) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/tree',\n    method: 'get',\n    params: {\n      tenantId,\n    },\n  });\n};\n```\n\n### 导出接口\n\n```javascript\nimport { exportBlob } from '@/api/common';\n\n/**\n * 导出{中文名}数据\n */\nexport const exportData = (params) => {\n  return exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}`, params);\n};\n```\n\n---\n\n## 二、Option 配置层\n\n文件位置：`src/option/{module}/{modelCode}.js`\n\n### 标准 CRUD 配置\n\n```javascript\nexport const tableOption = {\n  height: 'auto',\n  calcHeight: 30,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  viewBtn: true,\n  dialogClickModal: false,\n  column: [\n    // --- 文本输入框 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      search: true,                    // 是否为搜索字段\n      rules: [\n        {\n          required: true,\n          message: '请输入{字段中文名}',\n          trigger: 'blur',\n        },\n      ],\n    },\n\n    // --- 数字输入 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'number',\n      align: 'right',\n      width: 100,\n    },\n\n    // --- 下拉选择（远程字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      search: true,\n      dicUrl: '/blade-system/dict/dictionary?code={dictCode}',\n      props: {\n        label: 'dictValue',\n        value: 'dictKey',\n      },\n      rules: [\n        {\n          required: true,\n          message: '请选择{字段中文名}',\n          trigger: 'change',\n        },\n      ],\n    },\n\n    // --- 下拉选择（本地字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 开关 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'switch',\n      align: 'center',\n      width: 100,\n      dicData: [\n        { label: '否', value: 0 },\n        { label: '是', value: 1 },\n      ],\n      value: 0,\n    },\n\n    // --- 单选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'radio',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n      value: 1,\n    },\n\n    // --- 多选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'checkbox',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 日期选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'date',\n      format: 'YYYY-MM-DD',\n      valueFormat: 'YYYY-MM-DD',\n    },\n\n    // --- 日期时间选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetime',\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 日期范围搜索 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetimerange',\n      searchRange: true,\n      search: true,\n      hide: true,                      // 列表不显示\n      display: false,                  // 表单不显示\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 多行文本 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'textarea',\n      span: 24,                        // 占满整行\n      hide: true,                      // 列表不显示\n      minRows: 3,\n    },\n\n    // --- 富文本编辑器 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      component: 'avue-ueditor',\n      options: {\n        action: '/blade-resource/oss/endpoint/put-file',\n        props: {\n          res: 'data',\n          url: 'link',\n        },\n      },\n      span: 24,\n      hide: true,\n      minRows: 6,\n    },\n\n    // --- 仅列表显示，表单不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      addDisplay: false,\n      editDisplay: false,\n    },\n\n    // --- 仅表单显示，列表不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      hide: true,\n    },\n\n    // --- 自定义插槽 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      slot: true,                      // 启用自定义渲染插槽\n    },\n\n    // --- 备注（隐藏在列表） ---\n    {\n      label: '备注',\n      prop: 'remark',\n      hide: true,\n    },\n  ],\n};\n```\n\n### 分组表单配置\n\n用于复杂表单（如用户管理），将字段分组到多个 Tab 页：\n\n```javascript\nexport const tableOption = (safe) => {\n  // 自定义校验器（使用闭包访问组件实例）\n  const validatePass = (rule, value, callback) => {\n    if (value === '') {\n      callback(new Error('请输入密码'));\n    } else {\n      callback();\n    }\n  };\n\n  return {\n    height: 'auto',\n    calcHeight: 150,\n    tip: false,\n    searchShow: true,\n    searchMenuSpan: 6,\n    border: true,\n    index: true,\n    selection: true,\n    viewBtn: true,\n    dialogType: 'drawer',\n    dialogWidth: 1000,\n    dialogClickModal: false,\n\n    // 列表使用 column\n    column: [\n      { label: '字段A', prop: 'fieldA', search: true },\n      { label: '字段B', prop: 'fieldB' },\n    ],\n\n    // 表单使用 group（分组标签页）\n    group: [\n      {\n        label: '基础信息',\n        prop: 'baseInfo',\n        icon: 'el-icon-user-solid',\n        column: [\n          { label: '字段A', prop: 'fieldA', rules: [{ required: true, message: '请输入', trigger: 'blur' }] },\n          { label: '字段B', prop: 'fieldB', type: 'select', dicData: [] },\n        ],\n      },\n      {\n        label: '详细信息',\n        prop: 'detailInfo',\n        icon: 'el-icon-s-order',\n        column: [\n          { label: '字段C', prop: 'fieldC' },\n          { label: '字段D', prop: 'fieldD', type: 'textarea', span: 24 },\n        ],\n      },\n    ],\n  };\n};\n```\n\n### 列属性速查\n\n| 属性 | 类型 | 说明 |\n|------|------|------|\n| `label` | String | 列标题/表单标签 |\n| `prop` | String | 字段名 |\n| `type` | String | 组件类型：input/select/radio/checkbox/switch/date/datetime/textarea/number/tree/upload |\n| `search` | Boolean | 是否为搜索字段 |\n| `hide` | Boolean | 列表中隐藏 |\n| `display` | Boolean | 表单中隐藏（false=不显示） |\n| `addDisplay` | Boolean | 新增表单中隐藏 |\n| `editDisplay` | Boolean | 编辑表单中隐藏 |\n| `addDisabled` | Boolean | 新增时禁用 |\n| `editDisabled` | Boolean | 编辑时禁用 |\n| `span` | Number | 表单栅格宽度（24=整行） |\n| `slot` | Boolean | 启用自定义插槽 |\n| `rules` | Array | 校验规则 |\n| `dicUrl` | String | 远程字典 URL |\n| `dicData` | Array | 本地字典数据 |\n| `props` | Object | 字典映射 { label, value } |\n| `value` | Any | 默认值 |\n| `width` | Number | 列宽度 |\n| `align` | String | 对齐方式：left/center/right |\n| `overHidden` | Boolean | 超长文本省略显示 |\n| `component` | String | 自定义组件名 |\n| `searchRange` | Boolean | 搜索使用范围选择 |\n| `format` | String | 显示格式 |\n| `valueFormat` | String | 值格式 |\n| `minRows` | Number | textarea 最小行数 |\n\n---\n\n## 三、Vue 页面 - Options API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"$refs.crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n\n      <!-- 自定义列插槽示例 -->\n      <template #{slotField}=\"{ row }\">\n        <el-tag>{{ row.{slotField} }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\nimport { mapGetters } from 'vuex';\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      option: tableOption,\n      data: [],\n    };\n  },\n\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: this.validData(this.permission.{modelCode}_add, false),\n        viewBtn: this.validData(this.permission.{modelCode}_view, false),\n        delBtn: this.validData(this.permission.{modelCode}_delete, false),\n        editBtn: this.validData(this.permission.{modelCode}_edit, false),\n      };\n    },\n    ids() {\n      let ids = [];\n      this.selectionList.forEach(ele => {\n        ids.push(ele.id);\n      });\n      return ids.join(',');\n    },\n  },\n\n  methods: {\n    rowSave(row, done, loading) {\n      add(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdate(row, index, done, loading) {\n      update(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(row.id);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n      });\n    },\n\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据');\n        return;\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(this.ids);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n        this.$refs.crud.toggleSelection();\n      });\n    },\n\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data;\n        });\n      }\n      done();\n    },\n\n    searchChange(params, done) {\n      this.query = params;\n      this.page.currentPage = 1;\n      this.onLoad(this.page, params);\n      done();\n    },\n\n    searchReset() {\n      this.query = {};\n      this.onLoad(this.page);\n    },\n\n    selectionChange(list) {\n      this.selectionList = list;\n    },\n\n    selectionClear() {\n      this.selectionList = [];\n      this.$refs.crud.toggleSelection();\n    },\n\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage;\n    },\n\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize;\n    },\n\n    refreshChange() {\n      this.onLoad(this.page, this.query);\n    },\n\n    onLoad(page, params = {}) {\n      this.loading = true;\n      getList(\n        page.currentPage,\n        page.pageSize,\n        Object.assign(params, this.query)\n      ).then(res => {\n        const data = res.data.data;\n        this.page.total = data.total;\n        this.data = data.records;\n        this.loading = false;\n        this.selectionClear();\n      });\n    },\n  },\n};\n</script>\n```\n\n---\n\n## 四、Vue 页面 - Composition API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed } from 'vue';\nimport { useStore } from 'vuex';\nimport { ElMessage, ElMessageBox } from 'element-plus';\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\n\nconst store = useStore();\nconst crud = ref(null);\n\n// 数据\nconst form = ref({});\nconst query = ref({});\nconst loading = ref(true);\nconst data = ref([]);\nconst selectionList = ref([]);\nconst option = reactive(tableOption);\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n});\n\n// 权限\nconst permission = computed(() => store.getters.permission);\nconst permissionList = computed(() => {\n  const validData = (value, defaultVal = false) => (value ? true : defaultVal);\n  return {\n    addBtn: validData(permission.value.{modelCode}_add),\n    viewBtn: validData(permission.value.{modelCode}_view),\n    delBtn: validData(permission.value.{modelCode}_delete),\n    editBtn: validData(permission.value.{modelCode}_edit),\n  };\n});\n\nconst ids = computed(() => {\n  return selectionList.value.map(ele => ele.id).join(',');\n});\n\n// 方法\nconst rowSave = (row, done, loading) => {\n  add(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(row.id);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n  });\n};\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据');\n    return;\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(ids.value);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n    crud.value.toggleSelection();\n  });\n};\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data;\n    });\n  }\n  done();\n};\n\nconst searchChange = (params, done) => {\n  query.value = params;\n  page.value.currentPage = 1;\n  onLoad(page.value, params);\n  done();\n};\n\nconst searchReset = () => {\n  query.value = {};\n  onLoad(page.value);\n};\n\nconst selectionChange = (list) => {\n  selectionList.value = list;\n};\n\nconst currentChange = (currentPage) => {\n  page.value.currentPage = currentPage;\n};\n\nconst sizeChange = (pageSize) => {\n  page.value.pageSize = pageSize;\n};\n\nconst refreshChange = () => {\n  onLoad(page.value, query.value);\n};\n\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(\n    page.currentPage,\n    page.pageSize,\n    Object.assign(params, query.value)\n  ).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n    selectionList.value = [];\n  });\n};\n</script>\n```\n\n### Options API vs Composition API 转换对照\n\n| Options API | Composition API |\n|-------------|-----------------|\n| `data() { return { form: {} } }` | `const form = ref({})` |\n| `computed: { ids() { ... } }` | `const ids = computed(() => { ... })` |\n| `methods: { rowSave() { ... } }` | `const rowSave = () => { ... }` |\n| `this.form` | `form.value`（模板中直接用 `form`） |\n| `this.$refs.crud` | `crud.value` |\n| `this.$message.success(...)` | `ElMessage.success(...)` |\n| `this.$confirm(...)` | `ElMessageBox.confirm(...)` |\n| `...mapGetters(['permission'])` | `const permission = computed(() => store.getters.permission)` |\n| `mounted() { ... }` | `onMounted(() => { ... })` |\n| `watch: { ... }` | `watch(source, callback)` |\n\n---\n\n## 五、树形结构模式\n\n### Option 配置（追加树形父节点字段）\n\n```javascript\n// 在 column 中添加树形父节点字段\n{\n  label: '上级{中文名}',\n  prop: 'parentId',\n  type: 'tree',\n  dicData: [],                    // 动态加载\n  hide: true,\n  value: 0,                       // 默认值为顶级\n  props: {\n    label: 'title',\n    value: 'value',\n    children: 'children',\n  },\n},\n```\n\n### Vue 页面（树形数据加载）\n\n在 `onLoad` 之后加载树形数据并更新列配置：\n\n```javascript\n// Options API\nmethods: {\n  onLoad(page, params = {}) {\n    this.loading = true;\n    getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n      const data = res.data.data;\n      this.page.total = data.total;\n      this.data = data.records;\n      this.loading = false;\n\n      // 加载树形数据\n      getTree().then(treeRes => {\n        const column = this.findObject(this.option.column, 'parentId');\n        column.dicData = treeRes.data.data;\n      });\n    });\n  },\n},\n\n// Composition API\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(page.currentPage, page.pageSize, Object.assign(params, query.value)).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n\n    getTree().then(treeRes => {\n      const column = findObject(option.column, 'parentId');\n      column.dicData = treeRes.data.data;\n    });\n  });\n};\n```\n\n### 表格树形展示\n\n在 avue-crud 模板中使用树形属性：\n\n```vue\n<avue-crud\n  :option=\"option\"\n  :data=\"data\"\n  row-key=\"id\"\n  :tree-props=\"{ children: 'children', hasChildren: 'hasChildren' }\"\n>\n```\n\n---\n\n## 六、主子表模式\n\n### API 层（主表 + 子表）\n\n```javascript\n// 主表 API（同标准 CRUD）\nexport const getList = (current, size, params) => { ... };\nexport const add = (row) => { ... };\nexport const update = (row) => { ... };\nexport const remove = (ids) => { ... };\n\n// 子表 API\nexport const getSubList = (current, size, {fkFieldName}, params) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n      {fkFieldName},\n    },\n  });\n};\n\nexport const addSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const updateSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const removeSub = (ids) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/remove',\n    method: 'post',\n    params: { ids },\n  });\n};\n```\n\n### Vue 页面（主子表 Drawer 模式）\n\n```vue\n<template>\n  <basic-container>\n    <!-- 主表 -->\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu=\"{ row }\">\n        <el-button text type=\"primary\" @click=\"openSub(row)\">子表</el-button>\n      </template>\n    </avue-crud>\n\n    <!-- 子表抽屉 -->\n    <el-drawer\n      title=\"{子表中文名}\"\n      v-model=\"subBox\"\n      size=\"60%\"\n      append-to-body\n    >\n      <avue-crud\n        :option=\"subOption\"\n        :table-loading=\"subLoading\"\n        :data=\"subData\"\n        :page=\"subPage\"\n        ref=\"crudSub\"\n        v-model=\"subForm\"\n        @row-del=\"rowDelSub\"\n        @row-update=\"rowUpdateSub\"\n        @row-save=\"rowSaveSub\"\n        @on-load=\"onLoadSub\"\n      />\n    </el-drawer>\n  </basic-container>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      // 主表数据\n      form: {},\n      data: [],\n      loading: true,\n      page: { pageSize: 10, currentPage: 1, total: 0 },\n      option: tableOption,\n\n      // 子表数据\n      subBox: false,\n      subForm: {},\n      subData: [],\n      subLoading: true,\n      subPage: { pageSize: 10, currentPage: 1, total: 0 },\n      subOption: subTableOption,\n      mainId: '',\n    };\n  },\n\n  methods: {\n    openSub(row) {\n      this.mainId = row.id;\n      this.subBox = true;\n      this.onLoadSub(this.subPage);\n    },\n\n    rowSaveSub(row, done, loading) {\n      row.{fkFieldName} = this.mainId;\n      addSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdateSub(row, index, done, loading) {\n      updateSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDelSub(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return removeSub(row.id);\n      }).then(() => {\n        this.onLoadSub(this.subPage);\n        this.$message.success('操作成功!');\n      });\n    },\n\n    onLoadSub(page, params = {}) {\n      this.subLoading = true;\n      getSubList(\n        page.currentPage,\n        page.pageSize,\n        this.mainId,\n        params\n      ).then(res => {\n        const data = res.data.data;\n        this.subPage.total = data.total;\n        this.subData = data.records;\n        this.subLoading = false;\n      });\n    },\n\n    // ... 主表方法同标准 CRUD\n  },\n};\n</script>\n```\n\n---\n\n## 附：常用工具函数\n\n### 数组与逗号字符串互转\n\n后端用逗号字符串存储多选值（如 deptId=\"1,2,3\"），前端需要转换：\n\n```javascript\nimport func from '@/utils/func';\n\n// 提交时：数组 → 逗号字符串\nrow.deptId = func.join(row.deptId);\n\n// 回显时：逗号字符串 → 数组\nthis.form.deptId = func.split(this.form.deptId);\n```\n\n### 导出功能\n\n```javascript\nimport NProgress from 'nprogress';\nimport { exportBlob } from '@/api/common';\nimport { downloadXls } from '@/utils/util';\n\nconst handleExport = () => {\n  NProgress.start();\n  exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}?${qs.stringify(this.query)}`).then(res => {\n    downloadXls(res.data, `{中文名}数据${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);\n    NProgress.done();\n  });\n};\n```\n\n### 辅助方法 findObject / findColumn\n\n```javascript\n// 在 option 的 column 中查找指定 prop 的列\nconst column = this.findObject(this.option.column, 'fieldName');\n\n// 在 group 中查找\nconst column = this.findColumn(this.option.group, 'fieldName');\n```\n\n这两个是全局挂载的工具方法（在 `main.js` 中注册）。\n","isInternal":false,"tokens":7246,"sizeBytes":27039},{"name":"SKILL.md","path":".claude/skills/blade-design/SKILL.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-design/SKILL.md","title":"blade-design","category":"anthropic-skill","format":"markdown","content":"---\nname: blade-design\ndescription: SpringBlade 全栈代码生成器。根据用户提供的模块名、实体名和字段列表，自动生成符合 SpringBlade 开源版框架规范的后端代码（Boot 单体/Cloud 微服务）、前端页面（Saber Options API/Composition API）和 MySQL 建表语句。当用户说\"帮我生成代码\"、\"创建一个新模块\"、\"生成 CRUD\"、\"新建实体\"、\"建表语句\"、\"生成前端页面\"、\"生成后端接口\"、\"生成 SpringBlade 代码\"、\"写一个 xxx 管理功能\"、\"帮我设计 xxx 表\"、\"blade-design\"等时触发。即使用户只是描述了业务字段或给出了表结构，也应触发此技能来生成对应的 SpringBlade 规范代码。\n---\n\n# SpringBlade 全栈代码生成器 (blade-design)\n\n根据用户需求生成符合 SpringBlade 开源版框架规范的完整 CRUD 代码，覆盖后端、前端、数据库三层。生成的代码与 SpringBlade 现有模块（如 Notice、Dict、Post、User）风格完全一致。\n\n## 信息收集协议\n\n在生成代码前，必须确认以下信息。如用户未提供，主动询问：\n\n### 必需信息\n| 参数 | 说明 | 示例 |\n|------|------|------|\n| **模块名** | 业务模块标识（英文小写） | `desk`、`system`、`order` |\n| **实体名** | 实体类名（PascalCase） | `Notice`、`Order`、`Product` |\n| **中文名** | 业务描述 | `通知公告`、`订单管理` |\n| **字段列表** | 业务字段（不含审计字段） | 见下方格式 |\n\n### 可选信息（有默认值）\n| 参数 | 默认值 | 可选值 |\n|------|--------|--------|\n| **架构类型** | `boot` | `boot`（单体）、`cloud`（微服务） |\n| **实体模式** | `tenant` | `tenant`（租户隔离）、`base`（无租户）、`raw`（原生 Serializable） |\n| **前端风格** | `options` | `options`（Options API）、`composition`（Composition API） |\n| **模板类型** | `crud` | `crud`（基础增删改查）、`tree`（树形结构）、`sub`（主子表） |\n| **是否含 Wrapper** | `true` | `true`、`false` |\n| **生成范围** | `all` | `all`、`backend`、`frontend`、`database` |\n| **服务名（Cloud）** | `blade-{模块名}` | 微服务名称 |\n| **作者** | `BladeX` | 代码注释中的作者 |\n\n> SpringBlade 开源版仅官方支持 MySQL，生成的建表脚本固定为 MySQL 方言。\n\n### 字段定义格式\n\n用户可以用表格或简洁文本描述字段，你需要解析为以下结构：\n\n| 属性 | 说明 |\n|------|------|\n| `name` | Java 属性名（camelCase） |\n| `column` | 数据库列名（snake_case） |\n| `type` | Java 类型：`String`、`Integer`、`Long`、`Date`、`BigDecimal`、`Double` |\n| `jdbcType` | 数据库类型：`varchar(n)`、`int`、`bigint`、`datetime`、`text`、`decimal(m,n)` |\n| `comment` | 中文字段说明 |\n| `required` | 是否必填（默认 false） |\n| `query` | 是否为查询字段（默认 false） |\n| `queryType` | 查询方式：`EQ`、`LIKE`、`BETWEEN`（默认 EQ） |\n| `list` | 是否在列表显示（默认 true） |\n| `form` | 是否在表单显示（默认 true） |\n| `component` | 前端组件：`input`、`textarea`、`select`、`radio`、`checkbox`、`switch`、`date`、`datetime`、`number`、`upload`（默认 input） |\n| `dictCode` | 字典编码（用于 select/radio 类型） |\n\n## Boot vs Cloud 核心架构差异\n\n生成代码时根据架构类型自动调整以下差异点：\n\n### 模块结构\n\n**Boot（单体应用）**：所有代码在同一模块内\n```\nsrc/main/java/org/springblade/modules/{module}/\n├── controller/\n├── service/ + impl/\n├── mapper/\n├── wrapper/\n├── entity/ + dto/ + vo/\n└── excel/\n```\n\n**Cloud（微服务）**：API 与实现分离为两个 Maven 模块\n```\nblade-service-api/blade-{module}-api/          ← 契约层\n└── org/springblade/{module}/\n    ├── entity/ + dto/ + vo/\n    └── feign/I{Name}Client.java               ← Feign 接口\n\nblade-service/blade-{module}/                  ← 实现层\n└── org/springblade/{module}/\n    ├── controller/\n    ├── service/ + impl/\n    ├── mapper/\n    ├── wrapper/\n    └── feign/{Name}Client.java                ← Feign 实现\n```\n\n### 关键差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| **基础包名** | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| **Controller 路由** | `@RequestMapping(AppConstant.APPLICATION_XXX_NAME + \"/{path}\")` | `@RequestMapping(\"/{path}\")` |\n| **服务间调用** | 直接注入 Service | Feign Client 远程调用 |\n| **Entity/VO 位置** | 与 Service 同模块 | 独立 API 模块中 |\n| **Feign Client** | 无 | API 模块：接口定义；Service 模块：实现类 |\n| **POM 依赖** | 单一 pom.xml | API 和 Service 各自的 pom.xml |\n| **服务注册** | 不需要 | Nacos 服务注册与发现 |\n| **Gateway 路由** | 不需要 | Gateway 自动按服务名路由 |\n\n### Boot 路由规则\nBoot 模式下 Controller 需要手动添加服务名前缀，因为没有 Gateway 路由：\n```java\n// Boot: 完整路径 /desk/notice/detail\n@RequestMapping(AppConstant.APPLICATION_DESK_NAME + \"/notice\")\n\n// Cloud: Gateway 自动添加 /blade-desk 前缀，只需 /notice/detail\n@RequestMapping(\"/notice\")\n```\n\n## 实体继承模式\n\n根据业务场景选择合适的实体基类：\n\n### 模式一：TenantEntity（默认，租户隔离业务表）\n- 继承 `TenantEntity`（`org.springblade.core.mp.base.TenantEntity`），自动拥有 `tenantId` + 全套审计字段\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 删除使用 `deleteLogic()`（逻辑删除）\n- 适用：User、Post 等绝大多数多租户业务表\n\n### 模式二：BaseEntity（无租户业务表）\n- 继承 `BaseEntity`（`org.springblade.core.mp.base.BaseEntity`），拥有审计字段但无 `tenantId`\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 适用：Tenant、Notice 等跨租户业务表\n- 也可通过在 `application.yml`（Boot）或 Nacos `blade.yaml`（Cloud）的 `blade.tenant.tables` 中配置表名实现字段隔离，不强制继承 TenantEntity\n\n### 模式三：Raw Serializable（原生模式）\n- 实现 `Serializable`，所有字段手动定义\n- Service 继承 `IService<Entity>` / `ServiceImpl<Mapper, Entity>`\n- 不继承 SpringBlade 基类，使用 MyBatis-Plus 原生 CRUD\n- 删除使用 `removeBatchByIds()`\n- 适用：Menu、Role、Dept、Dict、RoleMenu 等跨租户系统/关系表\n\n## 代码生成工作流\n\n### Step 1: 收集信息\n确认所有必需参数和可选参数，构建完整的生成上下文。\n\n### Step 2: 生成代码\n根据用户选择的生成范围，读取对应的参考文件并生成代码：\n\n| 生成范围 | 参考文件 | 生成产物 |\n|----------|----------|----------|\n| **后端** | 读取 `references/backend.md` | Entity、DTO、VO、Mapper、Mapper.xml、Service、ServiceImpl、Controller、Wrapper、Excel、Feign（Cloud） |\n| **前端** | 读取 `references/frontend.md` | API 模块、Option 配置、Vue 页面 |\n| **数据库** | 读取 `references/database.md` | MySQL 建表 SQL、菜单 SQL |\n\n### Step 3: 输出代码\n按文件维度逐个输出，每个文件包含：\n1. 完整文件路径（相对于项目根目录）\n2. 完整可运行的代码内容\n3. 必要的说明注释\n\n## 输出文件清单\n\n### 后端产物（Boot）\n```\nsrc/main/java/org/springblade/modules/{module}/entity/{Name}.java\nsrc/main/java/org/springblade/modules/{module}/dto/{Name}DTO.java\nsrc/main/java/org/springblade/modules/{module}/vo/{Name}VO.java\nsrc/main/java/org/springblade/modules/{module}/excel/{Name}Excel.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.xml\nsrc/main/java/org/springblade/modules/{module}/service/I{Name}Service.java\nsrc/main/java/org/springblade/modules/{module}/service/impl/{Name}ServiceImpl.java\nsrc/main/java/org/springblade/modules/{module}/controller/{Name}Controller.java\nsrc/main/java/org/springblade/modules/{module}/wrapper/{Name}Wrapper.java\n```\n\n### 后端产物（Cloud）\n```\nblade-service-api/blade-{module}-api/.../entity/{Name}.java\nblade-service-api/blade-{module}-api/.../dto/{Name}DTO.java\nblade-service-api/blade-{module}-api/.../vo/{Name}VO.java\nblade-service-api/blade-{module}-api/.../feign/I{Name}Client.java\nblade-service/blade-{module}/.../excel/{Name}Excel.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.xml\nblade-service/blade-{module}/.../service/I{Name}Service.java\nblade-service/blade-{module}/.../service/impl/{Name}ServiceImpl.java\nblade-service/blade-{module}/.../controller/{Name}Controller.java\nblade-service/blade-{module}/.../wrapper/{Name}Wrapper.java\nblade-service/blade-{module}/.../feign/{Name}Client.java\n```\n\n### 前端产物\n```\nsrc/api/{module}/{modelCode}.js\nsrc/option/{module}/{modelCode}.js\nsrc/views/{module}/{modelCode}.vue\n```\n\n### 数据库产物\n```\n{name}.mysql.sql              -- MySQL 建表语句\n{name}.menu.sql               -- 菜单数据 SQL\n```\n\n## 命名约定\n\n| 维度 | 规则 | 示例（实体名：OrderItem） |\n|------|------|--------------------------|\n| 实体类 | `{Name}` | `OrderItem` |\n| VO 类 | `{Name}VO` | `OrderItemVO` |\n| DTO 类 | `{Name}DTO` | `OrderItemDTO` |\n| Excel 类 | `{Name}Excel` | `OrderItemExcel` |\n| Mapper 接口 | `{Name}Mapper` | `OrderItemMapper` |\n| Mapper XML | `{Name}Mapper.xml` | `OrderItemMapper.xml` |\n| Service 接口 | `I{Name}Service` | `IOrderItemService` |\n| Service 实现 | `{Name}ServiceImpl` | `OrderItemServiceImpl` |\n| Controller | `{Name}Controller` | `OrderItemController` |\n| Wrapper | `{Name}Wrapper` | `OrderItemWrapper` |\n| Feign 接口 | `I{Name}Client` | `IOrderItemClient` |\n| Feign 实现 | `{Name}Client` | `OrderItemClient` |\n| 数据库表名 | `blade_{snake_case}` | `blade_order_item` |\n| API 路径 | `/{modelCode}` | `/order-item` |\n| 前端文件 | `{modelCode}` | `orderItem` |\n\n> SpringBlade 开源版 Entity 统一**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。请严格遵循此命名。\n\n## 重要规范\n\n1. **所有 Long 类型的 ID 字段**必须添加 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n2. **日期字段**必须同时添加 `@DateTimeFormat` 和 `@JsonFormat`，pattern 统一使用 `DateUtil.PATTERN_DATETIME`\n3. **逻辑删除字段** `isDeleted` 必须添加 `@TableLogic` 注解（继承 BaseEntity / TenantEntity 时由基类自动处理）\n4. **Swagger 文档**使用 OpenAPI 3 注解：`@Tag`、`@Operation`、`@Schema`、`@Parameter`\n5. **接口排序**：控制器类级标注无参 `@ApiOrder`（置于 `@Tag` 上方），分组内接口按源码声明顺序展示；个别接口需调整顺序时才在方法上标 `@ApiOrder(n)`\n6. **响应包装**统一使用 `R<T>` 类\n7. **Wrapper 的 entityVO() 方法**中通过 `DictCache.getValue()` 翻译字典字段\n8. **Controller 的 remove 方法**：TenantEntity / BaseEntity 用 `deleteLogic()`，Raw 模式用 `removeBatchByIds()`\n9. **权限控制**：使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 做角色级控制（SpringBlade 开源版仅支持角色权限）\n10. **前端权限标识**格式：`{modelCode}_{action}`，如 `notice_add`、`notice_edit`、`notice_delete`、`notice_view`\n\n## 参考文件索引\n\n| 生成任务 | 参考文件 | 说明 |\n|----------|----------|------|\n| 后端代码 | `references/backend.md` | 完整的后端各层代码模板，含 Boot/Cloud 变体 |\n| 前端代码 | `references/frontend.md` | API、Option 配置、Vue 页面模板（Options/Composition API） |\n| 数据库 SQL | `references/database.md` | MySQL 建表模板、字段类型映射与菜单 SQL |\n","frontmatter":{"name":"blade-design","description":"SpringBlade 全栈代码生成器。根据用户提供的模块名、实体名和字段列表，自动生成符合 SpringBlade 开源版框架规范的后端代码（Boot 单体/Cloud 微服务）、前端页面（Saber Options API/Composition API）和 MySQL 建表语句。当用户说\"帮我生成代码\"、\"创建一个新模块\"、\"生成 CRUD\"、\"新建实体\"、\"建表语句\"、\"生成前端页面\"、\"生成后端接口\"、\"生成 SpringBlade 代码\"、\"写一个 xxx 管理功能\"、\"帮我设计 xxx 表\"、\"blade-design\"等时触发。即使用户只是描述了业务字段或给出了表结构，也应触发此技能来生成对应的 SpringBlade 规范代码。"},"isInternal":false,"tokens":3700,"sizeBytes":11871},{"name":"README.md","path":".claude/skills/blade-plan/README.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-plan/README.md","title":"Blade-plan Skill","category":"anthropic-skill","format":"markdown","content":"# Blade Plan\n\nSpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为结构化任务，逐个执行，全程无辅助文件。\n\n## 核心理念\n\n**先想清楚，再动手写。** 面对涉及多个文件的改动，不急着写代码，而是先理清思路、拆好任务、确认方案，再逐个执行。用最小的流程开销，获得结构化开发的核心收益。\n\n```\n需求 → 分析规划 → 确认 → 逐任务执行 → 完成总结\n```\n\n## 快速开始\n\n```bash\n# 启动规划执行 — 展示方案后等待确认\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n\n# 自动模式 — 展示方案后直接执行，不等待确认\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n```\n\n## 工作流程\n\n**交互模式（默认）：** 展示规划方案后暂停等待确认，审批通过后开始逐任务执行。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析与规划                                              │\n  │  理解需求 → 扫描代码 → 制定方案 → 拆解任务                 │\n  │       ↓ 用户确认                                            │\n  │                                                             │\n  │  🔨 逐任务执行（TaskCreate/TaskUpdate 追踪进度）            │\n  │  Task 1 ✅ → Task 2 ✅ → Task 3 🔄 → Task 4 ⏳ → ...      │\n  │       ↓                                                     │\n  │  ✅ 完成总结                                                │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n**自动模式（--auto）：** 展示规划后直接开始执行，不等待确认。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析规划  →  🔨 逐任务执行  →  ✅ 完成总结             │\n  │  （展示方案后直接执行，不暂停）                             │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n## 适用场景\n\n| 场景 | 适合 | 不适合 |\n|------|------|--------|\n| 新增中等功能模块（如限流注解、审计日志） | ✅ | |\n| 跨多个文件的 Bug 修复 | ✅ | |\n| 模块级重构（如拆分策略类） | ✅ | |\n| 新增 Starter 模块 | ✅ | |\n| 现有功能增强（添加新维度、新策略） | ✅ | |\n| 单文件简单修改 | | ❌ 直接对话即可 |\n| 快速问答或查询 | | ❌ 直接对话即可 |\n| 从零开发完整业务系统 | | ❌ 建议拆成多次会话分阶段推进 |\n| 需要先独立设计数据模型或跨服务契约 | | ❌ 先单独设计再进入 Plan |\n\n## 复杂度分层\n\n| 复杂度 | 文件变更量 | 建议方式 |\n|--------|-----------|---------|\n| 低 | 1~5 个文件 | 直接对话 |\n| **中** | **5~15 个文件** | **Blade Plan** |\n| 高 | 15+ 个文件 | 拆成多次会话，每次走一次 `/blade-plan` |\n\n## 特性\n\n- **零文件开销**：不生成任何辅助文件，规划在对话中展示，进度由内置任务系统追踪\n- **上下文锚定**：每个任务执行前重读相关代码，防止偏离\n- **构建验证**：每组任务完成后自动执行编译检查\n- **技能协同**：自动识别可借助 `/blade-design`、`/avue-design` 加速的任务\n- **智能降级**：过于简单时直接执行，复杂度过高时建议拆成多次会话\n- **SpringBlade 体系适配**：内置 Boot / Cloud 分层最佳实践，同时可适配其他技术栈\n\n## 文件结构\n\n```\nblade-plan/             # Skill 定义\n├── README.md           # 本文件\n└── SKILL.md            # Skill 主指令（全部内容，无 references 目录）\n```\n\n运行时不产生任何文件，所有状态由 Claude Code 内置任务系统管理。\n","isInternal":false,"tokens":1280,"sizeBytes":4642},{"name":"SKILL.md","path":".claude/skills/blade-plan/SKILL.md","rawUrl":"https://raw.githubusercontent.com/chillzhuang/SpringBlade/HEAD/.claude/skills/blade-plan/SKILL.md","title":"blade-plan","category":"anthropic-skill","format":"markdown","content":"---\nname: blade-plan\ndescription: >\n  SpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为「分析规划 → 逐任务执行 → 完成总结」\n  三步流程，用 Claude Code 内置任务系统追踪进度，全程无辅助文件。适用于新增中等功能模块、修复跨模块 Bug、\n  中等规模重构、添加新 Starter 等涉及 5~15 个文件变更的开发任务。\n  当用户说\"帮我做一个XX功能\"、\"这个改动涉及几个文件\"、\"帮我重构一下XX\"、\"帮我加个XX\"、\n  \"plan\"、\"规划一下再做\"、\"先想清楚再写\"、\"这个稍微有点复杂\"等时触发。也可直接使用 /blade-plan 调用。\n  不适用于简单单文件修改（直接对话即可），也不适用于需要完整架构设计的复杂系统（建议分阶段拆解为多次会话）。\n  即使用户没有说\"plan\"，只要需求涉及多文件改动或需要一定设计思考，也应主动建议使用本工作流。\n---\n\n# Blade Plan — 轻量规划执行工作流\n\n先想清楚，再动手写。用最小的流程开销，获得结构化开发的核心收益。\n\n## 定位\n\nBlade Plan 面向**中等复杂度**的开发任务，在\"直接对话\"和\"分阶段设计\"之间提供一条中间路径：\n\n| 复杂度 | 方式 | 文件变更量 | 典型场景 |\n|--------|------|-----------|---------|\n| 低 | 直接对话 | 1~5 个文件 | 单个 bug 修复、配置调整、工具方法添加 |\n| **中** | **Blade Plan** | **5~15 个文件** | **新增中等功能、跨模块重构、复杂 bug、新 Starter** |\n| 高 | 分阶段执行 | 15+ 个文件 | 从零开发完整模块、复杂系统设计、大型架构重构 |\n\n判断标准不是绝对的文件数，而是：需求是否清晰到可以直接拆任务？改动是否能在一次会话内完成？如果两个答案都是\"是\"，Blade Plan 就是合适的选择。\n\n## 核心理念\n\n- **先规划后执行**：先把思路理清、任务拆好，再逐个动手——这能避免写到一半发现方向错了\n- **上下文锚定**：每个任务执行前重读相关代码，防止在对话累积中逐渐偏离\n- **最小变更**：只做计划中的事，不夹带\"顺手优化\"\n- **构建验证**：编译不过的代码等于没写\n- **零文件开销**：不生成任何辅助文件（无 spec.json、tasks.json、design.md 等），所有规划直接在对话中呈现，进度由 Claude Code 内置 TaskCreate / TaskUpdate 追踪\n\n## 使用方式\n\n```\n/blade-plan <需求描述>           # 启动规划执行（展示方案后等待确认）\n/blade-plan --auto <需求描述>    # 启动规划执行（自动模式，展示方案后直接执行）\n```\n\n**示例：**\n\n```\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n/blade-plan 修复多租户场景下动态数据源切换时连接池泄漏问题\n```\n\n## 路由逻辑\n\n收到用户输入后：\n\n1. **带 `--auto` 参数**：提取需求描述 → 自动模式（规划后直接执行，不等确认）\n2. **带需求描述**：→ 交互模式（规划后等待用户确认）\n3. **无参数**：提示输入需求描述\n\n---\n\n## 工作流\n\n```\n用户需求 → [分析与规划] → 确认 → [逐任务执行] → 完成总结\n                              ↑\n                         自动模式跳过\n```\n\n三个步骤，一次确认，全程用 Claude Code 内置任务系统追踪进度。\n\n---\n\n### 步骤一：分析与规划\n\n**目标**：理解需求，摸清现状，制定可执行的任务清单。\n\n**执行动作：**\n\n1. **理解需求**：\n   - 解析用户的需求描述，提炼核心功能点\n   - 识别隐含需求和边界条件\n   - 如有歧义，先向用户澄清关键问题再继续\n\n2. **扫描现状**：\n   - 阅读 CLAUDE.md 了解项目规范（如有）\n   - 扫描相关模块现有代码，理解代码结构和风格\n   - 搜索是否有可复用的类、工具方法或已有实现\n   - 评估改动对现有功能的影响范围\n\n3. **制定方案**：\n   - 确定技术实现策略（用什么方式解决问题）\n   - 识别需要新增和修改的文件\n   - 将改动拆解为有序的任务列表（按依赖关系排序）\n   - 任务粒度：一个任务对应一个逻辑完整的改动单元（通常 1~3 个文件）\n\n4. **展示规划**，格式如下：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  📋 Blade Plan\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n需求：{一句话概述}\n\n方案：\n  {2~4 句话描述技术方案的核心思路，\n   包括关键的技术选型和架构决策}\n\n任务清单（共 X 个）：\n  1. {任务标题}  — {涉及的文件或模块}\n  2. {任务标题}  — {涉及的文件或模块}\n  3. {任务标题}  — {涉及的文件或模块}\n  ...\n\n影响范围：\n  新增 X 个文件 | 修改 Y 个文件\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n5. **等待确认或自动推进**：\n   - **交互模式**：追加 `请确认方案后开始执行，或提出修改意见。` 并等待用户回复\n   - **自动模式**：展示规划后直接进入步骤二\n   - 用户说\"可以\"/\"没问题\"/\"开始\"等视为确认通过\n   - 用户提出修改意见则调整方案后重新展示\n\n6. **创建任务**：确认后，使用 **TaskCreate** 为每个任务创建跟踪条目，便于用户实时看到进度\n\n---\n\n### 步骤二：逐任务执行\n\n**目标**：按计划逐个完成任务，保持纪律性。\n\n**对每个任务执行以下循环：**\n\n1. **TaskUpdate** → 标记当前任务为 `in_progress`\n\n2. **加载上下文**（上下文锚定）：\n   - 重新阅读目标文件及其关联代码——即使刚才读过，也要再看一眼\n   - 查看前序任务的产出文件，确保接续一致\n   - 模仿同模块现有代码的风格和模式\n\n3. **执行编码**：\n   - 严格遵循项目 CLAUDE.md 编码规范（如有）\n   - 只做当前任务描述的事，不附带额外改动\n   - 执行前判断是否可借助其他 Blade 技能加速（见「技能协同」）\n\n4. **TaskUpdate** → 标记当前任务为 `completed`\n\n5. **继续下一个任务**\n\n> 为什么要逐任务而不是一口气全写完？因为每个任务完成后的代码状态是下一个任务的输入。逐步推进让每一步都建立在稳固的基础上，也让用户能实时看到进度。\n\n#### 构建检查点\n\n每完成一组逻辑相关的任务（或每 3~4 个任务），执行构建验证：\n\n- **Maven 项目**：`mvn clean compile -DskipTests`\n- **Gradle 项目**：`gradle compileJava`\n- **Node.js 项目**：`npm run build` 或 `tsc --noEmit`\n- **其他项目**：根据项目构建配置自动识别\n\n构建失败则立即修复后再继续，不跳过，不拖延。\n\n#### 技能协同\n\n执行任务时，识别任务类型并借助对应技能提升效率：\n\n| 任务特征 | 推荐技能 | 说明 |\n|---------|---------|------|\n| 后端 CRUD 全套（Entity → Mapper → Service → Controller） | `/blade-design` | 生成骨架代码，再根据实际需求补充业务逻辑 |\n| Avue 组件前端页面 | `/avue-design` | 生成配置化的 Avue 前端代码 |\n\n**协同原则**：技能生成的代码是起点而非终点——生成后必须根据实际需求调整，并通过构建验证。\n\n> **关于代码提交**：`/blade-commit` 绝不自动执行。完成总结后将提交作为后续建议告知用户，由用户自行决定何时提交。仅当用户明确要求\"帮我提交\"时才执行。\n\n---\n\n### 步骤三：完成总结\n\n所有任务完成后，输出总结报告：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  ✅ Blade Plan 执行完毕\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n任务: X/X 完成\n\n新增文件:\n  + path/to/NewFile.java\n  + path/to/AnotherFile.java\n\n修改文件:\n  ~ path/to/ExistingFile.java\n\n后续建议:\n  1. 功能测试（交由用户执行）\n  2. 确认无误后可使用 /blade-commit 提交代码\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n---\n\n## 关键原则\n\n### 上下文锚定\n\n每个任务执行前，重新阅读相关代码。哪怕觉得\"刚才看过了\"，也要再扫一眼。中等任务的对话虽不如大型任务长，但 5~6 个任务执行下来，上下文已经积累了不少内容，记忆会产生微妙偏差。花几秒重读比花几分钟修偏划算得多。\n\n### 遵守项目规范\n\n先看同模块现有代码怎么写的，再动手。代码风格、命名习惯、继承关系、分层结构——模仿现有代码是最可靠的规范来源。如有 CLAUDE.md，严格遵循其中的编码规范。在 SpringBlade 体系中，Boot 和 Cloud 共享统一分层：Controller → Service → Mapper → Wrapper → Entity，差异仅在包路径和路由前缀（Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 手动拼前缀，Cloud 由网关按服务名自动路由）。\n\n### 最小变更原则\n\n每个任务只做计划中描述的事。发现\"顺手可以优化\"的地方，记下来在总结中告诉用户，但不自行改动。未计划的改动是 bug 的温床。\n\n### 复用优先\n\n编写新代码前，先在项目中搜索是否已有可复用的工具类、基类或配置。尤其在 SpringBlade 体系中，`blade-core-tool` 和各 Starter 模块提供了大量基础设施，重复造轮子不仅浪费时间，还会引入风格不一致。\n\n### 构建验证\n\n代码变更后主动执行构建验证。编译不过就立即修，不要想着\"后面一起修\"——错误会像雪球一样越滚越大。\n\n---\n\n## 何时降级为直接对话\n\n如果分析后发现：\n\n- 只涉及 1~5 个文件的简单改动\n- 不需要拆解任务，直接改就行\n\n主动提示：`这个改动比较简单，不需要走规划流程，直接帮你改。` 然后直接执行。\n\n## 何时建议分阶段拆解\n\n如果在规划阶段发现以下信号，建议用户把需求拆成多次会话分阶段推进：\n\n- 任务数量超过 15 个，工作量超出一次会话的合理范围\n- 涉及复杂的数据库表结构设计（多表关联、索引策略），需要先单独设计数据模型\n- 需要跨多个服务的 API 协调，应当先固定契约再逐服务落地\n- 需求本身还不清晰，应先通过对话澄清、对齐理解后再进入 Plan 流程\n\n主动提示：`这个需求的复杂度较高，建议先拆成几个独立阶段（如：设计数据模型 → 落地后端 → 补全前端），每个阶段单独走一次 /blade-plan 会更稳。`\n","frontmatter":{"name":"blade-plan","description":"SpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为「分析规划 → 逐任务执行 → 完成总结」 三步流程，用 Claude Code 内置任务系统追踪进度，全程无辅助文件。适用于新增中等功能模块、修复跨模块 Bug、 中等规模重构、添加新 Starter 等涉及 5~15 个文件变更的开发任务。 当用户说\"帮我做一个XX功能\"、\"这个改动涉及几个文件\"、\"帮我重构一下XX\"、\"帮我加个XX\"、 \"plan\"、\"规划一下再做\"、\"先想清楚再写\"、\"这个稍微有点复杂\"等时触发。也可直接使用 /blade-plan 调用。 不适用于简单单文件修改（直接对话即可），也不适用于需要完整架构设计的复杂系统（建议分阶段拆解为多次会话）。 即使用户没有说\"plan\"，只要需求涉及多文件改动或需要一定设计思考，也应主动建议使用本工作流。\n"},"isInternal":false,"tokens":3727,"sizeBytes":10912}],"systemPromptSnippet":"<yakaai_skills repo=\"chillzhuang/SpringBlade\">\n<!-- File: CLAUDE.md (Tokens: ~7799 | Category: claude-rule) -->\n# CLAUDE.md\n\n欢迎来到 SpringBlade 开源微服务版（master 分支）。本文档汇总了微服务拓扑、API/Service 分层、Feign 调用、网关与配置中心等常见约定，帮助 Claude Code 快速上手，产出与项目风格一致的代码。\n\n作为 AI 助手参与本项目开发时，你必须：\n\n- 动手前先看一看目标服务及其 API 模块的现有实现，模仿它的结构与风格通常是最快的路径\n- 需要外部知识时，优先查阅 Spring Boot、Spring Cloud、MyBatis-Plus、Nacos 等官方文档，而不是凭记忆作答\n- 需求不够清晰时，先复述已知信息、抛出关键澄清问题，再动手\n- 面对跨服务或跨模块改动，拆成可验证的小步骤逐步推进比一气呵成更稳妥\n\n> 所有开发内容必须建立在深度思考过的基础之上，禁止机械生成与错误填充。\n> 如果你已了解所有规范，请在用户第一次对话时说明：\"我已充分了解 SpringBlade 开源微服务版开发规范。\"\n\n---\n\n## 1. 项目概述\n\nSpringBlade 是 BladeX 平台的**开源微服务版本**（master 分支），基于 Spring Cloud + Spring Boot 构建，由 Nacos 做服务发现与配置中心，Sentinel 做熔断限流，Spring Cloud Gateway 做统一入口，依赖 blade-tool 核心库。\n\n---\n\n## 2. 项目架构\n\n```\nSpringBlade/                            # Maven 父工程（pom 聚合）\n├── blade-auth/                         # 认证授权服务（多 granter：password/refresh/social/captcha）\n├── blade-gateway/                      # Spring Cloud Gateway 网关（WebFlux + JWT 鉴权过滤器）\n├── blade-common/                       # 公共模块（常量、启动器、工具、缓存键）\n├── blade-ops/                          # 运维服务父模块\n│   ├── blade-admin/                    #   Spring Boot Admin 监控\n│   ├── blade-develop/                  #   代码生成（Velocity 模板）\n│   ├── blade-report/                   #   报表服务（UReport2）\n│   ├── blade-resource/                 #   资源 / OSS 管理\n│   ├── blade-seata-order/              #   Seata 分布式事务演示：订单\n│   └── blade-seata-storage/            #   Seata 分布式事务演示：库存\n├── blade-service/                      # 业务服务父模块\n│   ├── blade-system/                   #   系统管理（用户/角色/菜单/字典/租户/部门/岗位）\n│   ├── blade-desk/                     #   工作台（通知公告）\n│   ├── blade-log/                      #   日志服务（API/Error/Usual 日志落库）\n│   └── blade-demo/                     #   业务演示模块\n├── blade-service-api/                  # 业务契约父模块（Entity / VO / DTO / Feign）\n│   ├── blade-system-api/               #   ISysClient（部门、租户、客户端信息）\n│   ├── blade-user-api/                 #   IUserClient（用户核心信息）\n│   ├── blade-desk-api/                 #   桌面 API 契约\n│   ├── blade-dict-api/                 #   IDictClient（字典翻译）\n│   ├── blade-scope-api/                #   数据权限契约\n│   └── blade-demo-api/                 #   演示模块契约\n├── doc/\n│   ├── nacos/                          # Nacos 配置脚本（blade.yaml、各环境 datasource）\n│   └── sql/                            # 多数据库建表 & 升级脚本\n├── script/                             # Docker / FatJar / 服务脚本\n└── pom.xml                             # 父 POM（blade-tool 依赖管理）\n```\n\n> **与开源 Boot 版的关键区别**：Cloud 为多模块 Maven 工程，存在 **API/Service 分离**，跨服务调用通过 Feign，Controller 路由**不带** `AppConstant` 前缀（网关按服务名路由）。\n\n### 2.1 分层架构\n\n每个业务域遵循 **API 模块 + Service 模块**双层划分：\n\n| 层次 | 包路径 | 所在模块 |\n| --- | --- | --- |\n| Controller | `controller/` | Service 模块（`blade-service/*` 或 `blade-ops/*`） |\n| Service / ServiceImpl | `service/` + `service/impl/` | Service 模块 |\n| Mapper（接口 + XML 同包） | `mapper/` | Service 模块 |\n| Wrapper | `wrapper/`（Entity → VO 转换） | Service 模块 |\n| Excel 导入导出 | `excel/` | Service 模块 |\n| Feign 实现类 | `feign/` | Service 模块（实现对应 API 模块的 Feign 接口） |\n| Entity | `entity/` | **API 模块（`blade-*-api`）** |\n| VO / DTO | `vo/` `dto/` | **API 模块** |\n| Feign Client + Fallback | `feign/` | **API 模块** |\n\n### 2.2 API / Service 分离原则\n\n- **API 模块**（`blade-*-api`）：定义 Entity、VO、DTO、Feign 接口 + Fallback，供其他服务依赖；**不含**具体业务逻辑\n- **Service 模块**（`blade-service/*`、`blade-ops/*`）：实现 Controller、ServiceImpl、Mapper、Wrapper 与 Feign 接口的服务端实现\n- 跨服务调用**必须**通过 Feign Client，禁止直接依赖其他 Service 模块\n- 公共常量与工具放在 `blade-common`\n\n---\n\n## 3. 技术栈\n\n| 类别 | 技术 |\n| --- | --- |\n| JDK | Java 21 |\n| 基础框架 | Spring Boot / Spring Cloud / Spring Cloud Alibaba / Maven |\n| 核心库 | blade-core-bom、blade-core-cloud、blade-core-boot |\n| 注册中心 / 配置中心 | Nacos |\n| 熔断限流 | Sentinel（`spring-cloud-starter-alibaba-sentinel`） |\n| 分布式事务 | Seata（`blade-starter-transaction`，见 `blade-seata-order`、`blade-seata-storage`） |\n| RPC | OpenFeign（与 Sentinel 集成） |\n| 网关 | Spring Cloud Gateway（WebFlux） |\n| ORM | MyBatis-Plus + dynamic-datasource-spring-boot4-starter |\n| 安全 | blade-core-secure（JWT + 可选 AES 加密 + SM2 国密登录） |\n| 缓存 | BladeRedis（Protostuff 序列化，响应式 Gateway 侧用 Reactive Redis） |\n| 数据权限 | blade-starter-datascope（DataScopeHandler 行级过滤） |\n| 监控 | Spring Boot Admin（`blade-admin`）+ Actuator |\n| 文档 | springdoc（各服务 OpenAPI 3 原生 UI） |\n| 报表 | UReport2（`blade-report`） |\n| 代码生成 | blade-starter-develop + Apache Velocity（`blade-develop`） |\n| 验证码 | easy-captcha |\n| JWT | jjwt-impl + jjwt-jackson |\n| 数据库 | MySQL / PostgreSQL / Oracle / SQL Server / 达梦 / 人大金仓 / 崖山 |\n\n---\n\n## 4. 开发规范\n\n### 4.1 编写新功能前\n\n1. 先阅读目标服务中已有的类，理解其结构、命名和风格\n2. 标准参考模块：`blade-service/blade-system`（Service 最完整）、`blade-service-api/blade-system-api`（API 契约最完整）、`blade-service/blade-desk` + `blade-desk-api`（最简样板）\n3. 主动模仿现有代码风格，包括缩进（Java 用 Tab，YAML/JSON 用 2 空格）、注解顺序、Javadoc 格式\n4. 新建 Java 源文件必须包含 Apache 2.0 许可证头部和 Javadoc 类注释（`@author Chill`）\n\n### 4.2 编写完成后\n\n1. 使用 `mvn clean compile -DskipTests` 编译验证（父工程根目录执行会编译所有子模块），编译不通过必须修复\n2. 引入模块依赖前先检查循环依赖；**跨服务依赖必须经由 `blade-*-api` + Feign**，禁止 `blade-service/A` 直接依赖 `blade-service/B`\n3. 编译通过后将测试交由用户执行，**不得自行执行任何测试**（包括 `mvn test`、启动 Nacos/服务、访问接口）\n4. 除非用户明确要求，不应撰写示例或额外文档\n\n### 4.3 代码生成\n\n当需要生成 CRUD 全套代码（Entity、VO、Service、Controller、Wrapper、Mapper、Feign、建表语句等）时，优先使用 **`/blade-design`** skill。该 skill 可根据模块名、实体名和字段列表，自动生成符合 BladeX 框架规范的后端代码和多数据库建表语句，确保生成结果与项目风格完全一致。\n\n本工程亦内置 `blade-ops/blade-develop` 代码生成服务（Velocity 模板），可在运行时通过后台界面生成。\n\n---\n\n## 5. 命名规范\n\n### 5.1 包名\n\n统一前缀 `org.springblade`，按业务域划分：`system`、`system.user`、`desk`、`auth`、`gateway`、`develop`、`report`、`resource`、`common` 等。API 与 Service 两侧使用相同业务域包名，彼此仅通过模块边界隔离。\n\n### 5.2 类名\n\n| 类型 | 规则 | 示例 |\n| --- | --- | --- |\n| Entity | 直接类名 | `Post`、`User`、`Notice` |\n| VO / DTO | `XxxVO` / `XxxDTO` | `MenuVO`、`RoleDTO` |\n| Service 接口 | `IXxxService` | `IParamService`、`IUserService` |\n| Service 实现 | `XxxServiceImpl` | `ParamServiceImpl` |\n| Controller | `XxxController` | `ParamController` |\n| Mapper | `XxxMapper` | `ParamMapper` |\n| Wrapper | `XxxWrapper` | `UserWrapper` |\n| Feign 接口 / 降级 | `IXxxClient` / `IXxxClientFallback` | `ISysClient` / `ISysClientFallback`、`IUserClient` / `IUserClientFallback` |\n| 表名 | `blade_` + 下划线 | `blade_user`、`blade_post` |\n\n### 5.3 变量命名\n\n- 必须具有明确语义：`Exception exception`（✅）`Exception e`（❌）；`List<Post> postList`（✅）`List<Post> list`（❌）\n- 冲突时提升语义：`Cache cache; Cache postCache`（✅）`Cache cache1; Cache cache2`（❌）\n\n---\n\n## 6. 编码规范\n\n### 6.1 注解顺序\n\n- **Controller 类**：`@RestController` → Lombok（`@AllArgsConstructor`） → `@RequestMapping(\"/resource\")` → `@Tag`\n- **Controller 方法**：HTTP 方法注解（`@GetMapping`/`@PostMapping`）→ `@Operation` → `@PreAuth`（如需角色控制）\n- **Feign 接口**：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- **Feign Fallback**：`@Component` → 实现对应接口\n- **Entity 类**：`@Data` → `@TableName` → `@EqualsAndHashCode(callSuper = true)`（继承 BaseEntity/TenantEntity 时必须） → `@Schema`（字段级使用）\n- **VO 类**：`@Data` → `@EqualsAndHashCode(callSuper = true)` → `@Schema`\n\n### 6.2 Entity-Service 继承体系（核心）\n\nEntity 基类的选择**直接决定** Service 和 ServiceImpl 的继承方式，三者必须配套：\n\n| 场景 | Entity | Service 接口 | ServiceImpl | 示例 |\n| --- | --- | --- | --- | --- |\n| **多租户业务表** | `extends TenantEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `User`、`Post`、`Notice` |\n| **非租户业务表** | `extends BaseEntity` | `extends BaseService<T>` | `extends BaseServiceImpl<M, T>` | `Tenant` |\n| **轻量级/关系表** | `implements Serializable` | `extends IService<T>` | `extends ServiceImpl<M, T>` | `RoleMenu`、`Dict`、`Role` |\n\n- `TenantEntity` 继承自 `BaseEntity` 并额外包含 `tenantId`\n- `BaseEntity` 包含：`id`、`createUser`、`createDept`、`createTime`、`updateUser`、`updateTime`、`status`、`isDeleted`\n- `BaseService`/`BaseServiceImpl` 是 BladeX 增强版，提供 `deleteLogic(List<Long>)` 等方法，**要求 Entity 继承 BaseEntity 或 TenantEntity**\n- `IService`/`ServiceImpl` 是 MyBatis-Plus 原生版，用于 `implements Serializable` 的轻量实体，删除用 `removeByIds()`\n\n### 6.3 Controller 约定\n\n- 视需要继承 `BladeController`（获取 `getUser()` 等便捷方法），使用 `@AllArgsConstructor` 注入依赖\n- **路由直接使用资源路径**（如 `@RequestMapping(\"/param\")`），网关按服务名路由，**无需** `AppConstant` 前缀（与 Boot 版区别点）\n- 统一返回 `R<T>` 响应体（`R.data(...)` / `R.status(...)` / `R.success(...)`）\n- Entity → VO 转换通过 `XxxWrapper.build().entityVO(entity)` / `.pageVO(page)` / `.listVO(list)`\n- 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（角色级）；**本版本不支持** 菜单级 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 写法，也不含 ApiScopeHandler 接口权限\n- 需要跳过 Swagger 展示时使用 `@Hidden`\n\n### 6.4 Feign Client（Cloud 特有）\n\n- **接口定义在 API 模块** 的 `feign/` 包中，由所有调用方共享依赖\n- 注解签名：`@FeignClient(value = AppConstant.APPLICATION_XXX_NAME, fallback = IXxxClientFallback.class)`\n- 路径常量定义在接口内部：`String API_PREFIX = \"/xxx\"`；方法路径使用 `API_PREFIX + \"/yyy\"` 拼接\n- 返回类型统一使用 `R<T>`（除非明确只返回原始对象，如 `ISysClient#getDept` 返回 `Dept`）\n- **降级类**：与接口同包，命名 `IXxxClientFallback`，标注 `@Component` 并实现全部方法，方法返回 `null` 或安全缺省值\n- Feign 实现（服务端）位于对应 Service 模块的 `feign/` 包，`@RestController` 实现接口并复用内部 Service\n- Feign + Sentinel 默认开启（`feign.sentinel.enabled=true`）\n\n### 6.5 依赖注入\n\n- Controller 使用 `@AllArgsConstructor` + `private` 字段\n- Service 需要额外依赖时使用 `@AllArgsConstructor` 或 `@RequiredArgsConstructor` + `private final` 字段\n- Feign 客户端通过构造器注入，Spring Cloud 自动代理\n\n### 6.6 Lombok\n\n禁止手写 getter/setter，统一使用 `@Data`、`@EqualsAndHashCode(callSuper = true)`、`@AllArgsConstructor`、`@RequiredArgsConstructor`、`@Slf4j`、`@SneakyThrows` 等\n\n### 6.7 Java 21 特性\n\n- **基线 JDK 21 LTS**，优先采用已稳定的现代语言特性提升可读性：`switch` 表达式与模式匹配（含 Record Patterns）、`instanceof` 模式匹配、Text Blocks（承载多行 SQL / JSON 字面量）、`@Serial`（标注 `serialVersionUID` 与序列化方法）\n- 实体 / VO 沿用 Lombok（`@Data` 等），**不改用 record**——需保留可变字段与无参构造以适配 MyBatis-Plus 映射\n- **禁止 `var`**，所有变量显式声明类型；优先使用 Stream API，Lambda 保持简洁\n- 虚拟线程（Virtual Threads）属运行期能力，由 Spring Boot 4 配置 `spring.threads.virtual.enabled` 按需开启，非编码约定\n\n### 6.8 MyBatis-Plus\n\n- 简单查询使用 `LambdaQueryWrapper` 或 `Wrappers.<T>query()`\n- 复杂查询写在 Mapper XML 中，Mapper XML 与接口**同包**放置（位于 `src/main/java` 下，由 `pom.xml` `<resources>` 声明打包）\n- 分页统一使用 `Condition.getPage(query)` + `Condition.getQueryWrapper(param, Entity.class)`\n- 禁止 JDBC 直连查询\n- 主键使用雪花算法 `IdType.ASSIGN_ID`，Long 类型主键必须标注 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n- 多数据源切换通过 dynamic-datasource 的 `@DS(\"...\")` 注解，数据源定义在 Nacos 配置\n\n### 6.9 日志\n\n- 使用 `@Slf4j`，占位符传参 `log.info(\"user {} login\", userId)`（禁止字符串拼接）\n- 包含关键业务标识，异常必须携带堆栈 `log.error(\"...\", e)`，禁止打印敏感信息（密码、Token、手机号等）\n- API 调用日志、错误日志、业务日志由 `blade-service/blade-log` 异步落库，事件通过 `AsyncEventDispatcher` 发布\n\n---\n\n## 7. 数据库规范\n\n- **表名前缀**：`blade_`（如 `blade_user`、`blade_role_menu`）\n- **业务表必含字段**：`id`（BIGINT）、`tenant_id`（VARCHAR，多租户表）、`create_user`、`create_dept`、`create_time`、`update_user`、`update_time`、`status`、`is_deleted`\n- **主键策略**：雪花算法（`IdType.ASSIGN_ID`），Long 字段用 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n- **逻辑删除**：`is_deleted`（INT），`0` = 未删除，`1` = 已删除\n- **索引命名**：唯一索引 `uk_` 前缀，普通索引 `idx_` 前缀\n- **多数据库**：支持 7 种数据库，SQL 脚本位于 `doc/sql/`（全量建表 + 版本升级脚本），修改表结构时需同步所有脚本\n\n---\n\n## 8. 多租户与安全\n\n### 8.1 多租户\n\n- 默认**字段隔离模式**（`tenant_id`），MP 内置方法自动注入\n- 租户表通过 Nacos `blade.yaml` 中 `blade.tenant.tables` 配置声明\n- 自定义 SQL 需手动过滤：`AuthUtil.getTenantId()` 或 `SecureUtil.getTenantId()`\n- **本版本不支持** `@TenantDS` / `@NonDS` 数据源切换注解；如需物理隔离，可结合 dynamic-datasource 的 `@DS` 配合业务逻辑实现\n\n### 8.2 权限体系\n\n- **角色权限**：`@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`（或自定义 SpEL 表达式）\n- **数据权限**：`blade-starter-datascope` DataScopeHandler，行级过滤，通过 `RoleScope` 配置\n- **本版本不含**：接口权限（ApiScopeHandler）、菜单级权限表达式 `@PreAuth(menu = \"xxx\")`、`@PreAuth(AuthConstant.PERMIT_ALL)` 通配放行\n- 接口放行请在 Nacos `blade.secure.skip-url` 中配置 URL 通配，或在 `blade-gateway` 的 `AuthFilter` 白名单中配置\n\n### 8.3 登录与 Token\n\n- 登录密码使用 **SM2 国密**加解密（`blade.auth.public-key` / `blade.auth.private-key`，由 `blade-auth` 模块的 `Sm2KeyGenerator` 生成）\n- Token 使用 JWT 签名（`blade.token.sign-key`）+ 可选 AES 加密（`blade.token.aes-key`）\n- 多 Granter 支持（`blade-auth/granter/`）：`PasswordTokenGranter`、`CaptchaTokenGranter`、`RefreshTokenGranter`、`SocialTokenGranter`\n- **网关统一鉴权**：`blade-gateway/filter/AuthFilter` 作为 `GlobalFilter` 验证 JWT，下游服务通过 `exchange attributes` 透传用户上下文\n- 获取当前用户：`SecureUtil.getUser()` / `SecureUtil.getUserId()` / `SecureUtil.getTenantId()` / `SecureUtil.isAdministrator()`\n\n### 8.4 分布式事务\n\n- 由 `blade-starter-transaction`（Seata）提供支持，参考 `blade-ops/blade-seata-order` + `blade-ops/blade-seata-storage` 双服务示例\n- 需要跨服务事务的场景标注 `@GlobalTransactional`\n\n---\n\n## 9. 缓存规范\n\n- `CacheUtil` 统一操作，常量命名在 `blade-common/cache/CacheNames`\n- 数据变更后清除缓存：`CacheUtil.clear(CACHE_NAME, Boolean.FALSE)`\n- 字典翻译：推荐跨服务通过 `IDictClient.getValue(code, dictKey)`（Feign）；服务本地可用 `DictCache.getValue(...)`\n- Redis 序列化使用 Protostuff（默认配置），键分布式作用域；Gateway 侧使用 Reactive Redis\n\n---\n\n## 10. 开源许可头部\n\n所有新建的 Java 源文件必须在文件顶部包含以下 Apache 2.0 许可头部：\n\n```java\n/**\n * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n```\n\n> **重要**：本工程为开源版本，**不要**写入 BladeX 商业许可证头部。若需要迁移为商业版本，请以商业项目代码样例为准。\n\n---\n\n## 11. 构建与部署\n\n### 11.1 Maven 构建\n\n```bash\nmvn clean compile -DskipTests                      # 根目录执行：编译所有子模块\nmvn clean package -DskipTests                      # 打包所有服务为 FatJar\nmvn clean package -DskipTests -pl blade-service/blade-system -am  # 只打包 system 服务及其依赖模块\n```\n\n- 父 POM 使用 `${revision}` 占位符管理版本，由 `flatten-maven-plugin` 在 `process-resources` 阶段展开\n- 各服务打包产物通过 `directory-maven-plugin` 汇总复制到根目录 `target/`\n- `finalName` 使用 `${project.name}`，产物命名与 artifactId 一致\n\n### 11.2 Nacos 配置\n\n- 配置文件位于 `doc/nacos/`：`blade.yaml`（公共配置）+ `blade-dev.yaml`/`blade-test.yaml`/`blade-prod.yaml`（环境差异）\n- 数据源、密钥、Swagger、Sentinel、Feign 等共享参数统一在 Nacos 配置\n- 各服务 `src/main/resources/application-{env}.yml` 仅保留 `server.port` 与 `spring.datasource.url` 等少量本地覆盖\n\n### 11.3 Docker 部署\n\n- `script/docker/docker-compose.yml` 提供一体化编排：Nacos + Sentinel + Redis + Nginx + 全部微服务\n- 默认网段 `blade_net`，Nacos `172.30.0.48`，Sentinel `172.30.0.58`\n- Harbor 镜像推送通过父 POM 的 `docker.registry.url` 属性控制\n\n### 11.4 必备环境变量\n\n```bash\nBLADE_OAUTH2_PUBLIC_KEY    # SM2 公钥\nBLADE_OAUTH2_PRIVATE_KEY   # SM2 私钥\nBLADE_TOKEN_SIGN_KEY       # JWT 签名密钥\nBLADE_TOKEN_CRYPTO_KEY     # Token AES 加密密钥\n```\n\n### 11.5 服务端口（默认）\n\n| 服务 | 端口 |\n| --- | --- |\n| blade-gateway | 80 |\n| blade-auth | 8100 |\n| blade-admin | 7002 |\n| blade-system | 8106 |\n| blade-desk | 8103 |\n| blade-log | 8102 |\n| blade-develop | 8108 |\n| blade-report | 8109 |\n| blade-resource | 8110 |\n\n> 实际端口以 `src/main/resources/application-{env}.yml` 中 `server.port` 为准。\n\n---\n\n## 12. Git 提交规范\n\n当需要提交代码时，优先使用 **`/blade-commit`** skill，它会自动分析变更内容并生成符合项目规范的 Gitmoji 提交信息。\n\n采用 **Gitmoji** 风格，中文描述。格式：`:<gitmoji>: 简要描述`\n\n| Gitmoji | 场景 | 示例 |\n| --- | --- | --- |\n| `:sparkles:` | 新增功能 | `:sparkles: 登录认证新增IP锁定逻辑` |\n| `:zap:` | 优化重构 | `:zap: 优化字典查询排序逻辑` |\n| `:bug:` | 修复缺陷 | `:bug: 修复用户查询未过滤已删除数据` |\n| `:tada:` | 版本发布 | `:tada: x.x.x.RELEASE` |\n| `:recycle:` | 代码重构 | `:recycle: 重构租户删除逻辑` |\n\n---\n\n## 13. 框架组件速查\n\n| 组件 | 用途 |\n| --- | --- |\n| `R<T>` | 统一 API 响应（`org.springblade.core.tool.api.R`） |\n| `BladeController` | Controller 基类（`org.springblade.core.boot.ctrl`） |\n| `BladeCloudApplication` | Cloud 服务启动注解（`blade-auth`、业务服务使用） |\n| `TenantEntity` / `BaseEntity` | 实体基类（`org.springblade.core.mp.base`） |\n| `BaseService` / `BaseServiceImpl` | BladeX 增强 Service（含 `deleteLogic`） |\n| `BaseEntityWrapper` | Entity→VO 转换基类 |\n| `BladeUser` | 当前登录用户（含租户信息） |\n| `Condition` / `Query` | 查询条件构建 / 分页参数 |\n| `CacheUtil` / `DictCache` | 缓存工具 / 字典缓存 |\n| `SecureUtil` / `AuthUtil` | 获取当前用户/租户信息 |\n| `BladeRedis` | Redis 操作封装 |\n| `Func` / `BeanUtil` / `StringUtil` | 通用工具类 |\n| `ServiceException` | 业务异常 |\n| `@PreAuth` | 角色权限注解（`RoleConstant.HAS_ROLE_ADMIN`） |\n| `@FeignClient` + `AppConstant.APPLICATION_XXX_NAME` | 服务间调用 |\n| `ISysClient` / `IUserClient` / `IDictClient` | 常用 Feign 契约 |\n| `@GlobalTransactional` | Seata 分布式事务 |\n| `@Operation` / `@Tag` / `@Schema` | springdoc / OpenAPI 3 文档注解 |\n| `LauncherConstant` / `AppConstant` / `CommonConstant` | 启动常量 / 应用名 / 公共常量 |\n\n---\n\n## 14. 风格一致性\n\n1. 风格不确定时，优先查阅现有服务并模仿当前业务域的实现方式\n2. 新增业务域时，**先建 API 模块再建 Service 模块**；API 模块只放契约，不引入 Service 依赖\n3. 新服务参考 `blade-service/blade-system` + `blade-service-api/blade-system-api`（最完整） 或 `blade-service/blade-desk` + `blade-desk-api`（最简样板）\n4. 现有服务或契约已满足需求时，禁止自写替代实现；跨服务使用数据请走 Feign，不要重复落库\n5. 与用户交互全程使用**中文**，代码注释和 Javadoc 亦使用中文\n6. 本工程为开源版，**不要**引用商业版特有能力（Flowable、PowerJob、`@TenantDS`、`@BladeView`、接口权限、`blade-ops-api`、`blade-plugin` 等）；如确实需要，请先向用户确认是否切换到商业版代码库\n\n<!-- File: .claude/skills/avue-design/README.md (Tokens: ~2645 | Category: anthropic-skill) -->\n# avue-design Skill 集合\n\nAvue 组件代码生成与配置指南，基于 Avue v3.8.2 (Vue3 + Element-Plus) 文档构建。\n\n## 概述\n\n本 Skill 集合为 Claude Code 提供完整的 Avue 组件库知识，使其能够根据用户需求精准生成 Avue 组件代码。覆盖 CRUD 表格、动态表单、树组件、数据展示等全部 Avue 组件，支持 Composition API 和 Options API 两种代码风格。\n\n## 目录结构\n\n```\navue-design/\n├── SKILL.md                           # 主入口 - 概览、路由、速查表\n├── README.md                          # 本文件\n└── references/                        # 参考文档（按需加载）\n    │\n    │── 基础配置 ───────────────────────\n    ├── global-config.md               # 安装、全局配置、国际化、云存储\n    ├── api-style-guide.md             # Composition API vs Options API 完整模板\n    ├── utility-apis.md                # 16个全局工具函数 ($DialogForm, $Export 等)\n    │\n    │── avue-crud 表格组件 ─────────────\n    ├── crud-core.md                   # 组件属性、Option配置、按钮、弹窗、分页、事件、方法、插槽\n    ├── crud-column.md                 # 列配置、字典、Props映射、排序过滤、格式化渲染\n    ├── crud-search.md                 # 搜索栏配置、搜索字段属性、级联搜索\n    ├── crud-form-dialog.md            # 弹窗表单配置、验证规则、字段控制、分组、权限\n    ├── crud-features.md               # 树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽、插件模式\n    │\n    │── avue-form 表单组件 ─────────────\n    ├── form-core.md                   # 组件属性、布局、按钮、Column通用属性、事件、方法、插槽\n    ├── form-field-types.md            # 全部27+字段类型详细配置（input含13子类型、select、date等）\n    ├── form-advanced.md               # 字典、验证、动态子表单、文件上传(含云存储)、字段联动、Mock\n    │\n    │── 其他组件 ─────────────────────\n    ├── tree-component.md              # avue-tree 树组件完整API\n    ├── data-display.md                # 23个数据展示组件\n    └── other-components.md            # Card、DialogForm、Flow、Login、Chat等22个独立组件\n```\n\n## 模块说明\n\n### 1. 基础配置（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `global-config.md` | npm安装、Element-Plus集成、全局crudOption/formOption、阿里云/七牛/腾讯COS云存储配置、水印配置、国际化、外部库CDN |\n| `api-style-guide.md` | Composition API 和 Options API 两种风格的完整 CRUD 页面模板，包含详细的差异对照表和转换规则 |\n| `utility-apis.md` | 全部16个Avue工具函数：$DialogForm、$Clipboard、$ImagePreview、$Print、$Export、$Log、findObject、deepClone等 |\n\n### 2. avue-crud 表格组件（5个文件）\n\n这是 Avue 最核心的组件，文档最为丰富。\n\n| 文件 | 内容 |\n|-----|------|\n| `crud-core.md` | 组件属性（21个）、Option表格级属性（索引/选择/展开/行/菜单/网格/工具栏共80+属性）、按钮配置（25+）、弹窗配置（19个）、分页（8个）、事件（35+）、方法（40+含行内编辑/滚动/搜索/视图方法组）、插槽（22+） |\n| `crud-column.md` | 列通用属性（20+含hide/cell/detail控制）、字典配置（7个属性+Props映射）、字典联动、显示控制、多级表头、排序过滤、formatter/render/html格式化、合计行、合并行列、搜索字段属性（20+含searchDisabled/searchControl/renderSearch）、表单字段属性（20+） |\n| `crud-search.md` | 搜索变量绑定（双API风格）、全局搜索属性（15+）、列级搜索属性（14个）、类型覆盖、折叠、级联联动、自定义插槽 |\n| `crud-form-dialog.md` | 弹窗属性（15个）、beforeOpen/beforeClose回调、验证规则(async-validator)、字段display/disabled控制、默认值、control联动、分组/标签页、数据过滤、权限控制 |\n| `crud-features.md` | 树表(基本/懒加载)、虚拟滚动(3.7.0+)、卡片网格模式(3.4.0+)、行编辑(单击/双击)、Excel导入导出(含对话框选项)、行/列拖拽排序、合计行、父子表、行复制、高级筛选(8种操作符)、列管理器、权限控制(对象/行级函数)、CRUD插件模式(Mixin)、API/Option文件模板、动态配置切换 |\n\n### 3. avue-form 表单组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `form-core.md` | 组件属性（9个含v-model:status）、Option属性（20个含formWidth/tabsActive/tabsType/tabsVerifyAll/filterDic/filterNull）、布局（栅格/分组/标签页/帮助文字）、Group属性（8个含arrow/display/header）、按钮配置（12个）、Column通用属性（25+）、事件（6个含change/tab-click/error/mock-change）、方法（12个含show/hide/scrollToField/getField）、插槽（6个）、两种API风格完整示例 |\n| `form-field-types.md` | 全部27+字段类型完整配置：input(含14子类型:password/textarea/search/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc + formatters/parser)、number、select(含all/noMatchText)、cascader、checkbox、radio、date(含disabledDate/shortcuts/unlinkPanels)、time(含arrowControl + TimeSelect自动切换)、switch、upload(含dragFile/oss/cropperOption)、tree、input-tag(含min/max/prefix/suffix)、mention(含split/prefix/whole)、input-table(含children/dialogWidth)、color、icon(含dialogWidth/分组iconList)、map(含mapChange/dialogWidth/值格式说明)、rate、slider、array/img/url、dynamic(含max + 完整children配置12属性)、title、input-cron |\n| `form-advanced.md` | 字典配置(本地/远程/格式化/联动/更新)、验证规则(基本/类型/自定义/方法)、动态子表单(表格/表单模式)、文件上传(回调/水印/云存储/数据格式)、字段联动(control/Promise)、数据格式(深层绑定/过滤)、自定义插槽、Mock数据 |\n\n### 4. 其他组件（3个文件）\n\n| 文件 | 内容 |\n|-----|------|\n| `tree-component.md` | avue-tree完整API：Option属性（12个）、Props属性（4个）、事件（7个）、方法（18个）、插槽（3个）、多选/懒加载/拖拽/虚拟模式示例 |\n| `data-display.md` | 23个数据展示组件列表、avue-data-display核心组件属性和数据结构 |\n| `other-components.md` | 22个独立组件：Card、DialogForm、Tabs、Search、Calendar、Flow、Login、Article、Chat、CountUp、Clipboard、Draggable、TextEllipsis、Sign、Print、Export、ImagePreview、Contextmenu、Video、Verify、Screenshot、License |\n\n## 工作原理\n\n1. **主入口 SKILL.md**（< 200行）始终加载到上下文中，提供：\n   - 快速安装指南\n   - 参考文件导航表（根据任务类型指引读取哪个文件）\n   - 代码生成核心规则\n   - 字段类型速查表\n\n2. **参考文件** 按需加载，当用户需要特定组件的配置时才读取对应文件：\n   - 需要创建 CRUD 页面 → 读取 `crud-core.md` + `crud-column.md`\n   - 需要配置搜索 → 读取 `crud-search.md`\n   - 需要特定字段类型 → 读取 `form-field-types.md`\n   - 需要文件上传 → 读取 `form-advanced.md`\n\n3. **双API风格** 支持：\n   - Composition API（推荐）：`<script setup>` + `ref()` + 箭头函数\n   - Options API：`export default` + `data()` + `methods`\n\n## 数据来源\n\n基于以下 Avue 官方文档与源码完整解析：\n\n- `crud/` 目录 32 个文档（CRUD表格组件）\n- `form/` 目录 38 个文档（表单组件）\n- `component/` 目录 20 个文档（独立组件）\n- `default/` 目录 25 个文档（默认组件）\n- `data/` 目录 23 个文档（数据展示组件）\n- `docs/` 目录 10 个文档（全局配置、API、安装等）\n- Saber3 项目参考文件（Composition API vs Options API 对照）\n- Avue v3.8.2 完整源码审计（packages/element-plus/ 全部组件 + packages/core/ 核心模块 + src/ 工具库）\n\n总计 **6,000+ 行** 参考文档，覆盖 Avue v3.8.2 全部组件配置。\n\n## 版本信息\n\n- Avue 版本：v3.8.2 (2026-03-27)\n- Vue 版本：Vue 3\n- UI 框架：Element-Plus\n- 创建日期：2026-04-05\n\n<!-- File: .claude/skills/avue-design/references/api-style-guide.md (Tokens: ~3073 | Category: anthropic-skill) -->\n# Avue 代码风格指南：Composition API vs Options API\n\n生成 Avue 代码时，需根据用户偏好选择以下两种风格之一。默认使用 Composition API。\n\n## 目录\n\n1. [Composition API 完整模板](#composition-api-完整模板)\n2. [Options API 完整模板](#options-api-完整模板)\n3. [关键差异对照表](#关键差异对照表)\n4. [转换规则](#转换规则)\n\n---\n\n## Composition API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <!-- 自定义左侧按钮 -->\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <!-- 自定义列内容 -->\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed, onMounted } from 'vue'\nimport { useStore } from 'vuex'\nimport { ElMessage, ElMessageBox } from 'element-plus'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\n// ============ 响应式数据 ============\nconst crud = ref(null)\nconst form = ref({})\nconst search = ref({})\nconst loading = ref(true)\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n})\nconst selectionList = ref([])\nconst data = ref([])\n\n// ============ Vuex ============\nconst store = useStore()\nconst permission = computed(() => store.getters.permission)\n\n// ============ 计算属性 ============\nconst permissionList = computed(() => ({\n  addBtn: !!permission.value.module_add,\n  viewBtn: !!permission.value.module_view,\n  delBtn: !!permission.value.module_delete,\n  editBtn: !!permission.value.module_edit,\n}))\n\nconst ids = computed(() =>\n  selectionList.value.map(ele => ele.id).join(',')\n)\n\n// ============ Option 配置 ============\nconst option = reactive({\n  height: 'auto',\n  calcHeight: 32,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  dialogClickModal: false,\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      search: true,\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      search: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n})\n\n// ============ 生命周期 ============\nonMounted(() => {\n  // 初始化逻辑\n})\n\n// ============ 方法 ============\nconst onLoad = () => {\n  loading.value = true\n  getList(page.value.currentPage, page.value.pageSize, Object.assign({}, search.value)).then(res => {\n    const resData = res.data.data\n    page.value.total = resData.total\n    data.value = resData.records\n    loading.value = false\n    selectionClear()\n  })\n}\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(() => {\n    onLoad()\n    ElMessage.success('操作成功!')\n    done()\n  }).catch(() => {\n    loading()\n  })\n}\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(row.id))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据')\n    return\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => remove(ids.value))\n    .then(() => {\n      onLoad()\n      ElMessage.success('操作成功!')\n    })\n}\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n    })\n  }\n  done()\n}\n\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\n\nconst searchReset = () => {\n  onLoad()\n}\n\nconst selectionChange = (list) => {\n  selectionList.value = list\n}\n\nconst selectionClear = () => {\n  selectionList.value = []\n  crud.value?.toggleSelection()\n}\n\nconst refreshChange = () => {\n  onLoad()\n}\n</script>\n```\n\n---\n\n## Options API 完整模板\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button type=\"danger\" @click=\"handleDelete\">删 除</el-button>\n      </template>\n      <template #category=\"{ row }\">\n        <el-tag>{{ row.categoryName }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex'\nimport { getList, add, update, remove, getDetail } from '@/api/module/entity'\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      data: [],\n      option: {\n        height: 'auto',\n        calcHeight: 32,\n        tip: false,\n        searchShow: true,\n        searchMenuSpan: 6,\n        border: true,\n        index: true,\n        selection: true,\n        dialogClickModal: false,\n        column: [\n          {\n            label: '名称',\n            prop: 'name',\n            search: true,\n            rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n          },\n          {\n            label: '状态',\n            prop: 'status',\n            type: 'select',\n            search: true,\n            dicData: [\n              { label: '启用', value: 1 },\n              { label: '禁用', value: 0 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: !!this.permission.module_add,\n        viewBtn: !!this.permission.module_view,\n        delBtn: !!this.permission.module_delete,\n        editBtn: !!this.permission.module_edit,\n      }\n    },\n    ids() {\n      return this.selectionList.map(ele => ele.id).join(',')\n    },\n  },\n  methods: {\n    onLoad(page, params = {}) {\n      this.loading = true\n      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n        const data = res.data.data\n        this.page.total = data.total\n        this.data = data.records\n        this.loading = false\n        this.selectionClear()\n      })\n    },\n    rowSave(row, done, loading) {\n      add(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowUpdate(row, index, done, loading) {\n      update(row).then(() => {\n        this.onLoad(this.page)\n        this.$message({ type: 'success', message: '操作成功!' })\n        done()\n      }).catch(() => {\n        loading()\n      })\n    },\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(row.id))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n        })\n    },\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据')\n        return\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => remove(this.ids))\n        .then(() => {\n          this.onLoad(this.page)\n          this.$message({ type: 'success', message: '操作成功!' })\n          this.$refs.crud.toggleSelection()\n        })\n    },\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data\n        })\n      }\n      done()\n    },\n    searchChange(params, done) {\n      this.query = params\n      this.page.currentPage = 1\n      this.onLoad(this.page, params)\n      done()\n    },\n    searchReset() {\n      this.query = {}\n      this.onLoad(this.page)\n    },\n    selectionChange(list) {\n      this.selectionList = list\n    },\n    selectionClear() {\n      this.selectionList = []\n      this.$refs.crud.toggleSelection()\n    },\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage\n    },\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize\n    },\n    refreshChange() {\n      this.onLoad(this.page)\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 关键差异对照表\n\n| 特性 | Composition API | Options API |\n|-----|----------------|-------------|\n| **脚本标签** | `<script setup>` | `<script> export default {}` |\n| **数据声明** | `const x = ref(val)` / `reactive({})` | `data() { return { x: val } }` |\n| **脚本中访问数据** | `x.value` | `this.x` |\n| **模板中访问数据** | `{{ x }}` (自动解包) | `{{ x }}` |\n| **计算属性** | `const x = computed(() => ...)` | `computed: { x() { ... } }` |\n| **方法** | `const fn = () => {}` | `methods: { fn() {} }` |\n| **生命周期** | `onMounted(() => {})` | `mounted() {}` |\n| **Vuex** | `const store = useStore()` | `...mapGetters([...])` |\n| **消息提示** | `ElMessage.success(...)` | `this.$message(...)` |\n| **确认框** | `ElMessageBox.confirm(...)` | `this.$confirm(...)` |\n| **组件引用** | `const crud = ref(null)` | `this.$refs.crud` |\n| **搜索参数** | `v-model:search=\"search\"` 自动同步 | 手动赋值 `this.query = params` |\n| **分页事件** | `v-model:page` 自动处理 | 需要 `currentChange`/`sizeChange` |\n\n## 转换规则\n\n从 Composition API 转换为 Options API：\n1. `ref()` 声明 → 移入 `data()` return\n2. 移除所有 `.value` 访问\n3. 箭头函数 → `methods` 对象方法\n4. `computed(() => ...)` → `computed` 对象\n5. `ElMessage` / `ElMessageBox` → `this.$message` / `this.$confirm`\n6. `useStore()` → `mapGetters`\n7. `onMounted()` → `mounted()`\n8. 添加 `currentChange` / `sizeChange` 事件处理\n\n从 Options API 转换为 Composition API：\n1. `data()` 中的属性 → `ref()` 或 `reactive()`\n2. 添加 `.value` 访问\n3. `methods` 中的方法 → 顶层箭头函数\n4. `this.$xxx` → 导入对应模块\n5. `v-model:search=\"search\"` 替代 `query` 变量\n6. 移除 `currentChange` / `sizeChange`（由 v-model:page 处理）\n\n<!-- File: .claude/skills/avue-design/references/crud-column.md (Tokens: ~3459 | Category: anthropic-skill) -->\n# avue-crud 列配置参考\n\n## 目录\n1. [列通用属性](#列通用属性)\n2. [字典配置](#字典配置)\n3. [显示控制](#显示控制)\n4. [排序与过滤](#排序与过滤)\n5. [格式化与渲染](#格式化与渲染)\n6. [搜索字段属性](#搜索字段属性)\n7. [表单字段属性](#表单字段属性)\n\n---\n\n## 列通用属性\n\ncolumn 数组中每个对象的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 列标题 |\n| prop | string | - | 列属性名 |\n| type | string | - | 字段类型 (input/select/date等，详见form-field-types) |\n| width | string | - | 列宽度 |\n| minWidth | string | - | 列最小宽度 |\n| fixed | boolean/string | - | 固定列: true/left/right |\n| align | string | 'left' | 对齐方式: left/center/right |\n| headerAlign | string | 'left' | 表头对齐方式 |\n| className | string | - | 单元格自定义class |\n| labelClassName | string | - | 表头自定义class |\n| resizable | boolean | true | 列宽可拖拽 |\n| slot | boolean | - | 启用具名插槽（插槽名=prop） |\n| bind | string | - | 深层数据绑定，如 'info.address' |\n| value | - | - | 列默认值 |\n| order | number | - | 字段排序，数字越大越靠前 |\n| gridRow | boolean | false | 网格模式新起一行 |\n| hide | boolean | false | 隐藏列（不在表格中显示） |\n| cell | boolean | false | 该列可行内编辑 |\n| addDetail | boolean | false | 新增时详情模式 |\n| editDetail | boolean | false | 编辑时详情模式 |\n| detail | boolean | false | 全局详情模式 |\n\n### 主键配置\n\n```javascript\noption: {\n  rowKey: 'id',           // 主键字段名，默认 'id'\n  rowParentKey: 'parentId' // 父级主键(树表用)\n}\n```\n\n### 对象格式列配置 (3.2.15+)\n\ncolumn 可以用对象格式替代数组，以 prop 为 key：\n\n```javascript\n// 数组格式\ncolumn: [\n  { label: '名称', prop: 'name' },\n  { label: '状态', prop: 'status' }\n]\n\n// 对象格式\ncolumn: {\n  name: { label: '名称' },\n  status: { label: '状态' }\n}\n```\n\n---\n\n## 字典配置\n\n### 字典属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| dicData | array/function/promise | - | 本地字典数据 (3.5.0+ 支持函数/Promise) |\n| dicUrl | string | - | 远程字典URL |\n| dicFlag | boolean | false | 打开表单时重新请求字典 |\n| dicQuery | object | - | 字典请求查询参数 |\n| dicHeaders | object | - | 字典请求头参数 |\n| dicFormatter | function(res) | (res)=>res | 字典返回数据格式化 |\n| dicMethod | string | 'GET' | 字典请求方法 |\n\n### Props 字段映射\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 选项标签属性 |\n| value | string | 'value' | 选项值属性 |\n| children | string | 'children' | 子选项属性 |\n| disabled | string | 'disabled' | 禁用属性 |\n| res | string | - | 响应数据层级，如 'data.data' |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  // 本地字典\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n  // 或远程字典\n  dicUrl: '/api/dict/type',\n  props: {\n    label: 'name',\n    value: 'code',\n    res: 'data.records',\n  },\n}\n```\n\n### 字典联动\n\n通过 `cascader` 属性实现级联联动：\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city'],  // 指定子级字段prop\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}', // {{key}} 为父级选中值\n  },\n]\n```\n\n### 更新字典\n\n```javascript\n// 更新指定字段的字典\ncrudRef.value.updateDic('prop', [{ label: '新选项', value: 1 }])\n// 重新加载所有字典\ncrudRef.value.dicInit()\n```\n\n---\n\n## 显示控制\n\n### 溢出省略\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| overHidden | boolean/object | false | 内容溢出显示tooltip |\n| showOverflowTooltip | boolean/object | false | 同上 |\n| ellipsis | boolean | false | 文本省略 (3.6.4+) |\n\n### 多级表头\n\n通过 `children` 嵌套实现：\n\n```javascript\ncolumn: [\n  {\n    label: '用户信息',\n    children: [\n      { label: '姓名', prop: 'name' },\n      { label: '年龄', prop: 'age' },\n    ]\n  }\n]\n```\n\n### 表头固定\n\n```javascript\noption: {\n  height: '500px',  // 设置高度后表头自动固定\n  showHeader: true,  // 默认true\n}\n```\n\n---\n\n## 排序与过滤\n\n### 排序\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| sortable | boolean/string | - | 排序: true(本地)/custom(远程) |\n| defaultSort | object | - | 默认排序: {prop, order} |\n| columnSort | boolean | - | 列拖拽排序 |\n\n```javascript\n// 远程排序\n{\n  label: '日期',\n  prop: 'date',\n  sortable: 'custom',  // 需监听 sort-change 事件\n}\n\n// 默认排序\noption: {\n  defaultSort: { prop: 'date', order: 'descending' }\n}\n```\n\n### 过滤\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| filters | boolean/array | false | 数据过滤 |\n| filterMethod | function | - | 自定义过滤逻辑 |\n| filter-multiple | boolean | true | 多选过滤 |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  filters: true,  // 自动从 dicData 生成\n  // 或手动指定\n  filters: [\n    { text: '启用', value: 1 },\n    { text: '禁用', value: 0 },\n  ],\n}\n```\n\n---\n\n## 格式化与渲染\n\n### formatter 格式化\n\n```javascript\n{\n  label: '金额',\n  prop: 'amount',\n  formatter: (row, value, label, column) => {\n    return '¥' + value.toFixed(2)\n  },\n}\n```\n\n### tooltipFormatter (3.6.3+)\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  overHidden: true,\n  tooltipFormatter: (row, value, label, column) => {\n    return `详细内容: ${value}`\n  },\n}\n```\n\n### html 属性\n\n```javascript\n{\n  label: '内容',\n  prop: 'content',\n  html: true,  // 支持HTML翻译\n}\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  // 列区域渲染\n  render: (h, { row, index, dic }) => {\n    return h('el-tag', { type: row.status === 1 ? 'success' : 'danger' }, row.statusName)\n  },\n  // 表单区域渲染\n  renderForm: (h, { column, $index, dic }) => {\n    return h('el-input', { modelValue: form.value[column.prop] })\n  },\n  // 表头渲染\n  renderHeader: (h, { column }) => {\n    return h('span', { style: 'color: red' }, column.label)\n  },\n}\n```\n\n### 合计行\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },     // 求和\n    { name: 'price', type: 'average' },   // 平均值\n    { name: 'count', type: 'count' },     // 计数\n  ],\n}\n\n// 或自定义合计\n// 通过 summary-method 属性传入函数\n```\n\n### 合并行列\n\n```javascript\n// 通过 span-method 属性设置\n<avue-crud :span-method=\"spanMethod\">\n\nconst spanMethod = ({ row, column, rowIndex, columnIndex }) => {\n  if (rowIndex === 0 && columnIndex === 0) {\n    return { rowspan: 2, colspan: 1 }\n  }\n}\n```\n\n---\n\n## 搜索字段属性\n\n在 column 中通过 `search: true` 启用搜索，以下为搜索相关属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | string | - | 搜索初始值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索字段排序 |\n| searchGutter | number | - | 搜索字段间距 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索字段类型（覆盖 type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 搜索可清空 |\n| searchMultiple | boolean | false | 搜索多选 |\n| searchTags | boolean | false | 搜索标签显示 |\n| searchLabelPosition | string | 'left' | 搜索标签位置 |\n| searchRules | array | - | 搜索验证规则 |\n| searchDisabled | boolean | false | 搜索字段禁用 |\n| searchReadonly | boolean | false | 搜索字段只读 |\n| searchIndex | number | - | 搜索栏初始显示时包含此字段 |\n| searchControl | function | - | 搜索字段联动控制 |\n| renderSearch | function | - | 搜索区域自定义渲染函数 (3.4.2+) |\n\n### 搜索栏全局配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| searchBtnIcon | string | - | 搜索按钮图标 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| emptyBtnIcon | string | - | 清空按钮图标 |\n| searchShow | boolean | true | 初始显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留数量 |\n| searchMenuPosition | string | 'center' | 搜索按钮位置 |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示内容 |\n| searchTipPlacement | string | 'bottom' | 提示方向 |\n| searchMenuSpan | number | - | 搜索按钮栅格数 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索默认栅格数 |\n| searchGutter | number | - | 搜索默认间距 |\n| searchLabelWidth | number | 80 | 搜索默认标签宽度 |\n| searchLabelPosition | string | 'left' | 搜索默认标签位置 |\n\n---\n\n## 表单字段属性\n\ncolumn 中控制字段在表单（新增/编辑/查看弹窗）中表现的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| display | boolean | true | 在表单中显示 |\n| disabled | boolean | false | 在表单中禁用 |\n| addDisabled | boolean | false | 新增时禁用 |\n| editDisabled | boolean | false | 编辑时禁用 |\n| viewDisabled | boolean | false | 查看时禁用 |\n| addDisplay | boolean | true | 新增时显示 |\n| editDisplay | boolean | true | 编辑时显示 |\n| viewDisplay | boolean | true | 查看时显示 |\n| span | number | 12 | 表单栅格数 |\n| gutter | number | 0 | 表单间距 |\n| offset | number | 0 | 表单偏移 |\n| row | boolean | false | 表单换行 |\n| labelWidth | string | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| rules | array | - | 验证规则 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 帮助文字方向 |\n| labelTip | string | - | 标题帮助文字 |\n| labelTipPlacement | string | 'top' | 标题帮助文字方向 |\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n| render | function | - | 列区域渲染 (3.4.2+) |\n| renderForm | function | - | 表单区域渲染 (3.4.2+) |\n| renderHeader | function | - | 表头渲染 (3.4.2+) |\n\n<!-- File: .claude/skills/avue-design/references/crud-core.md (Tokens: ~5689 | Category: anthropic-skill) -->\n# avue-crud 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表格级属性](#option-表格级属性)\n3. [按钮配置](#按钮配置)\n4. [弹窗配置](#弹窗配置)\n5. [分页配置](#分页配置)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n获取表格原生引用：\n```javascript\nconst crudRef = ref(null)\n// 访问 el-table 实例\ncrudRef.value.$refs.table\n```\n\n---\n\n## 组件属性\n\n在 `<avue-crud>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| data | array | - | 表格显示数据 |\n| option | object | - | 组件配置项 |\n| before-open | function(done, type, loading) | - | 打开弹窗前回调，type: add/edit/view |\n| before-close | function(done, type) | - | 关闭弹窗前回调 |\n| permission | function/object | - | 按钮权限控制（支持行级） |\n| v-model:search | object | - | 搜索变量双向绑定 |\n| v-model:page | object | - | 分页变量双向绑定 |\n| cell-class-name | function/string | - | 单元格 className 回调 |\n| cell-style | function/object | - | 单元格样式回调 |\n| header-cell-class-name | function/string | - | 表头单元格 className |\n| header-row-class-name | function/string | - | 表头行 className |\n| header-row-style | function/object | - | 表头行样式 |\n| header-cell-style | function/object | - | 表头单元格样式 |\n| row-class-name | function/string | - | 行 className 回调 |\n| row-style | function/object | - | 行样式回调 |\n| span-method | function | - | 合并行或列 |\n| summary-method | function | - | 自定义合计计算 |\n| table-loading | boolean | - | 加载状态 |\n| upload-before | function(file, done, loading) | - | 上传组件上传前回调 |\n| upload-after | function(res, done) | - | 上传组件上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 上传预览回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制回调 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小回调 (3.4.4+) |\n| disabled | boolean | false | 禁用所有交互元素 |\n| width | number/string | - | 表格宽度 |\n| height | number/string | - | 表格高度 |\n\n---\n\n## Option 表格级属性\n\n在 `option` 对象中设置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 列配置数组 |\n| height | string | - | 表格高度，设 'auto' 为自适应窗口 |\n| maxHeight | string | - | 表格最大高度 |\n| title | string | - | 表格标题 |\n| border | boolean | false | 纵向边框 |\n| stripe | boolean | false | 斑马纹 |\n| fit | boolean | true | 列宽自适应 |\n| showHeader | boolean | true | 显示表头 |\n| header | boolean | false | 隐藏表格头部操作栏 |\n| emptyText | string | '暂无数据' | 空数据提示文字 |\n| tooltipEffect | string | 'dark' | tooltip 主题: dark/light |\n| tooltipOptions | object | - | tooltip 配置项 |\n| scrollbarAlwaysOn | boolean | true | 始终显示滚动条 (3.6.0+) |\n\n### 索引列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| index | boolean | false | 显示序号 |\n| indexLabel | string | '#' | 序号列标题 |\n| indexWidth | number | 50 | 序号列宽度 |\n| indexFixed | boolean/string | true | 序号列固定: true/left/right |\n| indexClassName | string | - | 序号列单元格自定义class |\n| indexLabelClassName | string | - | 序号列表头自定义class |\n\n### 选择列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| selection | boolean | false | 显示多选框 |\n| selectable | function | - | 判断行是否可选 |\n| selectionWidth | number | 50 | 选择列宽度 |\n| selectionFixed | boolean/string | true | 选择列固定 |\n| selectionClassName | string | - | 选择列单元格class |\n| selectionLabelClassName | string | - | 选择列表头class |\n| reserveSelection | boolean | false | 翻页保留选择 |\n\n### 展开行\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| expand | boolean | false | 显示展开 |\n| expandWidth | number | 50 | 展开列宽度 |\n| expandFixed | boolean/string | true | 展开列固定 |\n| expandClassName | string | - | 展开列单元格class |\n| expandLabelClassName | string | - | 展开列表头class |\n| defaultExpandAll | boolean | false | 默认展开所有行 |\n| expandRowKeys | array | - | 展开行的keys |\n\n### 行配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowKey | string | 'id' | 行数据主键 |\n| rowParentKey | string | 'parentId' | 父行主键(树表用) |\n| highlightCurrentRow | boolean | false | 高亮当前行 |\n| defaultSort | object | - | 默认排序 {prop, order: 'ascending'/'descending'} |\n| showOverflowTooltip | boolean/object | false | 溢出提示 |\n| showSummary | boolean | false | 显示合计行 |\n| lazy | boolean | false | 懒加载子节点 |\n\n### 菜单列\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menu | boolean | true | 显示操作菜单 |\n| menuWidth | number | 220 | 菜单列宽度 |\n| menuTitle | string | '操作' | 菜单列标题 |\n| menuFixed | boolean/string | true | 菜单列固定 |\n| menuType | string | 'text' | 菜单按钮样式: button/icon/text/menu |\n| menuHeaderAlign | string | 'center' | 菜单表头对齐 |\n| menuAlign | string | 'center' | 菜单按钮对齐 |\n| menuClassName | string | - | 菜单单元格class |\n| menuLabelClassName | string | - | 菜单表头class |\n\n### 卡片/网格模式 (3.4.0+)\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| grid | boolean | false | 启用网格模式 |\n| gridBtn | boolean | true | 显示模式切换按钮 |\n| gridSpan | number | 8 | 网格栅格数 |\n| gridBackgroundImage | string | - | 网格背景图片 |\n| gridBackground | string/function | - | 网格背景色 |\n\n### 工具栏与功能开关\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addRowBtn | boolean | false | 显示新增行内编辑按钮 |\n| cellBtn | boolean | false | 显示行内编辑操作按钮 |\n| dateBtn | boolean | false | 显示日期范围选择按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮 |\n| selectClearBtn | boolean | true | 显示清空选中按钮 |\n| tip | boolean | true | 显示选中项提示 |\n| page | boolean | true | 显示分页 |\n| searchShow | boolean | true | 搜索栏默认显示 |\n| searchIcon | boolean | true | 搜索栏展开/收起图标 |\n| searchIndex | number | - | 搜索栏初始显示字段数（超出折叠） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchSpan | number | 6 | 搜索栅格数 |\n| calcHeight | number | 300 | 表格自适应高度偏移量 |\n| menuXsWidth | number | 100 | 移动端菜单列宽度 |\n| gridXsSpan | number | 12 | 移动端网格栅格数 |\n| headerAlign | string | 'left' | 表头默认对齐方式 |\n| width | string | '100%' | 表格宽度 |\n| formFullscreen | boolean | false | 弹窗显示全屏切换按钮 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| filterMultiple | boolean | true | 过滤器默认多选 |\n\n---\n\n## 按钮配置\n\n### 操作按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| addBtnText | string | '新增' | 新增按钮文字 |\n| addBtnIcon | string | - | 新增按钮图标 |\n| editBtn | boolean | true | 修改按钮 |\n| editBtnText | string | '修改' | 修改按钮文字 |\n| editBtnIcon | string | - | 修改按钮图标 |\n| delBtn | boolean | true | 删除按钮 |\n| delBtnText | string | '删除' | 删除按钮文字 |\n| delBtnIcon | string | - | 删除按钮图标 |\n| viewBtn | boolean | false | 查看按钮 |\n| viewBtnText | string | '查看' | 查看按钮文字 |\n| viewBtnIcon | string | - | 查看按钮图标 |\n| copyBtn | boolean | false | 复制按钮 |\n\n### 弹窗按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| updateBtn | boolean | true | 弹窗修改按钮 |\n| updateBtnText | string | '修改' | 修改按钮文字 |\n| updateBtnIcon | string | - | 修改按钮图标 |\n| saveBtn | boolean | true | 弹窗保存按钮 |\n| saveBtnText | string | '保存' | 保存按钮文字 |\n| saveBtnIcon | string | - | 保存按钮图标 |\n| cancelBtn | boolean | true | 弹窗取消按钮 |\n| cancelBtnText | string | '取消' | 取消按钮文字 |\n| cancelBtnIcon | string | - | 取消按钮图标 |\n\n### 工具栏按钮\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| printBtn | boolean | false | 打印按钮 |\n| excelBtn | boolean | false | 导出按钮 |\n| filterBtn | boolean | false | 筛选按钮 |\n| refreshBtn | boolean | true | 刷新按钮 |\n| columnBtn | boolean | true | 列操作按钮 |\n| searchShowBtn | boolean | true | 搜索显隐按钮 |\n| addRowBtn | boolean | false | 新增行内编辑按钮 |\n| dateBtn | boolean | false | 日期范围选择按钮 |\n| gridBtn | boolean | true | 网格/表格切换按钮 |\n\n### 按钮文字图标规则\n\n按钮名 + `Text` = 按钮文字，按钮名 + `Icon` = 按钮图标。设置图标为 `' '`（空格）可禁用图标。\n\n---\n\n## 弹窗配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogDrag | boolean | false | 弹窗可拖拽 |\n| dialogFullscreen | boolean | false | 弹窗全屏 |\n| dialogCustomClass | string | - | 弹窗自定义class |\n| dialogEscape | boolean | true | ESC关闭弹窗 |\n| dialogClickModal | boolean | false | 点击遮罩关闭弹窗 |\n| dialogCloseBtn | boolean | true | 显示关闭按钮 |\n| dialogModal | boolean | true | 显示遮罩层 |\n| dialogMenuPosition | string | 'right' | 弹窗按钮位置 |\n| dialogTop | number | 25 | 弹窗距顶部距离 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogAppendToBody | boolean | true | 弹窗追加到body |\n| headerClass | string | - | 弹窗头部自定义class (3.7.0+) |\n| bodyClass | string | - | 弹窗内容区自定义class (3.7.0+) |\n| footerClass | string | - | 弹窗底部自定义class (3.7.0+) |\n\n---\n\n## 分页配置\n\n通过 `v-model:page` 绑定分页对象：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currentPage | number | 1 | 当前页码 |\n| pageSize | number | 20 | 每页条数 |\n| total | number | 0 | 总条数 |\n| pagerCount | number | 7 | 页码按钮数量 |\n| background | boolean | true | 分页按钮背景 |\n| layout | string | 'prev, pager, next, jumper, ->, total' | 分页组件布局 |\n| pageSizes | number[] | [10, 20, 30, 40, 50, 100] | 每页条数选项 |\n| simplePage | boolean | false | 只有一页时隐藏分页 |\n\n---\n\n## 事件\n\n### 数据操作事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-save | (row, done, loading) | 新增数据后触发。done() 关闭表单，loading() 保持表单 |\n| row-update | (row, index, done, loading) | 修改数据后触发 |\n| row-del | (row, index) | 删除行触发 |\n| refresh-change | - | 点击刷新按钮后触发 |\n\n### 搜索事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| search-change | (form, done) | 搜索后触发 |\n| search-reset | - | 清空搜索回调 |\n| date-change | (date) | 日期选择回调 |\n\n### 选择事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| select | (selection, row) | 用户勾选 |\n| select-all | (selection) | 用户全选 |\n| selection-change | (selection) | 选择项变化 |\n| current-row-change | (currentRow, oldCurrentRow) | 当前行变化 |\n| selection-clear | (selection) | 清空选中时触发 |\n\n### 行/单元格事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| cell-mouse-enter | (row, column, cell, event) | 单元格hover进入 |\n| cell-mouse-leave | (row, column, cell, event) | 单元格hover离开 |\n| cell-click | (row, column, cell, event) | 单元格点击 |\n| cell-dblclick | (row, column, cell, event) | 单元格双击 |\n| row-click | (row, column, event) | 行点击 |\n| row-contextmenu | (row, column, event) | 行右击 |\n| row-dblclick | (row, column, event) | 行双击 |\n\n### 表头事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| header-click | (column, event) | 表头点击 |\n| header-contextmenu | (column, event) | 表头右击 |\n| header-dragend | (newWidth, oldWidth, column, event) | 列宽改变 |\n\n### 排序/过滤事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| sort-change | ({ column, prop, order }) | 排序变化 |\n| filter-change | (filters) | 过滤变化 |\n| sortable-change | ({ newIndex, oldIndex }) | 行拖拽排序 |\n| column-sortable-change | ({ newIndex, oldIndex }) | 列拖拽排序 (3.5.2+) |\n\n### 其他事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| on-load | (page) | 页面加载/分页变化时触发 |\n| tree-load | (row, treeNode, resolve) | 加载子节点 (lazy=true) |\n| expand-change | (row, expandedRows/expanded) | 行展开/收起 |\n| grid-status-change | (status) | 网格/表格模式切换 (3.4.7+) |\n| scroll | ({scrollLeft, scrollTop}) | 表格滚动 (3.6.0+) |\n| tab-click | (tab, event) | 弹窗标签页点击 |\n| error | (error) | 表单验证失败 |\n| filter | (filterData) | 高级筛选应用 |\n| date-change | (date) | 日期范围选择回调 |\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowAdd | - | 打开新增弹窗 |\n| rowEdit | (row, index) | 打开编辑弹窗 |\n| rowView | (row, index) | 打开查看弹窗 |\n| updateDic | (prop, dic) | 更新字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | (valid, done, msg) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置所有字段 |\n| clearValidate | (props) | 清除验证结果 |\n| clearSelection | - | 清除多选 |\n| toggleSelection | (array) | 切换选择 |\n| toggleAllSelection | - | 切换全选 |\n| toggleRowExpansion | (row, expanded) | 切换行展开 |\n| setCurrentRow | (row) | 设置当前行 |\n| clearSort | - | 清除排序 |\n| clearFilter | (columnKey) | 清除过滤 |\n| doLayout | - | 重新布局表格 |\n| refreshTable | - | 重新初始化表格 |\n| sort | (prop, order) | 手动排序 |\n| updateKeyChildren | (key, data) | 更新懒加载子节点 (3.6.0+) |\n\n### 行内编辑方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| rowCopy | (row) | 复制行数据到新增弹窗 |\n| rowCellAdd | (row) | 新增一行行内编辑数据 |\n| rowCell | (row, index) | 切换行编辑状态 |\n| rowCellEdit | (row, index) | 进入行编辑模式 |\n| rowCellUpdate | (row, index) | 保存行编辑数据 |\n| rowCancel | (row, index) | 取消行编辑 |\n| closeDialog | (row, index) | 关闭弹窗并更新数据 |\n| validateCellForm | - | 验证所有行内编辑表单 |\n| validateCellField | (index) | 验证指定行的编辑表单 |\n\n### 数据方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| findData | (id) | 通过主键值查找行数据 |\n| getPermission | (key, row, index) | 获取按钮权限 |\n| getTableHeight | - | 重新计算表格高度 |\n\n### 滚动方法 (3.7.0+ 虚拟滚动)\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| scrollTo | (coordinates) | 滚动到指定位置 |\n| scrollToTop | - | 滚动到顶部 |\n| scrollToLeft | - | 滚动到最左侧 |\n| scrollRow | (rowKey) | 滚动到指定行（虚拟滚动模式） |\n| setScrollTop | (top) | 设置垂直滚动位置 |\n| setScrollLeft | (left) | 设置水平滚动位置 |\n\n### 搜索方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| searchReset | - | 重置搜索表单 |\n| searchChange | (form, done) | 手动触发搜索 |\n\n### 视图方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| handleGridShow | - | 切换网格/表格视图 |\n| columns | - | 获取当前列配置 |\n\n---\n\n## 插槽\n\n### 列相关插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义列内容，参数: {row, index, dic, size, label} |\n| `{prop}-header` | 自定义列表头 |\n| `{prop}-search` | 自定义搜索输入 |\n| `{prop}-form` | 自定义表单字段 |\n| `{prop}-label` | 自定义表单标签 |\n| `{prop}-error` | 自定义表单错误提示 |\n| `{prop}-desc` | 自定义表单描述 (3.6.4+) |\n| `{prop}-type` | 自定义字段类型选择器 |\n\n### 区域插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| search | 自定义整个搜索区域 |\n| searchMenu | 自定义搜索菜单 |\n| header | 自定义表格头部 |\n| body | 自定义表格主体 (3.4.3+) |\n| page | 自定义分页 |\n| empty | 自定义空状态 |\n| tip | 自定义选中提示区域 |\n\n### 按钮插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu-left | 自定义菜单左侧 |\n| menu-right | 自定义菜单右侧 |\n| menu | 自定义整个操作菜单，参数: {row, index, type, size} |\n| menu-before | 操作菜单前 (3.4.3+) |\n| menu-btn | 自定义菜单按钮 |\n| menu-btn-before | 菜单按钮前 (3.4.3+) |\n| menu-form | 自定义表单菜单 |\n| menu-form-before | 表单菜单前 (3.4.3+) |\n| menu-header | 菜单表头插槽 |\n\n<!-- File: .claude/skills/avue-design/references/crud-features.md (Tokens: ~4348 | Category: anthropic-skill) -->\n# avue-crud 高级功能参考\n\n## 目录\n1. [树形表格](#树形表格)\n2. [虚拟滚动](#虚拟滚动)\n3. [卡片/网格模式](#卡片网格模式)\n4. [行编辑（单元格编辑）](#行编辑)\n5. [导入导出](#导入导出)\n6. [拖拽排序](#拖拽排序)\n7. [合计行](#合计行)\n8. [父子表](#父子表)\n9. [加载状态](#加载状态)\n10. [CRUD 插件模式](#crud-插件模式)\n11. [行复制](#行复制)\n12. [高级筛选](#高级筛选-filterbtn)\n13. [列管理器](#列管理器-columnbtn)\n14. [Excel 导出对话框](#excel-导出对话框)\n15. [权限控制](#权限控制)\n\n---\n\n## 树形表格\n\n### 基本树表\n\n```javascript\noption: {\n  rowKey: 'id',\n  rowParentKey: 'parentId',\n  defaultExpandAll: true,  // 默认展开所有\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n  ],\n}\n\n// 数据需要有 id 和 parentId 字段\ndata: [\n  { id: 1, parentId: 0, name: '父节点', code: 'P1' },\n  { id: 2, parentId: 1, name: '子节点', code: 'C1' },\n]\n```\n\n### 懒加载树表\n\n```vue\n<avue-crud :option=\"option\" @tree-load=\"treeLoad\">\n\noption: {\n  rowKey: 'id',\n  lazy: true,\n  column: [\n    { label: '名称', prop: 'name', hasChildren: 'hasChildren' },\n  ],\n}\n\nconst treeLoad = (row, treeNode, resolve) => {\n  getChildren(row.id).then(res => {\n    resolve(res.data.data)\n  })\n}\n```\n\n注意：非懒加载时需去掉 `hasChildren` 字段。\n\n---\n\n## 虚拟滚动 (3.7.0+)\n\n用于大数据量场景的虚拟滚动：\n\n```javascript\noption: {\n  virtualize: true,\n  height: 500,  // 必须为数字\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      width: 200,   // 虚拟滚动时 width 必须为数字\n      fixed: true,   // 列自适应\n    },\n  ],\n}\n```\n\n注意：虚拟滚动模式下 `width` 和 `height` 必须是数字类型。\n\n自适应高度：\n```javascript\noption: {\n  virtualize: true,\n  height: 'auto',  // 自适应\n}\n```\n\n---\n\n## 卡片/网格模式 (3.4.0+)\n\n将表格显示为卡片网格形式：\n\n```javascript\noption: {\n  grid: true,           // 启用网格模式\n  gridBtn: true,        // 显示切换按钮（默认true）\n  gridSpan: 8,          // 每卡片占栅格数\n  gridBackgroundImage: 'url(xxx)',  // 背景图\n  gridBackground: '#f5f5f5',       // 背景色\n}\n\n// 背景色也可以是函数\noption: {\n  gridBackground: (row, index) => {\n    return index % 2 === 0 ? '#f0f9ff' : '#fff'\n  },\n}\n```\n\n自定义卡片插槽：\n```vue\n<template #default=\"{ row, index }\">\n  <div class=\"custom-card\">\n    <h3>{{ row.name }}</h3>\n    <p>{{ row.description }}</p>\n  </div>\n</template>\n```\n\n---\n\n## 行编辑\n\n### 基本行编辑\n\n```javascript\noption: {\n  cellBtn: true,     // 显示行编辑按钮\n  addRowBtn: true,   // 显示行新增按钮\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      cell: true,    // 该列可编辑\n    },\n    {\n      label: '状态',\n      prop: 'status',\n      type: 'select',\n      cell: true,\n      dicData: [\n        { label: '启用', value: 1 },\n        { label: '禁用', value: 0 },\n      ],\n    },\n  ],\n}\n```\n\n### 首次进入编辑模式\n\n```javascript\n// 数据中设置 $cellEdit: true\ndata: [\n  { id: 1, name: 'test', $cellEdit: true },\n]\n```\n\n### 单击/双击编辑\n\n```vue\n<!-- 单击编辑 -->\n<avue-crud @cell-click=\"cellClick\">\n\nconst cellClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n\n<!-- 双击编辑 -->\n<avue-crud @cell-dblclick=\"cellDblClick\">\n\nconst cellDblClick = (row, column, cell, event) => {\n  row.$cellEdit = true\n}\n```\n\n### 保存行编辑\n\n数据必须包含 `rowKey`（默认 id）字段才能保存。\n\n---\n\n## 导入导出\n\n### 导出 Excel\n\n```javascript\noption: {\n  excelBtn: true,  // 显示导出按钮\n}\n```\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n### 编程式导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 导入 Excel\n\n参考 `default/export/import` 示例。\n\n---\n\n## 拖拽排序\n\n### 行拖拽排序\n\n```javascript\noption: {\n  sortable: true,  // 启用行拖拽\n  rowKey: 'id',    // 必须设置主键\n}\n```\n\n需引入 Sortable.js：\n```html\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n```\n\n监听排序事件：\n```vue\n<avue-crud @sortable-change=\"sortableChange\">\n\nconst sortableChange = ({ newIndex, oldIndex }) => {\n  // 处理排序逻辑\n}\n```\n\n### 列拖拽排序 (3.5.2+)\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    columnSort: true,  // 启用列拖拽\n  },\n]\n```\n\n监听事件：\n```vue\n<avue-crud @column-sortable-change=\"columnSortableChange\">\n```\n\n---\n\n## 合计行\n\n### 内置合计\n\n```javascript\noption: {\n  showSummary: true,\n  sumColumnList: [\n    { name: 'amount', type: 'sum' },      // 求和\n    { name: 'price', type: 'average' },    // 平均值\n    { name: 'count', type: 'count' },      // 计数\n  ],\n}\n```\n\n### 自定义合计\n\n```vue\n<avue-crud :summary-method=\"summaryMethod\">\n\nconst summaryMethod = ({ columns, data }) => {\n  const sums = []\n  columns.forEach((column, index) => {\n    if (index === 0) {\n      sums[index] = '合计'\n      return\n    }\n    const values = data.map(item => Number(item[column.property]))\n    if (!values.every(isNaN)) {\n      sums[index] = values.reduce((a, b) => a + b, 0).toFixed(2)\n    } else {\n      sums[index] = '-'\n    }\n  })\n  return sums\n}\n```\n\n---\n\n## 父子表\n\n```vue\n<avue-crud :option=\"parentOption\" :data=\"parentData\" @row-click=\"parentRowClick\">\n  <!-- 子表通过展开行或独立组件实现 -->\n  <template #expand=\"{ row }\">\n    <avue-crud :option=\"childOption\" :data=\"row.children\" />\n  </template>\n</avue-crud>\n```\n\n或使用独立组件封装子表，通过 `crud-children` 模式。\n\n---\n\n## 加载状态\n\n```vue\n<avue-crud :table-loading=\"loading\">\n\nconst loading = ref(true)\n// 数据加载完成后\nloading.value = false\n```\n\n---\n\n## CRUD 插件模式\n\n### Mixin 模式（Options API）\n\n```javascript\n// @/mixins/crud.js\nexport default (app, option = {}) => {\n  let optionObj = import.meta.glob(`../option/**/**`)[`../option/${option.name}.js`]\n  let apiObj = import.meta.glob(`../api/**/**`)[`../api/${option.name}.js`]\n  // ... mixin 逻辑\n}\n\n// 全局注册\n// main.js\nimport crudCommon from '@/mixins/crud'\nwindow.$crudCommon = crudCommon\n\n// 使用\nexport default window.$crudCommon({\n  data() { return {} },\n  methods: {\n    listBefore() {},\n    listAfter() {},\n    addBefore() { this.form.createUser = 'admin' },\n    addAfter() {},\n    updateBefore() {},\n    updateAfter() {},\n    delBefore() {},\n    delAfter() {},\n  }\n}, {\n  name: 'system/params',\n  list: 'list',\n  update: 'update',\n  add: 'add',\n  del: 'del',\n  rowKey: 'id',\n  pageNumber: 'pageNumber',\n  pageSize: 'pageSize',\n  res: (data) => ({ total: data.total, data: data.records }),\n  total: 'total',\n  data: 'records',\n})\n```\n\n### 绑定辅助（Options API）\n\n```vue\n<avue-crud v-bind=\"bindVal\" v-on=\"onEvent\" v-model=\"form\" v-model:page=\"page\">\n\ncomputed: {\n  bindVal() {\n    return {\n      ref: 'crud',\n      option: this.option,\n      data: this.data,\n      tableLoading: this.loading,\n    }\n  },\n  onEvent() {\n    return {\n      'on-load': this.getList,\n      'row-save': this.rowSave,\n      'row-update': this.rowUpdate,\n      'row-del': this.rowDel,\n      'refresh-change': this.refreshChange,\n      'search-reset': this.searchChange,\n      'search-change': this.searchChange,\n    }\n  },\n}\n```\n\n### API 文件模板\n\n```javascript\n// @/api/module/entity.js\nimport request from '@/router/axios'\n\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/api/entity/list',\n    method: 'get',\n    params: { current, size, ...params },\n  })\n}\n\nexport const getDetail = (id) => {\n  return request({\n    url: '/api/entity/detail',\n    method: 'get',\n    params: { id },\n  })\n}\n\nexport const add = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const update = (data) => {\n  return request({\n    url: '/api/entity/submit',\n    method: 'post',\n    data,\n  })\n}\n\nexport const remove = (ids) => {\n  return request({\n    url: '/api/entity/remove',\n    method: 'post',\n    params: { ids },\n  })\n}\n```\n\n### Option 配置文件模板\n\n```javascript\n// @/option/module/entity.js\nexport default (safe) => {\n  // safe => vue 的 this 对象 (Options API)\n  return {\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    border: true,\n    stripe: true,\n    column: [\n      {\n        label: \"名称\",\n        prop: \"name\",\n        search: true,\n        rules: [{ required: true, message: \"请输入名称\", trigger: \"blur\" }],\n      },\n    ],\n  }\n}\n```\n\n---\n\n## 配置动态切换\n\n### 初始化加载\n\n```vue\n<avue-crud v-if=\"option.column\" :option=\"option\">\n\nconst option = ref({})\nonMounted(() => {\n  // 从服务器加载配置\n  getConfig().then(res => {\n    option.value = res.data\n  })\n})\n```\n\n### 动态切换配置\n\n```javascript\nconst changeOption = (type) => {\n  if (type === 'simple') {\n    option.value = simpleOption\n  } else {\n    option.value = fullOption\n  }\n}\n```\n\n---\n\n## 空状态自定义\n\n```javascript\noption: {\n  emptyText: '暂无数据',  // 空状态文字\n}\n```\n\n```vue\n<!-- 自定义空状态 -->\n<template #empty>\n  <div class=\"custom-empty\">\n    <img src=\"empty.png\" />\n    <p>暂无数据，请添加</p>\n  </div>\n</template>\n```\n\n---\n\n## 深层数据绑定\n\n```javascript\n// 数据结构: { id: 1, info: { address: '北京' } }\ncolumn: [\n  {\n    label: '地址',\n    prop: 'address',      // prop 仍然需要\n    bind: 'info.address', // 绑定深层路径\n  },\n]\n```\n\n---\n\n## 行复制\n\n通过 `rowCopy` 方法复制已有行数据到新增弹窗：\n\n```vue\n<template #menu=\"{ row, index, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowCopy(row)\">复制</el-button>\n</template>\n```\n\n需设置 `copyBtn: true` 在 option 中也可直接显示内置复制按钮。\n\n---\n\n## 高级筛选 (filterBtn)\n\n通过 `filterBtn: true` 启用高级筛选对话框，支持多条件组合查询：\n\n```javascript\noption: {\n  filterBtn: true,  // 显示筛选按钮\n}\n```\n\n### 筛选操作符\n\n| 符号 | 含义 |\n|-----|------|\n| = | 等于 |\n| ≠ | 不等于 |\n| like | 模糊匹配 |\n| > | 大于 |\n| ≥ | 大于等于 |\n| < | 小于 |\n| ≤ | 小于等于 |\n| ∈ | 包含于 |\n\n### 筛选事件\n\n```vue\n<avue-crud @filter=\"handleFilter\">\n\nconst handleFilter = (filterData) => {\n  // filterData 格式: [[prop, operator, value], ...]\n  // 示例: [['name', 'like', '张'], ['age', '>', 18]]\n  console.log('筛选条件:', filterData)\n}\n```\n\n---\n\n## 列管理器 (columnBtn)\n\n通过 `columnBtn: true`（默认开启）显示列管理按钮，可动态控制列的显隐、固定、过滤和排序：\n\n```javascript\noption: {\n  columnBtn: true,  // 显示列管理按钮（默认true）\n}\n```\n\n列管理器支持以下操作：\n- **显隐** - 控制列是否在表格中显示\n- **固定** - 设置列固定在左侧或右侧\n- **过滤** - 启用列的筛选功能\n- **排序** - 启用列的排序功能\n- **拖拽排序** - 拖拽调整列的显示顺序\n\n---\n\n## Excel 导出对话框\n\n当设置 `excelBtn: true` 时，点击导出按钮会打开导出配置对话框：\n\n### 导出选项\n\n| 选项 | 说明 |\n|-----|------|\n| 导出范围 | 导出全部数据 / 仅导出选中行 |\n| 包含表头 | 是否包含列标题行 |\n| 包含数据 | 是否包含数据行 |\n| 包含合计 | 是否包含合计/汇总行 |\n| 选择列 | 选择要导出的列 |\n\n### 编程式导出（跳过对话框）\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.excel({\n  title: '导出标题',\n  columns: option.column,\n  data: data.value,\n})\n```\n\n### 编程式导入\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n$Export.xlsx(file).then(({ header, results }) => {\n  console.log('表头:', header)\n  console.log('数据:', results)\n})\n```\n\n---\n\n## 权限控制\n\n### 对象模式\n\n```javascript\n// 通过 permission 对象控制按钮权限\nconst permissionList = {\n  addBtn: true,\n  editBtn: true,\n  delBtn: false,\n  viewBtn: true,\n}\n```\n\n### 函数模式（行级权限）\n\n```javascript\n// 通过函数实现行级权限控制\nconst permission = (key, row, index) => {\n  // key: 按钮标识 (addBtn/editBtn/delBtn/viewBtn)\n  // row: 当前行数据\n  // index: 当前行索引\n  if (key === 'editBtn') {\n    return row.status !== 'locked'\n  }\n  return true\n}\n```\n\n---\n\n## 自定义按钮\n\n```vue\n<!-- 隐藏默认按钮，使用自定义 -->\noption: { addBtn: false, editBtn: false, delBtn: false, viewBtn: false }\n\n<!-- 自定义新增按钮 -->\n<template #menu-left>\n  <el-button type=\"primary\" @click=\"$refs.crud.rowAdd()\">自定义新增</el-button>\n</template>\n\n<!-- 自定义操作按钮 -->\n<template #menu=\"{ row, index, type, size }\">\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowEdit(row, index)\">编辑</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowView(row, index)\">查看</el-button>\n  <el-button :size=\"size\" type=\"text\" @click=\"$refs.crud.rowDel(row, index)\">删除</el-button>\n</template>\n\n<!-- 自定义弹窗按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\">自定义保存</el-button>\n  <el-button v-if=\"type === 'edit'\">自定义更新</el-button>\n</template>\n```\n\n<!-- File: .claude/skills/avue-design/references/crud-form-dialog.md (Tokens: ~2208 | Category: anthropic-skill) -->\n# avue-crud 弹窗表单配置参考\n\n## 弹窗基本配置\n\noption 中的弹窗属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addTitle | string | '新增' | 新增弹窗标题 |\n| editTitle | string | '修改' | 修改弹窗标题 |\n| viewTitle | string | '查看' | 查看弹窗标题 |\n| dialogType | string | 'dialog' | 弹窗类型: dialog/drawer |\n| dialogWidth | string | '60%' | 弹窗宽度 |\n| dialogTop | number | 25 | 弹窗距顶部 |\n| dialogDrag | boolean | false | 可拖拽 |\n| dialogFullscreen | boolean | false | 全屏 |\n| dialogCustomClass | string | - | 自定义class |\n| dialogEscape | boolean | true | ESC关闭 |\n| dialogClickModal | boolean | false | 点击遮罩关闭 |\n| dialogCloseBtn | boolean | true | 关闭按钮 |\n| dialogModal | boolean | true | 遮罩层 |\n| dialogMenuPosition | string | 'right' | 按钮位置: left/center/right |\n| dialogDirection | string | 'rtl' | 抽屉方向: rtl/ltr/ttb/tbb |\n\n## 打开/关闭回调\n\n```vue\n<avue-crud :before-open=\"beforeOpen\" :before-close=\"beforeClose\">\n\n<!-- Composition API -->\nconst beforeOpen = (done, type, loading) => {\n  // type: 'add' | 'edit' | 'view'\n  if (['edit', 'view'].includes(type)) {\n    // 加载详情数据\n    loading()  // 显示loading (3.5.2+)\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data\n      done()   // 完成加载\n    })\n  } else {\n    done()\n  }\n}\n\nconst beforeClose = (done, type) => {\n  done()\n}\n```\n\n## 表单验证规则\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    rules: [\n      { required: true, message: '请输入名称', trigger: 'blur' },\n      { min: 2, max: 20, message: '长度在2到20之间', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '邮箱',\n    prop: 'email',\n    rules: [\n      { required: true, message: '请输入邮箱', trigger: 'blur' },\n      { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n    ],\n  },\n  {\n    label: '年龄',\n    prop: 'age',\n    type: 'number',\n    rules: [{\n      required: true,\n      validator: (rule, value, callback) => {\n        if (value < 18) {\n          callback(new Error('年龄不能小于18岁'))\n        } else {\n          callback()\n        }\n      },\n      trigger: 'blur',\n    }],\n  },\n]\n```\n\n验证规则基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n## 字段显示/禁用控制\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    // 全局控制\n    display: true,       // 是否显示\n    disabled: false,     // 是否禁用\n    // 按操作类型控制\n    addDisplay: true,    // 新增时显示\n    editDisplay: true,   // 编辑时显示\n    viewDisplay: true,   // 查看时显示\n    addDisabled: false,  // 新增时禁用\n    editDisabled: true,  // 编辑时禁用（如ID不可编辑）\n    viewDisabled: false,\n  },\n]\n```\n\n## 表单默认值\n\n```javascript\ncolumn: [\n  {\n    label: '状态',\n    prop: 'status',\n    type: 'select',\n    value: 1,  // 新增时默认值\n    dicData: [\n      { label: '启用', value: 1 },\n      { label: '禁用', value: 0 },\n    ],\n  },\n]\n```\n\n## 字段联动控制 (control)\n\n```javascript\ncolumn: [\n  {\n    label: '类型',\n    prop: 'type',\n    type: 'select',\n    dicData: [\n      { label: '文章', value: 1 },\n      { label: '链接', value: 2 },\n    ],\n    control: (val, form) => {\n      if (val === 1) {\n        return {\n          content: { display: true },\n          url: { display: false },\n        }\n      } else {\n        return {\n          content: { display: false },\n          url: { display: true },\n        }\n      }\n    },\n  },\n  { label: '内容', prop: 'content', type: 'textarea' },\n  { label: '链接', prop: 'url', display: false },\n]\n```\n\n3.5.0+ 支持 Promise：\n```javascript\ncontrol: async (val, form) => {\n  const res = await getConfig(val)\n  return { subType: { dicData: res.data } }\n}\n```\n\n## 表单分组 (group)\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,  // 默认展开\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name' },\n        { label: '编码', prop: 'code' },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', type: 'textarea' },\n      ],\n    },\n  ],\n  // 使用标签页显示分组\n  tabs: true,\n  // column 也要配置（表格列用）\n  column: [\n    { label: '名称', prop: 'name' },\n    { label: '编码', prop: 'code' },\n    { label: '描述', prop: 'description' },\n  ],\n}\n```\n\n## 表单标签宽度\n\n```javascript\noption: {\n  labelWidth: 120,        // 全局标签宽度\n  labelPosition: 'right', // 全局标签位置: left/right/top\n}\n\n// 也可在 column 中单独设置\n{\n  label: '很长的标签名称',\n  prop: 'longLabel',\n  labelWidth: 180,\n}\n```\n\n## 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤字典翻译字段（$开头的字段）\n  filterNull: true,  // 过滤空值字段\n}\n```\n\n## 防止重复提交\n\n```javascript\n// row-save 和 row-update 中的 done 和 loading 参数：\n// - done(): 关闭表单弹窗\n// - loading(): 关闭loading但保持表单打开（用于接口报错时）\n\nconst rowSave = (row, done, loading) => {\n  add(row).then(() => {\n    ElMessage.success('操作成功')\n    done()     // 关闭弹窗\n    onLoad()\n  }).catch(() => {\n    loading()  // 关闭loading，保持弹窗\n  })\n}\n```\n\n## 表单字段排序\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', order: 1 },\n  { label: '编码', prop: 'code', order: 3 },  // order大的排前面\n  { label: '状态', prop: 'status', order: 2 },\n]\n```\n\n## 获取字段组件引用\n\n```javascript\nconst ref = crudRef.value.getPropRef('name')\n// ref 即为该字段对应的组件实例\n```\n\n## 连续新增（不关闭弹窗）\n\n通过不调用 `done()` 来保持弹窗打开，实现连续新增。\n\n## 自定义表单插槽\n\n```vue\n<!-- 自定义表单字段 -->\n<template #name-form=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"form.name\" />\n</template>\n\n<!-- 自定义表单标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义错误提示 -->\n<template #name-error=\"{ error }\">\n  <span style=\"color: orange\">{{ error }}</span>\n</template>\n\n<!-- 自定义描述 (3.6.4+) -->\n<template #name-desc=\"{ column }\">\n  <span>这是一段描述文字</span>\n</template>\n\n<!-- 自定义表单按钮 -->\n<template #menu-form=\"{ type }\">\n  <el-button v-if=\"type === 'add'\" type=\"primary\">自定义保存</el-button>\n</template>\n```\n\n## 权限控制\n\n```javascript\n// 对象形式\n<avue-crud :permission=\"{ addBtn: true, editBtn: true, delBtn: false }\">\n\n// 函数形式（行级控制）\n<avue-crud :permission=\"getPermission\">\n\nconst getPermission = (key, row, index) => {\n  // key: 'addBtn' | 'editBtn' | 'delBtn' | 'viewBtn' | 'menu'\n  if (key === 'editBtn' && row?.status === 1) {\n    return false  // 已启用的不允许编辑\n  }\n  return true\n}\n```\n\n<!-- File: .claude/skills/avue-design/references/crud-search.md (Tokens: ~1254 | Category: anthropic-skill) -->\n# avue-crud 搜索配置参考\n\n## 基本用法\n\n在 column 中设置 `search: true` 开启搜索字段：\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    search: true,\n    searchSpan: 6,\n  },\n  {\n    label: '日期',\n    prop: 'date',\n    type: 'daterange',\n    search: true,\n    searchRange: true,\n    searchSpan: 8,\n  },\n]\n```\n\n## 搜索变量\n\nComposition API 使用 `v-model:search` 自动同步：\n```vue\n<avue-crud v-model:search=\"search\" @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nconst search = ref({})\nconst searchChange = (params, done) => {\n  page.value.currentPage = 1\n  onLoad()\n  done()\n}\nconst searchReset = () => {\n  onLoad()\n}\n```\n\nOptions API 使用 query 变量手动同步：\n```vue\n<avue-crud @search-change=\"searchChange\" @search-reset=\"searchReset\">\n\nsearchChange(params, done) {\n  this.query = params\n  this.page.currentPage = 1\n  this.onLoad(this.page, params)\n  done()\n},\nsearchReset() {\n  this.query = {}\n  this.onLoad(this.page)\n},\n```\n\n## 搜索栏配置\n\n### 全局搜索属性（option 级别）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| searchBtn | boolean | true | 显示搜索按钮 |\n| searchBtnText | string | '搜索' | 搜索按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyBtnText | string | '清空' | 清空按钮文字 |\n| searchShow | boolean | true | 首次加载显示搜索 |\n| searchIcon | boolean | false | 显示折叠按钮 |\n| searchIndex | number | 2 | 折叠保留字段数 |\n| searchMenuPosition | string | 'center' | 按钮位置: left/center/right |\n| searchShowBtn | boolean | true | 显示搜索显隐按钮 |\n| searchTip | string | - | 提示文字 |\n| searchMenuSpan | number | - | 按钮栅格（单独一行时设24） |\n| searchSpan | number | 8 | 默认搜索栅格 |\n| searchGutter | number | - | 默认搜索间距 |\n| searchLabelWidth | number | 80 | 默认搜索标签宽度 |\n| searchLabelPosition | string | 'left' | 默认搜索标签位置 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n\n### 列级搜索属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| search | boolean | false | 启用搜索 |\n| searchValue | any | - | 搜索默认值 |\n| searchPlaceholder | string | - | 搜索占位文字 |\n| searchSpan | number | 8 | 搜索栅格数 |\n| searchOrder | number | - | 搜索排序 |\n| searchRange | boolean | false | 范围搜索 |\n| searchType | string | - | 搜索类型（覆盖type） |\n| searchLabelWidth | number | 80 | 搜索标签宽度 |\n| searchClearable | boolean | false | 可清空 |\n| searchMultiple | boolean | false | 多选 |\n| searchTags | boolean | false | 标签显示 |\n| searchLabelPosition | string | 'left' | 标签位置 |\n| searchRules | array | - | 验证规则 |\n\n## 搜索字段类型覆盖\n\n可以用 searchType 让搜索框使用不同于表格列的类型：\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  type: 'date',           // 表格/表单中是日期选择\n  search: true,\n  searchType: 'daterange', // 搜索中是日期范围\n  searchRange: true,\n}\n```\n\n## 搜索折叠\n\n```javascript\noption: {\n  searchIcon: true,    // 显示折叠按钮\n  searchIndex: 2,      // 折叠时保留2个字段\n}\n```\n\n## 搜索级联联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/province',\n    cascader: ['city'],\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    search: true,\n    dicUrl: '/api/city?province={{key}}',\n  },\n]\n```\n\n## 自定义搜索插槽\n\n```vue\n<!-- 自定义搜索菜单 -->\n<template #searchMenu=\"{ row, search, reset }\">\n  <el-button type=\"primary\" @click=\"search\">搜索</el-button>\n  <el-button @click=\"reset\">重置</el-button>\n  <el-button type=\"warning\" @click=\"handleExport\">导出</el-button>\n</template>\n\n<!-- 自定义搜索字段 -->\n<template #name-search=\"{ search }\">\n  <el-input v-model=\"search.name\" placeholder=\"自定义搜索\" />\n</template>\n```\n\n## 日期快捷按钮\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  dateBtn: true,  // 显示日期快捷按钮\n}\n```\n\n<!-- File: .claude/skills/avue-design/references/data-display.md (Tokens: ~749 | Category: anthropic-skill) -->\n# Avue 数据展示组件参考\n\n## 组件列表\n\n| 组件 | 说明 |\n|-----|------|\n| avue-data-display | 数据展示卡片（核心组件） |\n| avue-data-pay | 支付数据 |\n| avue-data-tabs | 标签数据 |\n| avue-data-box | 数据盒子 |\n| avue-data-card | 数据卡片 |\n| avue-data-cardtext | 文字卡片 |\n| avue-data-icons | 图标数据 |\n| avue-data-operatext | 操作文字 |\n| avue-data-progress | 进度数据 |\n| avue-data-rotate | 旋转数据 |\n| avue-data-imgtext | 图文数据 |\n| avue-data-panel | 面板 |\n| avue-data-price | 价格 |\n| avue-data-countdown | 倒计时 |\n| avue-data-dashboard | 仪表盘 |\n| avue-data-list | 数据列表 |\n| avue-data-notice | 公告 |\n| avue-data-product | 商品卡片 |\n| avue-data-profile | 人员档案 |\n| avue-data-rank | 排行榜 |\n| avue-data-statistic | 统计值 |\n| avue-data-task | 任务列表 |\n| avue-data-weather | 天气卡片 |\n\n## avue-data-display（核心组件）\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| animation | boolean | true | 是否动画 |\n| decimals | number | 0 | 小数位数 |\n| span | string | '8' | 栅格数 |\n| data | array | - | 数据数组 |\n\n### 数据结构\n\n```javascript\nconst data = [\n  {\n    title: '今日访问',\n    count: 1234,\n    icon: 'el-icon-view',\n    color: '#40c9c6',\n    decimals: 0,\n  },\n  {\n    title: '销售额',\n    count: 56789.50,\n    icon: 'el-icon-money',\n    color: '#36a3f7',\n    decimals: 2,\n  },\n]\n```\n\n### 使用示例\n\n```vue\n<avue-data-display :data=\"displayData\" :option=\"{ decimals: 0, animation: true }\" />\n```\n\n## 通用使用模式\n\n大部分数据展示组件遵循相同模式：\n\n```vue\n<component :data=\"data\" :option=\"option\">\n  <!-- 可选插槽自定义 -->\n</component>\n```\n\n各组件的具体配置请参考对应的示例页面：\n- `data/data0` ~ `data/data12` - 基础数据组件示例\n- `data/countdown` - 倒计时\n- `data/dashboard` - 仪表盘\n- `data/list` - 数据列表\n- `data/notice` - 公告\n- `data/product` - 商品卡片\n- `data/profile` - 人员档案\n- `data/rank` - 排行榜\n- `data/statistic` - 统计值\n- `data/task` - 任务列表\n- `data/weather` - 天气卡片\n\n<!-- File: .claude/skills/avue-design/references/form-advanced.md (Tokens: ~3670 | Category: anthropic-skill) -->\n# avue-form 高级功能参考\n\n## 目录\n1. [字典配置](#字典配置)\n2. [验证规则](#验证规则)\n3. [动态子表单](#动态子表单)\n4. [文件上传](#文件上传)\n5. [字段联动](#字段联动)\n6. [数据格式](#数据格式)\n7. [自定义插槽](#自定义插槽)\n8. [Mock数据](#mock数据)\n\n---\n\n## 字典配置\n\n### 安装配置\n\n字典组件需要 axios：\n```javascript\nimport axios from 'axios'\napp.use(Avue, { axios })\n```\n\n### 本地字典\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [\n    { label: '类型A', value: 1 },\n    { label: '类型B', value: 2, disabled: true },\n  ],\n}\n```\n\n3.5.0+ 支持函数和 Promise：\n```javascript\n{\n  dicData: () => {\n    return [{ label: 'A', value: 1 }]\n  },\n  // 或\n  dicData: async () => {\n    const res = await fetchOptions()\n    return res.data\n  },\n}\n```\n\n### 远程字典\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'select',\n  dicUrl: '/api/dept/list',\n  dicMethod: 'GET',      // 请求方法，默认GET\n  dicQuery: { status: 1 }, // 请求参数\n  dicHeaders: {},          // 请求头\n  props: {\n    label: 'name',\n    value: 'id',\n    children: 'children',\n    res: 'data.records',  // 响应数据层级\n  },\n}\n```\n\n### 字典格式化\n\n```javascript\n{\n  dicUrl: '/api/dict/list',\n  dicFormatter: (res) => {\n    // 自定义处理响应数据\n    return res.data.map(item => ({\n      label: item.dictValue,\n      value: item.dictKey,\n    }))\n  },\n}\n```\n\n### 字典联动\n\n```javascript\ncolumn: [\n  {\n    label: '省份',\n    prop: 'province',\n    type: 'select',\n    dicUrl: '/api/province',\n    cascader: ['city', 'district'],  // 指定子级字段\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    type: 'select',\n    dicUrl: '/api/city?province={{key}}',  // {{key}} = 父级选中值\n    cascader: ['district'],\n  },\n  {\n    label: '区县',\n    prop: 'district',\n    type: 'select',\n    dicUrl: '/api/district?city={{key}}',\n  },\n]\n```\n\n### 动态更新字典\n\n```javascript\n// 更新指定字段字典\nformRef.value.updateDic('type', [\n  { label: '新选项A', value: 'a' },\n  { label: '新选项B', value: 'b' },\n])\n\n// 重新加载所有字典\nformRef.value.dicInit()\n```\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 值存储为字符串\n  separator: ',',         // 分隔符，默认逗号\n  // 实际存储: \"1,2,3\"\n  // 组件显示: [1, 2, 3]\n}\n\n{\n  label: '年龄',\n  prop: 'age',\n  dataType: 'number',    // 强制转数字\n}\n```\n\n---\n\n## 验证规则\n\n基于 [async-validator](https://github.com/yiminghe/async-validator) 库。\n\n### 基本验证\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  rules: [\n    { required: true, message: '请输入名称', trigger: 'blur' },\n    { min: 2, max: 20, message: '长度在2-20之间', trigger: 'blur' },\n  ],\n}\n```\n\n### 类型验证\n\n```javascript\nrules: [\n  { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' },\n  { type: 'url', message: '请输入正确的URL', trigger: 'blur' },\n  { type: 'number', message: '请输入数字', trigger: 'blur' },\n]\n```\n\n### 自定义验证\n\n```javascript\n{\n  label: '密码',\n  prop: 'password',\n  rules: [{\n    required: true,\n    validator: (rule, value, callback) => {\n      if (!value) {\n        callback(new Error('请输入密码'))\n      } else if (value.length < 6) {\n        callback(new Error('密码长度不能少于6位'))\n      } else {\n        callback()\n      }\n    },\n    trigger: 'blur',\n  }],\n}\n```\n\n### 表单验证方法\n\n```javascript\n// 验证整个表单\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败\n    console.log('错误字段:', fields)\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'email'], (errorMsg) => {\n  if (!errorMsg) console.log('验证通过')\n})\n\n// 清除验证\nformRef.value.clearValidate()            // 清除所有\nformRef.value.clearValidate(['name'])    // 清除指定字段\n\n// 重置表单\nformRef.value.resetFields()\n```\n\n### 搜索验证 (CRUD)\n\n```javascript\n{\n  label: '日期',\n  prop: 'date',\n  search: true,\n  searchRules: [{ required: true, message: '请选择日期', trigger: 'change' }],\n}\n```\n\n---\n\n## 动态子表单\n\n### 表格模式\n\n```javascript\n{\n  label: '订单明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    index: true,\n    column: [\n      {\n        label: '商品名称',\n        prop: 'productName',\n        rules: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],\n      },\n      {\n        label: '数量',\n        prop: 'quantity',\n        type: 'number',\n        min: 1,\n      },\n      {\n        label: '单价',\n        prop: 'price',\n        type: 'number',\n        precision: 2,\n      },\n    ],\n  },\n}\n```\n\n### 表单模式\n\n```javascript\n{\n  label: '联系方式',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  limit: 5,  // 最多5条\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 8 },\n      { label: '电话', prop: 'phone', span: 8 },\n      { label: '职位', prop: 'position', span: 8 },\n    ],\n  },\n}\n```\n\n### 自定义增删\n\n```javascript\n{\n  type: 'dynamic',\n  rowAdd: (done) => {\n    // 自定义新增逻辑\n    done({ name: '默认值', quantity: 1 })\n  },\n  rowDel: (row, done) => {\n    ElMessageBox.confirm('确定删除?').then(() => done())\n  },\n}\n```\n\n### 父子联动\n\n动态子表单支持与父表单字段联动。\n\n---\n\n## 文件上传\n\n### 基本上传\n\n```javascript\n{\n  label: '附件',\n  prop: 'file',\n  type: 'upload',\n  action: '/api/upload',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n    fileName: 'file',\n  },\n}\n```\n\n### 上传回调\n\n```vue\n<avue-form\n  :upload-before=\"uploadBefore\"\n  :upload-after=\"uploadAfter\"\n  :upload-delete=\"uploadDelete\"\n  :upload-preview=\"uploadPreview\"\n  :upload-error=\"uploadError\"\n  :upload-exceed=\"uploadExceed\"\n>\n\nconst uploadBefore = (file, done, loading) => {\n  // 验证文件\n  if (file.size > 10 * 1024 * 1024) {\n    ElMessage.error('文件不能超过10MB')\n    loading()  // 中断上传\n    return\n  }\n  done(file)  // 继续上传\n}\n\nconst uploadAfter = (res, done) => {\n  // 上传完成\n  done()\n}\n\nconst uploadDelete = (file, column) => {\n  // 返回 false 或 rejected Promise 阻止删除\n  return new Promise((resolve, reject) => {\n    ElMessageBox.confirm('确定删除?')\n      .then(() => resolve())\n      .catch(() => reject())\n  })\n}\n\nconst uploadPreview = (file, column, done) => {\n  // 自定义预览\n  window.open(file.url)\n}\n\nconst uploadError = (error, column) => {\n  ElMessage.error('上传失败')\n}\n\nconst uploadExceed = (limit, files, fileList, column) => {\n  ElMessage.warning(`最多上传${limit}个文件`)\n}\n```\n\n### 图片水印\n\n```javascript\n{\n  type: 'upload',\n  canvasOption: {\n    text: '公司名称',\n    fontFamily: 'microsoft yahei',\n    color: 'rgba(0,0,0,0.3)',\n    fontSize: 20,\n    opacity: 50,\n    bottom: 20,\n    right: 20,\n    ratio: 0.8,\n  },\n}\n```\n\n### 云存储上传\n\n全局配置见 `global-config.md`。\n\n```javascript\n// 阿里云OSS\n{ type: 'upload', oss: 'ali' }\n\n// 七牛云\n{ type: 'upload', oss: 'qiniu' }\n\n// 腾讯COS\n{ type: 'upload', oss: 'cos' }\n```\n\n### 数据格式\n\n```javascript\n// 字符串格式（逗号分隔URL）\n{ type: 'upload', dataType: 'string' }\n\n// 对象格式\n{\n  type: 'upload',\n  dataType: 'object',\n  props: { label: 'name', value: 'url' },\n}\n\n// JSON字符串格式\n{ type: 'upload', dataType: 'json' }\n```\n\n---\n\n## 字段联动\n\n### control 属性\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'radio',\n  dicData: [\n    { label: '个人', value: 1 },\n    { label: '企业', value: 2 },\n  ],\n  control: (val, form) => {\n    if (val === 1) {\n      return {\n        idCard: { display: true, rules: [{ required: true }] },\n        companyName: { display: false },\n        businessLicense: { display: false },\n      }\n    } else {\n      return {\n        idCard: { display: false },\n        companyName: { display: true, rules: [{ required: true }] },\n        businessLicense: { display: true },\n      }\n    }\n  },\n},\n{ label: '身份证', prop: 'idCard' },\n{ label: '企业名称', prop: 'companyName', display: false },\n{ label: '营业执照', prop: 'businessLicense', type: 'upload', display: false },\n```\n\n### control 返回值可控制的属性\n\n可以动态控制字段的以下属性：\n- `display` - 显示/隐藏\n- `disabled` - 启用/禁用\n- `rules` - 验证规则\n- `dicData` - 字典数据\n- `label` - 标签文字\n- 其他 column 属性\n\n### Promise 支持 (3.5.0+)\n\n```javascript\ncontrol: async (val, form) => {\n  const res = await getSubOptions(val)\n  return {\n    subType: {\n      dicData: res.data,\n      display: true,\n    },\n  }\n}\n```\n\n### 获取组件引用\n\n```javascript\nconst ref = formRef.value.getPropRef('tableProp')\n// 可用于操作 input-table 等复杂组件\n```\n\n---\n\n## 数据格式\n\n### 深层数据绑定\n\n```javascript\n// 数据: { id: 1, info: { name: '张三', address: { city: '北京' } } }\ncolumn: [\n  {\n    label: '姓名',\n    prop: 'name',\n    bind: 'info.name',\n  },\n  {\n    label: '城市',\n    prop: 'city',\n    bind: 'info.address.city',\n  },\n]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,   // 过滤 $xxx 字典翻译字段\n  filterNull: true,  // 过滤空值字段\n  filterParams: ['id'], // 不被清空的字段（默认包含 rowKey）\n}\n```\n\n---\n\n## 自定义插槽\n\n### 字段内容插槽\n\n```vue\n<avue-form v-model=\"form\" :option=\"option\">\n  <!-- 自定义字段 -->\n  <template #name=\"{ value, column, dic, size, disabled }\">\n    <el-input v-model=\"form.name\" :disabled=\"disabled\" />\n  </template>\n\n  <!-- 自定义标签 -->\n  <template #name-label=\"{ column }\">\n    <span style=\"color: red\">*</span> {{ column.label }}\n  </template>\n\n  <!-- 自定义错误 -->\n  <template #name-error=\"{ error }\">\n    <span style=\"color: orange\">{{ error }}</span>\n  </template>\n\n  <!-- 自定义描述 (3.6.4+) -->\n  <template #name-desc=\"{ column }\">\n    <el-text type=\"info\">请输入真实姓名</el-text>\n  </template>\n\n  <!-- 自定义按钮 -->\n  <template #menu-form>\n    <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n    <el-button @click=\"handleDraft\">保存草稿</el-button>\n  </template>\n</avue-form>\n```\n\n### render 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-switch', {\n      modelValue: form.value.status,\n      'onUpdate:modelValue': (val) => { form.value.status = val },\n    })\n  },\n}\n```\n\n### 第三方组件集成\n\n```vue\n<template #editor=\"{ value, column }\">\n  <vue-editor v-model=\"form.content\" />\n</template>\n```\n\n### className 样式控制\n\n```javascript\n{\n  label: '名称',\n  prop: 'name',\n  className: 'highlight-field',\n}\n```\n\n```css\n.highlight-field {\n  background: #fef0f0;\n  border-left: 3px solid #f56c6c;\n}\n```\n\n---\n\n## Mock数据\n\n需引入 Mock.js：\n```html\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n\n```javascript\noption: {\n  mockBtn: true,  // 显示Mock按钮\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      mock: { type: 'cname' },  // 中文姓名\n    },\n    {\n      label: '邮箱',\n      prop: 'email',\n      mock: { type: 'email' },\n    },\n    {\n      label: '地址',\n      prop: 'address',\n      mock: { type: 'county', prefix: true },\n    },\n  ],\n}\n\n// 或整体 mock 函数\noption: {\n  mock: () => ({\n    name: '张三',\n    email: 'zhangsan@test.com',\n    address: '北京市朝阳区',\n  }),\n}\n```\n\n<!-- File: .claude/skills/avue-design/references/form-core.md (Tokens: ~3749 | Category: anthropic-skill) -->\n# avue-form 核心配置参考\n\n## 目录\n1. [组件属性](#组件属性)\n2. [Option 表单级属性](#option-表单级属性)\n3. [布局配置](#布局配置)\n4. [按钮配置](#按钮配置)\n5. [Column 通用属性](#column-通用属性)\n6. [事件](#事件)\n7. [方法](#方法)\n8. [插槽](#插槽)\n\n## 全局配置\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    // 所有 option 属性都可在此全局设置\n  }\n})\n```\n\n---\n\n## 组件属性\n\n在 `<avue-form>` 标签上设置的属性：\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| modelValue / v-model | object | - | 表单绑定值 |\n| option | object | - | 表单配置项 |\n| upload-before | function(file, done, loading) | - | 上传前回调 |\n| upload-after | function(res, done) | - | 上传后回调 |\n| upload-delete | function(file, column) | - | 删除文件钩子 |\n| upload-preview | function(file, column, done) | - | 预览文件回调 |\n| upload-error | function(error, column) | - | 上传错误回调 |\n| upload-exceed | function(limit, files, fileList, column) | - | 超出文件数限制 |\n| upload-size | function(fileSize, files, fileList, column) | - | 超出文件大小 (3.4.4+) |\n| v-model:status | boolean | false | 表单提交状态（禁用状态双向绑定） |\n\n---\n\n## Option 表单级属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| size | string | 'small' | 组件尺寸: medium/small/mini |\n| column | array | - | 字段配置数组 |\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置: left/right/top |\n| labelSuffix | string | ':' | 标签后缀 |\n| enter | boolean | false | 回车提交 |\n| group | array | - | 分组配置 |\n| tabs | boolean | false | 分组标签页显示 |\n| detail | boolean | false | 详情/只读模式 |\n| disabled | boolean | false | 全部禁用 |\n| readonly | boolean | false | 全部只读 |\n| formWidth | string | - | 表单容器宽度 |\n| tabsActive | number | 0 | 默认激活标签页索引 |\n| tabsType | string | - | 标签页类型(同 el-tabs type) |\n| tabsVerifyAll | boolean | false | 提交时验证所有标签页（默认仅当前页） |\n| filterDic | boolean | false | 过滤 $ 开头的字典翻译字段 |\n| filterNull | boolean | false | 过滤空值字段 |\n| filterParams | array | - | 重置时保留的字段 |\n\n---\n\n## 布局配置\n\n### 栅格布局\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 字段占用栅格数 (24栅格) |\n| gutter | number | 0 | 栅格间距 |\n| offset | number | 0 | 栅格左偏移 |\n| push | number | 0 | 栅格右移动 |\n| pull | number | 0 | 栅格左移动 |\n| row | boolean | false | 是否新起一行 |\n\n```javascript\ncolumn: [\n  { label: '名称', prop: 'name', span: 12 },\n  { label: '编码', prop: 'code', span: 12 },\n  { label: '备注', prop: 'remark', span: 24, row: true },\n]\n```\n\n### 分组显示\n\n```javascript\noption: {\n  group: [\n    {\n      label: '基本信息',\n      icon: 'el-icon-info',\n      collapse: false,\n      prop: 'base',\n      column: [\n        { label: '名称', prop: 'name', span: 12 },\n        { label: '编码', prop: 'code', span: 12 },\n      ],\n    },\n    {\n      label: '详细信息',\n      icon: 'el-icon-document',\n      prop: 'detail',\n      column: [\n        { label: '描述', prop: 'description', span: 24 },\n      ],\n    },\n  ],\n}\n```\n\n#### Group 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 分组标题 |\n| prop | string | - | 分组标识 |\n| icon | string | - | 分组图标 |\n| collapse | boolean | true | 是否可折叠 |\n| arrow | boolean | true | 是否显示折叠箭头 |\n| display | boolean | true | 是否显示该分组 |\n| header | boolean | true | 是否显示分组头部 |\n| column | array | - | 分组内的字段配置 |\n\n### 标签页显示\n\n```javascript\noption: {\n  tabs: true,\n  // tabsVerifyAll: true, // 提交时验证所有标签页\n  group: [\n    {\n      label: '基本信息',\n      prop: 'tab1',\n      column: [...],\n    },\n    {\n      label: '扩展信息',\n      prop: 'tab2',\n      column: [...],\n    },\n  ],\n}\n```\n\n### 帮助文字\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    tip: '请输入唯一标识名称',      // 字段右侧帮助文字\n    tipPlacement: 'bottom',          // 帮助文字方向\n    labelTip: '该字段用于系统唯一标识', // 标签帮助文字\n    labelTipPlacement: 'bottom',\n  },\n]\n```\n\n### className 样式\n\n```javascript\ncolumn: [\n  {\n    label: '名称',\n    prop: 'name',\n    className: 'custom-field-class',\n  },\n]\n```\n\n---\n\n## 按钮配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| menuBtn | boolean | true | 显示按钮区 |\n| menuSpan | number | 24 | 按钮栅格数 |\n| menuPosition | string | 'center' | 按钮位置: left/center/right |\n| submitBtn | boolean | true | 显示提交按钮 |\n| submitText | string | '提交' | 提交按钮文字 |\n| emptyBtn | boolean | true | 显示清空按钮 |\n| emptyText | string | '清空' | 清空按钮文字 |\n| printBtn | boolean | false | 显示打印按钮 |\n| mockBtn | boolean | false | 显示Mock数据按钮（需引入Mock.js） |\n| mockText | string | 'Mock' | Mock按钮文字 |\n| printText | string | '打印' | 打印按钮文字 |\n| submitIcon | string | - | 提交按钮图标 |\n| emptyIcon | string | - | 清空按钮图标 |\n\n---\n\n## Column 通用属性\n\n每个字段的通用配置：\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | - | 标签文字 |\n| prop | string | - | 字段属性名 |\n| type | string | 'input' | 字段类型 |\n| value | any | - | 默认值 |\n| bind | string | - | 深层数据绑定 |\n| disabled | boolean | false | 禁用 |\n| display | boolean | true | 显示 |\n| order | number | - | 排序，数字越大越靠前 |\n| size | string | 'small' | 组件尺寸 |\n| separator | string | ',' | 选项分隔符 (string dataType) |\n| rules | array | - | 验证规则 |\n\n### 栅格属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| span | number | 12 | 栅格数 |\n| gutter | number | 0 | 间距 |\n| offset | number | 0 | 偏移 |\n| push | number | 0 | 右移 |\n| pull | number | 0 | 左移 |\n| row | boolean | false | 换行 |\n\n### 标签属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| labelWidth | string/number | 80 | 标签宽度 |\n| labelPosition | string | 'left' | 标签位置 |\n| tip | string | - | 内容帮助文字 |\n| tipPlacement | string | 'top' | 内容帮助方向 |\n| labelTip | string | - | 标签帮助文字 |\n| labelTipPlacement | string | 'top' | 标签帮助方向 |\n\n### 联动控制\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| control | function/promise | - | 字段联动控制 (3.5.0+) |\n\n```javascript\n{\n  label: '类型',\n  prop: 'type',\n  type: 'select',\n  dicData: [{ label: 'A', value: 1 }, { label: 'B', value: 2 }],\n  control: (val, form) => {\n    return {\n      subField: {\n        display: val === 1,\n        disabled: val === 2,\n        dicData: val === 1 ? listA : listB,\n      }\n    }\n  },\n}\n```\n\n### 渲染函数 (3.4.2+)\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  render: (h, { column, $index, dic }) => {\n    return h('el-tag', {}, '自定义内容')\n  },\n}\n```\n\n---\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| submit | (form, done) | 表单提交回调。done() 关闭loading |\n| reset-change | - | 表单清空回调 |\n| change | (form) | 表单数据变化时触发 |\n| tab-click | (tab, event) | 标签页点击时触发 |\n| error | (error) | 表单验证失败时触发 |\n| mock-change | (form) | Mock 数据生成后触发 |\n\n```javascript\n// Composition API\nconst handleSubmit = (form, done) => {\n  saveData(form).then(() => {\n    ElMessage.success('保存成功')\n    done()  // 关闭loading\n  }).catch(() => {\n    done()\n  })\n}\n\nconst handleReset = () => {\n  // 清空后的逻辑\n}\n```\n\n---\n\n## 方法\n\n通过 `ref` 调用：\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| submit | - | 触发表单提交 |\n| resetForm | - | 重置表单 |\n| updateDic | (prop, dic) | 更新指定字典 |\n| dicInit | - | 重新加载所有字典 |\n| getPropRef | (prop) | 获取字段ref对象 |\n| validate | callback(boolean, object) | 验证整个表单 |\n| validateField | (props, callback) | 验证指定字段 |\n| resetFields | - | 重置表单到初始值 |\n| clearValidate | (props) | 清除验证结果 |\n| show | - | 禁用表单（显示loading状态） |\n| hide | - | 启用表单（隐藏loading状态） |\n| scrollToField | (prop) | 滚动到指定字段 |\n| getField | (prop) | 获取字段实例 |\n\n```javascript\n// 手动验证\nconst formRef = ref(null)\nformRef.value.validate((valid, fields) => {\n  if (valid) {\n    // 验证通过\n  } else {\n    // 验证失败，fields 包含错误信息\n  }\n})\n\n// 验证指定字段\nformRef.value.validateField(['name', 'code'], (errorMsg) => {\n  if (!errorMsg) {\n    // 验证通过\n  }\n})\n```\n\n---\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| `{prop}` | 自定义字段内容 |\n| `{prop}-label` | 自定义字段标签 |\n| `{prop}-error` | 自定义错误提示 |\n| `{prop}-desc` | 自定义描述 (3.6.4+) |\n| menu-form | 自定义按钮区域 |\n| menu-form-before | 按钮区域前内容 (3.4.3+) |\n\n```vue\n<!-- 自定义字段 -->\n<template #name=\"{ value, column, dic, size, disabled }\">\n  <el-input v-model=\"formData.name\" />\n</template>\n\n<!-- 自定义标签 -->\n<template #name-label=\"{ column }\">\n  <span style=\"color: red\">{{ column.label }}</span>\n</template>\n\n<!-- 自定义按钮 -->\n<template #menu-form>\n  <el-button type=\"primary\" @click=\"handleSave\">保存</el-button>\n  <el-button @click=\"handleCancel\">取消</el-button>\n</template>\n```\n\n---\n\n## 独立表单使用示例\n\n### Composition API\n\n```vue\n<template>\n  <avue-form ref=\"formRef\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script setup>\nimport { ref, reactive } from 'vue'\nimport { ElMessage } from 'element-plus'\n\nconst formRef = ref(null)\nconst formData = ref({})\n\nconst option = reactive({\n  labelWidth: 120,\n  column: [\n    {\n      label: '姓名',\n      prop: 'name',\n      span: 12,\n      rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n    },\n    {\n      label: '性别',\n      prop: 'gender',\n      type: 'radio',\n      span: 12,\n      dicData: [\n        { label: '男', value: 1 },\n        { label: '女', value: 2 },\n      ],\n    },\n  ],\n})\n\nconst handleSubmit = (form, done) => {\n  console.log('提交数据:', form)\n  done()\n}\n</script>\n```\n\n### Options API\n\n```vue\n<template>\n  <avue-form ref=\"form\" v-model=\"formData\" :option=\"option\" @submit=\"handleSubmit\" />\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      formData: {},\n      option: {\n        labelWidth: 120,\n        column: [\n          {\n            label: '姓名',\n            prop: 'name',\n            span: 12,\n            rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }],\n          },\n          {\n            label: '性别',\n            prop: 'gender',\n            type: 'radio',\n            span: 12,\n            dicData: [\n              { label: '男', value: 1 },\n              { label: '女', value: 2 },\n            ],\n          },\n        ],\n      },\n    }\n  },\n  methods: {\n    handleSubmit(form, done) {\n      console.log('提交数据:', form)\n      done()\n    },\n  },\n}\n</script>\n```\n\n---\n\n## 数据类型与过滤\n\n### 数据类型转换\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'select',\n  multiple: true,\n  dataType: 'string',    // 存储为逗号分隔字符串\n  separator: ',',         // 分隔符，默认逗号\n}\n// 值: \"1,2,3\" 而不是 [1, 2, 3]\n```\n\n### 数据过滤\n\n```javascript\noption: {\n  filterDic: true,  // 过滤 $ 开头的字典翻译字段\n  filterNull: true, // 过滤空值字段\n}\n```\n\n<!-- File: .claude/skills/avue-design/references/form-field-types.md (Tokens: ~9957 | Category: anthropic-skill) -->\n# avue-form 字段类型完整参考\n\n本文档列出 avue-form / avue-crud 中所有可用的字段类型及其完整配置属性。\n\n## 目录\n\n1. [input - 文本输入](#input---文本输入)\n2. [number - 数字输入](#number---数字输入)\n3. [select - 下拉选择](#select---下拉选择)\n4. [cascader - 级联选择](#cascader---级联选择)\n5. [checkbox - 多选框](#checkbox---多选框)\n6. [radio - 单选框](#radio---单选框)\n7. [date - 日期选择](#date---日期选择)\n8. [time - 时间选择](#time---时间选择)\n9. [switch - 开关](#switch---开关)\n10. [upload - 文件上传](#upload---文件上传)\n11. [tree - 树选择](#tree---树选择)\n12. [input-tag - 标签输入](#input-tag---标签输入)\n13. [mention - 提及](#mention---提及)\n14. [input-table - 表格选择](#input-table---表格选择)\n15. [color - 颜色选择](#color---颜色选择)\n16. [icon - 图标选择](#icon---图标选择)\n17. [map - 地图选择](#map---地图选择)\n18. [rate - 评分](#rate---评分)\n19. [slider - 滑块](#slider---滑块)\n20. [array/img/url - 数组输入](#arrayimgurl---数组输入)\n21. [dynamic - 动态子表单](#dynamic---动态子表单)\n22. [title - 标题分隔](#title---标题分隔)\n23. [input-cron - Cron表达式](#input-cron---cron表达式)\n\n---\n\n## 通用属性（所有类型共享）\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '请输入/请选择'+label | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| readonly | boolean | false | 只读 |\n| disabled | boolean | false | 禁用 |\n| change | function({column, value}) | - | 值变化事件 |\n| click | function({column, value}) | - | 点击事件 |\n| focus | function({column, value}) | - | 聚焦事件 |\n| blur | function({column, value}) | - | 失焦事件 |\n\n---\n\n## input - 文本输入\n\n`type: 'input'`（默认类型，可省略）\n\n### 基础属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| showWordLimit | boolean | false | 显示字数统计 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prependClick | function | - | 前置文字点击 |\n| appendClick | function | - | 后置文字点击 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| showPassword | boolean | true | 密码切换图标 |\n| autocomplete | string | 'off' | HTML autocomplete 属性 |\n| formatters | function | - | 格式化函数 (3.7.0+) |\n| parser | function | - | 解析函数（formatters 的逆操作）(3.7.0+) |\n| target | string | '_blank' | 链接目标 (url类型时有效) |\n\n### textarea 子类型\n\n```javascript\n{ type: 'textarea', rows: 4, minRows: 2, maxRows: 8 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rows | number | 2 | 文本域行数 |\n| minRows | number | 2 | 最小行数 |\n| maxRows | number | 6 | 最大行数 |\n\n### password 子类型\n\n```javascript\n{ type: 'password', showPassword: true }\n```\n\n### phone 子类型\n\n```javascript\n{ type: 'phone', defaultPhoneCode: '+86' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultPhoneCode | string | '+86' | 默认区号 |\n| phoneCodeOptions | array | - | 区号选项 |\n| phoneMaxLength | number | 11 | 手机号最大长度 |\n| phoneCodePlaceholder | string | '区号' | 区号占位 |\n| phonePlaceholder | string | '请输入手机号' | 手机号占位 |\n\n### currency 子类型\n\n```javascript\n{ type: 'currency', currency: 'CNY', currencyPrecision: 2 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| currency | string | 'CNY' | 货币类型 |\n| currencySymbolCustom | string | - | 自定义货币符号 |\n| currencyUnit | string | - | 货币单位 |\n| currencyPrecision | number | 2 | 小数位数 |\n| currencyPlaceholder | string | '请输入金额' | 占位 |\n| currencyMin | number | - | 最小值 |\n| currencyMax | number | - | 最大值 |\n\n### bankCard 子类型\n\n```javascript\n{ type: 'bankCard', showBankCardType: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| bankCardSeparator | string | ' ' | 分隔符 |\n| bankCardSegment | number | 4 | 分段长度 |\n| bankCardMaxDigits | number | 19 | 最大长度 |\n| bankCardPlaceholder | string | '请输入银行卡号' | 占位 |\n| showBankCardType | boolean | true | 显示卡类型 |\n\n### idCard 子类型\n\n```javascript\n{ type: 'idCard', showIdCardInfo: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| idCardPlaceholder | string | '请输入身份证号' | 占位 |\n| showIdCardInfo | boolean | true | 显示身份信息 |\n| idCardRealtimeValidate | boolean | true | 实时验证 |\n\n### email 子类型\n\n```javascript\n{ type: 'email', showEmailSuffix: true }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| emailPlaceholder | string | '请输入邮箱' | 占位 |\n| emailSuffixes | array | ['@qq.com','@163.com',...] | 邮箱后缀建议 |\n| showEmailSuffix | boolean | true | 显示建议 |\n\n### code 子类型\n\n```javascript\n{ type: 'code', codeLength: 6 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| codePlaceholder | string | - | 占位 |\n| codeLength | number | 6 | 验证码长度 |\n| codeSeparator | string | - | 分隔符 |\n\n### plate 子类型（车牌号）\n\n```javascript\n{ type: 'plate', defaultPlateProvince: '京' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| platePlaceholder | string | '请输入车牌号' | 占位 |\n| defaultPlateProvince | string | '京' | 默认省份 |\n| plateProvinceList | array | ['京','津','沪',...] | 省份列表 |\n\n### ip 子类型\n\n```javascript\n{ type: 'ip', ipVersion: 4 }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| ipPlaceholder | string | '请输入IP地址' | 占位 |\n| ipVersion | number | 4 | IP版本 |\n\n### mac 子类型\n\n```javascript\n{ type: 'mac', macSeparator: ':' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| macPlaceholder | string | '请输入MAC地址' | 占位 |\n| macSeparator | string | ':' | 分隔符 |\n\n### uscc 子类型（统一社会信用代码）\n\n```javascript\n{ type: 'uscc' }\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| usccPlaceholder | string | '请输入统一社会信用代码' | 占位 |\n| usccRealtimeValidate | boolean | true | 实时验证 |\n\n### search 子类型\n\n```javascript\n{ type: 'search' }\n```\n\n带搜索按钮的输入框，点击搜索按钮触发事件。属性同基础 input。\n\n---\n\n## number - 数字输入\n\n`type: 'number'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | -Infinity | 最小值 |\n| max | number | Infinity | 最大值 |\n| step | number | 1 | 步长 |\n| stepStrictly | boolean | false | 只能输入步长倍数 |\n| precision | number | 2 | 小数精度 |\n| controls | boolean | true | 显示控制按钮 |\n| controlsPosition | string | 'top' | 按钮位置: top/right |\n| suffix | string | - | 后缀文字 (3.5.6+) |\n| prefix | string | - | 前缀文字 (3.5.6+) |\n| suffixClick | function | - | 后缀点击 (3.5.6+) |\n| prefixClick | function | - | 前缀点击 (3.5.6+) |\n| align | string | 'left' | 文本对齐 (3.7.2+) |\n| disabledScientific | boolean | false | 禁用科学计数法 (3.7.2+) |\n\n```javascript\n{\n  label: '价格',\n  prop: 'price',\n  type: 'number',\n  min: 0,\n  max: 99999,\n  step: 0.01,\n  precision: 2,\n  controlsPosition: 'right',\n}\n```\n\n---\n\n## select - 下拉选择\n\n`type: 'select'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| limit | number | - | 多选最大数量 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠标签tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| allowCreate | boolean | false | 允许创建新条目 |\n| filterable | boolean | false | 可搜索 |\n| remote | boolean | false | 远程搜索 |\n| defaultFirstOption | boolean | false | 回车选中第一个 |\n| loadingText | string | '加载中' | 加载文字 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n| virtualize | boolean | false | 虚拟DOM (大数据) |\n| group | boolean | false | 分组显示 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| cascader | array | - | 级联子字段prop |\n| cascaderIndex | number | - | 级联默认选项索引 |\n| emptyValues | array | - | 空值配置 (3.4.4+) |\n| valueOnClear | any | - | 清空时的值 (3.4.4+) |\n| removeTag | function({value, column}) | - | 移除标签事件 (3.7.3+) |\n| all | boolean | false | 显示\"全选\"选项 |\n| noMatchText | string | '无匹配数据' | 无匹配时文字 |\n| noDataText | string | '无数据' | 无数据时文字 |\n\n```javascript\n// 基本用法\n{\n  label: '状态',\n  prop: 'status',\n  type: 'select',\n  dicData: [\n    { label: '启用', value: 1 },\n    { label: '禁用', value: 0 },\n  ],\n}\n\n// 远程搜索\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  remote: true,\n  filterable: true,\n  dicUrl: '/api/user/search?name={{key}}',\n  props: { label: 'name', value: 'id' },\n}\n\n// 自定义模板\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'select',\n  slot: true,  // 使用 userId + Type 插槽\n}\n\n// 分组\n{\n  label: '城市',\n  prop: 'city',\n  type: 'select',\n  group: true,\n  dicData: [\n    {\n      label: '热门城市',\n      children: [\n        { label: '北京', value: 'bj' },\n        { label: '上海', value: 'sh' },\n      ],\n    },\n  ],\n}\n```\n\n---\n\n## cascader - 级联选择\n\n`type: 'cascader'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAllLevels | boolean | true | 显示完整路径 |\n| tags | boolean | false | 标签显示 |\n| separator | string | '/' | 分隔符 |\n| filterable | boolean | false | 可搜索 |\n| filterMethod | function | - | 自定义搜索逻辑 |\n| expandTrigger | string | 'hover' | 展开触发方式: hover/click |\n| multiple | boolean | false | 多选 |\n| collapseTags | boolean | false | 折叠标签 |\n| collapseTagsTooltip | boolean | false | 折叠tooltip |\n| maxCollapseTags | number | - | 最大显示标签数 |\n| checkStrictly | boolean | false | 父子节点独立 |\n| emitPath | boolean | true | 返回完整路径数组 |\n| lazy | boolean | false | 懒加载 |\n| lazyLoad | function(node, resolve) | - | 懒加载函数 |\n| effect | string | 'dark' | tooltip主题 (3.7.2+) |\n| showCheckedStrategy | string | 'SHOW_ALL' | 选中项回填策略 (3.7.2+) |\n| popperClass | string | - | 下拉框class |\n\n```javascript\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  dicData: [\n    {\n      label: '北京',\n      value: 'bj',\n      children: [\n        { label: '朝阳区', value: 'cy' },\n        { label: '海淀区', value: 'hd' },\n      ],\n    },\n  ],\n}\n\n// 懒加载\n{\n  label: '地区',\n  prop: 'area',\n  type: 'cascader',\n  lazy: true,\n  lazyLoad: (node, resolve) => {\n    const { level, value } = node\n    getAreaChildren(value, level).then(res => {\n      resolve(res.data.map(item => ({\n        label: item.name,\n        value: item.id,\n        leaf: level >= 2,  // 标记叶子节点\n      })))\n    })\n  },\n}\n```\n\n---\n\n## checkbox - 多选框\n\n`type: 'checkbox'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| all | boolean | false | 全选按钮 |\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n| min | number | - | 最少选择数 |\n| max | number | - | 最多选择数 |\n| fill | string | '#409eff' | 选中颜色 |\n| textColor | string | '#ffffff' | 选中文字颜色 |\n\n```javascript\n{\n  label: '爱好',\n  prop: 'hobbies',\n  type: 'checkbox',\n  dicData: [\n    { label: '读书', value: 1 },\n    { label: '运动', value: 2 },\n    { label: '旅行', value: 3 },\n  ],\n  all: true,\n  min: 1,\n  max: 3,\n}\n```\n\n---\n\n## radio - 单选框\n\n`type: 'radio'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| border | boolean | false | 带边框 |\n| button | boolean | false | 按钮样式 |\n\n```javascript\n{\n  label: '性别',\n  prop: 'gender',\n  type: 'radio',\n  dicData: [\n    { label: '男', value: 1 },\n    { label: '女', value: 2 },\n  ],\n}\n```\n\n---\n\n## date - 日期选择\n\n`type: 'date'`\n\n可用 type 值：`date` | `datetime` | `daterange` | `datetimerange` | `week` | `month` | `year` | `dates` | `monthrange` | `yearrange`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultTime | string[] | - | 范围默认时间 |\n| defaultValue | Date | - | 默认日期 |\n| pickerOptions | object | - | 日期选项 |\n| popperClass | string | - | 下拉框class |\n| prefix-icon | string | - | 前缀图标 |\n| cellClassName | function | - | 自定义单元格class |\n| showNow | boolean | true | 显示\"此刻\"按钮 (3.6.0+) |\n| disabledHours | function | - | 禁用小时 (3.8.2+) |\n| disabledMinutes | function | - | 禁用分钟 (3.8.2+) |\n| disabledSeconds | function | - | 禁用秒 (3.8.2+) |\n| disabledDate | function(date) | - | 禁用日期判断函数 |\n| shortcuts | array/function | - | 快捷日期选项 |\n| editable | boolean | true | 输入框可编辑 |\n| unlinkPanels | boolean | false | 范围选择时取消面板联动 |\n\n**日期格式表：**\n\n| 格式 | 含义 | 示例 |\n|-----|------|------|\n| YYYY | 年 | 2024 |\n| MM | 月 | 01 |\n| DD | 日 | 02 |\n| HH | 时(24h) | 03 |\n| mm | 分 | 04 |\n| ss | 秒 | 05 |\n| X | JS时间戳 | 1483326245000 |\n\n```javascript\n// 日期\n{ type: 'date', format: 'YYYY-MM-DD', valueFormat: 'YYYY-MM-DD' }\n\n// 日期时间\n{ type: 'datetime', format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'YYYY-MM-DD HH:mm:ss' }\n\n// 日期范围\n{ type: 'daterange', startPlaceholder: '开始日期', endPlaceholder: '结束日期' }\n\n// 带默认时间的范围\n{\n  type: 'datetimerange',\n  defaultTime: ['00:00:00', '23:59:59'],\n}\n```\n\n---\n\n## time - 时间选择\n\n`type: 'time'` | `'timerange'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| format | string | - | 显示格式 |\n| valueFormat | string | - | 实际值格式 |\n| startPlaceholder | string | '开始' | 范围起始占位 |\n| endPlaceholder | string | '结束' | 范围结束占位 |\n| rangeSeparator | string | '-' | 范围分隔符 |\n| defaultValue | Date | - | 默认值 |\n| pickerOptions | object | - | 时间选项 |\n| popperClass | string | - | 下拉框class |\n| arrowControl | boolean | false | 箭头控制时间 |\n| editable | boolean | true | 输入框可编辑 |\n\n```javascript\n// 基本时间选择\n{ type: 'time', format: 'HH:mm:ss', valueFormat: 'HH:mm:ss' }\n\n// 固定时间点\n{\n  type: 'time',\n  pickerOptions: {\n    start: '08:00',\n    end: '18:00',\n    step: '00:30',\n  },\n}\n```\n\n> **TimeSelect 模式**：当配置了 `start`、`end`、`step`、`maxTime`、`minTime` 中的任一属性时，组件自动切换为 TimeSelect（固定时间点选择）模式。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | string | - | 开始时间 (TimeSelect) |\n| end | string | - | 结束时间 (TimeSelect) |\n| step | string | - | 时间步长 (TimeSelect) |\n| maxTime | string | - | 最大可选时间 (TimeSelect) |\n| minTime | string | - | 最小可选时间 (TimeSelect) |\n\n```javascript\n// TimeSelect 固定时间点选择\n{\n  type: 'time',\n  start: '08:00',\n  end: '18:00',\n  step: '00:30',\n  maxTime: '17:30',\n}\n```\n\n---\n\n## switch - 开关\n\n`type: 'switch'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| len | number | 40 | 开关宽度 |\n| activeColor | string | '#409EFF' | 打开背景色 |\n| inactiveColor | string | '#C0CCDA' | 关闭背景色 |\n| activeIcon | string | - | 打开图标 (3.2.20+) |\n| inactiveIcon | string | - | 关闭图标 (3.2.20+) |\n| activeActionIcon | string | - | 打开动作图标 (3.2.20+) |\n| inactiveActionIcon | string | - | 关闭动作图标 (3.2.20+) |\n| inlinePrompt | boolean | false | 只显示首字符 (3.2.20+) |\n| beforeChange | function(done) | - | 切换前钩子 (3.2.20+) |\n\n```javascript\n{\n  label: '状态',\n  prop: 'status',\n  type: 'switch',\n  dicData: [\n    { label: '关闭', value: 0 },\n    { label: '开启', value: 1 },\n  ],\n}\n```\n\n---\n\n## upload - 文件上传\n\n`type: 'upload'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| action | string | **必填** | 上传地址 |\n| accept | string/array | - | 文件类型限制 |\n| fileSize | number | - | 文件大小限制(KB) |\n| limit | number | - | 最大文件数 |\n| multiple | boolean | false | 多文件上传 |\n| directory | boolean | false | 文件夹上传 (3.8.0+) |\n| listType | string | 'text' | 文件列表类型: text/picture/picture-card |\n| showFileList | boolean | true | 显示文件列表 |\n| fileType | string | 'img' | 文件类型: img/audio/video |\n| fileText | string | '点击上传' | 上传按钮文字 |\n| drag | boolean | false | 拖拽排序（需Sortable.js） |\n| data | object | - | 额外参数 |\n| headers | object | - | 请求头 |\n| httpRequest | function(file, column) | - | 自定义上传逻辑 |\n| canvasOption | object | - | 图片水印配置 |\n| propsHttp | object | - | 响应结构配置 |\n| dataType | string | - | 数据类型: string/object/json |\n| loadText | string | - | 加载文字 |\n| tip | string | - | 提示文字 |\n| dragFile | boolean | false | 拖拽上传区域（区别于 drag 拖拽排序）|\n| oss | string | - | 云存储类型: ali/qiniu/cos |\n| paramsList | array | - | 额外参数列表 |\n| cropperOption | object | - | 图片裁剪配置 |\n\n### propsHttp 响应配置\n\n| 属性 | 类型 | 说明 |\n|-----|------|------|\n| home | string | 图片根路径 |\n| res | string | 响应层级 |\n| url | string | 图片地址属性 |\n| name | string | 图片名称属性 |\n| fileName | string | 上传文件名（默认'file'） |\n\n### canvasOption 水印配置\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | 'avuejs.com' | 水印文字 |\n| fontFamily | string | 'microsoft yahei' | 字体 |\n| color | string | '#999' | 颜色 |\n| fontSize | number | 16 | 字号 |\n| opacity | number | 100 | 透明度 |\n| bottom | number | 10 | 底部距离 |\n| right | number | 10 | 右侧距离 |\n| ratio | number | 1 | 压缩比(0-1) |\n\n```javascript\n{\n  label: '附件',\n  prop: 'files',\n  type: 'upload',\n  action: '/api/upload',\n  listType: 'picture-card',\n  multiple: true,\n  limit: 5,\n  fileSize: 10240,\n  accept: 'image/*',\n  propsHttp: {\n    res: 'data',\n    url: 'link',\n    name: 'originalName',\n  },\n  tip: '支持jpg/png格式，单文件不超过10MB',\n}\n```\n\n---\n\n## tree - 树选择\n\n`type: 'tree'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| multiple | boolean | false | 多选 |\n| tags | boolean | false | 标签显示 |\n| collapseTags | boolean | false | 折叠标签 (3.2.20+) |\n| collapseTagsTooltip | boolean | false | 折叠tooltip (3.2.20+) |\n| maxCollapseTags | number | - | 最大显示标签数 (3.2.20+) |\n| checkStrictly | boolean | false | 父子独立 |\n| parent | boolean | true | 父节点可选 |\n| accordion | boolean | false | 手风琴模式 |\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| expandOnClickNode | boolean | false | 点击展开 |\n| checkOnClickLeaf | boolean | false | 点击叶子勾选 (3.6.3+) |\n| defaultExpandedKeys | array | - | 默认展开keys |\n| defaultCheckedKeys | array | - | 默认勾选keys |\n| filterable | boolean | false | 可筛选 |\n| filterText | string | '请输入搜索关键字' | 筛选占位 |\n| iconClass | string | - | 自定义图标 |\n| leafOnly | boolean | true | 仅叶子节点 |\n| lazy | boolean | false | 懒加载 |\n| treeLoad | function | - | 加载函数 |\n| cacheData | array | - | 懒加载缓存 (3.2.20+) |\n| nodeClick | function | - | 节点点击回调 |\n| checked | function | - | 勾选回调 |\n| popperClass | string | - | 下拉框class |\n| popperAppendToBody | boolean | true | 弹出层追加到body |\n\n```javascript\n{\n  label: '部门',\n  prop: 'deptId',\n  type: 'tree',\n  dicUrl: '/api/dept/tree',\n  props: { label: 'name', value: 'id' },\n  defaultExpandAll: true,\n  checkStrictly: true,\n}\n```\n\n---\n\n## input-tag - 标签输入 (3.6.0+)\n\n`type: 'input-tag'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| maxlength | number | - | 最大输入长度 |\n| minlength | number | - | 最小输入长度 |\n| drag | boolean | false | 拖拽排序 |\n| size | string | - | 尺寸: large/small |\n| min | number | -Infinity | 最小标签数 |\n| max | number | Infinity | 最大标签数 |\n| prefix | string | - | 前置内容 |\n| prefixClick | function | - | 前置内容点击回调 |\n| suffix | string | - | 后置内容 |\n| suffixClick | function | - | 后置内容点击回调 |\n\n```javascript\n{\n  label: '标签',\n  prop: 'tags',\n  type: 'input-tag',\n  // 按回车添加标签\n}\n```\n\n---\n\n## mention - 提及 (3.6.0+)\n\n`type: 'mention'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | - | 最大提及数 |\n| remote | boolean | false | 远程搜索 |\n| maxlength | number | - | 最大长度 |\n| minlength | number | - | 最小长度 |\n| prepend | string | - | 前置文字 |\n| append | string | - | 后置文字 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| split | string | ',' | 提及分隔符 |\n| prefix | string | '@' | 触发前缀 |\n| whole | boolean | false | 仅匹配完整提及 |\n| checkIsWhole | function | - | 自定义完整匹配检测 |\n| prependClick | function | - | 前置点击回调 |\n| appendClick | function | - | 后置点击回调 |\n\n```javascript\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  dicData: [\n    { label: '张三', value: 'zhangsan' },\n    { label: '李四', value: 'lisi' },\n  ],\n}\n\n// 远程搜索\n{\n  label: '备注',\n  prop: 'remark',\n  type: 'mention',\n  remote: true,\n  dicUrl: '/api/user/search?name={{key}}',\n}\n```\n\n---\n\n## input-table - 表格选择\n\n`type: 'input-table'`\n\n内部使用 avue-crud 组件，配置参考 crud 文档。\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| formatter | function | - | 数据格式化 |\n| onLoad | function | - | 加载/查询/分页方法 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| multiple | boolean | false | 多选 |\n| children | object | - | 表格 CRUD 配置（支持所有 avue-crud option 属性） |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n\n```javascript\n{\n  label: '用户',\n  prop: 'userId',\n  type: 'input-table',\n  formatter: (row) => row.name,\n  onLoad: ({ page, value, data }, callback) => {\n    getList(page.currentPage, page.pageSize, data).then(res => {\n      callback({\n        total: res.data.total,\n        data: res.data.records,\n      })\n    })\n  },\n  children: {\n    border: true,\n    column: [\n      { label: '姓名', prop: 'name', search: true },\n      { label: '手机号', prop: 'phone' },\n    ],\n  },\n}\n```\n\n---\n\n## color - 颜色选择\n\n`type: 'color'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| showAlpha | boolean | true | 显示透明度 |\n| colorFormat | string | 'rgb' | 颜色格式: hsl/hsv/hex/rgb |\n| popperClass | string | - | 下拉框class |\n| predefine | array | - | 预定义颜色 |\n\n```javascript\n{\n  label: '主题色',\n  prop: 'themeColor',\n  type: 'color',\n  colorFormat: 'hex',\n  predefine: ['#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff'],\n}\n```\n\n---\n\n## icon - 图标选择\n\n`type: 'icon'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| iconList | array | - | 图标数组 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n```javascript\n{\n  label: '图标',\n  prop: 'icon',\n  type: 'icon',\n  iconList: [\n    { label: '首页', value: 'el-icon-s-home' },\n    { label: '设置', value: 'el-icon-setting' },\n  ],\n}\n```\n\niconList 支持分组结构：\n```javascript\niconList: [\n  {\n    label: 'Element Plus',\n    list: [\n      { label: 'Plus', value: 'el-icon-plus' },\n      { label: 'Edit', value: 'el-icon-edit' },\n    ]\n  },\n  {\n    label: 'Font Awesome',\n    list: [\n      { label: 'Home', value: 'fa fa-home' },\n    ]\n  }\n]\n```\n\n需引入图标库：\n```html\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n```\n\n---\n\n## map - 地图选择\n\n`type: 'map'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| params | object | - | 高德地图初始化参数 |\n| before-close | function(done) | - | 关闭前回调 (3.4.7+) |\n| mapChange | function | - | 地图变化回调 |\n| rows | number | - | 文本域行数 |\n| minRows | number | 1 | 最小行数 |\n| maxRows | number | - | 最大行数 |\n| dialogWidth | string | '80%' | 弹窗宽度 |\n\n需引入高德地图SDK（见 global-config.md）。\n\n```javascript\n{\n  label: '位置',\n  prop: 'location',\n  type: 'map',\n  params: { zoom: 15, center: [116.397428, 39.90923] },\n}\n```\n\n> 值格式为数组：`[经度, 纬度, 格式化地址]`，需引入高德地图 SDK。\n\n---\n\n## rate - 评分\n\n`type: 'rate'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| max | number | 5 | 最大分数 |\n| allowHalf | boolean | false | 允许半星 |\n| lowThreshold | number | 2 | 低分界限 |\n| highThreshold | number | 4 | 高分界限 |\n| colors | array/object | ['#F7BA2A','#F7BA2A','#F7BA2A'] | 颜色 |\n| voidColor | string | '#C6D1DE' | 未选颜色 |\n| disabledVoidColor | string | '#EFF2F7' | 禁用未选颜色 |\n| iconClasses | array/object | - | 图标class |\n| voidIconClass | string | 'el-icon-star-off' | 未选图标 |\n| showText | boolean | false | 显示文字 |\n| showScore | boolean | false | 显示分数 |\n| textColor | string | '#1F2D3D' | 文字颜色 |\n| texts | array | ['极差','失望','一般','满意','惊喜'] | 文字列表 |\n| scoreTemplate | string | '{value}' | 分数模板 |\n\n```javascript\n{\n  label: '评分',\n  prop: 'score',\n  type: 'rate',\n  max: 5,\n  allowHalf: true,\n  showText: true,\n  texts: ['极差', '失望', '一般', '满意', '惊喜'],\n}\n```\n\n---\n\n## slider - 滑块\n\n`type: 'slider'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| min | number | 0 | 最小值 |\n| max | number | 100 | 最大值 |\n| step | number | 1 | 步长 |\n| showInput | boolean | false | 显示输入框 |\n| showInputControls | boolean | true | 输入框控制按钮 |\n| showStops | boolean | false | 显示间断点 |\n| showTooltip | boolean | true | 显示tooltip |\n| formatTooltip | function | - | tooltip格式化 |\n| tooltipClass | string | - | tooltip class |\n| range | boolean | false | 范围选择 |\n| vertical | boolean | false | 垂直模式 |\n| height | string | - | 垂直模式高度 |\n| marks | object | - | 标记 |\n\n```javascript\n{\n  label: '进度',\n  prop: 'progress',\n  type: 'slider',\n  min: 0,\n  max: 100,\n  step: 10,\n  showStops: true,\n  marks: { 0: '0%', 50: '50%', 100: '100%' },\n}\n```\n\n---\n\n## array/img/url - 数组输入\n\n`type: 'array'` | `'img'` | `'url'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| alone | boolean | false | 单条模式 |\n| limit | number | - | 最大条数 |\n| fileType | string | 'img' | 文件类型 (img类型): img/audio/video |\n\n```javascript\n// 数组输入\n{ label: '标签', prop: 'tags', type: 'array', limit: 5 }\n\n// 图片输入\n{ label: '图片', prop: 'images', type: 'img', limit: 3, fileType: 'img' }\n\n// 链接输入\n{ label: '链接', prop: 'links', type: 'url', limit: 5 }\n```\n\n---\n\n## dynamic - 动态子表单\n\n`type: 'dynamic'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| type | string | 'crud' | 显示类型: crud/form |\n| limit | number | - | 最大行数 |\n| children | object | - | 子表配置 |\n| index | boolean | true | 显示序号 |\n| max | number | - | 最大行数 |\n\n### children 配置属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| type | string | 'crud' | 子表类型: crud/form |\n| column | array | - | 子表字段配置 |\n| index | boolean | true | 显示序号 |\n| indexLabel | string | '#' | 序号标题 |\n| addBtn | boolean | true | 显示新增按钮 |\n| delBtn | boolean | true | 显示删除按钮 |\n| viewBtn | boolean | true | 显示查看按钮 |\n| align | string | - | 内容对齐方式 |\n| headerAlign | string | - | 表头对齐方式 |\n| rowAdd | function | - | 自定义新增行方法 |\n| rowDel | function | - | 自定义删除行方法 |\n| selectionChange | function | - | 选择变化回调 |\n| sortableChange | function | - | 拖拽排序回调 |\n\n```javascript\n// 表格模式（默认）\n{\n  label: '明细',\n  prop: 'items',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    align: 'center',\n    headerAlign: 'center',\n    column: [\n      { label: '商品', prop: 'product' },\n      { label: '数量', prop: 'quantity', type: 'number' },\n      { label: '单价', prop: 'price', type: 'number', precision: 2 },\n    ],\n  },\n}\n\n// 表单模式\n{\n  label: '联系人',\n  prop: 'contacts',\n  type: 'dynamic',\n  span: 24,\n  children: {\n    type: 'form',\n    index: false,\n    column: [\n      { label: '姓名', prop: 'name', span: 12 },\n      { label: '电话', prop: 'phone', span: 12 },\n    ],\n  },\n}\n```\n\n---\n\n## title - 标题分隔\n\n`type: 'title'`\n\n用于表单中的分组标题，无输入功能。\n\n```javascript\n{\n  label: '基本信息',\n  prop: 'title1',\n  type: 'title',\n  span: 24,\n}\n```\n\n---\n\n## input-cron - Cron表达式 (3.8.2+)\n\n`type: 'input-cron'`\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| placeholder | string | '点击配置Cron表达式' | 占位文字 |\n| clearable | boolean | true | 可清空 |\n| prefixIcon | string | - | 前缀图标 |\n| suffixIcon | string | - | 后缀图标 |\n| dialogWidth | string | '700px' | 弹窗宽度 |\n\n```javascript\n{\n  label: '定时任务',\n  prop: 'cron',\n  type: 'input-cron',\n  span: 24,\n}\n```\n\n<!-- File: .claude/skills/avue-design/references/global-config.md (Tokens: ~1536 | Category: anthropic-skill) -->\n# Avue 全局配置参考\n\n## 安装依赖\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n**必须依赖：**\n- `element-plus` - UI组件库\n- `@element-plus/icons-vue` - 图标库\n\n**可选依赖：**\n- `axios` - 字典组件和上传组件需要\n\n## 完整安装示例\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\n\n// 注册所有图标\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\n\napp.use(ElementPlus)\napp.use(Avue)\n```\n\n## 带 axios 的安装（字典/上传组件必需）\n\n```javascript\nimport axios from 'axios'\nconst app = createApp({})\napp.use(Avue, { axios })\n```\n\n## 全局配置选项\n\n```javascript\napp.use(AVUE, {\n  size: 'small',           // 组件默认尺寸: 'small' | 'mini' | 'medium'\n  crudOption: {},           // Crud组件全局默认配置\n  formOption: {},           // Form组件全局默认配置\n  appendToBody: true,       // 弹窗是否追加到body\n  modalAppendToBody: true,  // 遮罩层是否追加到body\n  cos: {},                  // 腾讯云COS配置\n  qiniu: {},                // 七牛云配置\n  ali: {},                  // 阿里云OSS配置\n  canvas: {},               // 全局水印配置\n})\n```\n\n### Crud 全局配置示例\n\n```javascript\napp.use(Avue, {\n  crudOption: {\n    border: true,\n    stripe: true,\n    index: true,\n    align: 'center',\n    headerAlign: 'center',\n    menuWidth: 200,\n    searchLabelWidth: 110,\n    height: 'auto',\n  }\n})\n```\n\n### Form 全局配置示例\n\n```javascript\napp.use(Avue, {\n  formOption: {\n    labelWidth: 120,\n    labelPosition: 'right',\n    size: 'small',\n  }\n})\n```\n\n## 云存储配置\n\n### 阿里云 OSS\n\n```javascript\napp.use(AVUE, {\n  ali: {\n    region: 'oss-cn-beijing',\n    endpoint: 'oss-cn-beijing.aliyuncs.com',\n    stsToken: '',\n    accessKeyId: 'your-access-key-id',\n    accessKeySecret: 'your-access-key-secret',\n    bucket: 'your-bucket-name',\n  }\n})\n```\n\n### 七牛云\n\n```javascript\n// 需引入 CryptoJS：\n// <script src=\"https://cdn.staticfile.net/crypto-js/4.0.0/crypto-js.min.js\"></script>\napp.use(AVUE, {\n  qiniu: {\n    AK: 'your-access-key',\n    SK: 'your-secret-key',\n    scope: 'your-scope',\n    url: 'https://upload.qiniup.com',\n    deadline: 1,\n  }\n})\n```\n\n### 腾讯云 COS\n\n```javascript\n// 需引入 cos-js-sdk-v5：\n// <script src=\"https://cdn.staticfile.net/cos-js-sdk-v5/1.3.2/cos-js-sdk-v5.min.js\"></script>\napp.use(AVUE, {\n  cos: {\n    SecretId: 'your-secret-id',\n    SecretKey: 'your-secret-key',\n    Bucket: 'your-bucket',\n    Region: 'ap-beijing',\n  }\n})\n```\n\n## 全局水印配置\n\n```javascript\napp.use(AVUE, {\n  canvas: {\n    text: 'avuejs.com',        // 水印文字\n    fontFamily: 'microsoft yahei', // 字体\n    color: '#999',              // 颜色\n    fontSize: 16,               // 字号\n    opacity: 100,               // 透明度\n    bottom: 10,                 // 底部距离\n    right: 10,                  // 右侧距离\n    ratio: 1,                   // 压缩比 0-1\n  }\n})\n```\n\n## 国际化\n\n```javascript\nimport Avue from '@smallwei/avue'\nimport enLocale from '@smallwei/avue/lib/locale/lang/en'\nimport zhLocale from '@smallwei/avue/lib/locale/lang/zh'\n\n// 使用英文\napp.use(Avue, { locale: enLocale })\n\n// 配合 vue-i18n\nconst messages = {\n  en: { ...enLocale },\n  zh: { ...zhLocale },\n}\n```\n\n支持的语言：`zh`（简体中文）、`en`（英文）。\n\n## 外部库引入（按需）\n\n```html\n<!-- Excel 导入导出 -->\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n\n<!-- 拖拽排序 -->\n<script src=\"https://cdn.staticfile.net/Sortable/1.10.0-rc2/Sortable.min.js\"></script>\n\n<!-- 高德地图 -->\n<script>\n  window._AMapSecurityConfig = { securityJsCode: 'xxxxx' }\n</script>\n<script src=\"https://webapi.amap.com/maps?v=2.0&key=xxxxxxxx&plugin=AMap.PlaceSearch,AMap.Geocoder\"></script>\n<script src=\"https://webapi.amap.com/ui/1.1/main.js?v=1.0.11\"></script>\n\n<!-- 图标库 -->\n<script src=\"//at.alicdn.com/t/font_2621503_zcbiqy2g1i.js\"></script>\n\n<!-- 流程图 -->\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n\n<!-- 截图 -->\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n\n<!-- PDF导出 -->\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n\n<!-- Mock数据 -->\n<script src=\"https://cdn.staticfile.net/Mock.js/1.0.1-beta3/mock-min.js\"></script>\n```\n\n<!-- File: .claude/skills/avue-design/references/other-components.md (Tokens: ~2290 | Category: anthropic-skill) -->\n# Avue 其他组件参考\n\n## 目录\n1. [Card 卡片](#card-卡片)\n2. [DialogForm 弹窗表单](#dialogform-弹窗表单)\n3. [Tabs 选项卡](#tabs-选项卡)\n4. [Search 搜索](#search-搜索)\n5. [Calendar 日历](#calendar-日历)\n6. [Flow 流程图](#flow-流程图)\n7. [Login 登录](#login-登录)\n8. [Article 文章](#article-文章)\n9. [Chat 聊天](#chat-聊天)\n10. [CountUp 数字动画](#countup-数字动画)\n11. [Clipboard 剪切板](#clipboard-剪切板)\n12. [Draggable 拖拽](#draggable-拖拽)\n13. [TextEllipsis 文本省略](#textellipsis-文本省略)\n14. [Sign 电子签名](#sign-电子签名)\n15. [Print 打印](#print-打印)\n16. [Export 导入导出](#export-导入导出)\n17. [ImagePreview 图片预览](#imagepreview-图片预览)\n18. [Contextmenu 右键菜单](#contextmenu-右键菜单)\n19. [Video 摄像头](#video-摄像头)\n20. [Verify 验证码](#verify-验证码)\n21. [Screenshot 截图](#screenshot-截图)\n22. [License 授权书](#license-授权书)\n\n---\n\n## Card 卡片\n\n```vue\n<avue-card :option=\"option\" :data=\"data\" @row-click=\"rowClick\" @row-add=\"rowAdd\">\n  <template #menu=\"{ row, index }\">\n    <el-button @click=\"handleEdit(row)\">编辑</el-button>\n  </template>\n</avue-card>\n```\n\n### Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| addBtn | boolean | true | 新增按钮 |\n| span | number | 8 | 栅格数 |\n| gutter | number | 20 | 间距 |\n\n### Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| title | string | 'title' | 标题属性 |\n| img | string | 'img' | 头像属性 |\n| info | string | 'info' | 副标题属性 |\n\n### 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| row-add | - | 新增 |\n| row-click | (row, index) | 行点击 |\n\n---\n\n## DialogForm 弹窗表单\n\n全局API方式调用 (3.4.1+)：\n\n```javascript\nimport { $DialogForm } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\n\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"确认\",\n    column: [\n      { label: \"姓名\", prop: \"name\", span: 24 },\n    ],\n  },\n})\n\n// 3.5.2+ 主动关闭\ndialog.close()\n```\n\n也可作为组件使用，参考 `default/dialog-form/index` 示例。\n\n---\n\n## Tabs 选项卡\n\n```vue\n<avue-tabs :option=\"option\" @change=\"tabChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Search 搜索\n\n```vue\n<avue-search v-model=\"searchForm\" :option=\"option\" @change=\"searchChange\" />\n```\n\n配合 form、crud 组件使用。\n\n---\n\n## Calendar 日历\n\n```vue\n<avue-calendar :option=\"option\" :data=\"calendarData\" />\n```\n\n---\n\n## Flow 流程图\n\n需引入 jsPlumb：\n```html\n<script src=\"https://cdn.staticfile.net/jsPlumb/2.11.1/js/jsplumb.min.js\"></script>\n```\n\n```vue\n<avue-flow :option=\"option\" :width=\"800\" :height=\"600\" @click=\"nodeClick\" />\n```\n\n### 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| width | number | - | 画布宽度 |\n| height | number | - | 画布高度 |\n| option | object | - | 配置 |\n\n### 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| nodeAdd | (name) | 添加节点 |\n| deleteNode | (id) | 删除节点 |\n\n---\n\n## Login 登录\n\n```vue\n<avue-login :option=\"option\" @submit=\"handleLogin\" />\n```\n\n---\n\n## Article 文章\n\n```vue\n<avue-article :id=\"container\" :offset-top=\"0\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | 'window' | 指定父元素 |\n| offset-top | number | 0 | 触发偏移量 |\n\n---\n\n## Chat 聊天\n\n```vue\n<avue-chat :option=\"option\" />\n```\n\n支持图片、视频、文件、地图等消息类型。\n\n---\n\n## CountUp 数字动画\n\n```vue\n<avue-count-up :start=\"0\" :end=\"12345\" :decimals=\"0\" :duration=\"2\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| start | number | 0 | 起始数字 |\n| end | number | - | 结束数字 |\n| decimals | number | 0 | 小数位数 |\n| duration | number | 2 | 动画时长(秒) |\n| options | object | - | CountUp.js配置 |\n| callback | function | - | 开始回调 |\n\n---\n\n## Clipboard 剪切板\n\n```vue\n<avue-clipboard :text=\"copyText\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 要复制的文字 |\n\n---\n\n## Draggable 拖拽\n\n```vue\n<avue-draggable :width=\"200\" :height=\"100\" :top=\"50\" :left=\"100\" :disabled=\"false\">\n  <div>可拖拽内容</div>\n</avue-draggable>\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| disabled | boolean | false | 禁用拖拽 |\n| width | string | - | 宽度 |\n| height | string | - | 高度 |\n| top | number | 0 | X位置 |\n| left | number | 0 | Y位置 |\n| z-index | number | 1 | 层级 |\n\n### 事件\n\n| 事件名 | 说明 |\n|-------|------|\n| focus | 获取焦点 |\n| blur | 失去焦点 |\n\n---\n\n## TextEllipsis 文本省略\n\n```vue\n<avue-text-ellipsis :text=\"longText\" :height=\"60\" :is-limit-height=\"true\" :use-tooltip=\"true\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| text | string | - | 文本内容 |\n| width | number | - | 宽度限制 |\n| height | number | - | 高度限制 |\n| is-limit-height | boolean | true | 启用高度限制 |\n| use-tooltip | boolean | false | 使用tooltip |\n| placement | string | - | tooltip方向 |\n\n---\n\n## Sign 电子签名\n\n```vue\n<avue-sign />\n```\n\n兼容移动端和PC端。\n\n---\n\n## Print 打印\n\n```vue\n<avue-print :id=\"printId\" :html=\"htmlContent\" />\n```\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| id | string | - | DOM元素ID |\n| html | string | - | HTML代码片段 |\n\n---\n\n## Export 导入导出\n\n需引入外部库：\n```html\n<script src=\"https://cdn.staticfile.net/FileSaver.js/2014-11-29/FileSaver.min.js\"></script>\n<script src=\"https://cdn.staticfile.net/xlsx/0.18.2/xlsx.full.min.js\"></script>\n```\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 导出\n$Export.excel({\n  title: '导出标题',\n  columns: [{ label: '姓名', prop: 'name' }],\n  data: [{ name: '张三' }],\n})\n```\n\n---\n\n## ImagePreview 图片预览\n\n```javascript\nimport { $ImagePreview } from \"@smallwei/avue\"\nimport { getCurrentInstance } from \"vue\"\n\nconst { appContext } = getCurrentInstance()\nconst images = [\n  { thumbUrl: '缩略图.jpg', url: '原图.jpg' },\n]\n$ImagePreview(appContext)(images, 0)\n```\n\n---\n\n## Contextmenu 右键菜单\n\n```vue\n<div v-contextmenu=\"menuOptions\">\n  右键点击此区域\n</div>\n```\n\n---\n\n## Video 摄像头\n\n```vue\n<avue-video ref=\"video\" />\n```\n\n支持 start/end 方法控制录制。\n\n---\n\n## Verify 验证码\n\n```vue\n<avue-verify />\n```\n\n---\n\n## Screenshot 截图\n\n需引入 html2canvas：\n```html\n<script src=\"https://cdn.staticfile.net/html2canvas/0.5.0-beta4/html2canvas.min.js\"></script>\n```\n\n---\n\n## License 授权书\n\n需引入 jsPDF (用于PDF导出)：\n```html\n<script src=\"https://cdn.staticfile.net/jspdf/1.5.3/jspdf.min.js\"></script>\n```\n\n<!-- File: .claude/skills/avue-design/references/tree-component.md (Tokens: ~1094 | Category: anthropic-skill) -->\n# avue-tree 树组件参考\n\n## 基本用法\n\n```vue\n<avue-tree\n  :option=\"option\"\n  :data=\"treeData\"\n  @node-click=\"nodeClick\"\n  @save=\"handleSave\"\n  @update=\"handleUpdate\"\n  @del=\"handleDel\"\n/>\n```\n\n## Option 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| defaultExpandAll | boolean | false | 默认展开所有 |\n| virtualize | boolean | false | 虚拟DOM模式 |\n| dialogWidth | string | '50%' | 弹窗宽度 |\n| formOption | object | - | 自定义表单（参考avue-form） |\n| menu | boolean | true | 显示菜单栏 |\n| addBtn | boolean | true | 新增按钮 |\n| editBtn | boolean | true | 编辑按钮 |\n| delBtn | boolean | true | 删除按钮 |\n| filter | boolean | true | 显示搜索框 |\n| props | object | - | 配置选项 |\n| lazy | boolean | false | 懒加载 |\n\n## Props 属性\n\n| 属性 | 类型 | 默认值 | 说明 |\n|-----|------|-------|------|\n| label | string | 'label' | 节点名称属性 |\n| labelText | string | '名称' | 弹窗新增名称 |\n| children | string | 'children' | 子节点属性 |\n| value | string | 'value' | 节点值属性和nodeKey |\n\n## 数据结构\n\n```javascript\nconst treeData = ref([\n  {\n    label: '一级节点',\n    value: '1',\n    children: [\n      { label: '二级节点A', value: '1-1' },\n      { label: '二级节点B', value: '1-2' },\n    ],\n  },\n])\n```\n\n## 事件\n\n| 事件名 | 参数 | 说明 |\n|-------|------|------|\n| save | (parent, data, done, loading) | 新增回调 |\n| update | (parent, data, done, loading) | 编辑回调 |\n| del | (data, done) | 删除回调 |\n| before-open | (done, type) | 打开前回调 |\n| before-close | (done, type) | 关闭前回调 |\n| node-click | (data, node, component) | 节点点击 |\n| check-change | (data, checked, hasCheckedChildren) | 勾选变化 |\n\n## 方法\n\n| 方法名 | 参数 | 说明 |\n|-------|------|------|\n| filter | (value) | 筛选树节点 |\n| updateKeyChildren | (key, data) | 更新子节点 |\n| getCheckedNodes | (leafOnly, includeHalfChecked) | 获取勾选节点 |\n| setCheckedNodes | (nodes) | 设置勾选节点 |\n| getCheckedKeys | (leafOnly) | 获取勾选keys |\n| setCheckedKeys | (keys, leafOnly) | 设置勾选keys |\n| setChecked | (key/data, checked, deep) | 设置勾选状态 |\n| getHalfCheckedNodes | - | 获取半选节点 |\n| getHalfCheckedKeys | - | 获取半选keys |\n| getCurrentKey | - | 获取当前选中key |\n| getCurrentNode | - | 获取当前选中数据 |\n| setCurrentKey | (key) | 设置当前选中 |\n| setCurrentNode | (node) | 设置当前选中 |\n| getNode | (data) | 获取节点 |\n| remove | (data) | 删除节点 |\n| append | (data, parentNode) | 追加子节点 |\n| insertBefore | (data, refNode) | 在前面插入 |\n| insertAfter | (data, refNode) | 在后面插入 |\n\n## 插槽\n\n| 插槽名 | 说明 |\n|-------|------|\n| menu | 按钮插槽 |\n| add-btn | 新增按钮插槽 |\n| default | 节点内容插槽 |\n\n## 示例\n\n### 多选模式\n\n```vue\n<avue-tree :option=\"{ ...option, multiple: true }\" :data=\"treeData\" @check-change=\"checkChange\" />\n```\n\n### 懒加载\n\n```vue\n<avue-tree :option=\"option\" :data=\"treeData\">\n\nconst option = reactive({\n  lazy: true,\n  props: { label: 'name', value: 'id' },\n})\n\n// 在 treeLoad 中处理加载逻辑\n```\n\n### 拖拽\n\n参考 `default/tree/drag` 示例。\n\n### 权限控制\n\n参考 `default/tree/permission` 示例。\n\n### 虚拟模式\n\n```javascript\noption: { virtualize: true }\n// 适用于大数据量场景\n```\n\n<!-- File: .claude/skills/avue-design/references/utility-apis.md (Tokens: ~1305 | Category: anthropic-skill) -->\n# Avue 全局工具函数参考\n\n## $DialogForm - 弹窗表单\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $DialogForm } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\n\n// 打开弹窗表单\nconst dialog = $DialogForm(appContext)({\n  option: {\n    submitText: \"完成\",\n    column: [\n      {\n        label: \"姓名\",\n        prop: \"name\",\n        span: 24,\n        rules: [{ required: true, message: \"请输入姓名\", trigger: \"blur\" }],\n      },\n    ],\n  },\n})\n\n// 关闭弹窗\ndialog.close()\n```\n\n3.5.2+ 版本支持 `close()` 方法主动关闭。\n\n## $Clipboard - 复制剪切板\n\n```javascript\nimport { $Clipboard } from \"@smallwei/avue\"\n\n$Clipboard({ text: \"要复制的文本\" })\n  .then(() => ElMessage.success(\"复制成功\"))\n  .catch(() => ElMessage.error(\"复制失败\"))\n```\n\n## $ImagePreview - 图片预览\n\n```javascript\nimport { getCurrentInstance } from \"vue\"\nimport { $ImagePreview } from \"@smallwei/avue\"\n\nconst { appContext } = getCurrentInstance()\nconst datas = [\n  { thumbUrl: \"缩略图URL\", url: \"大图URL\" },\n  { thumbUrl: \"缩略图URL2\", url: \"大图URL2\" },\n]\n\n$ImagePreview(appContext)(datas, 0)  // 第二个参数为起始索引\n```\n\n## $Print - 打印\n\n```javascript\nimport { $Print } from \"@smallwei/avue\"\n\n// 传入 DOM 元素的 id 或 class\n$Print(\"#test\")\n```\n\n## $Export - Excel 导出\n\n```javascript\nimport { $Export } from \"@smallwei/avue\"\n\n// 需引入 FileSaver.js 和 xlsx\n$Export.excel({\n  title: \"文档标题\",\n  columns: [\n    { label: \"标题\", prop: \"title\" },\n    { label: \"内容\", prop: \"content\" },\n  ],\n  data: [\n    { title: \"测试1\", content: \"内容1\" },\n    { title: \"测试2\", content: \"内容2\" },\n  ],\n})\n```\n\n## $Log - 彩色日志\n\n```javascript\nimport { $Log } from \"@smallwei/avue\"\n\n// 内置颜色: default(#35495E), primary(#3488ff), success(#43B883), warning(#e6a23c), danger(#f56c6c)\n$Log.capsule(\"标题\", \"内容\", \"primary\")\n$Log.primary(\"内容\")\n$Log.success(\"内容\")\n$Log.warning(\"内容\")\n$Log.danger(\"内容\")\n```\n\n## findObject - 查找列配置对象\n\n```javascript\nimport { findObject } from \"@smallwei/avue\"\n\nconst option = { column: [{ label: '名称', prop: 'name' }] }\nconst nameColumn = findObject(option.column, 'name')\n// 返回: { label: '名称', prop: 'name' }\n```\n\n## findNode - 在数组中查找对象\n\n```javascript\nimport { findNode } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst obj = findNode(list, \"sex\", \"prop\")  // 返回 {prop: 'sex'}\n```\n\n## findArray - 查找对象在数组中的索引\n\n```javascript\nimport { findArray } from \"@smallwei/avue\"\n\nconst list = [{ prop: \"name\" }, { prop: \"sex\" }]\nconst index = findArray(list, \"sex\", \"prop\")  // 返回 1\n```\n\n## downFile - 文件下载\n\n```javascript\nimport { downFile } from \"@smallwei/avue\"\n\ndownFile(\"https://example.com/file.pdf\", \"document.pdf\")\n```\n\n## randomId - 生成随机ID\n\n```javascript\nimport { randomId } from \"@smallwei/avue\"\n\nconst id = randomId()  // 返回随机字符串\n```\n\n## loadScript - 动态加载脚本\n\n```javascript\nimport { loadScript } from \"@smallwei/avue\"\n\n// 加载 JS\nloadScript(\"js\", \"https://cdn.example.com/lib.js\").then(() => {\n  // JS 加载完成\n})\n\n// 加载 CSS\nloadScript(\"css\", \"https://cdn.example.com/style.css\").then(() => {\n  // CSS 加载完成\n})\n```\n\n## deepClone - 深拷贝\n\n```javascript\nimport { deepClone } from \"@smallwei/avue\"\n\nconst original = { name: \"张三\", info: { age: 18 } }\nconst cloned = deepClone(original)\n```\n\n## setPx - 设置像素值\n\n```javascript\nimport { setPx } from \"@smallwei/avue\"\n\nsetPx(23)       // '23px'\nsetPx(\"100%\")   // '100%'\nsetPx(\"23px\")   // '23px'\n```\n\n## validatenull - 判断空值\n\n```javascript\nimport { validatenull } from \"@smallwei/avue\"\n\nvalidatenull({})         // true\nvalidatenull([])         // true\nvalidatenull(\"\")         // true\nvalidatenull(undefined)  // true\nvalidatenull(null)       // true\nvalidatenull(\"hello\")    // false\n```\n\n## validData - 数据验证并提供默认值\n\n```javascript\nimport { validData } from \"@smallwei/avue\"\n\nvalidData(undefined, \"默认值\")     // '默认值'\nvalidData({}, { name: 11 })        // { name: 11 }\nvalidData(true, false)             // true (非空返回原值)\n```\n\n<!-- File: .claude/skills/avue-design/SKILL.md (Tokens: ~2243 | Category: anthropic-skill) -->\n---\nname: avue-design\ndescription: \"Avue 组件代码生成与配置指南。当用户需要创建、配置或生成基于 Avue (avue-crud, avue-form, avue-tree 等) 的 Vue 页面代码时使用此 skill。覆盖 CRUD 表格、表单、树组件、数据展示等全部 Avue 组件。支持 Composition API 和 Options API 两种代码风格。当用户提到 avue、crud表格、动态表单、JSON配置表单、低代码表单、avue-crud、avue-form 等关键词时，务必使用此 skill。\"\n---\n\n# Avue Design - 组件代码生成 Skill 集合\n\nAvue 是基于 Vue3 + Element-Plus 的低代码前端框架，通过 JSON 配置生成页面。版本：v3.8.2，MIT 协议。\n\n## 快速安装\n\n```bash\nnpm i @smallwei/avue -S\n# 或\nyarn add @smallwei/avue -S\n```\n\n```javascript\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport { createApp } from 'vue'\nimport Avue from '@smallwei/avue'\nimport '@smallwei/avue/lib/index.css'\n\nconst app = createApp({})\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n  app.component(key, component)\n}\napp.use(ElementPlus)\napp.use(Avue)\n// 若使用字典或上传组件，需传入 axios：\n// import axios from 'axios'\n// app.use(Avue, { axios })\n```\n\n## 参考文件导航\n\n根据用户需求，读取对应的参考文件获取完整配置信息：\n\n### 基础配置\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/global-config.md` | 全局配置、安装、国际化、云存储 |\n| `references/api-style-guide.md` | 需要生成 Composition API 或 Options API 代码时 |\n| `references/utility-apis.md` | 使用 Avue 全局工具函数 ($DialogForm, $Clipboard 等) |\n\n### avue-crud (CRUD 表格组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/crud-core.md` | CRUD 基础属性、事件、方法、插槽、按钮、弹窗、分页 |\n| `references/crud-column.md` | 列配置、字典、排序、过滤、格式化、渲染 |\n| `references/crud-search.md` | 搜索栏配置、搜索字段属性 |\n| `references/crud-form-dialog.md` | CRUD 弹窗表单配置、验证规则、字段状态控制 |\n| `references/crud-features.md` | 高级功能：树表、虚拟滚动、卡片模式、行编辑、导入导出、拖拽排序、权限控制等 |\n\n### avue-form (表单组件)\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/form-core.md` | 表单基础属性、事件、方法、插槽、布局 |\n| `references/form-field-types.md` | 全部字段类型 (input/select/date/upload 等 27+ 种) 的详细配置属性 |\n| `references/form-advanced.md` | 字典配置、验证规则、动态子表单、文件上传(含云存储)、字段联动 |\n\n### 其他组件\n| 参考文件 | 何时读取 |\n|---------|---------|\n| `references/tree-component.md` | avue-tree 树组件 |\n| `references/data-display.md` | 数据展示组件 (DataDisplay, DataCard 等) |\n| `references/other-components.md` | Card、DialogForm、Calendar、Flow、Login、Tabs 等独立组件 |\n\n## 代码生成核心规则\n\n### 1. 两种 API 风格\n\n生成代码时必须确认用户要用哪种风格（默认 Composition API）。详细模板见 `references/api-style-guide.md`。\n\n**Composition API (推荐)**：使用 `<script setup>`，`ref()`/`reactive()` 声明数据，箭头函数定义方法。\n\n**Options API**：使用 `export default {}`，`data()` 返回数据，`methods` 对象定义方法。\n\n### 2. avue-crud 基本结构\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      ref=\"crud\"\n      :option=\"option\"\n      :data=\"data\"\n      :table-loading=\"loading\"\n      v-model=\"form\"\n      v-model:page=\"page\"\n      v-model:search=\"search\"\n      :permission=\"permissionList\"\n      :before-open=\"beforeOpen\"\n      @row-save=\"rowSave\"\n      @row-update=\"rowUpdate\"\n      @row-del=\"rowDel\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    />\n  </basic-container>\n</template>\n```\n\n### 3. avue-form 基本结构\n\n```vue\n<template>\n  <avue-form\n    ref=\"form\"\n    v-model=\"formData\"\n    :option=\"option\"\n    @submit=\"handleSubmit\"\n    @reset-change=\"handleReset\"\n  />\n</template>\n```\n\n### 4. option 配置核心\n\noption 是 Avue 组件的核心配置对象，包含：\n- 表格/表单级属性（size, height, border, stripe 等）\n- `column` 数组：每个字段的完整配置（label, prop, type, rules, dicData 等）\n- `group` 数组：分组配置（仅表单）\n\n```javascript\nconst option = {\n  // 表格级配置\n  height: 'auto',\n  border: true,\n  stripe: true,\n  index: true,\n  selection: true,\n  // 列配置\n  column: [\n    {\n      label: '名称',\n      prop: 'name',\n      type: 'input',        // 字段类型\n      search: true,          // 启用搜索\n      span: 12,              // 表单中占栅格数\n      rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],\n      // 字典配置\n      dicData: [],           // 本地字典\n      dicUrl: '',            // 远程字典URL\n      props: { label: 'label', value: 'value' },\n    }\n  ]\n}\n```\n\n### 5. 字段类型速查\n\n| 类型 | type 值 | 说明 |\n|-----|---------|------|\n| 文本输入 | input | 支持 password/textarea/phone/currency/bankCard/idCard/email/code/plate/ip/mac/uscc 子类型 |\n| 数字输入 | number | 数字步进器 |\n| 下拉选择 | select | 单选/多选/远程搜索/分组 |\n| 级联选择 | cascader | 多级联动选择 |\n| 多选框 | checkbox | 多选组 |\n| 单选框 | radio | 单选组 |\n| 日期 | date/datetime/daterange/datetimerange/week/month/year/dates | 日期时间选择 |\n| 时间 | time/timerange | 时间选择 |\n| 开关 | switch | 布尔切换 |\n| 文件上传 | upload | 支持阿里云/七牛/腾讯COS |\n| 数组 | array | 数组输入框 |\n| 图片 | img | 图片输入 |\n| 链接 | url | 超链接输入 |\n| 树选择 | tree | 树型选择器 |\n| 标签输入 | input-tag | 标签输入 (3.6.0+) |\n| 提及 | mention | @提及组件 (3.6.0+) |\n| 表格选择 | input-table | 表格弹窗选择 |\n| 颜色选择 | color | 颜色选择器 |\n| Cron表达式 | input-cron | Cron可视化配置 (3.8.2+) |\n| 图标选择 | icon | 图标选择器 |\n| 地图选择 | map | 高德地图坐标选择 |\n| 评分 | rate | 星级评分 |\n| 滑块 | slider | 滑动条 |\n| 标题 | title | 分组标题 |\n| 动态表单 | dynamic | 子表单/子表格 |\n\n### 6. 关键注意事项\n\n- `column` 可以是数组也可以是对象格式（3.2.15+，用 prop 做 key）\n- 深层数据绑定用 `bind` 属性：`bind: 'info.address'`\n- 字典联动用 `cascader` 属性指定子字段 prop\n- 字段动态控制用 `control` 属性（支持 Promise，3.5.0+）\n- 表单分组用 `group` 配置，标签页显示设 `tabs: true`\n- 所有组件都支持 `render` 函数自定义渲染（3.4.2+）\n- CRUD 弹窗类型用 `dialogType: 'drawer'` 可改为抽屉模式\n- 搜索字段通过在 column 中设 `search: true` 启用\n\n<!-- File: .claude/skills/blade-commit/README.md (Tokens: ~601 | Category: anthropic-skill) -->\n# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 快速开始\n\n```bash\n# 简单模式（默认）— 单行 gitmoji + 描述\n/blade-commit\n\n# 详细模式 — gitmoji + conventional commit + 变更列表\n/blade-commit -d\n\n# 自定义信息 — 自动补全 gitmoji 格式\n/blade-commit -m \"新增接口权限模块\"\n```\n\n## 两种模式\n\n### 简单模式（默认）\n\n单行格式，适用于日常小改动：\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n```\n\n### 详细模式（`-d`）\n\n多行格式，适用于涉及多文件的较大提交：\n\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n```\n\n## 常用 Gitmoji\n\n| Gitmoji | 图标 | 场景 |\n|---|---|---|\n| `:sparkles:` | ✨ | 新增功能 |\n| `:bug:` | 🐛 | 修复 Bug |\n| `:zap:` | ⚡ | 性能优化 |\n| `:recycle:` | ♻️ | 重构 |\n| `:memo:` | 📝 | 文档变更 |\n| `:wrench:` | 🔧 | 配置变更 |\n| `:fire:` | 🔥 | 移除代码 |\n| `:tada:` | 🎉 | 发布版本 |\n| `:white_check_mark:` | ✅ | 测试 |\n| `:lock:` | 🔒 | 安全修复 |\n\n完整对照表见 SKILL.md。\n\n## 安全保障\n\n- **只做 commit**，禁止 push、pull 等任何远程操作\n- **零署名**，不添加任何 AI/工具署名\n- **逐文件暂存**，不使用 `git add .`，排除敏感文件\n- **自动适配**，根据项目历史提交记录匹配语言和风格\n\n<!-- File: .claude/skills/blade-commit/SKILL.md (Tokens: ~2794 | Category: anthropic-skill) -->\n---\nname: blade-commit\ndescription: SpringBlade 通用 Git 提交工具。基于 Gitmoji 规范生成提交信息。当用户要求提交代码、commit 变更、生成 commit message、或使用 /blade-commit 时触发。默认强制简单模式（gitmoji + 一句话描述），仅当显式传入 --d 或 --detail 参数时才启用详细模式（gitmoji + conventional commit + 变更列表）。只执行 commit，绝不 push 或执行其他远程操作。确保每次提交都带有 gitmoji 图标。\n---\n\n# Blade Commit\n\n通用 Git 提交工具，基于 Gitmoji 规范生成风格统一的提交信息。\n\n## 铁律\n\n- **只做 commit**：禁止执行 `git push`、`git pull`、`git rebase`、`git merge`、`git reset --hard` 或任何非暂存/提交的 git 操作\n- **零署名**：提交信息中不得出现 `Powered by`、`Created by`、`Co-authored-by`、`Generated by` 或任何形式的工具/AI 署名\n- **必须有 Gitmoji**：每条提交信息必须以 gitmoji 代码开头（如 `:sparkles:`）\n\n## 使用方式\n\n- `/blade-commit` — 简单模式（默认，强制单行）\n- `/blade-commit --d` 或 `/blade-commit --detail` — 详细模式（多行带变更列表）\n- `/blade-commit -m \"自定义信息\"` — 使用用户指定的信息，自动补全 gitmoji 格式\n\n## 模式判定规则\n\n**无论变更多大、涉及多少文件，只要用户没有显式传入 `--d` 或 `--detail` 参数，就必须使用简单模式。** 不要因为 diff 内容多就自动升级为详细模式——模式选择权在用户手中，而非由变更规模决定。\n\n## 两种模式\n\n### 简单模式（默认，强制）\n\n单行格式：`:<gitmoji>: <简要描述>`\n\n一句话说清楚改了什么。无论改动大小，只要没有 `--d` / `--detail` 参数，一律使用此格式。\n\n```\n:sparkles: 新增角色授权接口\n:bug: 修复用户分页查询未过滤已删除数据\n:zap: 优化字典查询排序逻辑\n:recycle: 重构租户删除逻辑，迁移至 TenantService\n:memo: 补充 OSS 接入文档\n:wrench: 调整 application-dev.yml 数据源配置\n:tada: x.x.x.RELEASE\n```\n\n### 详细模式（需显式传入 `--d` 或 `--detail`）\n\n多行格式，适用于涉及多文件、多项改动的较大提交：\n\n```\n:<gitmoji>: <type>(<scope>): <标题摘要>\n\n- <变更点1>\n- <变更点2>\n- ...\n```\n\n标题行由 gitmoji + conventional commit 类型 + 作用域 + 摘要组成，空一行后用 bullet points 逐条列出具体变更。\n\n**示例 1 — 新功能：**\n```\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\n```\n\n**示例 2 — Bug 修复：**\n```\n:bug: fix(auth): 修复登录 IP 锁定后刷新令牌仍可通过的问题\n\n- RefreshTokenGranter 增加 IP 锁定状态校验\n- LoginAttemptService 补充刷新场景下的失败计数复位逻辑\n- 新增单元测试覆盖刷新 Token 的 IP 锁定分支\n```\n\n**示例 3 — 重构：**\n```\n:recycle: refactor(resource): 重构 OssEndpoint 按职责拆分 Controller\n\n- 拆分文件上传、文件下载、文件删除为独立 Controller\n- 抽取 OssService 处理跨厂商统一逻辑\n- 原 OssEndpoint 保留兼容入口，内部委派至新 Controller\n```\n\n**示例 4 — 性能优化：**\n```\n:zap: perf(system): 优化字典批量查询性能\n\n- DictCache 改用 Caffeine 二级缓存，减少 Redis 往返\n- DictMapper 补充 code + dict_key 联合索引提示\n- 调整 DictService 的 getList 为按租户分片查询\n```\n\n## Gitmoji 对照表\n\n根据变更内容选择最匹配的 gitmoji：\n\n| Gitmoji | 图标 | type | 场景 |\n|---|---|---|---|\n| `:sparkles:` | ✨ | feat | 新增功能、新特性 |\n| `:bug:` | 🐛 | fix | 修复 Bug |\n| `:zap:` | ⚡ | perf | 性能优化、改进提升 |\n| `:recycle:` | ♻️ | refactor | 重构（不改变外部行为） |\n| `:memo:` | 📝 | docs | 文档新增或变更 |\n| `:wrench:` | 🔧 | chore | 配置变更、杂项 |\n| `:white_check_mark:` | ✅ | test | 添加或修改测试 |\n| `:tada:` | 🎉 | release | 发布新版本、里程碑 |\n| `:fire:` | 🔥 | remove | 移除代码或文件 |\n| `:lipstick:` | 💄 | style | UI / 样式调整 |\n| `:ambulance:` | 🚑 | hotfix | 紧急修复 |\n| `:construction:` | 🚧 | wip | 开发进行中 |\n| `:arrow_up:` | ⬆️ | deps | 升级依赖版本 |\n| `:arrow_down:` | ⬇️ | deps | 降级依赖版本 |\n| `:lock:` | 🔒 | security | 安全相关修复 |\n| `:truck:` | 🚚 | move | 移动 / 重命名文件 |\n| `:art:` | 🎨 | style | 改进代码结构 / 格式化 |\n| `:package:` | 📦 | build | 构建系统变更 |\n| `:rewind:` | ⏪ | revert | 回退变更 |\n| `:heavy_plus_sign:` | ➕ | deps | 添加依赖 |\n| `:heavy_minus_sign:` | ➖ | deps | 移除依赖 |\n\n## 执行流程\n\n### 第一步：收集信息\n\n并行执行：\n- `git status` — 查看工作区状态\n- `git diff --staged` — 已暂存的变更\n- `git diff` — 未暂存的变更\n- `git log --oneline -5` — 最近提交记录，确认当前项目的提交风格\n\n### 第二步：适配项目风格\n\n阅读 `git log` 结果，判断当前项目的提交语言（中文/英文）和习惯。提交信息的语言应与项目历史保持一致：\n- 如果历史提交以中文为主，使用中文\n- 如果历史提交以英文为主，使用英文\n- 如果混合使用，默认中文\n\n### 第三步：分析变更\n\n阅读 diff 内容，判断：\n1. **变更类型**：新功能？Bug修复？重构？文档？配置？性能优化？\n2. **影响范围（scope）**：涉及哪个模块或功能（如 `system`、`auth`、`desk`、`resource`、`develop`、`gateway`、`tool` 等）\n3. **核心改动**：最关键的变更是什么\n4. 如果变更混合了多种类型，选最主要的类型\n\n### 第四步：暂存文件\n\n- 使用 `git add <具体文件路径>` 逐个添加，不要用 `git add .` 或 `git add -A`\n- 排除不该提交的文件：`.env`、密钥文件、IDE 配置（`.idea/`、`.vscode/`）、编译产物（`target/`、`dist/`）、系统文件（`.DS_Store`）\n- **前端工程排除规则**：如果项目根目录存在 `package.json`、`vite.config.*`、`vue.config.*` 等前端工程标识文件，则自动将 `website.js`（通常位于 `src/config/website.js`）从暂存列表中排除，即使该文件有变更也不提交。如果 `website.js` 已被暂存，执行 `git reset HEAD <website.js路径>` 将其移出暂存区\n- 如果用户已经手动暂存过文件，跳过此步（但仍需检查并移除已暂存的 `website.js`）\n\n### 第五步：生成提交信息并提交\n\n根据模式选择对应格式，使用 HEREDOC 执行 commit：\n\n**简单模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: 新增角色授权接口\nEOF\n)\"\n```\n\n**详细模式：**\n```bash\ngit commit -m \"$(cat <<'EOF'\n:sparkles: feat(system): 新增接口权限模块\n\n- 新增 ApiScope 实体与对应 Mapper / Service / Controller\n- 新增 ApiScopeHandler 处理端点级权限过滤\n- 在 RoleController 中追加接口权限授权接口\n- 补充 blade_api_scope 建表语句与初始化菜单 SQL\nEOF\n)\"\n```\n\n### 第六步：确认结果\n\n执行 `git status` 确认提交成功，向用户展示提交结果。\n\n## 写好提交信息的要点\n\n- **简单模式**：一句话说清楚\"做了什么\"，精炼但不含糊，80 字符以内为佳\n- **详细模式**：\n  - 标题行概括全局，不超过 72 字符\n  - bullet points 按重要性排序：先新增，后优化，再修复\n  - 每个 bullet 要具体，提到文件名或模块名让人知道改了哪里\n  - 不要超过 8 条 bullet，太多说明应该拆分 commit\n- **选对 gitmoji**：看对照表选最贴合的。不确定时 `:sparkles:` 适用于大多数新功能，`:zap:` 适用于优化改进\n- 如果用户通过 `-m` 指定了信息，保留用户原意，只在格式上对齐规范（补 gitmoji、调整格式）\n\n<!-- File: .claude/skills/blade-design/README.md (Tokens: ~1691 | Category: anthropic-skill) -->\n# blade-design - SpringBlade 全栈代码生成器\n\n基于 SpringBlade 开源版框架规范的智能代码生成 Skill，覆盖后端、前端、数据库三层，支持 Boot 单体和 Cloud 微服务两种架构模式。\n\n## 功能概览\n\n```\nblade-design\n├── 后端子技能 (backend)\n│   ├── Entity / DTO / VO / Excel    ← 数据模型层\n│   ├── Mapper / Mapper.xml          ← 数据访问层\n│   ├── Service / ServiceImpl        ← 业务逻辑层\n│   ├── Controller                   ← 接口控制层\n│   ├── Wrapper                      ← 数据转换层\n│   └── Feign Client (Cloud)         ← 微服务通信层\n│\n├── 前端子技能 (frontend)\n│   ├── API 模块                      ← Axios 请求封装\n│   ├── Option 配置                   ← Avue CRUD 列/表单配置\n│   └── Vue 页面                      ← Options API / Composition API\n│\n└── 数据库子技能 (database)\n    ├── MySQL 建表 SQL                ← 开源版官方支持方言\n    └── 菜单 SQL                      ← 权限菜单数据\n```\n\n## 支持的架构模式\n\n### Boot 单体架构\n- 所有代码在同一模块内\n- 包名：`org.springblade.modules.{module}`\n- Controller 路由含服务名前缀：`AppConstant.APPLICATION_XXX_NAME + \"/path\"`\n- 无 Feign Client\n\n### Cloud 微服务架构\n- API 模块（契约）与 Service 模块（实现）分离\n- 包名：`org.springblade.{module}`\n- Controller 路由无前缀（Gateway 自动路由）\n- 含 Feign Client 接口 + 实现\n\n## 支持的实体模式\n\n| 模式 | 基类 | 适用场景 | 审计字段 | 租户隔离 |\n|------|------|---------|---------|---------|\n| **tenant** | `TenantEntity` | 多租户业务表 | 自动继承 | 有 |\n| **base** | `BaseEntity` | 跨租户业务表 | 自动继承 | 无 |\n| **raw** | `Serializable` | 系统 / 关系表 | 手动定义 | 无 |\n\n## 支持的数据库\n\nSpringBlade 开源版**仅官方支持 MySQL**，建表脚本固定输出 MySQL 方言。\n\n| 方言 | 引号 | 大小写 | 注释方式 | 引擎 |\n|------|------|--------|---------|------|\n| MySQL | `` ` `` | 小写 | `COMMENT` 内联 | InnoDB + utf8mb4 |\n\n## 支持的前端风格\n\n| 风格 | 说明 | 适用场景 |\n|------|------|---------|\n| **Options API** | Vue 3 传统选项式写法 | 与现有代码保持一致 |\n| **Composition API** | Vue 3 组合式 `<script setup>` | 新功能开发推荐 |\n\n## 支持的模板类型\n\n| 类型 | 说明 | 适用场景 |\n|------|------|---------|\n| **crud** | 标准增删改查 | 通用业务管理 |\n| **tree** | 树形结构 CRUD | 分类、部门、菜单等层级数据 |\n| **sub** | 主子表 CRUD | 订单-订单项、合同-条款等主从关系 |\n\n## 使用方式\n\n### 快速开始\n```\n帮我生成一个订单管理模块的代码\n```\n\n### 指定参数\n```\n帮我生成一个产品管理的 CRUD：\n- 模块名：product\n- 实体名：Product\n- 架构：boot\n- 字段：\n  - productName (String) 产品名称，必填，搜索\n  - category (Integer) 产品分类，字典 product_category\n  - price (BigDecimal) 单价\n  - description (String/text) 产品描述\n  - status (Integer) 上架状态\n```\n\n### 仅生成部分\n```\n帮我生成 Order 的建表语句（MySQL）\n帮我生成前端页面，用 Composition API\n只需要后端代码，Cloud 架构\n```\n\n## 文件结构\n\n```\nblade-design/\n├── SKILL.md                  ← 主技能文件（路由、配置、工作流）\n├── README.md                 ← 本文档\n└── references/\n    ├── backend.md            ← 后端代码模板（Entity/DTO/VO/Mapper/Service/Controller/Wrapper/Feign）\n    ├── frontend.md           ← 前端代码模板（API/Option/Vue 页面）\n    └── database.md           ← MySQL 建表模板 + 菜单 SQL\n```\n\n## Boot vs Cloud 核心差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| 基础包名 | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| Controller 路由 | `AppConstant.APPLICATION_XXX_NAME + \"/path\"` | `\"/path\"` |\n| Entity 位置 | 同模块 | API 模块 |\n| 服务间调用 | 直接注入 | Feign Client |\n| Feign Client | 无 | 接口在 API 模块，实现在 Service 模块 |\n| POM 结构 | 单一 pom.xml | API + Service 各一个 |\n| 服务注册 | 不需要 | Nacos |\n| 网关路由 | 不需要 | Spring Cloud Gateway |\n\n## 代码规范要点\n\n1. Long 类型 ID 必须 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失\n2. Date 字段同时添加 `@DateTimeFormat` 和 `@JsonFormat`\n3. 逻辑删除字段 `isDeleted` 添加 `@TableLogic`（基类自动处理）\n4. Swagger 使用 OpenAPI 3 注解（`@Tag`/`@Operation`/`@Schema`/`@Parameter`），控制器类级标注无参 `@ApiOrder` 排序\n5. 响应统一使用 `R<T>` 包装\n6. Wrapper 中通过 `DictCache.getValue()` 翻译字典字段\n7. 前端权限标识格式：`{modelCode}_{action}`\n8. 表名统一 `blade_` 前缀\n9. 主键使用雪花算法（ASSIGN_ID）\n10. 所有表包含标准审计字段\n11. Entity 类名不带 `Entity` 后缀（如 `User` 而非 `UserEntity`）\n12. 权限控制使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)`，开源版仅支持角色级权限\n\n<!-- File: .claude/skills/blade-design/references/backend.md (Tokens: ~7090 | Category: anthropic-skill) -->\n# 后端代码生成参考\n\n本文档包含 SpringBlade 开源版后端各层的完整代码模板。所有模板均来源于 SpringBlade 开源工程真实业务代码（参考 `modules/system`、`modules/desk` 及 Cloud 的 `blade-service/blade-system` 等）。\n\n## 目录\n- [一、Entity 实体类](#一entity-实体类)\n- [二、DTO 数据传输对象](#二dto-数据传输对象)\n- [三、VO 视图对象](#三vo-视图对象)\n- [四、Excel 导出类](#四excel-导出类)\n- [五、Mapper 接口](#五mapper-接口)\n- [六、Mapper XML](#六mapper-xml)\n- [七、Service 接口](#七service-接口)\n- [八、ServiceImpl 实现](#八serviceimpl-实现)\n- [九、Controller 控制器](#九controller-控制器)\n- [十、Wrapper 数据转换器](#十wrapper-数据转换器)\n- [十一、Feign Client 接口（Cloud）](#十一feign-client-接口cloud)\n- [十二、Feign Client 实现（Cloud）](#十二feign-client-实现cloud)\n\n---\n\n## 一、Entity 实体类\n\n> 开源版 Entity 命名**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。\n\n### 模式A：TenantEntity（多租户业务表，默认）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.TenantEntity;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends TenantEntity {\n\n    /**\n     * {字段注释}\n     */\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 字段类型注解模式 ---\n\n    // Long 类型字段（防止前端精度丢失）\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"关联ID\")\n    private Long refId;\n\n    // Date 类型字段\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"发布时间\")\n    private Date releaseTime;\n\n    // Integer 字典字段（配合 Wrapper 翻译）\n    @Schema(description = \"分类\")\n    private Integer category;\n\n    // String 普通文本\n    @Schema(description = \"标题\")\n    private String title;\n\n    // String 大文本\n    @Schema(description = \"内容\")\n    private String content;\n}\n```\n\n**TenantEntity 自动继承的字段**（不需要在子类中声明）：\n- `id` (Long) - 主键，@TableId(type = IdType.ASSIGN_ID)，雪花 ID\n- `tenantId` (String) - 租户 ID\n- `createUser` (Long) - 创建人\n- `createDept` (Long) - 创建部门\n- `createTime` (Date) - 创建时间\n- `updateUser` (Long) - 更新人\n- `updateTime` (Date) - 更新时间\n- `status` (Integer) - 业务状态，默认 1\n- `isDeleted` (Integer) - 逻辑删除标记，@TableLogic，默认 0\n\n### 模式B：BaseEntity（跨租户业务表）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"{中文名}\")\npublic class {Name} extends BaseEntity {\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n}\n```\n\n**BaseEntity 继承字段**：与 TenantEntity 相同但不含 `tenantId`。\n\n### 模式C：Raw Serializable（原生模式）\n\n```java\npackage org.springblade.modules.{module}.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * {中文名}实体类\n *\n * @author {author}\n */\n@Data\n@TableName(\"blade_{table_name}\")\n@Schema(description = \"{中文名}\")\npublic class {Name} implements Serializable {\n\n    @Serial\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 主键id\n     */\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键id\")\n    @TableId(value = \"id\", type = IdType.ASSIGN_ID)\n    private Long id;\n\n    // --- 业务字段 ---\n\n    @Schema(description = \"{字段注释}\")\n    private {Type} {fieldName};\n\n    // --- 审计字段（手动定义） ---\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建人\", hidden = true)\n    private Long createUser;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"创建部门\", hidden = true)\n    private Long createDept;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"创建时间\", hidden = true)\n    private Date createTime;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"更新人\", hidden = true)\n    private Long updateUser;\n\n    @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @JsonFormat(pattern = DateUtil.PATTERN_DATETIME)\n    @Schema(description = \"更新时间\", hidden = true)\n    private Date updateTime;\n\n    @Schema(description = \"业务状态\", hidden = true)\n    private Integer status;\n\n    @TableLogic\n    @Schema(description = \"是否已删除\", hidden = true)\n    private Integer isDeleted;\n}\n```\n\n### Cloud 版本 Entity 差异\n\nCloud 中 Entity 放在 API 模块，包名不同：\n```java\n// Boot\npackage org.springblade.modules.{module}.entity;\n\n// Cloud\npackage org.springblade.{module}.entity;\n```\n\n其余代码完全相同。\n\n---\n\n## 二、DTO 数据传输对象\n\n### 标准 DTO（继承 Entity）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}DTO extends {Name} {\n}\n```\n\n大多数情况下 DTO 为空类，仅作为扩展点。如有特定传输需求可添加字段。\n\n### 精简 DTO（仅特定字段）\n\n```java\npackage org.springblade.modules.{module}.dto;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}数据传输对象\n *\n * @author {author}\n */\n@Data\npublic class {Name}DTO implements Serializable {\n\n    @Schema(description = \"特定字段\")\n    private String specificField;\n}\n```\n\n---\n\n## 三、VO 视图对象\n\n### 标准 VO（简单扩展）\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名}视图对象\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} {\n\n    /**\n     * 分类名称（由 Wrapper 通过字典翻译填充）\n     */\n    @Schema(description = \"分类名称\")\n    private String categoryName;\n}\n```\n\n对于每个需要字典翻译的字段，在 VO 中添加对应的 `{字段名}Name` 字段。\n\n### 树形 VO（实现 INode 接口）\n\n用于 Menu、Dict、Dept 等树形结构：\n\n```java\npackage org.springblade.modules.{module}.vo;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.tool.node.INode;\nimport org.springblade.modules.{module}.entity.{Name};\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * {中文名}视图对象（树形）\n *\n * @author {author}\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class {Name}VO extends {Name} implements INode<{Name}VO> {\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"主键ID\")\n    private Long id;\n\n    @JsonSerialize(using = ToStringSerializer.class)\n    @Schema(description = \"父节点ID\")\n    private Long parentId;\n\n    @JsonInclude(JsonInclude.Include.NON_EMPTY)\n    @Schema(description = \"子节点\")\n    private List<{Name}VO> children;\n\n    @Schema(description = \"是否有子节点\")\n    private Boolean hasChildren;\n\n    @Schema(description = \"上级名称\")\n    private String parentName;\n\n    @Override\n    public List<{Name}VO> getChildren() {\n        if (this.children == null) {\n            this.children = new ArrayList<>();\n        }\n        return this.children;\n    }\n}\n```\n\n---\n\n## 四、Excel 导出类\n\n```java\npackage org.springblade.modules.{module}.excel;\n\nimport cn.idev.excel.annotation.ExcelProperty;\nimport cn.idev.excel.annotation.write.style.ColumnWidth;\nimport cn.idev.excel.annotation.write.style.ContentRowHeight;\nimport cn.idev.excel.annotation.write.style.HeadRowHeight;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * {中文名}导出对象\n *\n * @author {author}\n */\n@Data\n@ColumnWidth(25)\n@HeadRowHeight(20)\n@ContentRowHeight(18)\npublic class {Name}Excel implements Serializable {\n\n    @ExcelProperty(\"{字段中文名}\")\n    private {Type} {fieldName};\n\n    // 不导出审计字段（createUser、createTime 等）\n}\n```\n\n> 开源版使用 FastExcel（`cn.idev.excel.*`，由 `blade-starter-excel` 引入）。\n\n---\n\n## 五、Mapper 接口\n\n```java\npackage org.springblade.modules.{module}.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.excel.{Name}Excel;\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\nimport java.util.List;\n\n/**\n * {中文名} Mapper 接口\n *\n * @author {author}\n */\npublic interface {Name}Mapper extends BaseMapper<{Name}> {\n\n    /**\n     * 自定义分页查询\n     */\n    List<{Name}VO> select{Name}Page(IPage page, {Name}VO {modelCode});\n\n    /**\n     * 导出数据查询\n     */\n    List<{Name}Excel> export{Name}(@Param(\"ew\") Wrapper<{Name}> queryWrapper);\n}\n```\n\n**树形模板追加方法**：\n```java\n    /**\n     * 树形结构查询\n     */\n    List<{Name}VO> tree(String tenantId);\n```\n\n---\n\n## 六、Mapper XML\n\n### 标准 CRUD\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"org.springblade.modules.{module}.mapper.{Name}Mapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"{modelCode}ResultMap\" type=\"org.springblade.modules.{module}.entity.{Name}\">\n        <result column=\"id\" property=\"id\"/>\n        <!-- 每个业务字段一行映射 -->\n        <result column=\"{column_name}\" property=\"{fieldName}\"/>\n        <!-- 审计字段映射（TenantEntity 模式需要） -->\n        <result column=\"create_user\" property=\"createUser\"/>\n        <result column=\"create_dept\" property=\"createDept\"/>\n        <result column=\"create_time\" property=\"createTime\"/>\n        <result column=\"update_user\" property=\"updateUser\"/>\n        <result column=\"update_time\" property=\"updateTime\"/>\n        <result column=\"status\" property=\"status\"/>\n        <result column=\"is_deleted\" property=\"isDeleted\"/>\n    </resultMap>\n\n    <!-- 自定义分页查询 -->\n    <select id=\"select{Name}Page\" resultMap=\"{modelCode}ResultMap\">\n        SELECT\n            t.*,\n            d.dict_value AS categoryName\n        FROM blade_{table_name} t\n        LEFT JOIN (SELECT * FROM blade_dict WHERE code = '{dictCode}') d\n            ON t.category = d.dict_key\n        WHERE t.is_deleted = 0\n        <if test=\"{modelCode}.tenantId != null and {modelCode}.tenantId != ''\">\n            AND t.tenant_id = #{{{modelCode}.tenantId}}\n        </if>\n        <!-- 动态查询条件 -->\n        <if test=\"{modelCode}.{queryField} != null and {modelCode}.{queryField} != ''\">\n            AND t.{query_column} LIKE CONCAT('%', #{{{modelCode}.{queryField}}}, '%')\n        </if>\n        <if test=\"{modelCode}.{eqField} != null\">\n            AND t.{eq_column} = #{{{modelCode}.{eqField}}}\n        </if>\n    </select>\n\n    <!-- 导出查询 -->\n    <select id=\"export{Name}\" resultType=\"org.springblade.modules.{module}.excel.{Name}Excel\">\n        SELECT * FROM blade_{table_name}\n        ${ew.customSqlSegment}\n    </select>\n\n</mapper>\n```\n\n**注意事项**：\n- 如果有字典关联查询，在 select 中 LEFT JOIN blade_dict 表\n- 动态条件中 `LIKE` 用于模糊查询字段，`=` 用于精确查询字段\n- TenantEntity 模式需要 `tenant_id` 过滤条件\n- resultMap 中每个数据库列名（snake_case）映射到 Java 属性（camelCase）\n\n---\n\n## 七、Service 接口\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends BaseService<{Name}> {\n\n    /**\n     * 自定义分页\n     */\n    IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode});\n}\n```\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service;\n\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport org.springblade.modules.{module}.entity.{Name};\n\n/**\n * {中文名} 服务接口\n *\n * @author {author}\n */\npublic interface I{Name}Service extends IService<{Name}> {\n}\n```\n\n---\n\n## 八、ServiceImpl 实现\n\n### TenantEntity / BaseEntity 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends BaseServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n\n    @Override\n    public IPage<{Name}VO> select{Name}Page(IPage<{Name}VO> page, {Name}VO {modelCode}) {\n        {modelCode}.setTenantId(AuthUtil.getTenantId());\n        return page.setRecords(baseMapper.select{Name}Page(page, {modelCode}));\n    }\n}\n```\n\n**注意**：BaseEntity 模式下不需要 `setTenantId()` 那行。\n\n### Raw Serializable 模式\n\n```java\npackage org.springblade.modules.{module}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.mapper.{Name}Mapper;\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * {中文名} 服务实现\n *\n * @author {author}\n */\n@Service\npublic class {Name}ServiceImpl extends ServiceImpl<{Name}Mapper, {Name}> implements I{Name}Service {\n}\n```\n\n---\n\n## 九、Controller 控制器\n\n### Boot 版本\n\n```java\npackage org.springblade.modules.{module}.controller;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.service.I{Name}Service;\nimport org.springblade.modules.{module}.vo.{Name}VO;\nimport org.springblade.modules.{module}.wrapper.{Name}Wrapper;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.Map;\n\n/**\n * {中文名} 控制器\n *\n * @author {author}\n */\n@RestController\n@AllArgsConstructor\n@RequestMapping(AppConstant.APPLICATION_{MODULE}_NAME + \"/{modelCode}\")\n@ApiOrder\n@Tag(name = \"{中文名}\", description = \"{中文名}接口\")\npublic class {Name}Controller extends BladeController {\n\n    private final I{Name}Service {modelCode}Service;\n\n    /**\n     * 详情\n     */\n    @GetMapping(\"/detail\")\n    @Operation(summary = \"详情\", description = \"传入{modelCode}\")\n    public R<{Name}VO> detail({Name} {modelCode}) {\n        {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n        return R.data({Name}Wrapper.build().entityVO(detail));\n    }\n\n    /**\n     * 分页列表\n     */\n    @GetMapping(\"/list\")\n    @Operation(summary = \"分页\", description = \"传入{modelCode}\")\n    @PreAuth(RoleConstant.HAS_ROLE_ADMIN)\n    public R<IPage<{Name}VO>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n        IPage<{Name}> pages = {modelCode}Service.page(\n            Condition.getPage(query),\n            Condition.getQueryWrapper({modelCode}, {Name}.class)\n        );\n        return R.data({Name}Wrapper.build().pageVO(pages));\n    }\n\n    /**\n     * 自定义分页\n     */\n    @GetMapping(\"/page\")\n    @Operation(summary = \"自定义分页\", description = \"传入{modelCode}\")\n    public R<IPage<{Name}VO>> page({Name}VO {modelCode}, Query query) {\n        IPage<{Name}VO> pages = {modelCode}Service.select{Name}Page(Condition.getPage(query), {modelCode});\n        return R.data(pages);\n    }\n\n    /**\n     * 新增\n     */\n    @PostMapping(\"/save\")\n    @Operation(summary = \"新增\", description = \"传入{modelCode}\")\n    public R save(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.save({modelCode}));\n    }\n\n    /**\n     * 修改\n     */\n    @PostMapping(\"/update\")\n    @Operation(summary = \"修改\", description = \"传入{modelCode}\")\n    public R update(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.updateById({modelCode}));\n    }\n\n    /**\n     * 新增或修改\n     */\n    @PostMapping(\"/submit\")\n    @Operation(summary = \"新增或修改\", description = \"传入{modelCode}\")\n    public R submit(@Valid @RequestBody {Name} {modelCode}) {\n        return R.status({modelCode}Service.saveOrUpdate({modelCode}));\n    }\n\n    /**\n     * 删除\n     */\n    @PostMapping(\"/remove\")\n    @Operation(summary = \"逻辑删除\", description = \"传入ids\")\n    public R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n        return R.status({modelCode}Service.deleteLogic(Func.toLongList(ids)));\n    }\n}\n```\n\n### Cloud 版本差异\n\nCloud 版本仅以下几处不同：\n\n```java\n// 1. 包名不同\npackage org.springblade.{module}.controller;\n\n// 2. import 路径不同（Entity/VO/Service 都在 org.springblade.{module} 下）\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.vo.{Name}VO;\nimport org.springblade.{module}.service.I{Name}Service;\n\n// 3. RequestMapping 不需要服务名前缀\n@RequestMapping(\"/{modelCode}\")\n```\n\n### 无 Wrapper 版本\n\n如不使用 Wrapper，detail 和 list 方法简化为：\n\n```java\n@GetMapping(\"/detail\")\npublic R<{Name}> detail({Name} {modelCode}) {\n    {Name} detail = {modelCode}Service.getOne(Condition.getQueryWrapper({modelCode}));\n    return R.data(detail);\n}\n\n@GetMapping(\"/list\")\npublic R<IPage<{Name}>> list(@RequestParam Map<String, Object> {modelCode}, Query query) {\n    IPage<{Name}> pages = {modelCode}Service.page(\n        Condition.getPage(query),\n        Condition.getQueryWrapper({modelCode}, {Name}.class)\n    );\n    return R.data(pages);\n}\n```\n\n### Raw Serializable 模式的 remove 方法\n\n```java\n@PostMapping(\"/remove\")\n@Operation(summary = \"删除\", description = \"传入ids\")\npublic R remove(@Parameter(description = \"主键集合\", required = true) @RequestParam String ids) {\n    return R.status({modelCode}Service.removeBatchByIds(Func.toLongList(ids)));\n}\n```\n\n### 权限注解使用规则\n\nSpringBlade 开源版仅支持角色级权限：\n\n| 场景 | 写法 |\n|------|------|\n| 仅管理员可访问（类级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在类上 |\n| 仅管理员可访问（方法级） | `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 加在方法上 |\n| 隐藏 Swagger 文档 | `@Hidden`（来自 OpenAPI 3） |\n| 全公开 | 不加任何权限注解 |\n\n### 导出接口\n\n```java\n@GetMapping(\"/export-{modelCode}\")\n@Operation(summary = \"导出数据\", description = \"传入{modelCode}\")\n@PreAuth(RoleConstant.HAS_ROLE_ADMIN)\npublic void export{Name}(@RequestParam Map<String, Object> {modelCode}, HttpServletResponse response) {\n    QueryWrapper<{Name}> queryWrapper = Condition.getQueryWrapper({modelCode}, {Name}.class);\n    List<{Name}Excel> list = {modelCode}Service.export{Name}(queryWrapper);\n    ExcelUtil.export(response, \"{中文名}数据\", \"{中文名}数据表\", list, {Name}Excel.class);\n}\n```\n\n---\n\n## 十、Wrapper 数据转换器\n\n```java\npackage org.springblade.modules.{module}.wrapper;\n\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.modules.system.cache.DictCache;\nimport org.springblade.modules.{module}.entity.{Name};\nimport org.springblade.modules.{module}.vo.{Name}VO;\n\n/**\n * {中文名}包装类，返回视图层所需的字段\n *\n * @author {author}\n */\npublic class {Name}Wrapper extends BaseEntityWrapper<{Name}, {Name}VO> {\n\n    public static {Name}Wrapper build() {\n        return new {Name}Wrapper();\n    }\n\n    @Override\n    public {Name}VO entityVO({Name} {modelCode}) {\n        {Name}VO {modelCode}VO = BeanUtil.copyProperties({modelCode}, {Name}VO.class);\n        // 字典翻译：将字典键值转换为可读名称\n        String categoryName = DictCache.getValue(\"{dictCode}\", {modelCode}.getCategory());\n        {modelCode}VO.setCategoryName(categoryName);\n        return {modelCode}VO;\n    }\n}\n```\n\n**常见翻译模式**：\n\n```java\n// 1. 字典翻译\nString categoryName = DictCache.getValue(\"{dictCode}\", entity.getCategory());\n\n// 2. 关联实体名称（通过缓存）\nUser user = UserCache.getUser(entity.getUserId());\nif (Func.isNotEmpty(user)) {\n    vo.setUserName(user.getAccount());\n}\n\n// 3. 父节点名称（树形结构）\nif (Func.equals(entity.getParentId(), BladeConstant.TOP_PARENT_ID)) {\n    vo.setParentName(BladeConstant.TOP_PARENT_NAME);\n} else {\n    {Name} parent = {modelCode}Service.getById(entity.getParentId());\n    vo.setParentName(parent.getName());\n}\n```\n\n**树形 Wrapper 追加方法**：\n\n```java\npublic List<{Name}VO> listNodeVO(List<{Name}> list) {\n    List<{Name}VO> collect = list.stream()\n        .map(entity -> BeanUtil.copyProperties(entity, {Name}VO.class))\n        .collect(Collectors.toList());\n    return ForestNodeMerger.merge(collect);\n}\n```\n\n---\n\n## 十一、Feign Client 接口（Cloud）\n\n放在 API 模块中（`blade-service-api/blade-{module}-api`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport org.springblade.core.launch.constant.AppConstant;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.{module}.entity.{Name};\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n/**\n * {中文名} Feign 接口\n *\n * @author {author}\n */\n@FeignClient(value = AppConstant.APPLICATION_{MODULE}_NAME)\npublic interface I{Name}Client {\n\n    String API_PREFIX = \"/feign/client/{modelCode}\";\n    String TOP = API_PREFIX + \"/top\";\n\n    /**\n     * 获取分页数据\n     */\n    @GetMapping(TOP)\n    BladePage<{Name}> top(@RequestParam(\"current\") Integer current, @RequestParam(\"size\") Integer size);\n}\n```\n\n---\n\n## 十二、Feign Client 实现（Cloud）\n\n放在 Service 模块中（`blade-service/blade-{module}`）：\n\n```java\npackage org.springblade.{module}.feign;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Hidden;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.mp.support.BladePage;\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.{module}.entity.{Name};\nimport org.springblade.{module}.service.I{Name}Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * {中文名} Feign 实现\n *\n * @author {author}\n */\n@Hidden\n@RestController\n@AllArgsConstructor\npublic class {Name}Client implements I{Name}Client {\n\n    private final I{Name}Service service;\n\n    @Override\n    @GetMapping(TOP)\n    public BladePage<{Name}> top(Integer current, Integer size) {\n        Query query = new Query();\n        query.setCurrent(current);\n        query.setSize(size);\n        IPage<{Name}> page = service.page(Condition.getPage(query));\n        return BladePage.of(page);\n    }\n}\n```\n\n**Feign 实现要点**：\n- `@Hidden`：对 Swagger 文档隐藏（内部接口）\n- `@RestController`：作为 HTTP 端点暴露\n- 路径常量复用接口中定义的常量\n\n---\n\n## 附：完整 import 参考\n\n### Entity 常用 import\n```java\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.springblade.core.mp.base.BaseEntity;      // BaseEntity\nimport org.springblade.core.mp.base.TenantEntity;   // TenantEntity\nimport org.springblade.core.tool.utils.DateUtil;\nimport org.springframework.format.annotation.DateTimeFormat;\n```\n\n### Controller 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport io.swagger.v3.oas.annotations.Operation;\nimport io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport jakarta.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport org.springblade.core.boot.ctrl.BladeController;\nimport org.springblade.core.launch.constant.AppConstant;      // Boot 路由前缀使用\nimport org.springblade.core.mp.support.Condition;\nimport org.springblade.core.mp.support.Query;\nimport org.springblade.core.secure.annotation.PreAuth;\nimport org.springblade.core.swagger.annotation.ApiOrder;\nimport org.springblade.core.tool.api.R;\nimport org.springblade.core.tool.constant.RoleConstant;\nimport org.springblade.core.tool.utils.Func;\nimport org.springframework.web.bind.annotation.*;\n```\n\n### Service 常用 import\n```java\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.springblade.core.mp.base.BaseService;\nimport org.springblade.core.mp.base.BaseServiceImpl;\nimport org.springblade.core.secure.utils.AuthUtil;\nimport org.springframework.stereotype.Service;\n```\n\n### Wrapper 常用 import\n```java\nimport org.springblade.core.mp.support.BaseEntityWrapper;\nimport org.springblade.core.tool.constant.BladeConstant;\nimport org.springblade.core.tool.node.ForestNodeMerger;\nimport org.springblade.core.tool.utils.BeanUtil;\nimport org.springblade.core.tool.utils.Func;\nimport org.springblade.modules.system.cache.DictCache;        // 字典缓存（Boot 路径）\n```\n\n<!-- File: .claude/skills/blade-design/references/database.md (Tokens: ~2713 | Category: anthropic-skill) -->\n# 数据库建表语句生成参考\n\n本文档包含 SpringBlade 开源版 MySQL 建表模板与菜单数据 SQL。所有模板均来源于开源工程 `doc/sql/` 下的真实 SQL 脚本。\n\n> SpringBlade 开源版仅官方支持 **MySQL**，生成的建表脚本固定为 MySQL 方言。\n\n## 目录\n- [一、通用审计字段](#一通用审计字段)\n- [二、字段类型映射表](#二字段类型映射表)\n- [三、MySQL 建表模板](#三mysql-建表模板)\n- [四、菜单 SQL 生成](#四菜单-sql-生成)\n\n---\n\n## 一、通用审计字段\n\n所有继承 `TenantEntity` 或 `BaseEntity` 的业务表都包含以下固定审计字段：\n\n| 字段名 | 说明 | Java 类型 | 是否可空 | 默认值 |\n|--------|------|-----------|----------|--------|\n| `id` | 主键（雪花 ID） | Long | NOT NULL | 无 |\n| `tenant_id` | 租户 ID（仅 TenantEntity） | String | NULL | '000000' |\n| `create_user` | 创建人 | Long | NULL | NULL |\n| `create_dept` | 创建部门 | Long | NULL | NULL |\n| `create_time` | 创建时间 | Date | NULL | NULL |\n| `update_user` | 修改人 | Long | NULL | NULL |\n| `update_time` | 修改时间 | Date | NULL | NULL |\n| `status` | 业务状态 | Integer | NULL | 1 |\n| `is_deleted` | 逻辑删除标记 | Integer | NULL | 0 |\n\n- **BaseEntity 模式**不包含 `tenant_id` 字段\n- **Raw Serializable 模式**手动定义全部字段（结构相同）\n\n---\n\n## 二、字段类型映射表\n\n### Java 类型 → MySQL 类型映射\n\n| Java 类型 | MySQL 类型 | 说明 |\n|-----------|-----------|------|\n| `String` (短) | `varchar(n)` | `n` 为字符数，通常 `varchar(255)` 以内 |\n| `String` (长) | `text` | 超过 255 字符或富文本 |\n| `Integer` | `int` | 32 位整型 |\n| `Long` | `bigint` | 64 位整型（雪花 ID 必须用） |\n| `Double` | `double` | 双精度浮点 |\n| `BigDecimal` | `decimal(m,n)` | 金额、精确数值 |\n| `Date` | `datetime` | 日期 + 时间 |\n| `Boolean` | `tinyint(1)` | 0 = false，1 = true |\n\n### 审计字段各字段定义（MySQL）\n\n| 字段 | MySQL 定义 |\n|------|-----------|\n| id | `bigint NOT NULL` |\n| tenant_id | `varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000'` |\n| create_user | `bigint NULL DEFAULT NULL` |\n| create_dept | `bigint NULL DEFAULT NULL` |\n| create_time | `datetime NULL DEFAULT NULL` |\n| update_user | `bigint NULL DEFAULT NULL` |\n| update_time | `datetime NULL DEFAULT NULL` |\n| status | `int NULL DEFAULT 1` |\n| is_deleted | `int NULL DEFAULT 0` |\n\n---\n\n## 三、MySQL 建表模板\n\n### TenantEntity 模式（多租户业务表）\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for blade_{table_name}\n-- ----------------------------\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  `tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户ID',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### BaseEntity 模式（跨租户业务表）\n\n与 TenantEntity 模板一致，**仅删除 `tenant_id` 这一行**：\n\n```sql\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\nDROP TABLE IF EXISTS `blade_{table_name}`;\nCREATE TABLE `blade_{table_name}`  (\n  `id` bigint NOT NULL COMMENT '主键',\n  -- 业务字段\n  `{column_name}` {mysql_type} {null_constraint} {default_value} COMMENT '{字段注释}',\n  -- 审计字段\n  `create_user` bigint NULL DEFAULT NULL COMMENT '创建人',\n  `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',\n  `update_user` bigint NULL DEFAULT NULL COMMENT '修改人',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `status` int NULL DEFAULT 1 COMMENT '状态',\n  `is_deleted` int NULL DEFAULT 0 COMMENT '是否已删除',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '{表注释}';\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### Raw Serializable 模式（系统 / 关系表）\n\n结构同 BaseEntity 模板（同样无 `tenant_id`）。差异在于 Java 侧手动定义全部字段，数据库层面建表语句本身不变。\n\n### 索引建议\n\n对于查询频繁的字段：\n\n```sql\n-- 普通索引\nCREATE INDEX idx_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 唯一索引\nCREATE UNIQUE INDEX uk_{table_name}_{column} ON blade_{table_name} ({column_name});\n\n-- 复合索引（多字段）\nCREATE INDEX idx_{table_name}_{col1}_{col2} ON blade_{table_name} ({col1}, {col2});\n```\n\n**命名约定**：\n- 唯一索引前缀 `uk_`\n- 普通索引前缀 `idx_`\n- 字段名用下划线拼接\n\n### MySQL 约定\n\n- 列名使用反引号 `` ` `` 包裹\n- 注释直接用 `COMMENT` 关键字写在列定义后\n- 表注释在 `CREATE TABLE` 末尾\n- 字符集固定 `utf8mb4`，排序规则 `utf8mb4_general_ci`\n- 存储引擎 `InnoDB`\n- 主键索引类型 `USING BTREE`\n- 租户 ID 列 `varchar(12)` 默认 `'000000'`\n\n---\n\n## 四、菜单 SQL 生成\n\n每个新模块需要插入 5 条菜单记录：1 个主菜单 + 4 个操作按钮（新增、修改、删除、查看）。\n\n### MySQL 菜单 SQL\n\n```sql\n-- ----------------------------\n-- {中文名}菜单数据\n-- ----------------------------\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({menuId}, {parentMenuId}, '{modelCode}', '{中文名}', 'menu', '/{module}/{modelCode}', NULL, 1, 1, 0, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({addMenuId}, {menuId}, '{modelCode}_add', '新增', 'add', '/{module}/{modelCode}/add', 'plus', 1, 2, 1, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({editMenuId}, {menuId}, '{modelCode}_edit', '修改', 'edit', '/{module}/{modelCode}/edit', 'form', 2, 2, 2, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({removeMenuId}, {menuId}, '{modelCode}_delete', '删除', 'delete', '/{module}/{modelCode}/delete', 'delete', 3, 2, 3, 1, NULL, 0);\n\nINSERT INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)\nVALUES ({viewMenuId}, {menuId}, '{modelCode}_view', '查看', 'view', '/{module}/{modelCode}/view', 'eye-open', 4, 2, 2, 1, NULL, 0);\n```\n\n### 菜单字段说明\n\n| 字段 | 说明 | 值 |\n|------|------|-----|\n| `id` | 主菜单 ID | 生成雪花 ID 或自定义（如 1123598812738675301） |\n| `parent_id` | 父菜单 ID | 由用户指定或放在顶级（0） |\n| `code` | 权限编码 | 主菜单: `{modelCode}`，按钮: `{modelCode}_{action}` |\n| `name` | 显示名称 | 主菜单: `{中文名}`，按钮: 新增/修改/删除/查看 |\n| `alias` | 别名 | 主菜单: `menu`，按钮: add/edit/delete/view |\n| `path` | 路由路径 | `/{module}/{modelCode}` |\n| `source` | 图标 | 按钮: plus/form/delete/eye-open |\n| `sort` | 排序 | 按钮从 1 到 4 |\n| `category` | 类别 | 1=菜单，2=按钮 |\n| `action` | 操作类型 | 0=无，1=新增，2=修改，3=删除 |\n| `is_open` | 是否打开 | 1=是 |\n| `is_deleted` | 是否删除 | 0=否 |\n\n---\n\n## 附：无租户表模式速查\n\n如果实体不需要 `tenant_id`（BaseEntity 或 Raw 模式），从建表模板中直接移除 `tenant_id` 行即可，其余审计字段保持不变。\n\n<!-- File: .claude/skills/blade-design/references/frontend.md (Tokens: ~7246 | Category: anthropic-skill) -->\n# 前端代码生成参考\n\n本文档包含 SpringBlade 开源版 Saber 前端各层的完整代码模板。基于 Vue 3 + Element Plus + Avue 技术栈，支持 Options API 和 Composition API 两种风格。\n\n> 前端 API 路径在 Boot 单体与 Cloud 微服务下**完全一致**，都走 `/blade-{module}/...`。Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 前缀对齐该路径，Cloud 则由 Gateway 按服务名 `blade-{module}` 自动路由。\n\n## 目录\n- [一、API 层](#一api-层)\n- [二、Option 配置层](#二option-配置层)\n- [三、Vue 页面 - Options API](#三vue-页面---options-api)\n- [四、Vue 页面 - Composition API](#四vue-页面---composition-api)\n- [五、树形结构模式](#五树形结构模式)\n- [六、主子表模式](#六主子表模式)\n\n---\n\n## 一、API 层\n\n文件位置：`src/api/{module}/{modelCode}.js`\n\n### 标准 CRUD API\n\n```javascript\nimport request from '@/axios';\n\n/**\n * 获取{中文名}列表（分页）\n */\nexport const getList = (current, size, params) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n    },\n  });\n};\n\n/**\n * 获取{中文名}详情\n */\nexport const getDetail = (id) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/detail',\n    method: 'get',\n    params: {\n      id,\n    },\n  });\n};\n\n/**\n * 删除{中文名}\n */\nexport const remove = (ids) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/remove',\n    method: 'post',\n    params: {\n      ids,\n    },\n  });\n};\n\n/**\n * 新增{中文名}\n */\nexport const add = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\n/**\n * 修改{中文名}\n */\nexport const update = (row) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n```\n\n**注意事项**：\n- 新增和修改共用 `/submit` 接口（后端 `saveOrUpdate`）\n- `remove` 的 `ids` 是逗号分隔的字符串\n- GET 请求用 `params`，POST 请求用 `data`（请求体）\n- 如需加密传输，添加 `cryptoToken: true` 和 `cryptoData: true`\n\n### 树形模式追加\n\n```javascript\n/**\n * 获取{中文名}树形结构\n */\nexport const getTree = (tenantId) => {\n  return request({\n    url: '/blade-{module}/{modelCode}/tree',\n    method: 'get',\n    params: {\n      tenantId,\n    },\n  });\n};\n```\n\n### 导出接口\n\n```javascript\nimport { exportBlob } from '@/api/common';\n\n/**\n * 导出{中文名}数据\n */\nexport const exportData = (params) => {\n  return exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}`, params);\n};\n```\n\n---\n\n## 二、Option 配置层\n\n文件位置：`src/option/{module}/{modelCode}.js`\n\n### 标准 CRUD 配置\n\n```javascript\nexport const tableOption = {\n  height: 'auto',\n  calcHeight: 30,\n  tip: false,\n  searchShow: true,\n  searchMenuSpan: 6,\n  border: true,\n  index: true,\n  selection: true,\n  viewBtn: true,\n  dialogClickModal: false,\n  column: [\n    // --- 文本输入框 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      search: true,                    // 是否为搜索字段\n      rules: [\n        {\n          required: true,\n          message: '请输入{字段中文名}',\n          trigger: 'blur',\n        },\n      ],\n    },\n\n    // --- 数字输入 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'number',\n      align: 'right',\n      width: 100,\n    },\n\n    // --- 下拉选择（远程字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      search: true,\n      dicUrl: '/blade-system/dict/dictionary?code={dictCode}',\n      props: {\n        label: 'dictValue',\n        value: 'dictKey',\n      },\n      rules: [\n        {\n          required: true,\n          message: '请选择{字段中文名}',\n          trigger: 'change',\n        },\n      ],\n    },\n\n    // --- 下拉选择（本地字典） ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'select',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 开关 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'switch',\n      align: 'center',\n      width: 100,\n      dicData: [\n        { label: '否', value: 0 },\n        { label: '是', value: 1 },\n      ],\n      value: 0,\n    },\n\n    // --- 单选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'radio',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n      value: 1,\n    },\n\n    // --- 多选 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'checkbox',\n      dicData: [\n        { label: '选项A', value: 1 },\n        { label: '选项B', value: 2 },\n      ],\n    },\n\n    // --- 日期选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'date',\n      format: 'YYYY-MM-DD',\n      valueFormat: 'YYYY-MM-DD',\n    },\n\n    // --- 日期时间选择 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetime',\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 日期范围搜索 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'datetimerange',\n      searchRange: true,\n      search: true,\n      hide: true,                      // 列表不显示\n      display: false,                  // 表单不显示\n      format: 'YYYY-MM-DD HH:mm:ss',\n      valueFormat: 'YYYY-MM-DD HH:mm:ss',\n    },\n\n    // --- 多行文本 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      type: 'textarea',\n      span: 24,                        // 占满整行\n      hide: true,                      // 列表不显示\n      minRows: 3,\n    },\n\n    // --- 富文本编辑器 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      component: 'avue-ueditor',\n      options: {\n        action: '/blade-resource/oss/endpoint/put-file',\n        props: {\n          res: 'data',\n          url: 'link',\n        },\n      },\n      span: 24,\n      hide: true,\n      minRows: 6,\n    },\n\n    // --- 仅列表显示，表单不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      addDisplay: false,\n      editDisplay: false,\n    },\n\n    // --- 仅表单显示，列表不显示 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      hide: true,\n    },\n\n    // --- 自定义插槽 ---\n    {\n      label: '{字段中文名}',\n      prop: '{fieldName}',\n      slot: true,                      // 启用自定义渲染插槽\n    },\n\n    // --- 备注（隐藏在列表） ---\n    {\n      label: '备注',\n      prop: 'remark',\n      hide: true,\n    },\n  ],\n};\n```\n\n### 分组表单配置\n\n用于复杂表单（如用户管理），将字段分组到多个 Tab 页：\n\n```javascript\nexport const tableOption = (safe) => {\n  // 自定义校验器（使用闭包访问组件实例）\n  const validatePass = (rule, value, callback) => {\n    if (value === '') {\n      callback(new Error('请输入密码'));\n    } else {\n      callback();\n    }\n  };\n\n  return {\n    height: 'auto',\n    calcHeight: 150,\n    tip: false,\n    searchShow: true,\n    searchMenuSpan: 6,\n    border: true,\n    index: true,\n    selection: true,\n    viewBtn: true,\n    dialogType: 'drawer',\n    dialogWidth: 1000,\n    dialogClickModal: false,\n\n    // 列表使用 column\n    column: [\n      { label: '字段A', prop: 'fieldA', search: true },\n      { label: '字段B', prop: 'fieldB' },\n    ],\n\n    // 表单使用 group（分组标签页）\n    group: [\n      {\n        label: '基础信息',\n        prop: 'baseInfo',\n        icon: 'el-icon-user-solid',\n        column: [\n          { label: '字段A', prop: 'fieldA', rules: [{ required: true, message: '请输入', trigger: 'blur' }] },\n          { label: '字段B', prop: 'fieldB', type: 'select', dicData: [] },\n        ],\n      },\n      {\n        label: '详细信息',\n        prop: 'detailInfo',\n        icon: 'el-icon-s-order',\n        column: [\n          { label: '字段C', prop: 'fieldC' },\n          { label: '字段D', prop: 'fieldD', type: 'textarea', span: 24 },\n        ],\n      },\n    ],\n  };\n};\n```\n\n### 列属性速查\n\n| 属性 | 类型 | 说明 |\n|------|------|------|\n| `label` | String | 列标题/表单标签 |\n| `prop` | String | 字段名 |\n| `type` | String | 组件类型：input/select/radio/checkbox/switch/date/datetime/textarea/number/tree/upload |\n| `search` | Boolean | 是否为搜索字段 |\n| `hide` | Boolean | 列表中隐藏 |\n| `display` | Boolean | 表单中隐藏（false=不显示） |\n| `addDisplay` | Boolean | 新增表单中隐藏 |\n| `editDisplay` | Boolean | 编辑表单中隐藏 |\n| `addDisabled` | Boolean | 新增时禁用 |\n| `editDisabled` | Boolean | 编辑时禁用 |\n| `span` | Number | 表单栅格宽度（24=整行） |\n| `slot` | Boolean | 启用自定义插槽 |\n| `rules` | Array | 校验规则 |\n| `dicUrl` | String | 远程字典 URL |\n| `dicData` | Array | 本地字典数据 |\n| `props` | Object | 字典映射 { label, value } |\n| `value` | Any | 默认值 |\n| `width` | Number | 列宽度 |\n| `align` | String | 对齐方式：left/center/right |\n| `overHidden` | Boolean | 超长文本省略显示 |\n| `component` | String | 自定义组件名 |\n| `searchRange` | Boolean | 搜索使用范围选择 |\n| `format` | String | 显示格式 |\n| `valueFormat` | String | 值格式 |\n| `minRows` | Number | textarea 最小行数 |\n\n---\n\n## 三、Vue 页面 - Options API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"$refs.crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n\n      <!-- 自定义列插槽示例 -->\n      <template #{slotField}=\"{ row }\">\n        <el-tag>{{ row.{slotField} }}</el-tag>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script>\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\nimport { mapGetters } from 'vuex';\n\nexport default {\n  data() {\n    return {\n      form: {},\n      query: {},\n      loading: true,\n      page: {\n        pageSize: 10,\n        currentPage: 1,\n        total: 0,\n      },\n      selectionList: [],\n      option: tableOption,\n      data: [],\n    };\n  },\n\n  computed: {\n    ...mapGetters(['permission']),\n    permissionList() {\n      return {\n        addBtn: this.validData(this.permission.{modelCode}_add, false),\n        viewBtn: this.validData(this.permission.{modelCode}_view, false),\n        delBtn: this.validData(this.permission.{modelCode}_delete, false),\n        editBtn: this.validData(this.permission.{modelCode}_edit, false),\n      };\n    },\n    ids() {\n      let ids = [];\n      this.selectionList.forEach(ele => {\n        ids.push(ele.id);\n      });\n      return ids.join(',');\n    },\n  },\n\n  methods: {\n    rowSave(row, done, loading) {\n      add(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdate(row, index, done, loading) {\n      update(row).then(\n        () => {\n          this.onLoad(this.page);\n          this.$message({\n            type: 'success',\n            message: '操作成功!',\n          });\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDel(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(row.id);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n      });\n    },\n\n    handleDelete() {\n      if (this.selectionList.length === 0) {\n        this.$message.warning('请选择至少一条数据');\n        return;\n      }\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return remove(this.ids);\n      }).then(() => {\n        this.onLoad(this.page);\n        this.$message({\n          type: 'success',\n          message: '操作成功!',\n        });\n        this.$refs.crud.toggleSelection();\n      });\n    },\n\n    beforeOpen(done, type) {\n      if (['edit', 'view'].includes(type)) {\n        getDetail(this.form.id).then(res => {\n          this.form = res.data.data;\n        });\n      }\n      done();\n    },\n\n    searchChange(params, done) {\n      this.query = params;\n      this.page.currentPage = 1;\n      this.onLoad(this.page, params);\n      done();\n    },\n\n    searchReset() {\n      this.query = {};\n      this.onLoad(this.page);\n    },\n\n    selectionChange(list) {\n      this.selectionList = list;\n    },\n\n    selectionClear() {\n      this.selectionList = [];\n      this.$refs.crud.toggleSelection();\n    },\n\n    currentChange(currentPage) {\n      this.page.currentPage = currentPage;\n    },\n\n    sizeChange(pageSize) {\n      this.page.pageSize = pageSize;\n    },\n\n    refreshChange() {\n      this.onLoad(this.page, this.query);\n    },\n\n    onLoad(page, params = {}) {\n      this.loading = true;\n      getList(\n        page.currentPage,\n        page.pageSize,\n        Object.assign(params, this.query)\n      ).then(res => {\n        const data = res.data.data;\n        this.page.total = data.total;\n        this.data = data.records;\n        this.loading = false;\n        this.selectionClear();\n      });\n    },\n  },\n};\n</script>\n```\n\n---\n\n## 四、Vue 页面 - Composition API\n\n文件位置：`src/views/{module}/{modelCode}.vue`\n\n```vue\n<template>\n  <basic-container>\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      v-model:search=\"query\"\n      :before-open=\"beforeOpen\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @search-change=\"searchChange\"\n      @search-reset=\"searchReset\"\n      @selection-change=\"selectionChange\"\n      @current-change=\"currentChange\"\n      @size-change=\"sizeChange\"\n      @refresh-change=\"refreshChange\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu-left>\n        <el-button\n          type=\"primary\"\n          icon=\"el-icon-plus\"\n          @click=\"crud.rowAdd()\"\n          v-if=\"permission.{modelCode}_add\"\n        >新 增</el-button>\n        <el-button\n          type=\"danger\"\n          icon=\"el-icon-delete\"\n          plain\n          @click=\"handleDelete\"\n          v-if=\"permission.{modelCode}_delete\"\n        >删 除</el-button>\n      </template>\n    </avue-crud>\n  </basic-container>\n</template>\n\n<script setup>\nimport { ref, reactive, computed } from 'vue';\nimport { useStore } from 'vuex';\nimport { ElMessage, ElMessageBox } from 'element-plus';\nimport { getList, getDetail, add, update, remove } from '@/api/{module}/{modelCode}';\nimport { tableOption } from '@/option/{module}/{modelCode}';\n\nconst store = useStore();\nconst crud = ref(null);\n\n// 数据\nconst form = ref({});\nconst query = ref({});\nconst loading = ref(true);\nconst data = ref([]);\nconst selectionList = ref([]);\nconst option = reactive(tableOption);\nconst page = ref({\n  pageSize: 10,\n  currentPage: 1,\n  total: 0,\n});\n\n// 权限\nconst permission = computed(() => store.getters.permission);\nconst permissionList = computed(() => {\n  const validData = (value, defaultVal = false) => (value ? true : defaultVal);\n  return {\n    addBtn: validData(permission.value.{modelCode}_add),\n    viewBtn: validData(permission.value.{modelCode}_view),\n    delBtn: validData(permission.value.{modelCode}_delete),\n    editBtn: validData(permission.value.{modelCode}_edit),\n  };\n});\n\nconst ids = computed(() => {\n  return selectionList.value.map(ele => ele.id).join(',');\n});\n\n// 方法\nconst rowSave = (row, done, loading) => {\n  add(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowUpdate = (row, index, done, loading) => {\n  update(row).then(\n    () => {\n      onLoad(page.value);\n      ElMessage.success('操作成功!');\n      done();\n    },\n    error => {\n      loading();\n    }\n  );\n};\n\nconst rowDel = (row) => {\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(row.id);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n  });\n};\n\nconst handleDelete = () => {\n  if (selectionList.value.length === 0) {\n    ElMessage.warning('请选择至少一条数据');\n    return;\n  }\n  ElMessageBox.confirm('确定将选择数据删除?', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning',\n  }).then(() => {\n    return remove(ids.value);\n  }).then(() => {\n    onLoad(page.value);\n    ElMessage.success('操作成功!');\n    crud.value.toggleSelection();\n  });\n};\n\nconst beforeOpen = (done, type) => {\n  if (['edit', 'view'].includes(type)) {\n    getDetail(form.value.id).then(res => {\n      form.value = res.data.data;\n    });\n  }\n  done();\n};\n\nconst searchChange = (params, done) => {\n  query.value = params;\n  page.value.currentPage = 1;\n  onLoad(page.value, params);\n  done();\n};\n\nconst searchReset = () => {\n  query.value = {};\n  onLoad(page.value);\n};\n\nconst selectionChange = (list) => {\n  selectionList.value = list;\n};\n\nconst currentChange = (currentPage) => {\n  page.value.currentPage = currentPage;\n};\n\nconst sizeChange = (pageSize) => {\n  page.value.pageSize = pageSize;\n};\n\nconst refreshChange = () => {\n  onLoad(page.value, query.value);\n};\n\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(\n    page.currentPage,\n    page.pageSize,\n    Object.assign(params, query.value)\n  ).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n    selectionList.value = [];\n  });\n};\n</script>\n```\n\n### Options API vs Composition API 转换对照\n\n| Options API | Composition API |\n|-------------|-----------------|\n| `data() { return { form: {} } }` | `const form = ref({})` |\n| `computed: { ids() { ... } }` | `const ids = computed(() => { ... })` |\n| `methods: { rowSave() { ... } }` | `const rowSave = () => { ... }` |\n| `this.form` | `form.value`（模板中直接用 `form`） |\n| `this.$refs.crud` | `crud.value` |\n| `this.$message.success(...)` | `ElMessage.success(...)` |\n| `this.$confirm(...)` | `ElMessageBox.confirm(...)` |\n| `...mapGetters(['permission'])` | `const permission = computed(() => store.getters.permission)` |\n| `mounted() { ... }` | `onMounted(() => { ... })` |\n| `watch: { ... }` | `watch(source, callback)` |\n\n---\n\n## 五、树形结构模式\n\n### Option 配置（追加树形父节点字段）\n\n```javascript\n// 在 column 中添加树形父节点字段\n{\n  label: '上级{中文名}',\n  prop: 'parentId',\n  type: 'tree',\n  dicData: [],                    // 动态加载\n  hide: true,\n  value: 0,                       // 默认值为顶级\n  props: {\n    label: 'title',\n    value: 'value',\n    children: 'children',\n  },\n},\n```\n\n### Vue 页面（树形数据加载）\n\n在 `onLoad` 之后加载树形数据并更新列配置：\n\n```javascript\n// Options API\nmethods: {\n  onLoad(page, params = {}) {\n    this.loading = true;\n    getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {\n      const data = res.data.data;\n      this.page.total = data.total;\n      this.data = data.records;\n      this.loading = false;\n\n      // 加载树形数据\n      getTree().then(treeRes => {\n        const column = this.findObject(this.option.column, 'parentId');\n        column.dicData = treeRes.data.data;\n      });\n    });\n  },\n},\n\n// Composition API\nconst onLoad = (page, params = {}) => {\n  loading.value = true;\n  getList(page.currentPage, page.pageSize, Object.assign(params, query.value)).then(res => {\n    const resData = res.data.data;\n    page.total = resData.total;\n    data.value = resData.records;\n    loading.value = false;\n\n    getTree().then(treeRes => {\n      const column = findObject(option.column, 'parentId');\n      column.dicData = treeRes.data.data;\n    });\n  });\n};\n```\n\n### 表格树形展示\n\n在 avue-crud 模板中使用树形属性：\n\n```vue\n<avue-crud\n  :option=\"option\"\n  :data=\"data\"\n  row-key=\"id\"\n  :tree-props=\"{ children: 'children', hasChildren: 'hasChildren' }\"\n>\n```\n\n---\n\n## 六、主子表模式\n\n### API 层（主表 + 子表）\n\n```javascript\n// 主表 API（同标准 CRUD）\nexport const getList = (current, size, params) => { ... };\nexport const add = (row) => { ... };\nexport const update = (row) => { ... };\nexport const remove = (ids) => { ... };\n\n// 子表 API\nexport const getSubList = (current, size, {fkFieldName}, params) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/list',\n    method: 'get',\n    params: {\n      ...params,\n      current,\n      size,\n      {fkFieldName},\n    },\n  });\n};\n\nexport const addSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const updateSub = (row) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/submit',\n    method: 'post',\n    data: row,\n  });\n};\n\nexport const removeSub = (ids) => {\n  return request({\n    url: '/blade-{module}/{subModelCode}/remove',\n    method: 'post',\n    params: { ids },\n  });\n};\n```\n\n### Vue 页面（主子表 Drawer 模式）\n\n```vue\n<template>\n  <basic-container>\n    <!-- 主表 -->\n    <avue-crud\n      :option=\"option\"\n      :table-loading=\"loading\"\n      :data=\"data\"\n      :page=\"page\"\n      :permission=\"permissionList\"\n      ref=\"crud\"\n      v-model=\"form\"\n      @row-del=\"rowDel\"\n      @row-update=\"rowUpdate\"\n      @row-save=\"rowSave\"\n      @on-load=\"onLoad\"\n    >\n      <template #menu=\"{ row }\">\n        <el-button text type=\"primary\" @click=\"openSub(row)\">子表</el-button>\n      </template>\n    </avue-crud>\n\n    <!-- 子表抽屉 -->\n    <el-drawer\n      title=\"{子表中文名}\"\n      v-model=\"subBox\"\n      size=\"60%\"\n      append-to-body\n    >\n      <avue-crud\n        :option=\"subOption\"\n        :table-loading=\"subLoading\"\n        :data=\"subData\"\n        :page=\"subPage\"\n        ref=\"crudSub\"\n        v-model=\"subForm\"\n        @row-del=\"rowDelSub\"\n        @row-update=\"rowUpdateSub\"\n        @row-save=\"rowSaveSub\"\n        @on-load=\"onLoadSub\"\n      />\n    </el-drawer>\n  </basic-container>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      // 主表数据\n      form: {},\n      data: [],\n      loading: true,\n      page: { pageSize: 10, currentPage: 1, total: 0 },\n      option: tableOption,\n\n      // 子表数据\n      subBox: false,\n      subForm: {},\n      subData: [],\n      subLoading: true,\n      subPage: { pageSize: 10, currentPage: 1, total: 0 },\n      subOption: subTableOption,\n      mainId: '',\n    };\n  },\n\n  methods: {\n    openSub(row) {\n      this.mainId = row.id;\n      this.subBox = true;\n      this.onLoadSub(this.subPage);\n    },\n\n    rowSaveSub(row, done, loading) {\n      row.{fkFieldName} = this.mainId;\n      addSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowUpdateSub(row, index, done, loading) {\n      updateSub(row).then(\n        () => {\n          this.onLoadSub(this.subPage);\n          this.$message.success('操作成功!');\n          done();\n        },\n        error => {\n          loading();\n        }\n      );\n    },\n\n    rowDelSub(row) {\n      this.$confirm('确定将选择数据删除?', {\n        confirmButtonText: '确定',\n        cancelButtonText: '取消',\n        type: 'warning',\n      }).then(() => {\n        return removeSub(row.id);\n      }).then(() => {\n        this.onLoadSub(this.subPage);\n        this.$message.success('操作成功!');\n      });\n    },\n\n    onLoadSub(page, params = {}) {\n      this.subLoading = true;\n      getSubList(\n        page.currentPage,\n        page.pageSize,\n        this.mainId,\n        params\n      ).then(res => {\n        const data = res.data.data;\n        this.subPage.total = data.total;\n        this.subData = data.records;\n        this.subLoading = false;\n      });\n    },\n\n    // ... 主表方法同标准 CRUD\n  },\n};\n</script>\n```\n\n---\n\n## 附：常用工具函数\n\n### 数组与逗号字符串互转\n\n后端用逗号字符串存储多选值（如 deptId=\"1,2,3\"），前端需要转换：\n\n```javascript\nimport func from '@/utils/func';\n\n// 提交时：数组 → 逗号字符串\nrow.deptId = func.join(row.deptId);\n\n// 回显时：逗号字符串 → 数组\nthis.form.deptId = func.split(this.form.deptId);\n```\n\n### 导出功能\n\n```javascript\nimport NProgress from 'nprogress';\nimport { exportBlob } from '@/api/common';\nimport { downloadXls } from '@/utils/util';\n\nconst handleExport = () => {\n  NProgress.start();\n  exportBlob(`/blade-{module}/{modelCode}/export-{modelCode}?${qs.stringify(this.query)}`).then(res => {\n    downloadXls(res.data, `{中文名}数据${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);\n    NProgress.done();\n  });\n};\n```\n\n### 辅助方法 findObject / findColumn\n\n```javascript\n// 在 option 的 column 中查找指定 prop 的列\nconst column = this.findObject(this.option.column, 'fieldName');\n\n// 在 group 中查找\nconst column = this.findColumn(this.option.group, 'fieldName');\n```\n\n这两个是全局挂载的工具方法（在 `main.js` 中注册）。\n\n<!-- File: .claude/skills/blade-design/SKILL.md (Tokens: ~3700 | Category: anthropic-skill) -->\n---\nname: blade-design\ndescription: SpringBlade 全栈代码生成器。根据用户提供的模块名、实体名和字段列表，自动生成符合 SpringBlade 开源版框架规范的后端代码（Boot 单体/Cloud 微服务）、前端页面（Saber Options API/Composition API）和 MySQL 建表语句。当用户说\"帮我生成代码\"、\"创建一个新模块\"、\"生成 CRUD\"、\"新建实体\"、\"建表语句\"、\"生成前端页面\"、\"生成后端接口\"、\"生成 SpringBlade 代码\"、\"写一个 xxx 管理功能\"、\"帮我设计 xxx 表\"、\"blade-design\"等时触发。即使用户只是描述了业务字段或给出了表结构，也应触发此技能来生成对应的 SpringBlade 规范代码。\n---\n\n# SpringBlade 全栈代码生成器 (blade-design)\n\n根据用户需求生成符合 SpringBlade 开源版框架规范的完整 CRUD 代码，覆盖后端、前端、数据库三层。生成的代码与 SpringBlade 现有模块（如 Notice、Dict、Post、User）风格完全一致。\n\n## 信息收集协议\n\n在生成代码前，必须确认以下信息。如用户未提供，主动询问：\n\n### 必需信息\n| 参数 | 说明 | 示例 |\n|------|------|------|\n| **模块名** | 业务模块标识（英文小写） | `desk`、`system`、`order` |\n| **实体名** | 实体类名（PascalCase） | `Notice`、`Order`、`Product` |\n| **中文名** | 业务描述 | `通知公告`、`订单管理` |\n| **字段列表** | 业务字段（不含审计字段） | 见下方格式 |\n\n### 可选信息（有默认值）\n| 参数 | 默认值 | 可选值 |\n|------|--------|--------|\n| **架构类型** | `boot` | `boot`（单体）、`cloud`（微服务） |\n| **实体模式** | `tenant` | `tenant`（租户隔离）、`base`（无租户）、`raw`（原生 Serializable） |\n| **前端风格** | `options` | `options`（Options API）、`composition`（Composition API） |\n| **模板类型** | `crud` | `crud`（基础增删改查）、`tree`（树形结构）、`sub`（主子表） |\n| **是否含 Wrapper** | `true` | `true`、`false` |\n| **生成范围** | `all` | `all`、`backend`、`frontend`、`database` |\n| **服务名（Cloud）** | `blade-{模块名}` | 微服务名称 |\n| **作者** | `BladeX` | 代码注释中的作者 |\n\n> SpringBlade 开源版仅官方支持 MySQL，生成的建表脚本固定为 MySQL 方言。\n\n### 字段定义格式\n\n用户可以用表格或简洁文本描述字段，你需要解析为以下结构：\n\n| 属性 | 说明 |\n|------|------|\n| `name` | Java 属性名（camelCase） |\n| `column` | 数据库列名（snake_case） |\n| `type` | Java 类型：`String`、`Integer`、`Long`、`Date`、`BigDecimal`、`Double` |\n| `jdbcType` | 数据库类型：`varchar(n)`、`int`、`bigint`、`datetime`、`text`、`decimal(m,n)` |\n| `comment` | 中文字段说明 |\n| `required` | 是否必填（默认 false） |\n| `query` | 是否为查询字段（默认 false） |\n| `queryType` | 查询方式：`EQ`、`LIKE`、`BETWEEN`（默认 EQ） |\n| `list` | 是否在列表显示（默认 true） |\n| `form` | 是否在表单显示（默认 true） |\n| `component` | 前端组件：`input`、`textarea`、`select`、`radio`、`checkbox`、`switch`、`date`、`datetime`、`number`、`upload`（默认 input） |\n| `dictCode` | 字典编码（用于 select/radio 类型） |\n\n## Boot vs Cloud 核心架构差异\n\n生成代码时根据架构类型自动调整以下差异点：\n\n### 模块结构\n\n**Boot（单体应用）**：所有代码在同一模块内\n```\nsrc/main/java/org/springblade/modules/{module}/\n├── controller/\n├── service/ + impl/\n├── mapper/\n├── wrapper/\n├── entity/ + dto/ + vo/\n└── excel/\n```\n\n**Cloud（微服务）**：API 与实现分离为两个 Maven 模块\n```\nblade-service-api/blade-{module}-api/          ← 契约层\n└── org/springblade/{module}/\n    ├── entity/ + dto/ + vo/\n    └── feign/I{Name}Client.java               ← Feign 接口\n\nblade-service/blade-{module}/                  ← 实现层\n└── org/springblade/{module}/\n    ├── controller/\n    ├── service/ + impl/\n    ├── mapper/\n    ├── wrapper/\n    └── feign/{Name}Client.java                ← Feign 实现\n```\n\n### 关键差异速查\n\n| 维度 | Boot | Cloud |\n|------|------|-------|\n| **基础包名** | `org.springblade.modules.{module}` | `org.springblade.{module}` |\n| **Controller 路由** | `@RequestMapping(AppConstant.APPLICATION_XXX_NAME + \"/{path}\")` | `@RequestMapping(\"/{path}\")` |\n| **服务间调用** | 直接注入 Service | Feign Client 远程调用 |\n| **Entity/VO 位置** | 与 Service 同模块 | 独立 API 模块中 |\n| **Feign Client** | 无 | API 模块：接口定义；Service 模块：实现类 |\n| **POM 依赖** | 单一 pom.xml | API 和 Service 各自的 pom.xml |\n| **服务注册** | 不需要 | Nacos 服务注册与发现 |\n| **Gateway 路由** | 不需要 | Gateway 自动按服务名路由 |\n\n### Boot 路由规则\nBoot 模式下 Controller 需要手动添加服务名前缀，因为没有 Gateway 路由：\n```java\n// Boot: 完整路径 /desk/notice/detail\n@RequestMapping(AppConstant.APPLICATION_DESK_NAME + \"/notice\")\n\n// Cloud: Gateway 自动添加 /blade-desk 前缀，只需 /notice/detail\n@RequestMapping(\"/notice\")\n```\n\n## 实体继承模式\n\n根据业务场景选择合适的实体基类：\n\n### 模式一：TenantEntity（默认，租户隔离业务表）\n- 继承 `TenantEntity`（`org.springblade.core.mp.base.TenantEntity`），自动拥有 `tenantId` + 全套审计字段\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 删除使用 `deleteLogic()`（逻辑删除）\n- 适用：User、Post 等绝大多数多租户业务表\n\n### 模式二：BaseEntity（无租户业务表）\n- 继承 `BaseEntity`（`org.springblade.core.mp.base.BaseEntity`），拥有审计字段但无 `tenantId`\n- Service 继承 `BaseService<Entity>` / `BaseServiceImpl<Mapper, Entity>`\n- 适用：Tenant、Notice 等跨租户业务表\n- 也可通过在 `application.yml`（Boot）或 Nacos `blade.yaml`（Cloud）的 `blade.tenant.tables` 中配置表名实现字段隔离，不强制继承 TenantEntity\n\n### 模式三：Raw Serializable（原生模式）\n- 实现 `Serializable`，所有字段手动定义\n- Service 继承 `IService<Entity>` / `ServiceImpl<Mapper, Entity>`\n- 不继承 SpringBlade 基类，使用 MyBatis-Plus 原生 CRUD\n- 删除使用 `removeBatchByIds()`\n- 适用：Menu、Role、Dept、Dict、RoleMenu 等跨租户系统/关系表\n\n## 代码生成工作流\n\n### Step 1: 收集信息\n确认所有必需参数和可选参数，构建完整的生成上下文。\n\n### Step 2: 生成代码\n根据用户选择的生成范围，读取对应的参考文件并生成代码：\n\n| 生成范围 | 参考文件 | 生成产物 |\n|----------|----------|----------|\n| **后端** | 读取 `references/backend.md` | Entity、DTO、VO、Mapper、Mapper.xml、Service、ServiceImpl、Controller、Wrapper、Excel、Feign（Cloud） |\n| **前端** | 读取 `references/frontend.md` | API 模块、Option 配置、Vue 页面 |\n| **数据库** | 读取 `references/database.md` | MySQL 建表 SQL、菜单 SQL |\n\n### Step 3: 输出代码\n按文件维度逐个输出，每个文件包含：\n1. 完整文件路径（相对于项目根目录）\n2. 完整可运行的代码内容\n3. 必要的说明注释\n\n## 输出文件清单\n\n### 后端产物（Boot）\n```\nsrc/main/java/org/springblade/modules/{module}/entity/{Name}.java\nsrc/main/java/org/springblade/modules/{module}/dto/{Name}DTO.java\nsrc/main/java/org/springblade/modules/{module}/vo/{Name}VO.java\nsrc/main/java/org/springblade/modules/{module}/excel/{Name}Excel.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.java\nsrc/main/java/org/springblade/modules/{module}/mapper/{Name}Mapper.xml\nsrc/main/java/org/springblade/modules/{module}/service/I{Name}Service.java\nsrc/main/java/org/springblade/modules/{module}/service/impl/{Name}ServiceImpl.java\nsrc/main/java/org/springblade/modules/{module}/controller/{Name}Controller.java\nsrc/main/java/org/springblade/modules/{module}/wrapper/{Name}Wrapper.java\n```\n\n### 后端产物（Cloud）\n```\nblade-service-api/blade-{module}-api/.../entity/{Name}.java\nblade-service-api/blade-{module}-api/.../dto/{Name}DTO.java\nblade-service-api/blade-{module}-api/.../vo/{Name}VO.java\nblade-service-api/blade-{module}-api/.../feign/I{Name}Client.java\nblade-service/blade-{module}/.../excel/{Name}Excel.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.java\nblade-service/blade-{module}/.../mapper/{Name}Mapper.xml\nblade-service/blade-{module}/.../service/I{Name}Service.java\nblade-service/blade-{module}/.../service/impl/{Name}ServiceImpl.java\nblade-service/blade-{module}/.../controller/{Name}Controller.java\nblade-service/blade-{module}/.../wrapper/{Name}Wrapper.java\nblade-service/blade-{module}/.../feign/{Name}Client.java\n```\n\n### 前端产物\n```\nsrc/api/{module}/{modelCode}.js\nsrc/option/{module}/{modelCode}.js\nsrc/views/{module}/{modelCode}.vue\n```\n\n### 数据库产物\n```\n{name}.mysql.sql              -- MySQL 建表语句\n{name}.menu.sql               -- 菜单数据 SQL\n```\n\n## 命名约定\n\n| 维度 | 规则 | 示例（实体名：OrderItem） |\n|------|------|--------------------------|\n| 实体类 | `{Name}` | `OrderItem` |\n| VO 类 | `{Name}VO` | `OrderItemVO` |\n| DTO 类 | `{Name}DTO` | `OrderItemDTO` |\n| Excel 类 | `{Name}Excel` | `OrderItemExcel` |\n| Mapper 接口 | `{Name}Mapper` | `OrderItemMapper` |\n| Mapper XML | `{Name}Mapper.xml` | `OrderItemMapper.xml` |\n| Service 接口 | `I{Name}Service` | `IOrderItemService` |\n| Service 实现 | `{Name}ServiceImpl` | `OrderItemServiceImpl` |\n| Controller | `{Name}Controller` | `OrderItemController` |\n| Wrapper | `{Name}Wrapper` | `OrderItemWrapper` |\n| Feign 接口 | `I{Name}Client` | `IOrderItemClient` |\n| Feign 实现 | `{Name}Client` | `OrderItemClient` |\n| 数据库表名 | `blade_{snake_case}` | `blade_order_item` |\n| API 路径 | `/{modelCode}` | `/order-item` |\n| 前端文件 | `{modelCode}` | `orderItem` |\n\n> SpringBlade 开源版 Entity 统一**不带 `Entity` 后缀**（如 `User.java`、`Post.java`、`Notice.java`）。请严格遵循此命名。\n\n## 重要规范\n\n1. **所有 Long 类型的 ID 字段**必须添加 `@JsonSerialize(using = ToStringSerializer.class)` 防止前端精度丢失\n2. **日期字段**必须同时添加 `@DateTimeFormat` 和 `@JsonFormat`，pattern 统一使用 `DateUtil.PATTERN_DATETIME`\n3. **逻辑删除字段** `isDeleted` 必须添加 `@TableLogic` 注解（继承 BaseEntity / TenantEntity 时由基类自动处理）\n4. **Swagger 文档**使用 OpenAPI 3 注解：`@Tag`、`@Operation`、`@Schema`、`@Parameter`\n5. **接口排序**：控制器类级标注无参 `@ApiOrder`（置于 `@Tag` 上方），分组内接口按源码声明顺序展示；个别接口需调整顺序时才在方法上标 `@ApiOrder(n)`\n6. **响应包装**统一使用 `R<T>` 类\n7. **Wrapper 的 entityVO() 方法**中通过 `DictCache.getValue()` 翻译字典字段\n8. **Controller 的 remove 方法**：TenantEntity / BaseEntity 用 `deleteLogic()`，Raw 模式用 `removeBatchByIds()`\n9. **权限控制**：使用 `@PreAuth(RoleConstant.HAS_ROLE_ADMIN)` 做角色级控制（SpringBlade 开源版仅支持角色权限）\n10. **前端权限标识**格式：`{modelCode}_{action}`，如 `notice_add`、`notice_edit`、`notice_delete`、`notice_view`\n\n## 参考文件索引\n\n| 生成任务 | 参考文件 | 说明 |\n|----------|----------|------|\n| 后端代码 | `references/backend.md` | 完整的后端各层代码模板，含 Boot/Cloud 变体 |\n| 前端代码 | `references/frontend.md` | API、Option 配置、Vue 页面模板（Options/Composition API） |\n| 数据库 SQL | `references/database.md` | MySQL 建表模板、字段类型映射与菜单 SQL |\n\n<!-- File: .claude/skills/blade-plan/README.md (Tokens: ~1280 | Category: anthropic-skill) -->\n# Blade Plan\n\nSpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为结构化任务，逐个执行，全程无辅助文件。\n\n## 核心理念\n\n**先想清楚，再动手写。** 面对涉及多个文件的改动，不急着写代码，而是先理清思路、拆好任务、确认方案，再逐个执行。用最小的流程开销，获得结构化开发的核心收益。\n\n```\n需求 → 分析规划 → 确认 → 逐任务执行 → 完成总结\n```\n\n## 快速开始\n\n```bash\n# 启动规划执行 — 展示方案后等待确认\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n\n# 自动模式 — 展示方案后直接执行，不等待确认\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n```\n\n## 工作流程\n\n**交互模式（默认）：** 展示规划方案后暂停等待确认，审批通过后开始逐任务执行。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析与规划                                              │\n  │  理解需求 → 扫描代码 → 制定方案 → 拆解任务                 │\n  │       ↓ 用户确认                                            │\n  │                                                             │\n  │  🔨 逐任务执行（TaskCreate/TaskUpdate 追踪进度）            │\n  │  Task 1 ✅ → Task 2 ✅ → Task 3 🔄 → Task 4 ⏳ → ...      │\n  │       ↓                                                     │\n  │  ✅ 完成总结                                                │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n**自动模式（--auto）：** 展示规划后直接开始执行，不等待确认。\n\n```\n  ┌─────────────────────────────────────────────────────────────┐\n  │                                                             │\n  │  📋 分析规划  →  🔨 逐任务执行  →  ✅ 完成总结             │\n  │  （展示方案后直接执行，不暂停）                             │\n  │                                                             │\n  └─────────────────────────────────────────────────────────────┘\n```\n\n## 适用场景\n\n| 场景 | 适合 | 不适合 |\n|------|------|--------|\n| 新增中等功能模块（如限流注解、审计日志） | ✅ | |\n| 跨多个文件的 Bug 修复 | ✅ | |\n| 模块级重构（如拆分策略类） | ✅ | |\n| 新增 Starter 模块 | ✅ | |\n| 现有功能增强（添加新维度、新策略） | ✅ | |\n| 单文件简单修改 | | ❌ 直接对话即可 |\n| 快速问答或查询 | | ❌ 直接对话即可 |\n| 从零开发完整业务系统 | | ❌ 建议拆成多次会话分阶段推进 |\n| 需要先独立设计数据模型或跨服务契约 | | ❌ 先单独设计再进入 Plan |\n\n## 复杂度分层\n\n| 复杂度 | 文件变更量 | 建议方式 |\n|--------|-----------|---------|\n| 低 | 1~5 个文件 | 直接对话 |\n| **中** | **5~15 个文件** | **Blade Plan** |\n| 高 | 15+ 个文件 | 拆成多次会话，每次走一次 `/blade-plan` |\n\n## 特性\n\n- **零文件开销**：不生成任何辅助文件，规划在对话中展示，进度由内置任务系统追踪\n- **上下文锚定**：每个任务执行前重读相关代码，防止偏离\n- **构建验证**：每组任务完成后自动执行编译检查\n- **技能协同**：自动识别可借助 `/blade-design`、`/avue-design` 加速的任务\n- **智能降级**：过于简单时直接执行，复杂度过高时建议拆成多次会话\n- **SpringBlade 体系适配**：内置 Boot / Cloud 分层最佳实践，同时可适配其他技术栈\n\n## 文件结构\n\n```\nblade-plan/             # Skill 定义\n├── README.md           # 本文件\n└── SKILL.md            # Skill 主指令（全部内容，无 references 目录）\n```\n\n运行时不产生任何文件，所有状态由 Claude Code 内置任务系统管理。\n\n<!-- File: .claude/skills/blade-plan/SKILL.md (Tokens: ~3727 | Category: anthropic-skill) -->\n---\nname: blade-plan\ndescription: >\n  SpringBlade 轻量规划执行工作流。将中等复杂度需求快速拆解为「分析规划 → 逐任务执行 → 完成总结」\n  三步流程，用 Claude Code 内置任务系统追踪进度，全程无辅助文件。适用于新增中等功能模块、修复跨模块 Bug、\n  中等规模重构、添加新 Starter 等涉及 5~15 个文件变更的开发任务。\n  当用户说\"帮我做一个XX功能\"、\"这个改动涉及几个文件\"、\"帮我重构一下XX\"、\"帮我加个XX\"、\n  \"plan\"、\"规划一下再做\"、\"先想清楚再写\"、\"这个稍微有点复杂\"等时触发。也可直接使用 /blade-plan 调用。\n  不适用于简单单文件修改（直接对话即可），也不适用于需要完整架构设计的复杂系统（建议分阶段拆解为多次会话）。\n  即使用户没有说\"plan\"，只要需求涉及多文件改动或需要一定设计思考，也应主动建议使用本工作流。\n---\n\n# Blade Plan — 轻量规划执行工作流\n\n先想清楚，再动手写。用最小的流程开销，获得结构化开发的核心收益。\n\n## 定位\n\nBlade Plan 面向**中等复杂度**的开发任务，在\"直接对话\"和\"分阶段设计\"之间提供一条中间路径：\n\n| 复杂度 | 方式 | 文件变更量 | 典型场景 |\n|--------|------|-----------|---------|\n| 低 | 直接对话 | 1~5 个文件 | 单个 bug 修复、配置调整、工具方法添加 |\n| **中** | **Blade Plan** | **5~15 个文件** | **新增中等功能、跨模块重构、复杂 bug、新 Starter** |\n| 高 | 分阶段执行 | 15+ 个文件 | 从零开发完整模块、复杂系统设计、大型架构重构 |\n\n判断标准不是绝对的文件数，而是：需求是否清晰到可以直接拆任务？改动是否能在一次会话内完成？如果两个答案都是\"是\"，Blade Plan 就是合适的选择。\n\n## 核心理念\n\n- **先规划后执行**：先把思路理清、任务拆好，再逐个动手——这能避免写到一半发现方向错了\n- **上下文锚定**：每个任务执行前重读相关代码，防止在对话累积中逐渐偏离\n- **最小变更**：只做计划中的事，不夹带\"顺手优化\"\n- **构建验证**：编译不过的代码等于没写\n- **零文件开销**：不生成任何辅助文件（无 spec.json、tasks.json、design.md 等），所有规划直接在对话中呈现，进度由 Claude Code 内置 TaskCreate / TaskUpdate 追踪\n\n## 使用方式\n\n```\n/blade-plan <需求描述>           # 启动规划执行（展示方案后等待确认）\n/blade-plan --auto <需求描述>    # 启动规划执行（自动模式，展示方案后直接执行）\n```\n\n**示例：**\n\n```\n/blade-plan 给 blade-starter-redis 新增分布式限流注解，支持按IP和用户维度限流\n/blade-plan --auto 重构 OssTemplate，将各厂商实现拆分为独立策略类\n/blade-plan 修复多租户场景下动态数据源切换时连接池泄漏问题\n```\n\n## 路由逻辑\n\n收到用户输入后：\n\n1. **带 `--auto` 参数**：提取需求描述 → 自动模式（规划后直接执行，不等确认）\n2. **带需求描述**：→ 交互模式（规划后等待用户确认）\n3. **无参数**：提示输入需求描述\n\n---\n\n## 工作流\n\n```\n用户需求 → [分析与规划] → 确认 → [逐任务执行] → 完成总结\n                              ↑\n                         自动模式跳过\n```\n\n三个步骤，一次确认，全程用 Claude Code 内置任务系统追踪进度。\n\n---\n\n### 步骤一：分析与规划\n\n**目标**：理解需求，摸清现状，制定可执行的任务清单。\n\n**执行动作：**\n\n1. **理解需求**：\n   - 解析用户的需求描述，提炼核心功能点\n   - 识别隐含需求和边界条件\n   - 如有歧义，先向用户澄清关键问题再继续\n\n2. **扫描现状**：\n   - 阅读 CLAUDE.md 了解项目规范（如有）\n   - 扫描相关模块现有代码，理解代码结构和风格\n   - 搜索是否有可复用的类、工具方法或已有实现\n   - 评估改动对现有功能的影响范围\n\n3. **制定方案**：\n   - 确定技术实现策略（用什么方式解决问题）\n   - 识别需要新增和修改的文件\n   - 将改动拆解为有序的任务列表（按依赖关系排序）\n   - 任务粒度：一个任务对应一个逻辑完整的改动单元（通常 1~3 个文件）\n\n4. **展示规划**，格式如下：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  📋 Blade Plan\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n需求：{一句话概述}\n\n方案：\n  {2~4 句话描述技术方案的核心思路，\n   包括关键的技术选型和架构决策}\n\n任务清单（共 X 个）：\n  1. {任务标题}  — {涉及的文件或模块}\n  2. {任务标题}  — {涉及的文件或模块}\n  3. {任务标题}  — {涉及的文件或模块}\n  ...\n\n影响范围：\n  新增 X 个文件 | 修改 Y 个文件\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n5. **等待确认或自动推进**：\n   - **交互模式**：追加 `请确认方案后开始执行，或提出修改意见。` 并等待用户回复\n   - **自动模式**：展示规划后直接进入步骤二\n   - 用户说\"可以\"/\"没问题\"/\"开始\"等视为确认通过\n   - 用户提出修改意见则调整方案后重新展示\n\n6. **创建任务**：确认后，使用 **TaskCreate** 为每个任务创建跟踪条目，便于用户实时看到进度\n\n---\n\n### 步骤二：逐任务执行\n\n**目标**：按计划逐个完成任务，保持纪律性。\n\n**对每个任务执行以下循环：**\n\n1. **TaskUpdate** → 标记当前任务为 `in_progress`\n\n2. **加载上下文**（上下文锚定）：\n   - 重新阅读目标文件及其关联代码——即使刚才读过，也要再看一眼\n   - 查看前序任务的产出文件，确保接续一致\n   - 模仿同模块现有代码的风格和模式\n\n3. **执行编码**：\n   - 严格遵循项目 CLAUDE.md 编码规范（如有）\n   - 只做当前任务描述的事，不附带额外改动\n   - 执行前判断是否可借助其他 Blade 技能加速（见「技能协同」）\n\n4. **TaskUpdate** → 标记当前任务为 `completed`\n\n5. **继续下一个任务**\n\n> 为什么要逐任务而不是一口气全写完？因为每个任务完成后的代码状态是下一个任务的输入。逐步推进让每一步都建立在稳固的基础上，也让用户能实时看到进度。\n\n#### 构建检查点\n\n每完成一组逻辑相关的任务（或每 3~4 个任务），执行构建验证：\n\n- **Maven 项目**：`mvn clean compile -DskipTests`\n- **Gradle 项目**：`gradle compileJava`\n- **Node.js 项目**：`npm run build` 或 `tsc --noEmit`\n- **其他项目**：根据项目构建配置自动识别\n\n构建失败则立即修复后再继续，不跳过，不拖延。\n\n#### 技能协同\n\n执行任务时，识别任务类型并借助对应技能提升效率：\n\n| 任务特征 | 推荐技能 | 说明 |\n|---------|---------|------|\n| 后端 CRUD 全套（Entity → Mapper → Service → Controller） | `/blade-design` | 生成骨架代码，再根据实际需求补充业务逻辑 |\n| Avue 组件前端页面 | `/avue-design` | 生成配置化的 Avue 前端代码 |\n\n**协同原则**：技能生成的代码是起点而非终点——生成后必须根据实际需求调整，并通过构建验证。\n\n> **关于代码提交**：`/blade-commit` 绝不自动执行。完成总结后将提交作为后续建议告知用户，由用户自行决定何时提交。仅当用户明确要求\"帮我提交\"时才执行。\n\n---\n\n### 步骤三：完成总结\n\n所有任务完成后，输出总结报告：\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  ✅ Blade Plan 执行完毕\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n任务: X/X 完成\n\n新增文件:\n  + path/to/NewFile.java\n  + path/to/AnotherFile.java\n\n修改文件:\n  ~ path/to/ExistingFile.java\n\n后续建议:\n  1. 功能测试（交由用户执行）\n  2. 确认无误后可使用 /blade-commit 提交代码\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n---\n\n## 关键原则\n\n### 上下文锚定\n\n每个任务执行前，重新阅读相关代码。哪怕觉得\"刚才看过了\"，也要再扫一眼。中等任务的对话虽不如大型任务长，但 5~6 个任务执行下来，上下文已经积累了不少内容，记忆会产生微妙偏差。花几秒重读比花几分钟修偏划算得多。\n\n### 遵守项目规范\n\n先看同模块现有代码怎么写的，再动手。代码风格、命名习惯、继承关系、分层结构——模仿现有代码是最可靠的规范来源。如有 CLAUDE.md，严格遵循其中的编码规范。在 SpringBlade 体系中，Boot 和 Cloud 共享统一分层：Controller → Service → Mapper → Wrapper → Entity，差异仅在包路径和路由前缀（Boot 通过 `AppConstant.APPLICATION_XXX_NAME` 手动拼前缀，Cloud 由网关按服务名自动路由）。\n\n### 最小变更原则\n\n每个任务只做计划中描述的事。发现\"顺手可以优化\"的地方，记下来在总结中告诉用户，但不自行改动。未计划的改动是 bug 的温床。\n\n### 复用优先\n\n编写新代码前，先在项目中搜索是否已有可复用的工具类、基类或配置。尤其在 SpringBlade 体系中，`blade-core-tool` 和各 Starter 模块提供了大量基础设施，重复造轮子不仅浪费时间，还会引入风格不一致。\n\n### 构建验证\n\n代码变更后主动执行构建验证。编译不过就立即修，不要想着\"后面一起修\"——错误会像雪球一样越滚越大。\n\n---\n\n## 何时降级为直接对话\n\n如果分析后发现：\n\n- 只涉及 1~5 个文件的简单改动\n- 不需要拆解任务，直接改就行\n\n主动提示：`这个改动比较简单，不需要走规划流程，直接帮你改。` 然后直接执行。\n\n## 何时建议分阶段拆解\n\n如果在规划阶段发现以下信号，建议用户把需求拆成多次会话分阶段推进：\n\n- 任务数量超过 15 个，工作量超出一次会话的合理范围\n- 涉及复杂的数据库表结构设计（多表关联、索引策略），需要先单独设计数据模型\n- 需要跨多个服务的 API 协调，应当先固定契约再逐服务落地\n- 需求本身还不清晰，应先通过对话澄清、对齐理解后再进入 Plan 流程\n\n主动提示：`这个需求的复杂度较高，建议先拆成几个独立阶段（如：设计数据模型 → 落地后端 → 补全前端），每个阶段单独走一次 /blade-plan 会更稳。`\n</yakaai_skills>"}