# Admin Framework（admin-framework2）

基于 **Vue 2**、**Vue Router 3**、**Vuex 3**、**View Design（iView 4）** 的后台管理框架：登录、布局、动态菜单与路由、权限、HTTP 封装、系统管理页面等。

---

## 快速导航（按角色）

### 框架使用者（接入业务项目）

- 目标：最快把框架跑起来并完成菜单页面映射。
- 建议阅读顺序：`§1 依赖与环境` → `§2 引入方式` → `§3 核心 API` → `§7 组件映射与动态路由` → `§12 组件使用说明`。
- 最小落地步骤：
  1. 安装 peer 依赖并引入 `admin-framework.js`。
  2. 调用 `AdminFramework.createApp({ title, apiUrl, componentMap })`。
  3. 在 `componentMap` 中注册业务页面路径并与后端菜单 `component` 字段对齐。

### 业务开发者（写页面和接口联调）

- 目标：在现有框架内开发业务页、调用接口、使用内置组件。
- 建议阅读顺序：`§5 HTTP 模块` → `§6 uiTool/tools` → `§8 systemApi` → `§9 Vuex` → `§12 组件使用说明`。
- 高频关注：
  - 请求约定（`code === 0`、`401` 自动处理、参数预处理）。
  - `this.$http` / `this.$uiTool` / `this.$tools` 的用法。
  - 全局组件与按需组件边界（见 `§12.14` 索引）。

### 二次封装维护者（改框架内核）

- 目标：扩展框架能力（路由、Store、组件注册、系统 API 聚合等）。
- 建议阅读顺序：`§3.2 createApp 执行流程` → `§4 框架实例` → `§7 动态路由` → `§8 systemApi` → `§9 createStore`。
- 变更建议：
  1. 先改 `src/index.js` 的初始化路径（插件、原型、Store、Router）。
  2. 同步维护 `src/views/index.js` 的默认 `componentMap`。
  3. 变更 `src/api/system` 时同步更新 `src/api/system/index.js` 聚合导出与 README `§8`。

### 常见任务直达

| 任务 | 直达章节 |
|------|----------|
| 第一次接入并启动 | `§1` + `§2` + `§3` |
| 菜单点击后页面不显示 | `§7`（重点看 `componentMap`） |
| 请求报错或 token 失效 | `§5` + `§9.1 user` |
| 顶部标题/Logo 不正确 | `§9.2 app`（`getSysTitle`） |
| 想知道组件怎么用 | `§12`（先看 `§12.14` 索引） |

### 5分钟上手清单

> 目标：5 分钟内把框架跑起来并看到一个业务页面。

#### 第 1 步：安装依赖（1 分钟）

在业务项目安装 peer 依赖：

```bash
npm i vue vue-router vuex view-design axios
```

#### 第 2 步：准备框架包（1 分钟）

- 已有 `dist/admin-framework.js`：直接拷贝到业务项目（例如 `src/vendor/`）。
- 没有构建产物：在框架项目根目录执行：

```bash
npm run build
```

#### 第 3 步：准备页面映射（1 分钟）

创建一个业务页面并准备 `componentMap`：

```javascript
// 例如 src/router/component-map.js
import DemoPage from '@/views/demo/index.vue'

export default {
  'demo/index': DemoPage,
  'demo/index.vue': DemoPage
}
```

#### 第 4 步：入口创建应用（1 分钟）

```javascript
import Vue from 'vue'
import AdminFramework from '@/vendor/admin-framework.js'
import componentMap from '@/router/component-map'

const app = AdminFramework.createApp({
  title: '后台管理系统',
  apiUrl: 'http://127.0.0.1:3000/admin_api/',
  componentMap
})

app.$mount('#app')
```

#### 第 5 步：验证（1 分钟）

- 能进入登录页（或已有 token 时进入主框架页）。
- 左侧菜单点击后，页面能正常渲染（无“页面组件未加载”提示）。
- 打开浏览器控制台，确认可访问：
  - `window.framework`
  - `window.rootVue`

#### 常见 3 个坑（上手必看）

1. 后端菜单里的 `component` 路径必须和 `componentMap` 键一致（推荐同时写带 `.vue` 和不带 `.vue` 两种）。
2. `apiUrl` 建议以 `/admin_api/` 结尾，避免接口前缀不一致。
3. 若登录后菜单空白，优先检查 `authorityMenus` 返回格式与 `user/setAuthorityMenus` 动态路由是否执行。

---

## 1. 依赖与环境

### 1.1 Peer 依赖（由业务项目安装）

框架不打包以下依赖，需在宿主项目中安装并与框架版本兼容：

| 包 | 说明 |
|----|------|
| `vue` ^2.6 | 运行时 |
| `vue-router` ^3 | 路由 |
| `vuex` ^3 | 状态 |
| `view-design` ^4 | UI 组件库 |
| `axios` | HTTP（与框架内封装配合） |

### 1.2 构建产物（`dist/`）

| 文件 | 说明 |
|------|------|
| `admin-framework.js` | **UMD**，Terser 压缩，对外默认导出为框架单例；浏览器中为 `window.AdminFramework`，也可由 Webpack 等从该文件解析 `import` |
| `admin-framework.md` | 与根目录 `README.md` 内容相同，随 `npm run build` 一并复制到 `dist/`，便于随包分发使用说明 |

在项目根目录执行：

```bash
npm run build
```

---

## 2. 引入方式

### 2.1 与 Webpack / Vue CLI 等业务工程配合

先安装 peer 依赖，将构建好的 `dist/admin-framework.js` 拷贝到业务项目或通过路径引用；在入口中：

```javascript
import AdminFramework from './vendor/admin-framework.js'
// 若已将包发布到 npm 且 package.json 的 main 指向 dist，则：
// import AdminFramework from 'admin-framework'
```

