289 lines
11 KiB
Markdown
289 lines
11 KiB
Markdown
# @apigo.cc/base - AI 逻辑操作说明书 (示例驱动)
|
||
|
||
本库是基于 `@apigo.cc/state` 增强的 UI 原子库,提供遵循 Bootstrap 5.3 规范的高阶业务组件。
|
||
|
||
---
|
||
|
||
## 1. 快速集成 (Quick Start)
|
||
|
||
### CDN 集成 (自包含 UMD,锁定版本号,无需引入 Bootstrap CSS)
|
||
```html
|
||
<!-- 依赖核心库 -->
|
||
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/state@1.0.16/dist/state.min.js"></script>
|
||
<!-- Bootstrap 集成引擎 (自包含 CSS 注入) -->
|
||
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/bootstrap@1.0.5/dist/bootstrap.min.js"></script>
|
||
<!-- 本组件库 -->
|
||
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/base@1.0.10/dist/base.min.js"></script>
|
||
```
|
||
|
||
### ESM 模块引入
|
||
```javascript
|
||
import { HTTP, UI, AutoForm, State } from '@apigo.cc/base'
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 组件超能力示例 (Mega Examples)
|
||
|
||
### 2.1 声明式数据组件 (`<API>`)
|
||
```html
|
||
<API id="userSearchApi" auto
|
||
$request='{
|
||
"url": "/api/users/search",
|
||
"method": "POST",
|
||
"data": { "keyword": State.searchKey }
|
||
}'
|
||
$onresponse="console.log('加载成功,响应对象:', event.detail); State.userList = event.detail.result"
|
||
$onerror="UI.toast('加载失败:' + event.detail.message, { type: 'danger' })">
|
||
</API>
|
||
|
||
<script>
|
||
// 手动调用 API 示例 (适合动态触发/事后赋值场景)
|
||
async function forceReload() {
|
||
try {
|
||
// do() 返回 Promise<ResponseObject>。传入 noui: true 屏蔽全局 UI 弹窗报错。
|
||
const resp = await userSearchApi.do({ noui: true });
|
||
console.log("手动加载完成,结果:", resp.result);
|
||
} catch (err) {
|
||
console.error("请求失败", err);
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
* **AI 核心要点**:
|
||
* **请求响应结构 (ResponseObject)**:
|
||
`{ ok: Boolean, status: Number, headers: Object, responseType: 'json'|'binary'|'text', result: Any, error: String|null }`
|
||
* 深度监听 `request` 任何字段的改变自动触发请求(微任务防抖)。`response` 事件为 `bubbles: false`,`error` 为 `bubbles: true`。
|
||
|
||
---
|
||
|
||
### 2.2 万能表单与内嵌控件 (`<AutoForm>`)
|
||
```html
|
||
<script>
|
||
// 1. 预先确定表单 Schema (合并了所有扩展控件配置)
|
||
const employeeFormSchema = [
|
||
{ name: 'name', label: '姓名', type: 'text', setting: { required: true } },
|
||
{
|
||
name: 'joinDate',
|
||
label: '入职周期',
|
||
type: 'DatePicker', // 内置日期范围选择控件
|
||
setting: { rangeEnd: 'leaveDate' } // 影子字段绑定为 leaveDate
|
||
},
|
||
{ name: 'color', label: '工位颜色', type: 'ColorPicker' }, // 内置颜色控件
|
||
{ name: 'avatarIcon', label: '头像图标', type: 'IconPicker' }, // 内置图标控件
|
||
{ name: 'skills', label: '专业技能', type: 'TagsInput' }, // 内置多标签控件
|
||
{
|
||
name: 'role',
|
||
label: '角色',
|
||
type: 'select',
|
||
options: [{ label: '管理员', value: 'admin' }, { label: '员工', value: 'staff' }],
|
||
placeholder: '请选择角色...'
|
||
},
|
||
{
|
||
name: 'status',
|
||
label: '状态',
|
||
type: 'checkbox',
|
||
options: ['在职', '离职'],
|
||
if: "this.data.age > 18" // 仅在 age > 18 时渲染,this 访问 AutoForm 实例
|
||
},
|
||
{ name: 'notify', label: '启用通知', type: 'switch' }
|
||
];
|
||
</script>
|
||
|
||
<!-- 2. HTML 声明式属性绑定 schema 状态,且绑定接口 API 实例 -->
|
||
<AutoForm id="employeeForm"
|
||
vertical
|
||
$state.schema="employeeFormSchema"
|
||
$api="saveEmployeeApi"
|
||
$onresponse="UI.toast('保存成功!')">
|
||
</AutoForm>
|
||
|
||
<script>
|
||
// 3. 数据层回显/事后赋值操作 (使用 Object.assign 保证绑定的 Proxy 响应式链路不丢失)
|
||
Object.assign(employeeForm.state.data, {
|
||
name: '张三',
|
||
joinDate: '2026-06-01',
|
||
leaveDate: '2026-06-30',
|
||
color: '#ff0000',
|
||
avatarIcon: 'person',
|
||
skills: ['Vue', 'React'],
|
||
role: 'staff',
|
||
notify: true
|
||
});
|
||
</script>
|
||
```
|
||
* **AI 核心要点**:
|
||
* 容器属性:`vertical` (垂直排列)、`inline` (无边框流式行内布局)、`nobutton` (隐藏底部默认按钮)。
|
||
* **表单数据操作红线**:禁止对 `form.state.data` 或 `form.data` 执行覆盖式重新赋值,必须使用 `Object.assign`。
|
||
|
||
---
|
||
|
||
### 2.3 增强列表组件 (`<List>`)
|
||
|
||
列表有三类布局模式,分别有不同的数据结构关联,均推荐在 HTML 中声明绑定:
|
||
|
||
#### 模式一:普通扁平列表 (`mode="normal"`)
|
||
* **关联逻辑**:直接渲染一维数组项。
|
||
* **数据结构**:
|
||
```javascript
|
||
const userList = [
|
||
{ id: '1', label: '小明', summary: '前端开发' },
|
||
{ id: '2', label: '小红', summary: '产品经理' }
|
||
];
|
||
```
|
||
* **HTML 写法**:
|
||
```html
|
||
<List mode="normal" $list="userList" $onitemclick="console.log(event.detail.item)"></List>
|
||
```
|
||
|
||
#### 模式二:分组列表 (`mode="group"`)
|
||
* **关联逻辑**:`list` 的项通过 `group` 字段去匹配 `groups` 数组中的 `id` 字段。
|
||
* **数据结构**:
|
||
```javascript
|
||
const deptGroups = [
|
||
{ id: 'devGroup', label: '研发中心', summary: '共 5 人' },
|
||
{ id: 'hrGroup', label: '人力资源', summary: '共 2 人' }
|
||
];
|
||
const groupUserList = [
|
||
{ id: '1', label: '小明', group: 'devGroup' },
|
||
{ id: '2', label: '小红', group: 'hrGroup' }
|
||
];
|
||
```
|
||
* **HTML 写法**:
|
||
```html
|
||
<List mode="group" $list="groupUserList" $state.groups="deptGroups"></List>
|
||
```
|
||
|
||
#### 模式三:树形可折叠列表 (`mode="tree"`) —— 超级示例
|
||
* **关联逻辑**:通过每项的 `parent` 关联父项的 `id` 属性,根项的 `parent` 设为空字符串 `''`。
|
||
* **数据结构**(内置 `_itemHeight` 优化虚拟滚动高度测量):
|
||
```javascript
|
||
const deptTreeList = [
|
||
{ id: '1', label: '总经办', parent: '', _itemHeight: 40 },
|
||
{ id: '2', label: '研发部', parent: '1', _itemHeight: 40 },
|
||
{ id: '3', label: '开发组', parent: '2', _itemHeight: 40 }
|
||
];
|
||
```
|
||
* **HTML 写法**:
|
||
```html
|
||
<List id="orgTreeList"
|
||
mode="tree"
|
||
fast
|
||
collapsible
|
||
auto-select
|
||
class="overflow-auto border"
|
||
style="height: 400px;"
|
||
$list="deptTreeList"
|
||
$onitemclick="console.log('点击节点:', event.detail.item, '全局绝对索引:', event.detail.index)">
|
||
|
||
<!-- 自定义单项右侧动作区域 -->
|
||
<template slot-id="item">
|
||
<span class="fw-semibold text-primary" $text="item.label"></span>
|
||
<div slot-id="item-actions">
|
||
<button class="btn btn-sm btn-link py-0 bi bi-plus-circle" onclick="addNode(item)"></button>
|
||
</div>
|
||
</template>
|
||
</List>
|
||
```
|
||
* **AI 核心要点**:
|
||
* **属性默认值**:当数据结构符合默认字段命名(`id`, `label`, `summary`, `parent`, `group`)时,**无须填写**任何映射属性(如 `idfield`, `labelfield`, `parentfield` 等),实际编写代码时应当将其省略。
|
||
* 虚拟滚动 (`fast`) 强制容器必须声明为 `overflow-auto` 类。
|
||
|
||
---
|
||
|
||
### 2.4 导航组件 (`<Nav>`)
|
||
```html
|
||
<script>
|
||
// 1. 预先确定品牌与菜单数据
|
||
const myBrand = { icon: 'shield-lock', label: '安全控制台' };
|
||
const myNavList = [
|
||
{ type: 'button', name: 'dashboard', label: '仪表盘', icon: 'speedometer' },
|
||
{
|
||
type: 'dropdown',
|
||
name: 'settings',
|
||
label: '系统设置',
|
||
icon: 'gear',
|
||
list: [
|
||
{ type: 'button', name: 'profile', label: '个人信息', icon: 'person' },
|
||
{ type: 'switch', name: 'darkMode', label: '暗黑模式', icon: 'moon', bind: LocalStorage, name: 'darkMode' }
|
||
]
|
||
},
|
||
{ type: 'fill' },
|
||
{ type: 'button', name: 'logout', label: '退出登录', icon: 'box-arrow-right', noselect: true }
|
||
];
|
||
</script>
|
||
|
||
<!-- 2. HTML 声明式属性绑定 -->
|
||
<Nav id="sidebarNav"
|
||
vertical
|
||
$state.brand="myBrand"
|
||
$state.list="myNavList"
|
||
$onnav="console.log('导航点击:', event.detail.item)">
|
||
</Nav>
|
||
```
|
||
* **AI 核心要点**:
|
||
* 常规导航项被点击时(且 `noselect` 不为 true),会自动更新全局 `Hash.nav = item.name`。
|
||
|
||
---
|
||
|
||
### 2.5 拖拽改变大小组件 (`<Resizer>`)
|
||
```html
|
||
<div class="d-flex" style="height: 300px;">
|
||
<div id="leftPanel" style="width: 200px;" class="bg-light">侧边栏</div>
|
||
<!-- 拖拽调节器,默认 target 为前一个兄弟节点 -->
|
||
<Resizer target="leftPanel"
|
||
min="100"
|
||
max="400"
|
||
$onresizing="console.log('拖动尺寸:', event.detail.newSize)"
|
||
$change="State.leftPanelWidth">
|
||
</Resizer>
|
||
<div class="flex-fill">内容区</div>
|
||
</div>
|
||
```
|
||
* **AI 核心要点**:
|
||
* `resizing` 与 `resize` 事件的 `detail` 结构均为 `{ oldSize: Number, newSize: Number }`;`change` 事件的 `detail` 为 `newSize` 像素数字。
|
||
|
||
---
|
||
|
||
## 3. 网络与交互工具集 (`HTTP` & `UI`)
|
||
|
||
### 3.1 HTTP 请求工具 (`HTTP`)
|
||
```javascript
|
||
// 支持 HTTP.get, HTTP.post, HTTP.put, HTTP.delete, HTTP.head
|
||
const resp = await HTTP.post({
|
||
url: '/api/user/save',
|
||
data: { name: 'Alice', file: avatarFile },
|
||
timeout: 5000
|
||
});
|
||
```
|
||
* **返回值 `ResponseObject` 结构**:
|
||
* `ok` (Boolean): 状态码是否在 200-299。
|
||
* `status` (Number): HTTP 状态码。
|
||
* `headers` (Object): 响应头键值对。
|
||
* `responseType` (String): `'json' | 'binary' | 'text'`。
|
||
* `result` (Any): 成功后的解析结果。
|
||
* `error` (String | null): 失败的错误描述。
|
||
* **数据自动转换**:若 `data` 内包含 `File`/`Blob`,自动转为 `FormData` 且清除 Content-Type;普通对象自动序列化为 JSON 并添加 `application/json`。
|
||
|
||
### 3.2 交互工具 (`UI`)
|
||
* **`UI.showDialog({ title, message, buttons, type })`**
|
||
* **返回值**:`Promise<Number>`。点击按钮返回其索引值 (从 `1` 开始),点击关闭或取消返回 `0`。
|
||
* **参数**:`type` 可以为 `'primary'|'danger'|'warning'|'success'` 控制主题色;`message` 支持 HTML。
|
||
* **`UI.alert(message, options)`**
|
||
* **返回值**:`Promise<Boolean>`(点击关闭按钮返回 `false`)。
|
||
* **`UI.confirm(message, options)`**
|
||
* **返回值**:`Promise<Boolean>`。点击“确认”返回 `true`,点击“取消”或关闭返回 `false`。
|
||
* **`UI.toast(message, options)`**
|
||
* **返回值**:`void`。`options.delay` 为自动消失延迟毫秒(`0` 代表不消失)。可以通过传入 `buttons: ['选项1']` 按钮提供交互,点击后在实例 `result` 上保存索引。
|
||
* **`UI.toastConfirm(message, options)`**
|
||
* **返回值**:`Promise<Boolean>`。在 Toast 中提供单确认按钮,点击确认返回 `true`,否则返回 `false`。
|
||
|
||
---
|
||
|
||
## 4. 开发红线 (Constraints)
|
||
|
||
1. **表单数据操作红线**:严禁直接覆盖表单的 `state.data` 或 `data` 对象(如 `form.data = {}`)。这会切断与内部 Proxy 的响应式链路。必须使用 `Object.assign(form.state.data, newData)`。
|
||
2. **列表布局红线**:开启虚拟滚动(`fast`)时,容器必须包含 `overflow-auto` 类。
|
||
3. **指令 DOM 保护**:严禁使用原生 DOM API 直接修改由 `$each`、`$if` 或组件渲染指令生成的 DOM 节点。所有 DOM 状态的变化应当完全通过修改与之绑定的底层 `State` 属性驱动。
|
||
4. **退出拦截约束**:在全局配置有 `State.exitBlocks > 0` 时,框架将强行拦截并警告任何刷新/关闭页面的行为。
|