base/README.md

840 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# @apigo.cc/base
`@apigo.cc/base` 是基于 `@apigo.cc/state` 和 Bootstrap 5.3 的同步组件层。页面只需要按顺序引入脚本,就能在首屏完成 DOM 处理和组件渲染。
## 目录
- [依赖加载](#依赖加载) - 同步加载顺序和来源说明
- [全局变量](#全局变量) - 本包直接挂到 `window` 的变量
- [HTTP](#http) - 请求工具
- [API](#api) - 声明式请求节点
- [AutoForm](#autoform) - 自动表单节点
- [表单扩展控件](#表单扩展控件) - `DatePicker` / `ColorPicker` / `IconPicker` / `TagsInput`
- [List](#list) - 普通、分组、树形和虚拟滚动列表
- [Nav](#nav) - 水平导航栏或垂直侧边栏
- [Modal / Dialog](#modal--dialog) - 弹窗容器和确认弹窗
- [Resizer](#resizer) - 拖拽分隔条
- [UI](#ui) - toast 和对话框工具
- [VirtualScroll / MouseMover](#virtualscroll--mousemover) - 虚拟滚动和鼠标拖拽辅助
## 依赖加载
按顺序同步加载:
`@apigo.cc/state` -> `@apigo.cc/bootstrap` -> `@apigo.cc/base`
`base` 会直接使用来自 `@apigo.cc/state` 的全局对象 `State``Hash``LocalStorage`
- [state 文档](https://apigo.cc/web/state)
### 推荐加载顺序
```html
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/state@1.0.23/dist/state.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/bootstrap@1.0.9/dist/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/base@1.0.23/dist/base.min.js"></script>
```
### 全局变量
| 变量 | 说明 |
|---|---|
| `HTTP` | 请求工具 |
| `UI` | 提示、确认、对话框工具 |
| `AutoForm` | 自动表单容器和扩展注册器 |
| `VirtualScroll` | 虚拟滚动工具 |
| `MouseMover` | 鼠标拖拽位移工具 |
## HTTP
`HTTP` 只处理请求,不处理 UI。
### 方法
| 方法 | 写法示例 | 说明 |
|---|---|---|
| `HTTP.request(options)` | `await HTTP.request({ url: '/api/user', method: 'POST', data: { id: 1 } })` | 通用请求入口 |
| `HTTP.get(...)` | `await HTTP.get({ url: '/api/user/1' })` | `GET` |
| `HTTP.post(...)` | `await HTTP.post({ url: '/api/user', data: form })` | `POST` |
| `HTTP.put(...)` | `await HTTP.put({ url: '/api/user/1', data: patch })` | `PUT` |
| `HTTP.delete(...)` | `await HTTP.delete({ url: '/api/user/1' })` | `DELETE` |
| `HTTP.head(...)` | `await HTTP.head({ url: '/api/user/1' })` | `HEAD` |
### `HTTP.request` 参数
| 参数 | 类型 | 写法示例 | 说明 |
|---|---|---|---|
| `url` | `string` | `url: '/api/user'` | 必填 |
| `method` | `string` | `method: 'POST'` | 默认 `POST`,内部会转大写 |
| `data` | `any` | `data: { name: 'Alice' }` | 请求体 |
| `headers` | `object` | `headers: { Authorization: 'Bearer xxx' }` | 请求头 |
| `responseType` | `string` | `responseType: 'json'` | 可手动指定 |
| `timeout` | `number` | `timeout: 5000` | 默认 `10000` ms |
### `HTTP` 对象属性
| 属性 | 写法示例 | 说明 |
|---|---|---|
| `keepHeaders` | `HTTP.keepHeaders = ['Session-Id']` | 需要跨请求保留的响应头名 |
### Content-Type 规则
- `data` 是普通对象时,默认按 JSON 发送,并自动补 `Content-Type: application/json`
- `data``FormData` 时,不手动设置 `Content-Type`,让浏览器生成带 boundary 的 multipart 请求头
- 如果你在 `headers` 里手动传了 `Content-Type`,普通对象请求会按你的设置发送;`FormData` 仍然以浏览器生成的请求头为准
### 返回值
```js
{
ok,
status,
headers,
responseType,
result,
error
}
```
### 推荐示例
```html
<script>
window.userRequest = {
url: '/api/users/1',
method: 'GET'
}
window.apiResponse = null
window.apiResult = null
window.user = null
</script>
<API id="userApi"
auto
$.request="window.userRequest"
$onresponse="window.user = event.detail.result"
$onerror="UI.toast(event.detail.message, { type: 'danger' })">
</API>
<button class="btn btn-primary" onclick="userApi.do({ noui: true })">Reload</button>
```
## API
`<API>` 是把请求封装成页面节点的方式。
### 属性和状态
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `auto` | `<API auto>` | 监听 `request` 变化后自动请求 |
| `request` | `$.request="window.userRequest"` | 请求配置对象 |
| `response` | `$.response="window.apiResponse"` | 最近一次请求状态 |
| `result` | `$.result="window.apiResult"` | 响应结果缓存 |
### `request` 默认结构
```js
{
url: '',
method: 'GET',
headers: {},
data: null,
timeout: 10000,
responseType: ''
}
```
### `response` 默认结构
```js
{
loading: false,
ok: null,
status: null,
error: null,
headers: {},
responseType: '',
result: null
}
```
### 方法
| 方法 | 写法示例 | 说明 |
|---|---|---|
| `do(opt = {})` | `await userApi.do()` | 手动请求 |
| `opt.noui` | `await userApi.do({ noui: true })` | 失败时不弹 toast |
### 事件
| 事件 | 触发时机 | `detail` |
|---|---|---|
| `response` | 请求成功 | 完整响应对象 |
| `error` | 请求失败或返回错误 | `Error` 对象 |
### 备注
- `auto` 只在 `request.url` 存在时生效
- `request` 深度变化会在微任务里触发一次请求,避免同一轮重复请求
## AutoForm
`<AutoForm>` 通过 `schema` 生成表单,适合和 `API``window` 上的页面数据直接联动。
### 属性
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `vertical` | `<AutoForm vertical>` | 垂直布局 |
| `horizontal` | `<AutoForm horizontal>` | 强制左右两列布局 |
| `inline` | `<AutoForm inline>` | 行内紧凑布局 |
| `nobutton` | `<AutoForm nobutton>` | 隐藏底部提交按钮 |
| `api` | `$.api="saveApi"` | 绑定 `<API>` 实例或其引用 |
| `submitlabel` | `submitlabel="Search"` | 提交按钮文案,默认 `{#Submit#}` |
### 状态和方法
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `data` | `$.state.data="window.profile"` | 表单数据,建议用 `Object.assign` 更新 |
| `schema` | `$.state.schema="window.profileSchema"` | 表单定义 |
| `request` | `$.request="{ method: 'POST' }"` | 默认提交配置 |
| `response` | `$.response="window.formResponse"` | 最近一次提交结果 |
| `result` | `$.result="window.formResult"` | 最近一次提交的 `response.result` |
| `submit(opt = {})` | `form.submit()` | 主动提交 |
### `schema` 字段
| 字段 | 写法示例 | 说明 |
|---|---|---|
| `name` | `name: 'role'` | 必填,数据键名 |
| `label` | `label: 'Role'` | 表单项标题 |
| `type` | `type: 'select'` | 控件类型 |
| `setting` | `setting: { required: true }` | 透传给控件的属性 |
| `options` | `options: ['A', 'B']` | `select` / `checkbox` / `radio` 选项 |
| `placeholder` | `placeholder: 'Please choose'` | `select` 的空值占位文案 |
| `if` | `if: 'this.data.enable'` | 条件渲染表达式 |
### `if` 说明
- `if` 不是字符串模板拼接,而是表达式
- 表达式执行时可直接访问 `this``State``Hash``LocalStorage``item``data`
- 这类条件是表单级动态渲染,适合做联动显隐
### 支持的 `type`
| `type` | 用途 |
|---|---|
| `text` | 单行文本 |
| `password` | 密码 |
| `email` | 邮箱 |
| `number` | 数字 |
| `date` | 日期 |
| `datetime` | 日期时间 |
| `file` | 文件 |
| `select` | 下拉单选 |
| `checkbox` | 多选 |
| `radio` | 单选按钮组 |
| `switch` | 开关 |
| `textarea` | 多行文本 |
| `DatePicker` | 扩展日期控件 |
| `ColorPicker` | 扩展颜色控件 |
| `IconPicker` | 扩展图标控件 |
| `TagsInput` | 扩展标签控件 |
### `options` 写法
```js
['Admin', 'Editor']
[{ label: 'Admin', value: 'admin' }]
```
### 事件
| 事件 | 触发时机 | `detail` |
|---|---|---|
| `submit` | 表单校验通过、提交前 | 当前表单数据 |
| `response` | 提交成功 | 完整响应对象 |
| `error` | 提交失败 | `Error` 对象 |
### 插槽
| 插槽 | 位置 | 用途 |
|---|---|---|
| `actions` | 默认提交按钮左侧 | 自定义动作按钮 |
### 推荐示例
```html
<script>
window.profile = { name: 'Alice' }
window.profileSchema = [
{ name: 'name', label: 'Name', type: 'text', setting: { required: true, placeholder: 'Enter name' } },
{ name: 'role', label: 'Role', type: 'select', options: [{ label: 'Admin', value: 'admin' }, { label: 'Editor', value: 'editor' }] },
{ name: 'active', label: 'Active', type: 'switch' },
{ name: 'birthday', label: 'Birthday', type: 'DatePicker', setting: { rangeEnd: 'birthdayEnd' } },
{ name: 'birthdayEnd', label: 'Birthday End', type: 'date' },
{ name: 'theme', label: 'Theme', type: 'ColorPicker' },
{ name: 'icon', label: 'Icon', type: 'IconPicker' },
{ name: 'tags', label: 'Tags', type: 'TagsInput' },
{ name: 'remark', label: 'Remark', type: 'textarea', if: 'this.data.active' }
]
</script>
<AutoForm vertical
$.state.schema="window.profileSchema"
$.state.data="window.profile"
$onsubmit="UI.toast('submit: ' + JSON.stringify(event.detail))">
<template slot="actions">
<button type="button" class="btn btn-outline-secondary" onclick="Object.assign(window.profile, { name: 'Alice' })">Reset</button>
</template>
</AutoForm>
```
### 编程注意
- 不要直接整体覆盖 `form.data``form.state.data`
- 需要更新时,优先用 `Object.assign(form.data, nextData)`
### `AutoForm.register(name, typeName = '')`
注册可被 `schema.type` 识别的自定义控件。
| 参数 | 写法示例 | 说明 |
|---|---|---|
| `name` | `AutoForm.register('MyPicker')` | 组件标签名 |
| `typeName` | `AutoForm.register('MyPicker', 'picker')` | 默认 `''`。只有当标签名和 `schema.type` 不同,或需要复用同一个标签名匹配另一个 `type` 时才传 |
### 什么时候传 `typeName`
- 你想让 `<MyPicker>` 处理 `schema.type === 'picker'`
- 你想把一个标签名映射成不同的 `type`
- 如果 `schema.type` 和标签名一致,直接省略第二个参数
## 表单扩展控件
这些控件既可以在 `AutoForm` 里作为 `schema.type` 使用,也可以单独放到页面上。单独使用时,直接绑定 `value`,再监听 `change` 即可。
### `<DatePicker>`
| 项目 | 说明 |
|---|---|
| `state.start` | 起始日期 |
| `state.end` | 结束日期 |
| `value` | 映射到 `state.start` |
| `isRange` | 是否启用范围模式 |
| `setting.rangeEnd` | 范围结束字段名 |
| `rangeEnd` | 组件属性形式的范围结束字段名 |
| `change` | 开始日期变化时触发,`detail` 为开始日期字符串 |
### `<DatePicker>` 示例
```html
<script>
window.rangeForm = {}
window.rangeSchema = [
{ name: 'rangeStart', label: 'Range', type: 'DatePicker', setting: { rangeEnd: 'rangeEnd' } },
{ name: 'rangeEnd', label: 'Range End', type: 'date' }
]
</script>
<AutoForm $.state.schema="window.rangeSchema" $.state.data="window.rangeForm"></AutoForm>
```
### `<DatePicker>` 单独使用
```html
<script>
window.birthDate = '2026-06-01'
</script>
<DatePicker $bind="window.birthDate"></DatePicker>
```
### `<ColorPicker>`
| 项目 | 说明 |
|---|---|
| `state.value` | 颜色值,默认 `#000000` |
| `value` | 可读写,映射到 `state.value` |
| `change` | 颜色变化时触发,`detail` 为颜色字符串 |
### `<ColorPicker>` 示例
```html
<script>
window.colorForm = {}
window.colorSchema = [
{ name: 'theme', label: 'Theme', type: 'ColorPicker' }
]
</script>
<AutoForm $.state.schema="window.colorSchema" $.state.data="window.colorForm"></AutoForm>
```
### `<IconPicker>`
| 项目 | 说明 |
|---|---|
| `state.value` | 当前图标名 |
| `state.search` | 搜索词 |
| `state.open` | 下拉是否展开 |
| `filteredIcons` | 过滤后的图标列表 |
| `value` | 可读写,映射到 `state.value` |
| `change` | 选择图标时触发,`detail` 为图标名 |
### `<IconPicker>` 示例
```html
<script>
window.iconForm = {}
window.iconSchema = [
{ name: 'icon', label: 'Icon', type: 'IconPicker' }
]
</script>
<AutoForm $.state.schema="window.iconSchema" $.state.data="window.iconForm"></AutoForm>
```
### `<TagsInput>`
| 项目 | 说明 |
|---|---|
| `state.tags` | 标签数组 |
| `change` | 标签变更时触发,`detail` 为最新数组 |
### `<TagsInput>` 示例
```html
<script>
window.tagForm = {}
window.tagsSchema = [
{ name: 'tags', label: 'Tags', type: 'TagsInput' }
]
</script>
<AutoForm $.state.schema="window.tagsSchema" $.state.data="window.tagForm"></AutoForm>
```
### `<TagsInput>` 单独使用
```html
<script>
window.tags = ['Vue', 'AI']
</script>
<TagsInput $bind="window.tags"></TagsInput>
```
## List
`<List>` 支持普通、分组、树形和虚拟滚动。它的重点不是“能渲染”,而是“能直接拿来绑定状态”。
### 属性
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `mode` | `<List mode="group">` | `normal` / `group` / `tree`,默认 `normal` |
| `fast` | `<List fast>` | 开启虚拟滚动。任意模式都能用 |
| `collapsible` | `<List collapsible>` | 树形模式下允许折叠 |
| `auto-select` | `<List auto-select>` | 点击项后把当前项 id 写回 `selectedItem` |
| `auto-select-group` | `<List auto-select-group>` | 点击分组后把当前分组 id 写回 `selectedGroup` |
### 状态
| 名称 | 说明 |
|---|---|
| `list` | 原始列表 |
| `groups` | 分组列表,`group` 模式使用 |
| `filter` | 字符串或对象筛选条件 |
| `order` | 排序规则,例如 `name``-name``name desc` |
| `selectedItem` | 当前选中的项 id |
| `selectedGroup` | 当前选中的分组 id |
| `renderedList` | 当前实际渲染的列表片段 |
| `flatList` | 逻辑处理后的扁平列表 |
| `collapsed` | 树形折叠状态 |
### 实例属性
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `filterFunc` | `list.filterFunc = item => item.active` | 自定义过滤函数 |
| `orderFunc` | `list.orderFunc = (a, b) => a.name.localeCompare(b.name)` | 自定义排序函数 |
### 事件
| 事件 | 触发时机 | `detail` |
|---|---|---|
| `change` | 选中项变化 | `selectedItem` |
| `itemclick` | 点击普通项 | `{ item, index }` |
| `groupclick` | 点击分组项 | `{ item, index }` |
### 绑定习惯
- 推荐配合 `$bind` 保存当前选中 id比如 `$bind="window.currentUserId"`
- 如果你只关心点击事件,也可以不绑,直接监听 `itemclick` / `groupclick`
### 插槽
| 插槽 | 说明 |
|---|---|
| `item` | 单项自定义内容 |
| `item-actions` | 单项右侧动作区 |
| `group-actions` | 分组右侧动作区 |
### 字段默认映射
不写映射属性时,默认字段名就是这些:
| 字段 | 默认值 |
|---|---|
| `idfield` | `id` |
| `labelfield` | `label` |
| `summaryfield` | `summary` |
| `groupidfield` | `id` |
| `grouplabelfield` | `label` |
| `groupsummaryfield` | `summary` |
| `groupfield` | `group` |
| `parentfield` | `parent` |
| `groupicon` | `folder` |
| `itemicon` | `file` |
### 推荐示例
#### 普通列表 + `$bind`
```html
<script>
window.currentUserId = null
window.userList = [
{ id: '1', label: 'Alice', summary: 'Admin' },
{ id: '2', label: 'Bob', summary: 'Editor' }
]
</script>
<List class="h-100 overflow-auto" auto-select
$.state.list="window.userList"
$bind="window.currentUserId"
$onitemclick="UI.toast('item: ' + event.detail.item.label)">
<template slot="item">
<span $text="item.label"></span>
<span class="ms-auto text-muted small" $text="item.summary"></span>
</template>
</List>
```
#### 分组列表
```html
<script>
window.groups = [
{ id: 'team-a', label: 'Team A', summary: '2 people' },
{ id: 'team-b', label: 'Team B', summary: '1 person' }
]
window.members = [
{ id: '1', label: 'Alice', summary: 'Admin', group: 'team-a' },
{ id: '2', label: 'Bob', summary: 'Editor', group: 'team-b' }
]
</script>
<List mode="group" class="h-100 overflow-auto" auto-select-group
$.state.list="window.members"
$.state.groups="window.groups"
$ongroupclick="UI.toast('group: ' + event.detail.item.label)">
<template slot="item">
<span $text="item.label"></span>
<span class="ms-auto text-muted small" $text="item.summary"></span>
</template>
</List>
```
#### 树形列表
```html
<script>
window.treeList = [
{ id: '1', label: 'Root', parent: '' },
{ id: '2', label: 'Child A', parent: '1' },
{ id: '3', label: 'Child B', parent: '1' }
]
</script>
<List mode="tree" collapsible class="h-100 overflow-auto"
$.state.list="window.treeList">
</List>
```
#### 虚拟滚动
```html
<script>
window.bigList = Array.from({ length: 2000 }, (_, i) => ({
id: String(i + 1),
label: 'Row ' + (i + 1),
summary: 'virtual row'
}))
</script>
<List fast class="h-100 overflow-auto"
$.state.list="window.bigList">
</List>
```
### 行为说明
- `filter` 是字符串时,同时匹配 `label``summary`
- `filter` 是对象时,按字段模糊匹配
- `tree` 模式会自动保留命中的祖先链
- `fast` 模式要求外层容器有 `overflow-auto`
- 数据项可加 `_itemHeight` 优化虚拟滚动测量
## Nav
`<Nav>` 默认是水平导航。需要侧边栏时才加 `vertical`
### 属性
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `vertical` | `<Nav vertical>` | 垂直侧边栏 |
### 状态
| 名称 | 说明 |
|---|---|
| `brand` | `{ image, icon, label }` |
| `list` | 导航项数组 |
| `value` | 当前选中项 |
| `openName` | 当前展开的下拉项 |
### 导航项结构
| 字段 | 说明 |
|---|---|
| `type` | `button` / `dropdown` / `label` / `fill` |
| `name` | 导航项标识 |
| `label` | 文本 |
| `icon` | Bootstrap Icons 名称 |
| `class` | 额外 class |
| `noselect` | 选中时不写回 `value` |
| `width` | 仅 `dropdown` 在水平模式下控制下拉宽度 |
| `list` | `dropdown` 的子项 |
### 子项结构
`dropdown.list` 内的子项支持:
- `type: 'button'`
- `type: 'label'`
- `type: 'switch'`
`switch` 子项需要:
- `bind`: 一个状态对象
- `name`: 写回的字段名
### 事件
| 事件 | 触发时机 | `detail` |
|---|---|---|
| `change` | 选中项变化 | 当前 `name` |
| `nav` | 点击或展开时 | `{ item }``{ item, open }` |
### 推荐示例
```html
<script>
window.currentNav = 'users'
window.brand = { icon: 'cpu', label: 'Admin' }
window.navSettings = { darkMode: false }
window.navList = [
{ type: 'button', name: 'users', label: 'Users', icon: 'people' },
{ type: 'dropdown', name: 'settings', label: 'Settings', icon: 'gear', list: [
{ type: 'button', name: 'profile', label: 'Profile', icon: 'person' },
{ type: 'switch', name: 'darkMode', label: 'Dark Mode', icon: 'moon', bind: window.navSettings }
]}
]
</script>
<Nav class="border-bottom"
$.state.brand="window.brand"
$.state.list="window.navList"
$bind="window.currentNav"
$onnav="UI.toast(event.detail.item.label)">
</Nav>
```
### 备注
- 水平模式是默认推荐形态
- 需要左侧栏时再加 `vertical`
- `list` 推荐在 JS 里单独定义,不要硬塞成一长串 HTML 字面量
- 如果需要同步当前路由或菜单名,可以再加 `$bind`
## Modal / Dialog
### `<Modal>`
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `show()` | `editModal.show()` | 打开弹窗 |
| `hide()` | `editModal.hide()` | 关闭弹窗 |
| `change` | `@change` | 关闭时派发 `false` |
### `<Modal>` 的插槽
| 插槽 | 说明 |
|---|---|
| `header` | 弹窗头部 |
| `body` | 弹窗主体 |
| `footer` | 弹窗底部 |
### `<Modal>` 示例
```html
<Modal id="editModal">
<div slot="header">Edit User</div>
<div slot="body">
<AutoForm nobutton inline $.state.schema="window.profileSchema" $.state.data="window.profile"></AutoForm>
</div>
<div slot="footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</Modal>
<button class="btn btn-primary" onclick="editModal.show()">Open</button>
```
### `<Dialog>`
`<Dialog>` 是基于 `Modal` 的消息确认弹窗,平时直接用 `UI.showDialog` 更快。
### `UI.showDialog`
| 参数 | 写法示例 | 说明 |
|---|---|---|
| `title` | `title: 'Delete'` | 标题 |
| `message` | `message: 'Are you sure?'` | 内容 |
| `buttons` | `buttons: ['Cancel', 'Confirm']` | 按钮文本数组 |
| `type` | `type: 'danger'` | 样式类型 |
### `UI.showDialog` 示例
```js
const ok = await UI.confirm('Delete it?', { type: 'danger' })
if (ok) {
UI.toast('deleted', { type: 'success' })
}
```
## Resizer
`<Resizer>` 默认调整前一个元素的宽度。需要别的目标时再传 `target`
### 属性
| 名称 | 写法示例 | 说明 |
|---|---|---|
| `vertical` | `<Resizer vertical>` | 调整高度,不是宽度 |
| `min` | `min="180"` | 最小像素,默认 `10` |
| `max` | `max="420"` | 最大像素,默认 `1000` |
| `target` | `target="sidebar"` | 要调整的元素;不写时默认前一个兄弟节点 |
### 事件
| 事件 | `detail` | 说明 |
|---|---|---|
| `resizing` | `{ oldSize, newSize }` | 拖动中持续触发 |
| `resize` | `{ oldSize, newSize }` | 拖动结束触发 |
| `change` | `newSize` | 拖动结束触发,适合写回状态 |
### 推荐示例
#### 默认用法
```html
<div id="sidebar" style="width: 280px"></div>
<Resizer min="180" max="420"></Resizer>
```
#### 指定 target
```html
<div id="sidebar" style="width: 280px"></div>
<Resizer target="sidebar" min="180" max="420"></Resizer>
```
## UI
### `UI.toast(message, options)`
| 参数 | 写法示例 | 说明 |
|---|---|---|
| `message` | `UI.toast('Saved')` | 提示文案 |
| `type` | `UI.toast('Saved', { type: 'success' })` | 主题类型 |
| `delay` | `UI.toast('Saved', { delay: 2000 })` | 自动关闭延迟 |
| `buttons` | `UI.toast('Confirm?', { buttons: ['Cancel', 'Confirm'] })` | 底部按钮 |
| `container` | `UI.toast('...', { container: 'right-top' })` | 自定义容器 |
### `UI.toastConfirm(message, options)`
返回 `Promise<boolean>`
### `UI.alert(message, options)`
```js
await UI.alert('Saved')
```
### `UI.confirm(message, options)`
```js
const ok = await UI.confirm('Delete it?')
```
### `UI.showDialog(options)`
```js
await UI.showDialog({
title: 'Confirm',
message: 'Do you want to continue?',
buttons: ['Cancel', 'Confirm'],
type: 'primary'
})
```
## VirtualScroll / MouseMover
### `VirtualScroll(options = {})`
参数:
| 参数 | 写法示例 | 说明 |
|---|---|---|
| `itemHeight` | `VirtualScroll({ itemHeight: 40 })` | 默认行高预估值 |
返回值:
| 方法 | 说明 |
|---|---|
| `reset(list, container)` | 重置缓存并返回初始可见列表 |
| `init(list, refreshCallback)` | 初始化高度缓存 |
| `update(index, node)` | 记录节点实际高度 |
| `calc(container, list)` | 计算当前需要渲染的片段 |
### `MouseMover.start(event, handlers)`
```js
MouseMover.start(event, {
onmousemove: ({ w, h }) => {},
onmouseup: ({ w, h }) => {}
})
```
### 说明
- `VirtualScroll` 主要给 `List``DataTable`
- `MouseMover` 主要给 `Resizer`