打包器需将 `vue`、`vue-router`、`vuex`、`view-design`、`axios` 作为外部依赖或正常解析（与 UMD 的 externals 一致）。

### 2.2 页面中通过 `<script>` 引入

按顺序引入 Vue、VueRouter、Vuex、View Design、axios 的 UMD 脚本，再引入 `admin-framework.js`，使用全局 **`AdminFramework`** 调用 `createApp` 等 API。

---

## 3. 核心 API：一键创建应用

### 3.1 `AdminFramework.createApp(config)`

创建并配置 Vue 根实例：注册插件、全局属性、`componentMap`、Store、Router、布局等。**返回 Vue 实例**，需自行 `$mount`。

**`config` 常用字段：**

| 字段 | 类型 | 说明 |
|------|------|------|
| `title` | `string` | 站点标题；未登录或接口失败时用作默认 `document.title` |
| `apiUrl` | `string` | 后端 API 根地址（必填，用于 `http` baseURL） |
| `uploadUrl` | `string` | 可选；不传时由 `apiUrl` 自动推导为 `apiUrl + 'upload'` 或 `apiUrl + '/upload'` |
| `componentMap` | `object` | 动态路由组件映射，见 [§7](#7-组件映射与动态路由) |
| `HomePage` | `Vue` 组件 | 可选，自定义首页；默认框架内置欢迎页 |
| `onReady` | `function` | 可选，根实例 `created` 末尾调用，`this` 为 Vue 根实例 |

### 3.2 `createApp` 执行流程（源码行为）

1. 自动补齐 `uploadUrl`（未传时按 `apiUrl` 拼接 `/upload`）。
2. 注册 `ViewUI`、`VueRouter`、`Vuex`。
3. 注入全局原型：`$config`、`$http`、`$tools`、`$uiTool`、`$framework`。
4. 全局注册组件（`registerGlobalComponents`）。
5. 建立 `componentMap`（内置系统页 + 业务传入映射）。
6. 创建 Store 并初始化 HTTP。
7. 创建 Router（基础路由 + 动态主路由），并挂守卫。
8. 创建根 Vue；恢复菜单与系统标题；最后执行 `onReady`。

> 注意：框架实例是单例，`store/router` 初始化后会复用；通常整个应用只调用一次 `createApp`。

**示例：**

```javascript
import AdminFramework from 'admin-framework'
import componentMap from './router/component-map.js'

const app = AdminFramework.createApp({
  title: '某某管理系统',
  apiUrl: 'https://api.example.com/admin_api/',
  componentMap,
  HomePage: null,
  onReady() {
    console.log('框架与路由就绪', this.$route.path)
  }
})

app.$mount('#app')
```

---

## 4. 框架实例（`AdminFramework` / `window.framework`）

默认导出为**单例**。构建后也会挂到 `window.framework`（浏览器环境）。

### 4.1 常用属性

| 属性 | 说明 |
|------|------|
| `version` | 框架版本号字符串 |
| `config` | 最近一次 `createApp` 传入的配置 |
| `store` | Vuex Store 实例（`createApp` 之后可用） |
| `router` | Vue Router 实例 |
| `HomePage` | 当前使用的首页组件引用 |
| `http` | HTTP 封装实例，同 [§5](#5-http-模块) |
| `uiTool` | UI 与菜单工具类，同 [§6](#6-uitool-模块) |
| `tools` | 工具方法集合，同 [§6.2](#62-通用工具-tools) |
| `systemApi` | `src/api/system` 聚合导出，同 [§8](#8-系统-api-systemapi) |
| `pages` | 框架内置页面组件集合（登录、错误页、部分系统页等） |
| `components` | 布局与子组件导出（Main、TreeGrid 等） |
| `storeModules` | 默认 `user`、`app` 模块描述，扩展 Store 时可参考 |
| `createBaseRoutes` / `setupRouterGuards` / `registerComponents` | 高级自定义路由/组件时使用 |

### 4.4 运行时全局对象

浏览器环境下，框架会暴露：

- `window.framework`：框架单例。
- `window.rootVue`：`createApp` 创建的根实例（便于调试）。

### 4.2 `addComponentMap(customMap)`

在运行时追加组件映射（与 `createApp` 里的 `componentMap` 语义相同）。

```javascript
import Games from './views/ball/games.vue'

AdminFramework.addComponentMap({
  'ball/games': Games,
  'ball/games.vue': Games
})
```

### 4.3 `initHttp(config, store)`

若未走 `createApp`、自行组装 Store 时，可用此方法初始化 `http`（一般使用 `createApp` 即可，内部已调用）。

```javascript
AdminFramework.initHttp(
  { apiUrl: 'https://api.example.com/admin_api/', timeout: 60000 },
  storeInstance
)
```

---

## 5. HTTP 模块

通过 **`this.$http`**（组件内）或 **`AdminFramework.http`** / **`window.framework.http`** 访问。

### 5.1 初始化

由 `createApp({ apiUrl, ... })` 自动完成；请求头会带 `admin-token`（来自 `store.state.user.token`）。

### 5.2 约定

- 成功：后端 JSON 需 `code === 0`，否则走全局错误提示并 `reject`。
- `401`：清空 token，并跳转登录路由（依赖 `window.framework.router`）。
- 请求参数会做预处理：递归移除对象中值为 `''` 的键，并自动格式化 `Date`。

### 5.3 方法说明

| 方法 | 说明 |
|------|------|
| `init(config, store)` | 合并 `timeout` 等，绑定 Store |
| `baseUrl()` | 当前 `apiUrl` |
| `ImgSrc(src)` | 拼接图片完整 URL：`baseUrl() + src` |
| `getHttpInstance(extra)` | 返回带拦截器的 `axios` 实例 |
| `get(url, params, config)` | GET；`params` 会序列化，Date 会格式化为字符串 |
| `post(url, body, config)` | POST JSON |
| `postFormData(url, data)` | `application/x-www-form-urlencoded` |
| `fileExport(url, param, filename, is_down)` | 下载导出；内部可用 `uiTool.downloadFile` |
| `formatParamete` / `formatFormDataParam` | 参数预处理 |
| `showLoad` / `hideLoad` / `showError` | 与全局 loading、Message 联动 |

**示例（在 Vue 组件中）：**

```javascript
export default {
  async mounted() {
    try {
      const res = await this.$http.get('/sys_user/index', { page: 1, pageSize: 20 })
      this.list = res.data
    } catch (e) {
      // 已全局提示
    }
  }
}
```

隐藏 loading：

```javascript
await this.$http.get('/xxx', { id: 1 }, { hideLoad: true })
```

### 5.4 常见注意点

- `post(url, body, config)` 当前实现固定会显示 loading（`showLoad`），`hideLoad` 只在 `get` 内部判断。
- `postFormData` 使用 `application/x-www-form-urlencoded`。
- `fileExport` 返回 `blob` 响应；`is_down=false` 时可自行处理文件流。

---

## 6. uiTool 模块

通过 **`this.$uiTool`** 或 **`AdminFramework.uiTool`** 使用（类静态方法）。

| 方法 | 说明 |
|------|------|
| `setComponentMap(map)` | 合并组件映射表 |
| `getComponent(path)` | 按路径取组件，`path` 可带或不带 `.vue` |
| `downloadFile(res, fileName)` | Blob 下载 |
| `setRem()` | 根据屏宽设置根字体（自适应） |
| `getImgSrc(src)` | 图片地址（同 http 规则） |
| `getBtn(h, options)` | Render 函数里生成操作按钮组 |
| `getDropdown(h, items)` | 下拉更多菜单 |
| `delConfirm(callback)` | 删除确认框 |
| `showConfirm({ title, content }, callback)` | 通用确认框 |
| `transformTree(list, cb)` | 扁平列表转树 |
| `subTree` / `menuToRoute` / `getRoutes` | 菜单转路由（框架内部与权限菜单配合） |

**树表示例：**

```javascript
const tree = this.$uiTool.transformTree(flatListFromApi)
```

---

## 6.2 通用工具 `tools`

通过 **`this.$tools`** 使用（具体以源码 `src/utils/tools.js` 为准）。

常用包括：

- **Cookie / Token**：`getToken`、`setToken`、`TOKEN_KEY`
- **日期**：`formatDate(val, fmt)`（dayjs）
- **数组**：`forEach`、`hasOneOf`、`getUnion`、`getIntersection`
- **对象**：`objEqual`、`removeEmptyObject`、`isNullorEmpty`
- **路由/菜单**：`getBreadCrumbList`、`getHomeRoute`、`getMenuByRouter`、`filterMenu`
- **本地存储**：`localSave`、`localRead`
- **其它**：`generateUUID`、`getUrlParam`、`downStream`、`scrollTop` 等

**示例：**

```javascript
if (this.$tools.getToken()) { /* 已登录 */ }
```

---

## 7. 组件映射与动态路由

后端菜单里配置的「组件地址」会对应到**字符串路径**（如 `system/sys_user`）。框架通过 **`componentMap`** 把路径映射到真实 Vue 组件。

**在 `createApp` 中传入：**

```javascript
import SysUser from './views/system/sys_user.vue'

AdminFramework.createApp({
  apiUrl: '...',
  componentMap: {
    'system/sys_user': SysUser,
    'system/sys_user.vue': SysUser
  }
})
```

未映射的路径会显示占位提示（控制台 `warn`），便于排查。

框架内置已注册一批系统页（如 `system/sys_menu` 等），见 `src/views/index.js` 中 `setupComponentMap`；业务模块在 `componentMap` 中追加即可。

### 7.1 内置映射（默认可用）

默认已注入以下路径（可直接在后端菜单配置里使用）：

- `home/index`
- `system/sys_log`
- `system/sys_log_operate`
- `system/sys_param_setup`
- `system/sys_role`
- `system/sys_user`
- `system/sys_tenant`
- `system/sys_menu`
- `system/sys_title`

---

## 8. 系统 API（`systemApi`）

`AdminFramework.systemApi` 聚合导出 `src/api/system/index.js` 中的服务（以下为当前版本完整导出）：

| 导出名 | 模块文件 | 典型用途 |
|--------|----------|----------|
| `fileServe` | `fileServe.js` | 文件上传下载 |
| `plaAccountServer` | `pla_account_server.js` | 平台账号管理 |
| `rolePermissionServer` | `rolePermissionServer.js` | 角色权限 |
| `roleServer` | `roleServer.js` | 角色管理 |
| `sysAddressServer` | `sysAddressServer.js` | 地址字典 |
| `sysModuleServer` | `sysModuleServer.js` | 模块管理 |
| `sysLogServe` | `sys_log_serve.js` | 系统日志 |
| `systemTypeServer` | `systemType_server.js` | 系统类型配置 |
| `tableServer` | `tableServer.js` | 表结构相关 |
| `userServer` | `userServer.js` | 登录、用户 CRUD、权限菜单 |
| `formFieldServer` | `formFieldServer.js` | 表单字段 |
| `formServer` | `formServer.js` | 表单模型 |
| `menuServer` | `menuServer.js` | 菜单管理 |
| `modelFieldServer` | `modelFieldServer.js` | 数据模型字段 |
| `modelServer` | `modelServer.js` | 数据模型 |
| `paramSetupServer` | `paramSetupServer.js` | 系统参数（标题、Logo） |
| `sysTenantServer` | `sysTenantServer.js` | 租户管理 |

**调用示例：**

```javascript
const { userServer } = AdminFramework.systemApi

async function login() {
  const res = await userServer.login({ username: 'admin', password: '***' })
  if (res.code === 0) {
    // 写入 token、拉菜单等由登录页与 store 配合完成
  }
}
```

实际请求仍走全局配置的 `http`（`apiUrl`、token 头）。

> 说明：`src/api/system` 中若存在未在 `index.js` 聚合导出的文件（例如实验性模块），不会出现在 `AdminFramework.systemApi`。

---

## 9. Vuex（`this.$store`）

默认模块：

### 9.0 `createStore`（扩展自定义模块）

框架导出 `createStore(Vuex, customModules, createPersistedState)`，可在默认 `user/app` 基础上合并业务模块：

```javascript
import Vuex from 'vuex'
// 源码工程示例：按项目结构从 store 导入 createStore
import { createStore } from '@/store'
import foo from './store/foo'

const store = createStore(Vuex, { foo }, null)
```

`createPersistedState` 传入后会启用本地持久化插件（`window.localStorage`）。

### 9.1 `user`（`namespaced: true`）

| 项 | 说明 |
|----|------|
| `state` | `token`、`userName`、`avatorImgPath`、`authorityMenus`、`menuList`、`currentTenant` |
| `mutations` | `setToken`、`setUserName`、`setAuthorityMenus`、`setMenuList`、`setCurrentTenant` |
| `actions` | `setAuthorityMenus`（生成动态路由）、`handleLogin`、`handleLogOut` |

**登录后刷新菜单示例：**

```javascript
await this.$store.dispatch('user/setAuthorityMenus', {
  Main: this.$framework.components.Main,
  ParentView: this.$framework.components.ParentView,
  Page404: this.$framework.pages.Page404,
  HomePage: this.$framework.HomePage
})
```

（具体参数以当前框架版本 `user` 模块为准。）

### 9.2 `app`（`namespaced: true`）

| 项 | 说明 |
|----|------|
| `state` | `sysFormModel`（标题、Logo）、面包屑、首页路由 |
| `mutations` | `setBreadCrumb`、`setHomeRoute`、`setSysTitle` |
| `actions` | `getSysTitle`：根据登录状态和参数接口更新站点标题与 Logo |

```javascript
await this.$store.dispatch('app/getSysTitle', {
  defaultTitle: this.$config.title,
  defaultLogo: ''
})
```

---

## 10. 全局 Vue 原型（组件内）

| 属性 | 说明 |
|------|------|
| `this.$config` | `createApp` 的 `config` |
| `this.$http` | HTTP 封装 |
| `this.$tools` | 工具集 |
| `this.$uiTool` | UI 工具类 |
| `this.$framework` | 框架单例（同 `AdminFramework`） |

---

## 11. 内置页面（`pages`）

- **页面**：`LoginPage`、`Page401`、`Page404`、`Page500`、`HomePage` 及多套 `system/*` 管理页（见 `src/views/index.js`）。
- 业务中也可通过 `AdminFramework.pages` 取到上述导出。

---

## 12. 组件使用说明（`src/components`）

`createApp` 执行时会对下列组件**全局注册**（见 `src/components/index.js` 中 `registerGlobalComponents`），在任意 `.vue` 模板中可直接写 **PascalCase 或 kebab-case** 标签名（如 `<Tables>` / `<tree-grid>`）。也可按需：

```javascript
import { Tables, TreeGrid, editModal } from 'admin-framework'
// 或在源码工程中：import Tables from '@/components/tables'
const { TreeGrid, Main } = AdminFramework.components
```

以下按功能分类；**Props / 事件以源码为准**，此处列出常用项。

### 12.1 布局与路由壳

| 组件 | 说明 |
|------|------|
| **Main** | 后台主布局：左侧 `SideMenu`、顶栏 `HeaderBar`、用户信息、`Terminal`、主内容区 `<router-view />`、内嵌 `LoadFlower`、返回顶部。无业务 Props，标题/Logo 来自 Vuex `app/sysFormModel`，菜单来自 `user/menuList`。 |
| **ParentView** | 仅渲染一层 `<router-view />`，用于菜单多级嵌套时的中间层。 |

```vue
<!-- 动态路由中由框架注册，一般不在页面手写 Main -->
```

#### Main 子模块说明（`src/components/main/components`）

以下组件**默认未在 `registerGlobalComponents` 中注册**，由 **Main** 内部引用；若要在自定义布局中复用，需从路径手动 `import`（示例路径以源码工程 `@/components/...` 为准）。

**组合关系（`main.vue`）**：`Layout` 左侧为 `SideMenu` + 品牌区；右侧顶部 `HeaderBar`，其默认插槽内为 `Terminal` 与 `User`；内容区为 `<router-view />`、`LoadFlower`、`ABackTop`。

---

**1. `SideMenu`（`side-menu/side-menu.vue`）**

侧栏菜单，基于 iView `Menu`，支持折叠态下的 `CollapsedMenu` 下拉。

| Props | 类型 | 说明 |
|-------|------|------|
| `menuList` | `Array` | 菜单树，项需含 `name`、`meta`（如 `icon`、`title`）、子节点 `children` 等，与动态路由菜单结构一致 |
| `collapsed` | `Boolean` | 是否折叠（仅图标 / 下拉） |
| `theme` | `String` | 菜单主题，默认 `light`；Main 中传入 `dark` |
| `activeName` | `String` | 当前激活菜单 `name`，一般传 `$route.name` |
| `openNames` | `Array` | 初始展开的 Submenu 名称列表 |
| `rootIconSize` / `iconSize` | `Number` | 折叠/展开时图标大小，默认 `20` / `16` |

| 插槽 | 说明 |
|------|------|
| 默认 | 菜单上方的额外区域（可选） |

| 事件 | 说明 |
|------|------|
| `on-select` | 选中菜单项时，参数为路由 `name` 字符串（或外链特殊格式 `isTurnByHref_xxx`，由 Main 中 `turnToPage` 处理） |

```vue
<SideMenu
  theme="dark"
  :menu-list="menuList"
  :collapsed="collapsed"
  :active-name="$route.name"
  @on-select="handleMenuSelect"
/>
```

**2. `SideMenuItem`（`side-menu/side-menu-item.vue`）**

`SideMenu` 内部递归子组件：渲染 `Submenu` 标题与一层子 `menu-item`。依赖 mixin 提供的 `parentItem`、`children` 等逻辑，**一般不单独使用**。

**3. `CollapsedMenu`（`side-menu/collapsed-menu.vue`）**

侧栏折叠时的下拉菜单（`Dropdown` 嵌套）。由 `SideMenu` 使用。

| Props | 说明 |
|-------|------|
| `parentItem` | 当前菜单节点 |
| `hideTitle` | 为 true 时仅图标（侧栏窄条场景） |
| `rootIconSize` / `iconSize` | 图标尺寸 |
| `theme` | 与侧栏主题一致，用于图标颜色 |

| 事件 | `on-click` — 点击子项时传出菜单 `name` |

---

**4. `HeaderBar`（`header-bar/header-bar.vue`）**

顶栏：左侧 **折叠开关** + **面包屑**，右侧 **默认插槽**（Main 里放终端图标、用户区等）。

| Props | 说明 |
|-------|------|
| `collapsed` | 当前侧栏是否折叠，传给 `SiderTrigger` |

| 插槽 | 说明 |
|------|------|
| 默认 | 顶栏右侧自定义内容（如 `Terminal`、`User`） |

| 事件 | 说明 |
|------|------|
| `on-coll-change` | 折叠按钮切换，`payload` 为**新的** `collapsed` 布尔值（与 `SiderTrigger` 的 `on-change` 一致） |

面包屑数据来自 **`$route.matched`**（去掉第一项），每项包含 `name`、`path`、`meta`；展示标题用 `tools.showTitle`。

```vue
<HeaderBar :collapsed="collapsed" @on-coll-change="collapsed = $event">
  <Terminal /><User :userName="name" :user-avator="avatar" />
</HeaderBar>
```

**5. `SiderTrigger`（`header-bar/sider-trigger/sider-trigger.vue`）**

折叠侧栏的图标按钮。

| Props | `collapsed`、`icon`（默认 `navicon-round`，Main 里为 `md-menu`）、`size`（默认 `26`） |
| 事件 | `on-change` — 参数为切换后的 `collapsed` 状态（布尔） |

**6. `customBreadCrumb`（`header-bar/custom-bread-crumb/custom-bread-crumb.vue`）**

自定义面包屑（`Breadcrumb`）。

| Props | `list`（ `{ name, path, meta, to?, icon? }[]` ）、`fontSize`（默认 `14`）、`showIcon` |
| 说明 | 文案通过 `showTitle(item, this)` 解析，支持 `meta.title` 等 |

---

**7. `User`（`user/user.vue`）**

右上角用户名 + 头像下拉，**退出登录**走 Vuex `user/handleLogOut`。

| Props | `userName`、`userAvator`（字符串 URL；也可传对象，未用字符串时显示内置默认图） |
| 说明 | 内置 `message` 方法曾跳转 `message_page`，可按需扩展菜单项 |

**8. Terminal（`terminal/index.vue` + `terminal/terminal.vue`）**

入口为一颗 **iView `Icon`**，点击后用 **`AsyncModal`** 弹层展示内层终端视图。当前实现中日志区域为占位文案（**终端功能暂未启用**），按钮「重新加载 / 清空」会提示未启用。

复用时需保证工程内已注册 **`AsyncModal`**。`index.vue` 中 `ref="asyncModal"` 调用 `show()` 打开弹窗。

**9. `Language`（`language/language.vue`）**

语言下拉（`zh-CN` / `zh-TW` / `en-US`）。需在工程中配置 **`vue-i18n`**（`this.$i18n`）方可切换文案。

| Props | `lang` 当前语言代码 |
| 事件 | `on-lang-change` — 选中的语言 key |

> `main.vue` 当前模板未挂载 `Language`，若在自定义 Main 中需要多语言顶栏，可自行加入。

**10. `Fullscreen`（`fullscreen/fullscreen.vue`）**

全屏切换；非 IE 显示按钮。支持 **`v-model`**（`value` 是否全屏），全屏状态变化时触发 `input`、`on-change`。

> `main.vue` 当前未使用该组件，可按需放入 `HeaderBar` 插槽。

**11. `ABackTop`（`a-back-top/index.vue`）**

回到顶部浮动按钮，监听**滚动容器**滚动。

| Props | `height`（显示阈值，默认 `400`）、`bottom`、`right`、`duration`（滚动动画 ms）、`container`（默认 `window`，可为选择器字符串如 `'.content-wrapper'`） |
| 插槽 | 默认：自定义按钮外观；未提供时用内置箭头图标 |
| 方法 | 内部 `scrollTop` 滚动；点击触发回到顶部 |

Main 中示例：`<ABackTop container=".content-wrapper" :height="100" :bottom="80" :right="50" />`。

---

**小结**：自定义后台壳时，可照搬 `main.vue` 结构，替换或增删 `HeaderBar` 插槽内子组件；侧栏行为与菜单数据结构强相关，需与 **`user/menuList`**、**路由 `meta`** 保持一致。

### 12.2 表格：`Tables`

基于 View Design `Table` 封装，支持分页条、导出、列配置、可编辑列等。

| Props | 说明 |
|-------|------|
| `value` | 表格数据数组 |
| `columns` | 列配置（同 iView Table `columns`，框架内会做对齐等默认处理） |
| `title` / `tip` | 表格标题与灰色提示 |
| `isDown` | 为 true 且 `value` 非空时显示「下载」链接触发导出 |
| `pageOption` | 分页：`{ page, pageSize, total }`，存在则显示 `Page` |
| `width` / `height` | 传给内部 Table |
| `maxHeightOffset` | 表格外层最大高度：`auto`（默认，按视口计算）或数字，用于 `calc(100vh - offset)` |

| 插槽 | 说明 |
|------|------|
| `header` | 标题行右侧区域 |
| 默认 | 透传给内部 Table（如 `slot-scope` 列） |
| `footer` / `loading` | 透传 Table 同名插槽 |

| 事件 | 说明 |
|------|------|
| `changePage` | 分页变更（内部已更新 `pageOption.page`） |
| `on-select` | 多选变化 |
| `downExecl` | 点击下载时，`{ value, columns }` |

```vue
<Tables
  :value="tableData"
  :columns="columns"
  :page-option="{ page: 1, pageSize: 10, total: 100 }"
  title="用户列表"
  @changePage="loadData"
/>
```

### 12.3 树形表格：`TreeGrid`

树形多级表格，表头与行由 `columns`、`data` 驱动（数据需含 `children` 等树结构）。

| Props | 说明 |
|-------|------|
| `columns` | 列配置 |
| `data` | 树形数据 |

其余属性通过 `v-bind="$attrs"` 传到内部子组件。

```vue
<TreeGrid :columns="gridOption.columns" :data="gridOption.data" />
```

### 12.4 上传：`UploadSingle` / `UploadMultiple`

| 组件 | 说明 |
|------|------|
| **UploadSingle** | 单图。`v-model` 绑定服务器返回的**相对路径**字符串；上传地址为 `apiUrl + 'sys_file/upload_oos_img'`，请求头带 `admin-token`。成功：`input`、`change`。预览/删除支持。 |
| **UploadMultiple** | 多图（内部列表最多约 4 张）。`@handleSuccess` 回调 `{ file, uploadList }`；可调用 `setList(list)` / `getList()` 同步列表。 |

```vue
<UploadSingle v-model="form.avatar" />
<UploadMultiple ref="multi" @handleSuccess="onUploaded" />
```

### 12.5 富文本：`Editor`

基于 **wangEditor**。使用 `this.$config.apiUrl`（或等价配置）作为图片上传地址。

| Props | 说明 |
|-------|------|
| `value` | HTML 字符串，支持 `v-model` |

```vue
<Editor v-model="form.content" />
```

### 12.6 弹窗：`AsyncModal` / `editModal`

**AsyncModal** — 简单确认弹窗：

| Props | 说明 |
|-------|------|
| `title` | 标题 |
| `width` | 宽度，默认约 `80`（Modal 数字宽度） |

| 方法 | 说明 |
|------|------|
| `show(callback)` | 打开；确定时**先**执行 `callback`（可 async），再 `on-ok` 并关闭 |

```vue
<AsyncModal ref="asyncModal" title="提示">
  <p>自定义内容</p>
</AsyncModal>
<!-- this.$refs.asyncModal.show(async () => { await api(); }) -->
```

**editModal** — 由 `columns` 驱动表单项（内部用 `FieldRenderer`），用于新增/编辑：

| Props | 说明 |
|-------|------|
| `columns` | 列/字段配置：`key`、`title`、`com`（Input、Select、Radio、`editRender` 等） |
| `width` | 弹窗宽度 |
| `rules` | 可通过父级传入覆盖默认校验（与 `Form` 一致） |

| 插槽 | 说明 |
|------|------|
| `top` | 表单最上方 |
| `bottom` | 表单最下方（如额外表单项） |

| 方法 | 说明 |
|------|------|
| `addShow(row, callback)` | 新增；确定后 `callback(提交行数据)` |
| `editShow(row, callback)` | 编辑 |
| `showModal(row)` | 有 `id` 走编辑，否则新增 |
| `hide` / `setFooter` / `refresh` | 关闭、控制底部按钮、强制重渲染表单 |

| 事件 | 说明 |
|------|------|
| `on-save` | `{ data, isEdit }` |
| `on-visible-change` | Modal 显隐 |

```vue
<editModal ref="editModal" :columns="editColumns" :rules="rules">
  <div slot="bottom"><fieldItem name="备注"><Input v-model="extra" /></fieldItem></div>
</editModal>
```

### 12.7 表单项辅助：`fieldItem` / `FieldRenderer`

| 组件 | 说明 |
|------|------|
| **fieldItem** | 简单包装：`name`（标签）、`required`，默认插槽放控件。 |
| **FieldRenderer** | 根据 `col.com` 渲染 Input、Select、Radio、`UploadSingle`、`DatePicker` 等；一般被 `editModal` 使用，也可在自定义表单中复用。 |

```vue
<fieldItem name="名称" :required="true">
  <Input v-model="form.name" />
</fieldItem>
```

### 12.8 卡片：`InfoCard`

左侧色块 + 图标，右侧自定义内容。

| Props | 说明 |
|-------|------|
| `left` | 左侧宽度占比，默认 `36`（即 36%） |
| `color` | 左侧背景色 |
| `icon` / `iconSize` | 图标名与尺寸（走 `CommonIcon`） |
| `shadow` | 是否 Card 阴影 |

```vue
<InfoCard icon="md-person" color="#19be6b">
  <p>自定义统计文案</p>
</InfoCard>
```

### 12.9 分割面板：`SplitPane`

可拖拽调整左右或上下比例。

| Props | 说明 |
|-------|------|
| `value` | `0~1` 或带 `px` 的字符串，初始分割位置 |
| `mode` | `horizontal`（左右）或 `vertical`（上下） |
| `min` / `max` | 拖拽极限，默认约 `40px` |

| 插槽 | 说明 |
|------|------|
| `left` / `right` 或 `top` / `bottom` | 两侧内容 |
| `trigger` | 自定义拖拽条 |

| 事件 | 说明 |
|------|------|
| `on-move-start` / `on-moving` / `on-move-end` | 拖拽过程；`on-moving` 事件对象上可带 `atMin`、`atMax` |

```vue
<SplitPane v-model="split" mode="horizontal">
  <div slot="left">左</div>
  <div slot="right">右</div>
</SplitPane>
```

### 12.10 侧滑层：`FloatPanel`

从一侧滑出的面板，带返回/关闭。

| Props | 说明 |
|-------|------|
| `title` | 标题 |
| `width` / `height` | 尺寸，支持数字或带单位字符串 |
| `position` | `left` / `right` / `top` / `bottom` / `center` |
| `showBack` / `backText` | 返回按钮与文案 |
| `showClose` | 右上角关闭 |
| `closeOnClickBackdrop` | 点击遮罩是否关闭 |
| `mask` | 是否遮罩 |
| `zIndex` | 层级 |

| 方法 | 说明 |
|------|------|
| `show(callback)` | 显示；可选回调 |
| `hide()` | 隐藏 |

| 插槽 | 说明 |
|------|------|
| 默认 | 主体内容 |
| `header-right` | 标题栏右侧 |

```vue
<FloatPanel ref="panel" title="详情" position="right" :width="480">
  <p>内容</p>
</FloatPanel>
<!-- this.$refs.panel.show() -->
```

### 12.11 其他全局组件

| 组件 | 说明 |
|------|------|
| **TextArea** | 多行输入，`value` + `input` 事件，封装 iView `Input type="textarea"`。 |
| **CommonIcon** | `type`：普通 `Icon` 名；自定义图标以 `_` 开头配合 `md-icons`。`size`、`color` 可选。 |
| **LoadFlower** | 全屏 loading 占位（与 `http` 的 `showLoad` 通过 DOM `id` 联动），一般随 `Main` 挂载，无需单独使用。 |

### 12.12 未全局注册、按需引用的组件（详细）

以下组件不在 `registerGlobalComponents` 中，需要页面内 `import` 后再注册。示例路径基于源码工程 `@/components/...`。

#### 12.12.1 `login-form/login-form.vue`（`LoginForm`）

- 用途：登录页账号密码表单，内置 Enter 提交与 `Form.validate`。
- Props：
  - `userNameRules: Array`（默认必填）
  - `passwordRules: Array`（默认必填）
- 事件：
  - `on-success-valid`：校验成功后抛出 `{ userName, password }`

```vue
<LoginForm @on-success-valid="handleLogin" />
```

#### 12.12.2 `date-picker/index.vue`（`DatePicker` 封装）

- 用途：统一 `DatePicker` 的 `v-model` 语义。
- Props：`value`
- 事件：`input`、`change`
- 说明：其余能力通过 `v-bind="$attrs"` 透传到 iView `DatePicker`。

```vue
<DatePicker v-model="form.start_time" type="datetime" />
```

#### 12.12.3 `switch/index.vue`（`FrameworkSwitch`）

- 用途：布尔/0/1 字段统一开关封装，避免 `<Switch>` 名称冲突。
- Props：`value`（支持 `true/false/1/0/'1'/'0'/'true'/'false'`）
- 事件：`input`、`change`、`on-change`

```vue
<FrameworkSwitch v-model="form.is_platform" />
```

#### 12.12.4 `cron-input/index.vue`

- 用途：可视化拼 Cron（`minute hour day month dayOfWeek`）字符串。
- Props：`value`（Cron 字符串）
- 事件：`input`
- 说明：点击下拉后可选分钟/小时/日/月/周，点“确定”写回 Cron。

```vue
<cron-input v-model="form.cron_time" />
```

#### 12.12.5 `main/pageHead.vue`

- 用途：页面头部工具条；当 `this.$route.meta.type === '功能'` 时显示返回按钮。
- 插槽：默认插槽（放新增/导出等按钮）
- 内置方法：`goBack()`（`this.$router.go(-1)`）

```vue
<pageHead>
  <Button type="primary">新增</Button>
</pageHead>
```

#### 12.12.6 `markdown/markdown.vue`（`MarkdownEditor`）

- 用途：基于 `simplemde` 的 Markdown 编辑器。
- Props：
  - `value: String`
  - `options: Object`（SimpleMDE 配置）
  - `localCache: Boolean`（默认 `true`，写入 `localStorage.markdownContent`）
- 事件：`input`、`on-change`、`on-focus`、`on-blur`

```vue
<MarkdownEditor v-model="form.md_content" :local-cache="false" />
```

#### 12.12.7 `cropper/index.vue`（`Cropper`）

- 用途：图片裁剪（`cropperjs`），默认 1:1 比例。
- Props：`src`、`preview`、`moveStep`、`cropButtonText`
- 事件：`on-crop`（回传裁剪后的 `File`）
- 说明：默认要求图片尺寸至少 `500x500`，否则提示错误。

```vue
<Cropper :src="imageUrl" @on-crop="handleCroppedFile" />
```

#### 12.12.8 `upload/Custom.vue`

- 用途：通用文件上传（显示文件名与上传进度，上传至 OSS）。
- Props：`value`、`accept`、`btnText`
- 事件：`input`（上传成功后的 OSS 地址）
- 方法：`clear()`

```vue
<CustomUpload v-model="form.file_url" accept=".zip,.pdf" btn-text="上传附件" />
```

#### 12.12.9 `md-icons/icons.vue`（`Icons`）

- 用途：渲染项目内 iconfont 图标（`iconfont icon-${type}`）。
- Props：`type`（必填）、`color`、`size`

```vue
<Icons type="dashboard" :size="18" color="#2d8cf0" />
```

### 12.13 仅内部子组件（通常不单独直接使用）

这些组件由父组件组合使用，业务页面一般不直接引用；如需复用，建议复制其父组件交互一起接入。

| 组件路径 | 主要被谁使用 | 作用 |
|----------|--------------|------|
| `main/components/side-menu/side-menu-item.vue` | `SideMenu` | 递归渲染 `Submenu` 节点与子菜单。 |
| `main/components/terminal/terminal.vue` | `main/components/terminal/index.vue` | 终端内容面板（当前为“终端功能暂未启用”占位）。 |
| `split-pane/trigger.vue` | `split-pane/split.vue` | 分割条拖拽手柄视图。 |
| `treeGrid/component/subThead.vue` | `treeGrid/index.vue` | 树表头部渲染。 |
| `treeGrid/component/subColmns.vue` | `treeGrid/index.vue`/`subTreeGrid.vue` | `colgroup` 宽度控制。 |
| `treeGrid/component/subTreeGrid.vue` | `treeGrid/index.vue` | 子树递归渲染、展开收起状态维护。 |
| `treeGrid/component/renderCol.vue` | `treeGrid/index.vue`/`subTreeGrid.vue` | 安全执行列 `render(h, params)`。 |
| `upload/mod/fileBtn.vue` | `upload/Custom.vue`/`upload/mod/fileListModal.vue` | 文件选择按钮。 |
| `upload/mod/fileListModal.vue` | 上传业务页（按需） | 批量选文件 +（可选）批量上传 + 回调。 |

### 12.14 `src/components` 全量组件索引（按路径）

> 说明：下表覆盖 `src/components/**/*.vue`。`注册方式` 里“全局”表示由 `registerGlobalComponents` 自动注册；“按需”表示需手动引入；“内部”表示被父组件组合使用。

| 组件路径 | 注册方式 | 入口章节 |
|----------|----------|----------|
| `asyncModal/index.vue` | 全局 | 12.6 |
| `common-icon/common-icon.vue` | 全局 | 12.11 |
| `cropper/index.vue` | 按需 | 12.12.7 |
| `cron-input/index.vue` | 按需 | 12.12.4 |
| `date-picker/index.vue` | 按需 | 12.12.2 |
| `editor/index.vue` | 全局 | 12.5 |
| `FloatPanel/index.vue` | 全局 | 12.10 |
| `info-card/infor-card.vue` | 全局（`InfoCard`） | 12.8 |
| `load-flower/index.vue` | 全局 | 12.11 |
| `login-form/login-form.vue` | 按需 | 12.12.1 |
| `main/main.vue` | 全局（`Main`） | 12.1 |
| `main/pageHead.vue` | 按需 | 12.12.5 |
| `main/components/a-back-top/index.vue` | 内部（可按需） | 12.1 / 12.11 |
| `main/components/fullscreen/fullscreen.vue` | 内部（可按需） | 12.1 |
| `main/components/header-bar/header-bar.vue` | 内部（可按需） | 12.1 |
| `main/components/header-bar/custom-bread-crumb/custom-bread-crumb.vue` | 内部（可按需） | 12.1 |
| `main/components/header-bar/sider-trigger/sider-trigger.vue` | 内部（可按需） | 12.1 |
| `main/components/language/language.vue` | 内部（可按需） | 12.1 |
| `main/components/side-menu/collapsed-menu.vue` | 内部 | 12.1 |
| `main/components/side-menu/side-menu-item.vue` | 内部 | 12.13 |
| `main/components/side-menu/side-menu.vue` | 内部（可按需） | 12.1 |
| `main/components/terminal/index.vue` | 内部（可按需） | 12.1 |
| `main/components/terminal/terminal.vue` | 内部 | 12.13 |
| `main/components/user/user.vue` | 内部（可按需） | 12.1 |
| `markdown/markdown.vue` | 按需 | 12.12.6 |
| `md-icons/icons.vue` | 按需 | 12.12.9 |
| `parent-view/parent-view.vue` | 全局（`ParentView`） | 12.1 |
| `split-pane/split.vue` | 全局（`SplitPane`） | 12.9 |
| `split-pane/trigger.vue` | 内部 | 12.13 |
| `switch/index.vue` | 按需 | 12.12.3 |
| `tables/editModal.vue` | 全局（`editModal`） | 12.6 |
| `tables/fieldItem.vue` | 全局（`fieldItem`） | 12.7 |
| `tables/fieldRenderer.vue` | 全局（`FieldRenderer`） | 12.7 |
| `tables/index.vue` | 全局（`Tables`） | 12.2 |
| `text-area/index.vue` | 全局（`TextArea`） | 12.11 |
| `treeGrid/index.vue` | 全局（`TreeGrid`） | 12.3 |
| `treeGrid/component/renderCol.vue` | 内部 | 12.13 |
| `treeGrid/component/subColmns.vue` | 内部 | 12.13 |
| `treeGrid/component/subThead.vue` | 内部 | 12.13 |
| `treeGrid/component/subTreeGrid.vue` | 内部 | 12.13 |
| `upload/Custom.vue` | 按需 | 12.12.8 |
| `upload/Multiple.vue` | 全局（`UploadMultiple`） | 12.4 |
| `upload/Single.vue` | 全局（`UploadSingle`） | 12.4 |
| `upload/mod/fileBtn.vue` | 内部 | 12.13 |
| `upload/mod/fileListModal.vue` | 内部（可按需） | 12.13 |

---

## 13. 调试

- 浏览器中：`window.framework`、`window.rootVue`（根实例，在 `createApp` 创建后设置）。
- 需要未压缩源码调试时，可在业务工程里直接引用框架 **`src/index.js`**（需配置好 alias、loader 与 peer 依赖），或使用浏览器「在源映射中黑盒脚本」等能力。

---

## 14. Demo 项目

`demo/` 为示例：请先在**仓库根目录**执行 `npm run build` 生成 `dist/admin-framework.js`，再在 `demo` 目录安装依赖并执行 `npm run dev`。`demo/src/main.js` 通过相对路径引用上级 `dist` 中的 UMD 包。

---

以上为框架对外使用方式的说明；内部实现以 `src/` 源码为准。若升级版本，请同时核对 peer 依赖与本文档中的 API 是否变更。
