u
This commit is contained in:
1 parent
f572dce2f6
commit
e99a9fb274
356 files changed
+28877
-1055
No files matched your search
@@ -0,0 +1,2 @@
|
||||
title: 数据录入
|
||||
icon: i-lucide-text-cursor-input
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Checkbox 复选框
|
||||
description: 支持选中、未选中与半选三种状态的独立勾选控件
|
||||
---
|
||||
|
||||
# Checkbox 复选框
|
||||
|
||||
`Checkbox` 用于在一组互不排斥的选项里多选,或表示单个布尔开关(如「同意协议」)。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 多选:一组选项可以用任意组合选中;
|
||||
- 单个布尔:如「记住我」「同意条款」;
|
||||
- 父子联动:父项用 `indeterminate` 表达「部分选中」(见下方示例);
|
||||
- 只有一个开关、立即生效的场景请用 [Switch](/ui/form/switch)。
|
||||
|
||||
## 基础用法
|
||||
|
||||
`v-model` 绑定 `boolean`。
|
||||
|
||||
:::demo{name="checkbox/basic"}
|
||||
:::
|
||||
|
||||
## 半选联动
|
||||
|
||||
`indeterminate` 是纯展示状态:组件**不会**自动推导它,需要业务根据子项结果计算。
|
||||
|
||||
:::demo{name="checkbox/indeterminate"}
|
||||
:::
|
||||
|
||||
| 状态 | 表现 |
|
||||
| --- | --- |
|
||||
| `modelValue = true` | 显示勾选(优先级最高,会强制清掉半选) |
|
||||
| `modelValue = false` + `indeterminate = true` | 显示减号(半选) |
|
||||
| 两者都为 `false` | 显示空框 |
|
||||
|
||||
## 禁用态
|
||||
|
||||
:::demo{name="checkbox/disabled"}
|
||||
:::
|
||||
|
||||
## 富内容
|
||||
|
||||
默认插槽可以放任意内容(多行说明、链接、标签),此时仍然整块可点击。
|
||||
|
||||
:::demo{name="checkbox/rich-label"}
|
||||
:::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `modelValue` | `boolean` | `false` | 双向绑定的选中状态 |
|
||||
| `indeterminate` | `boolean` | `false` | 半选状态(仅展示,需业务自行计算)。选中时会被忽略 |
|
||||
| `disabled` | `boolean` | `false` | 禁用态 |
|
||||
| `name` | `string` | — | 原生 `name`,用于表单提交分组 |
|
||||
| `value` | `string \| number \| boolean` | — | 原生 `value`,随表单提交的值 |
|
||||
|
||||
### Events
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `update:modelValue` | `(value: boolean)` | 勾选状态变化 |
|
||||
| `change` | `(value: boolean, event: Event)` | 同上,附带原生事件 |
|
||||
| `focus` | `(event: FocusEvent)` | 获得焦点 |
|
||||
| `blur` | `(event: FocusEvent)` | 失去焦点 |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `default` | — | 复选框文案(可放富内容)。为空时不渲染文字容器 |
|
||||
|
||||
### 类型定义
|
||||
|
||||
```ts
|
||||
export interface CheckboxProps {
|
||||
modelValue?: boolean
|
||||
indeterminate?: boolean
|
||||
disabled?: boolean
|
||||
name?: string
|
||||
value?: string | number | boolean
|
||||
}
|
||||
```
|
||||
|
||||
### 样式变量
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `--g3-checkbox-size` | `16px` | 勾选框边长 |
|
||||
| `--g3-radius-sm` | `4px` | 勾选框圆角 |
|
||||
| `--g3-color-primary` | `#18181b` | 选中态底色与边框 |
|
||||
| `--g3-bg-disabled` / `--g3-text-disabled` | `rgba(0,0,0,.04 / .25)` | 禁用态 |
|
||||
|
||||
## 实现说明
|
||||
|
||||
- 底层是原生 `<input type="checkbox">`,通过 `<label>` 包裹:因此键盘 Space 切换、点击文字、屏幕阅读器状态全部由浏览器负责,组件只负责视觉;
|
||||
- `indeterminate` 是 **DOM property**(不是 attribute),Vue 的绑定语法覆盖不到,组件内部在 `mounted` 与相关 prop 变化时手动写入 `input.indeterminate`;
|
||||
- 勾选态优先:`modelValue = true` 时会强制把 DOM 的 `indeterminate` 置为 `false`,避免「已勾选却显示减号」;
|
||||
- 原生 input 视觉上隐藏(`opacity: 0` + 1px 尺寸),但**保留在布局中**,这样 `:focus-visible` 的焦点环可以作用在自绘的方框上,键盘用户依然有焦点提示。
|
||||
@@ -0,0 +1,187 @@
|
||||
---
|
||||
title: Form 表单
|
||||
description: 收集、校验与提交一组字段,支持横向 / 纵向布局与多列网格
|
||||
---
|
||||
|
||||
# Form 表单
|
||||
|
||||
`G3Form` 负责「数据、校验、提交」三件事,`G3FormItem` 负责「标签、控件、错误提示」。字段值统一放在 `model` 对象里,FormItem 通过 `name` 与它对应。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 需要多个字段一起校验、一起提交时;
|
||||
- 需要统一的标签宽度、错误提示位置与布局时;
|
||||
- 只有一两个独立输入、互不相关时,可以不用 Form,直接写 `v-model`。
|
||||
|
||||
## 基础用法
|
||||
|
||||
`model` 必须是响应式对象;`rules` 的键是字段名。
|
||||
|
||||
:::demo{name="form/basic"}
|
||||
:::
|
||||
|
||||
## 校验规则
|
||||
|
||||
一条规则可以同时带多个约束,错误消息会全部收集;`message` 也支持传函数做动态文案。
|
||||
|
||||
:::demo{name="form/rules"}
|
||||
:::
|
||||
|
||||
| 规则字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `required` | `boolean` | 必填。空值(`undefined` / `null` / `''`)时直接报错,其余约束不再执行 |
|
||||
| `type` | `'string' \| 'number' \| 'integer' \| 'email' \| 'url'` | 类型校验。`number` / `integer` 允许数字字符串(输入框的中间态) |
|
||||
| `pattern` | `RegExp` | 正则校验,值会被 `String()` 后匹配 |
|
||||
| `len` / `min` / `max` | `number` | 字符串取长度、数组取项数、数字取数值 |
|
||||
| `message` | `string \| () => string` | 覆盖默认文案;传函数则在每次校验时求值 |
|
||||
| `validator` | `(value, rule) => unknown` | 自定义校验:返回 `true`/`undefined`/`null` 通过;返回 `false` 用 `message`;返回非空字符串则作为错误消息;返回 Promise 则等待结果 |
|
||||
|
||||
## 布局与标签
|
||||
|
||||
`layout` 支持横向(label 在左)与纵向(label 在上);横向时可配 `labelAlign` 与 `labelWidth`。
|
||||
|
||||
:::demo{name="form/layout"}
|
||||
:::
|
||||
|
||||
## 校验时机
|
||||
|
||||
`validateTrigger` 决定何时校验:`change` 输入即校验(默认),`blur` 失焦才校验。两种情况都做了「首次为空不报错」的处理,避免打开表单就整片飘红。
|
||||
|
||||
:::demo{name="form/validate-trigger"}
|
||||
:::
|
||||
|
||||
## 提交、失败与重置
|
||||
|
||||
- `submit`:全部字段通过后触发,参数是 `model` 的浅拷贝;
|
||||
- `submitFailed`:有字段未通过时触发,载荷含 `values` 与 `errorFields`;
|
||||
- `reset`:点击 `html-type="reset"` 的按钮触发,同时清空所有字段的错误与触碰标记;
|
||||
- 也可以在保存前手动 `formRef.validateAll()`。
|
||||
|
||||
:::demo{name="form/submit"}
|
||||
:::
|
||||
|
||||
## 多列网格
|
||||
|
||||
`columns > 1` 时字段按网格排布,`FormItem` 用 `span` 跨列、`lineStart` 强制从下一行第一列开始。
|
||||
|
||||
:::demo{name="form/grid"}
|
||||
:::
|
||||
|
||||
## 分组
|
||||
|
||||
`G3FormGroup` 在长表单里做分组,组内可以覆盖列数、布局与 label 宽度。
|
||||
|
||||
:::demo{name="form/group"}
|
||||
:::
|
||||
|
||||
## API
|
||||
|
||||
### Props(Form)
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `model` | `Record<string, unknown>` | **必填** | 表单数据对象,字段值按 FormItem 的 `name` 读取 |
|
||||
| `rules` | `Record<string, FormRule[]>` | `{}` | 表单级规则;FormItem 自己的 `rules` 优先级更高 |
|
||||
| `layout` | `'horizontal' \| 'vertical'` | `'horizontal'` | 布局;未传时读 `componentDefaults.form.layout` |
|
||||
| `labelAlign` | `'left' \| 'right'` | `'right'` | 横向布局下 label 对齐 |
|
||||
| `labelWidth` | `string \| number` | — | 横向布局的 label 宽度(数字按 px) |
|
||||
| `columns` | `number` | `1` | 每行列数;> 1 时启用网格布局 |
|
||||
| `spanColumns` | `number` | `0` | 只影响 FormItem 的 `span` 计算,不改变 Form 自身布局(外部栅格场景) |
|
||||
| `maxWidth` | `string \| number` | — | 表单最大宽度(数字按 px) |
|
||||
| `requiredMark` | `boolean` | `true` | 是否显示必填红星 |
|
||||
| `validateTrigger` | `'change' \| 'blur'` | `'change'` | 校验时机 |
|
||||
| `gap` | `number \| { row?: number; col?: number }` | — | 表单项间距。数字表示行列同值;不传时用单列 16px / 网格 12px+16px 的内置默认 |
|
||||
|
||||
### Props(FormItem)
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `name` | `string \| number` | **必填** | 字段名,对应 `model` 的键,也是注册到 Form 的键 |
|
||||
| `label` | `string` | `''` | 标签文本,也可用 `#label` 插槽 |
|
||||
| `rules` | `FormRule[]` | — | 单项规则;不传时回退到 Form 级同名规则 |
|
||||
| `required` | `boolean` | 由规则推导 | 是否显示必填星;显式传入可覆盖规则推导 |
|
||||
| `extra` | `string` | `''` | 辅助说明(控件下方);有错误时被错误信息替代 |
|
||||
| `help` | `string` | `''` | 固定提示(无错误时显示) |
|
||||
| `labelAlign` | `'left' \| 'right'` | 继承 Form | 单项对齐覆盖 |
|
||||
| `labelWidth` | `string \| number` | 继承 Form | 单项 label 宽度覆盖 |
|
||||
| `span` | `number` | `1` | 网格布局下跨列数(自动限制在容器列数内) |
|
||||
| `lineStart` | `boolean` | `false` | 强制本项位于新行第一列 |
|
||||
| `validateStatus` | `'' \| 'error'` | `''` | 外部手动指定校验状态(优先于内部校验结果) |
|
||||
| `layout` | `'horizontal' \| 'vertical'` | 继承 Form | 独立使用(不在 Form 内)时的布局 |
|
||||
| `validateTrigger` | `'change' \| 'blur'` | 继承 Form | 独立使用时的校验时机 |
|
||||
|
||||
### Props(FormGroup)
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `title` | `string` | `''` | 分组标题,也可用 `#title` 插槽 |
|
||||
| `columns` | `number` | `1` | 组内列数(覆盖外层 Form) |
|
||||
| `layout` | `'horizontal' \| 'vertical'` | — | 组内布局覆盖 |
|
||||
| `labelWidth` | `string \| number` | — | 组内 label 宽度覆盖 |
|
||||
| `gap` | `number \| { row?: number; col?: number }` | — | 组内间距覆盖 |
|
||||
|
||||
### Events(Form)
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `submit` | `(values: Record<string, unknown>)` | 全部字段校验通过。由 `html-type="submit"` 或回车触发 |
|
||||
| `submitFailed` | `({ values, errorFields: Array<{ name, errors }> })` | 有字段未通过 |
|
||||
| `reset` | `()` | 点击 `html-type="reset"` 触发(同时清空校验态) |
|
||||
|
||||
### Slots
|
||||
|
||||
| 组件 | 插槽 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| Form | `default` | 一组 `G3FormItem` / `G3FormGroup` |
|
||||
| FormItem | `default` | 控件本体 |
|
||||
| FormItem | `label` | 自定义标签内容 |
|
||||
| FormGroup | `default` | 组内字段 |
|
||||
| FormGroup | `title` | 自定义分组标题 |
|
||||
|
||||
### Exposed(Form)
|
||||
|
||||
| 名称 | 签名 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `validateAll` | `() => Promise<Array<{ name: string; errors: string[] }>>` | 立即校验全部已注册字段,返回出错字段(空数组表示通过) |
|
||||
|
||||
### 类型定义
|
||||
|
||||
```ts
|
||||
export type FormLayout = 'horizontal' | 'vertical'
|
||||
export type FormLabelAlign = 'left' | 'right'
|
||||
export type FormValidateTrigger = 'change' | 'blur'
|
||||
export type FormItemStatus = '' | 'error'
|
||||
export type FormGap = number | { row?: number; col?: number }
|
||||
|
||||
export interface FormRule {
|
||||
required?: boolean
|
||||
type?: 'string' | 'number' | 'integer' | 'email' | 'url'
|
||||
pattern?: RegExp
|
||||
len?: number
|
||||
min?: number
|
||||
max?: number
|
||||
message?: string | (() => string)
|
||||
validator?: (value: unknown, rule: FormRule) => unknown
|
||||
}
|
||||
|
||||
export interface FormFieldController {
|
||||
validate: () => Promise<string[]>
|
||||
clearValidation: () => void
|
||||
}
|
||||
```
|
||||
|
||||
### 样式变量
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `--g3-form-label-width` | — | 未传 `labelWidth` 时的默认 label 宽度(不设则由内容决定) |
|
||||
| `--g3-color-danger` | `#ec4246` | 必填星与错误文案 |
|
||||
|
||||
## 实现说明
|
||||
|
||||
- **字段注册**:FormItem 挂载时把自己的 `validate` / `clearValidation` 注册进 Form 的 `Map`,卸载时注销;`validateAll` 就是并发调用这张表里的所有校验器;
|
||||
- **provide 用 computed 包装**:父级可能整体替换 `model` / `rules` 对象(切换编辑目标、克隆草稿),直接传引用会让 FormItem 读到过期对象;
|
||||
- **不误报的三条规则**:① 非 `immediate` 的 `watch`,初始值不校验;② 未触碰且值为空时跳过;③ `model` 对象被整体替换、或布尔值由 `true` 变 `false`(滑块验证码归零这类主动重置)时,清除校验态而不报错;
|
||||
- **blur 校验延后一拍**(`setTimeout`):让浮层类控件(Select / DatePicker 的面板)的点击先完成再取值,否则会「选中了却报未填」;
|
||||
- 校验文案不在组件里硬编码,全部来自语言包(`zhCN.form.*`),可用 `ConfigProvider.locale` 整体替换;
|
||||
- 提交事件回传的是 `{ ...model }` 浅拷贝,避免业务侧直接改到表单内部对象。
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
title: Input 输入框
|
||||
description: 单行 / 密码 / 数字 / 多行四种模式,支持清除、前后缀与字数统计
|
||||
---
|
||||
|
||||
# Input 输入框
|
||||
|
||||
`Input` 是受控输入控件:值通过 `v-model` 双向绑定,`type` 决定它是单行、密码、数字还是多行文本。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 录入短文本(名称、标题、编码)用默认模式;
|
||||
- 密码输入用 `type="password"`(自带显示/隐藏切换);
|
||||
- 只允许数字的字段用 `type="number"`(限制字符并转换类型);
|
||||
- 描述、备注等多行文本用 `type="textarea"`;
|
||||
- 需要从固定选项里选值,请用 [Select](/ui/form/select)。
|
||||
|
||||
## 基础用法
|
||||
|
||||
:::demo{name="input/basic"}
|
||||
:::
|
||||
|
||||
`v-model` 的值类型是 `string`;`type="number"` 时提交值为 `number`(空值保留为 `''`)。
|
||||
|
||||
## 密码输入
|
||||
|
||||
`type="password"` 会在右侧渲染一个显示/隐藏按钮,按钮带 `aria-pressed` 与无障碍标签。
|
||||
|
||||
:::demo{name="input/password"}
|
||||
:::
|
||||
|
||||
密码框建议同时设置 `autocomplete="current-password"`,让浏览器密码管理器正确识别。
|
||||
|
||||
## 数字输入
|
||||
|
||||
`type="number"` 做了三件事:过滤非数字字符、保留输入中间态(`12.` 不会被打断)、**失焦或回车时把字符串提交为 `Number`**。
|
||||
|
||||
:::demo{name="input/number"}
|
||||
:::
|
||||
|
||||
## 多行文本
|
||||
|
||||
`rows` 是初始行数;`show-count` 显示字数统计;右下角有拖拽手柄,可以拖拽或聚焦后用 `↑ / ↓ / Home` 调整高度(聚焦后是 `role="slider"`)。
|
||||
|
||||
:::demo{name="input/textarea"}
|
||||
:::
|
||||
|
||||
## 可清除
|
||||
|
||||
`allowClear` 时,有值且 hover / 聚焦会出现清除按钮;点击清空值并触发 `clear` 事件。
|
||||
|
||||
:::demo{name="input/clearable"}
|
||||
:::
|
||||
|
||||
## 前后缀插槽
|
||||
|
||||
`#prefix` / `#suffix` 放图标或单位,图标尺寸跟随字号(`1em`)。
|
||||
|
||||
:::demo{name="input/slots"}
|
||||
:::
|
||||
|
||||
## 校验状态
|
||||
|
||||
`status` 让输入框表达校验结果:`error` 红边、`warning` 黄边。配合 `G3FormItem` 时通常由表单自动管理,无需手写。
|
||||
|
||||
:::demo{name="input/status"}
|
||||
:::
|
||||
|
||||
## 实例方法
|
||||
|
||||
通过 `ref` 调用 `focus(options)` / `select()` / `blur()`,用于「点击单元格进入编辑」这类交互:
|
||||
|
||||
```ts
|
||||
const inputRef = ref<InstanceType<typeof G3Input> | null>(null)
|
||||
|
||||
inputRef.value?.focus({ cursor: 'all' }) // 聚焦并全选
|
||||
inputRef.value?.focus({ preventScroll: true }) // 聚焦但不滚动页面
|
||||
```
|
||||
|
||||
:::demo{name="input/methods"}
|
||||
:::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `modelValue` | `string \| number` | `''` | 双向绑定的值 |
|
||||
| `type` | `'default' \| 'password' \| 'number' \| 'textarea'` | `'default'` | 输入模式 |
|
||||
| `disabled` | `boolean` | `false` | 禁用态 |
|
||||
| `readonly` | `boolean` | `false` | 只读态:可聚焦、可复制,但不可修改 |
|
||||
| `status` | `'' \| 'error' \| 'warning'` | `''` | 校验状态 |
|
||||
| `allowClear` | `boolean` | `false` | 有值时显示清除按钮(hover / 聚焦时出现),多行模式不支持 |
|
||||
| `showCount` | `boolean` | `false` | 显示字数统计(`maxlength` 存在时显示 `当前 / 上限`) |
|
||||
| `maxlength` | `string \| number` | — | 最大长度,透传给原生 `maxlength` |
|
||||
| `rows` | `string \| number` | `3` | 多行模式的初始行数(仅 `textarea` 生效) |
|
||||
| `placeholder` | `string` | — | 占位文案。声明为 prop 是为了让它落到内部 `<input>` 而不是外层容器 |
|
||||
| `autocomplete` | `string` | — | 浏览器自动填充语义,如 `username` / `current-password` / `off` |
|
||||
|
||||
### Events
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `update:modelValue` | `(value: string \| number)` | 输入时触发。`number` 模式提交 `number`,清空时为 `''` |
|
||||
| `change` | `(value: string \| number, event: Event)` | 原生 `change`(失焦或回车后值变化)。`number` 模式在此时完成类型转换 |
|
||||
| `focus` | `(event: FocusEvent)` | 获得焦点 |
|
||||
| `blur` | `(event: FocusEvent)` | 失去焦点 |
|
||||
| `clear` | `()` | 点击清除按钮(此时 `update:modelValue` 先发出 `''`) |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `prefix` | — | 输入框左侧内容(图标、单位),多行模式不渲染 |
|
||||
| `suffix` | — | 输入框右侧内容,多行模式不渲染 |
|
||||
|
||||
### Exposed(通过 ref 调用)
|
||||
|
||||
| 名称 | 签名 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `focus` | `(options?: { preventScroll?: boolean; cursor?: 'all' }) => void` | 聚焦;`cursor: 'all'` 聚焦后全选 |
|
||||
| `select` | `() => void` | 选中全部内容 |
|
||||
| `blur` | `() => void` | 失焦 |
|
||||
|
||||
### 类型定义
|
||||
|
||||
```ts
|
||||
export type InputType = 'default' | 'password' | 'number' | 'textarea'
|
||||
export type InputStatus = '' | 'error' | 'warning'
|
||||
|
||||
export interface InputProps {
|
||||
modelValue?: string | number
|
||||
type?: InputType
|
||||
disabled?: boolean
|
||||
readonly?: boolean
|
||||
status?: InputStatus
|
||||
allowClear?: boolean
|
||||
showCount?: boolean
|
||||
maxlength?: string | number
|
||||
rows?: string | number
|
||||
placeholder?: string
|
||||
autocomplete?: string
|
||||
}
|
||||
|
||||
export interface InputFocusOptions {
|
||||
preventScroll?: boolean
|
||||
cursor?: 'all'
|
||||
}
|
||||
```
|
||||
|
||||
### 样式变量
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `--g3-control-height` | `32px` | 输入框高度(单行) |
|
||||
| `--g3-input-resize-handle` | 右下角手柄 | 多行模式的拖拽手柄位置由组件内部处理 |
|
||||
| `--g3-color-primary` / `--g3-color-primary-ring` | `#18181b` / 主色 20% | 聚焦边框与聚焦环 |
|
||||
| `--g3-color-danger` / `--g3-color-warning` | `#ec4246` / `#e79a0d` | `error` / `warning` 边框 |
|
||||
|
||||
## 实现说明
|
||||
|
||||
- **数字模式的取舍**:输入过程保留字符串(否则用户打不出 `12.`),只在 `change` / 失焦时转为 `Number`,空串保持 `''`,避免 `Number('')` 变成 `0`;
|
||||
- 光标位置在数字过滤后会重新计算(`setSelectionRange`),因此在中间插入非法字符不会把光标弹到末尾;
|
||||
- 多行模式的高度拖拽用 Pointer Events + `requestAnimationFrame` 合并更新,拖拽期间改的是内联 `height`(从小高度往大拖不会跳变);
|
||||
- `placeholder` 等原生属性声明为 prop 是刻意的:容器是 `<div>`,如果不声明,`placeholder` 会作为 fallthrough 属性挂到外层容器上,对真实输入框无效;
|
||||
- 聚焦样式用 `--g3-color-primary-ring` 画 2px 外环(而非 `outline`),这样在 `Space` / `Grid` 里不会因为 outline 被相邻元素裁掉。
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Radio 单选框
|
||||
description: 与 RadioGroup 组合使用,在一组选项里选中一个
|
||||
---
|
||||
|
||||
# Radio 单选框
|
||||
|
||||
`G3Radio` 必须放在 `G3RadioGroup` 里才能互斥。组负责值、禁用与 `name`,单选项只声明自己的 `value` 和内容。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 选项少(2-5 个)且需要**一次看全**时,比 [Select](/ui/form/select) 更快;
|
||||
- 选项是互斥的、必须选一个时使用;可以不选时用 Select 更合适;
|
||||
- 选项超过 5 个、或需要展示更多信息时,用 Select;
|
||||
- 只是控制某个功能的开与关,用 [Switch](/ui/form/switch)。
|
||||
|
||||
## 基础用法
|
||||
|
||||
:::demo{name="radio/basic"}
|
||||
:::
|
||||
|
||||
组会自动生成唯一的原生 `name`(`useId`),因此同页多组之间不会互相干扰,键盘方向键也能在组内来回切换。
|
||||
|
||||
## 纵向排列与整组禁用
|
||||
|
||||
组默认是水平换行的 `inline-flex`;纵向排列用外部 `style` 改 `flex-direction`,或给组包一层纵向容器。
|
||||
|
||||
:::demo{name="radio/vertical"}
|
||||
:::
|
||||
|
||||
## 卡片式选择
|
||||
|
||||
默认插槽可以放任意内容,配合 `style` 就能做成卡片选择器(常见于套餐、模板选择)。
|
||||
|
||||
:::demo{name="radio/cards"}
|
||||
:::
|
||||
|
||||
## API
|
||||
|
||||
### Props(Radio)
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `value` | `string \| number \| boolean` | **必填** | 选项值。与组 `modelValue` 做 `Object.is` 比较决定是否选中 |
|
||||
| `disabled` | `boolean` | `false` | 单独禁用该项(组禁用时以组为准) |
|
||||
|
||||
### Props(RadioGroup)
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `modelValue` | `string \| number \| boolean` | — | 双向绑定的选中值 |
|
||||
| `disabled` | `boolean` | `false` | 禁用整组 |
|
||||
| `name` | `string` | 自动生成 | 原生 radio 组名,不传时用 `useId` 生成 |
|
||||
|
||||
### Events(RadioGroup)
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `update:modelValue` | `(value: string \| number \| boolean)` | 选中项变化 |
|
||||
| `change` | `(value: string \| number \| boolean)` | 同上,便于只监听变化 |
|
||||
|
||||
### Slots
|
||||
|
||||
| 组件 | 插槽 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| Radio | `default` | 选项文案,可放富内容 |
|
||||
| RadioGroup | `default` | 组内的 `G3Radio` 列表 |
|
||||
|
||||
### 类型定义
|
||||
|
||||
```ts
|
||||
export type RadioValue = string | number | boolean
|
||||
|
||||
export interface RadioProps {
|
||||
value: RadioValue
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export interface RadioGroupProps {
|
||||
modelValue?: RadioValue
|
||||
disabled?: boolean
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface RadioGroupContext {
|
||||
name: ComputedRef<string | undefined>
|
||||
modelValue: ComputedRef<RadioValue | undefined>
|
||||
disabled: ComputedRef<boolean>
|
||||
select: (value: RadioValue) => void
|
||||
}
|
||||
```
|
||||
|
||||
### 样式变量
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `--g3-radio-size` | `16px` | 圆圈直径 |
|
||||
| `--g3-color-primary` | `#18181b` | 选中态边框与内点 |
|
||||
| `--g3-bg-disabled` / `--g3-text-disabled` | `rgba(0,0,0,.04 / .25)` | 禁用态 |
|
||||
|
||||
## 实现说明
|
||||
|
||||
- 底层同样是原生 `input[type=radio]`:组用 `<div>` 包裹、每项用 `<label>` 包裹,浏览器原生提供「组内互斥 + 方向键切换」;
|
||||
- 选中判断用 `Object.is`(而不是 `===`),这样 `NaN` 之类的值也能正确比较;
|
||||
- 组上下文通过 `provide` 下发:`{ name, modelValue, disabled, select }`,单选项目通过 `inject` 读取;**脱离组单独使用时**会退化为「永不选中」的原生 radio(不会报错,但也不会有选中态),因此务必成对使用;
|
||||
- 未显式传 `name` 时用 `useId` 生成组名,避免同页多个 radio 组因为浏览器按 `name` 分组而互相影响。
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: Select 选择器
|
||||
description: 下拉选择,支持筛选、多选、远程搜索、触底加载与自由输入
|
||||
---
|
||||
|
||||
# Select 选择器
|
||||
|
||||
`Select` 从一个列表里选值,是表单里最常被用到的控件之一。它把触发器做成了**输入框**:开启筛选后可直接键入过滤,键盘也能完成全部操作。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 选项较多(> 5 个)时;
|
||||
- 选项来自接口、需要分页或远程搜索时;
|
||||
- 需要多选,且选项数量不足以用 Checkbox 平铺时;
|
||||
- 选项很少(2-5 个)且需要一次看全时,用 [Radio](/ui/form/radio) / [Segmented](/ui/general/segmented) 更快。
|
||||
|
||||
## 基础用法
|
||||
|
||||
`options` 默认按 `{ label, value, disabled }` 取值。
|
||||
|
||||
:::demo{name="select/basic"}
|
||||
:::
|
||||
|
||||
## 字段名映射
|
||||
|
||||
后端返回的字段名不是 `label` / `value` 时,用 `labelKey` / `valueKey` 映射,不必在业务侧做数据转换。
|
||||
|
||||
:::demo{name="select/field-names"}
|
||||
:::
|
||||
|
||||
## 多选
|
||||
|
||||
`multiple` 时 `modelValue` 为数组,选中后下拉保持打开,选中项以顿号拼接显示。
|
||||
|
||||
:::demo{name="select/multiple"}
|
||||
:::
|
||||
|
||||
## 筛选
|
||||
|
||||
`filterable` 默认就是开启的;不传时按 `label` / `value` 做大小写不敏感的包含匹配,也可以传 `filterOption` 自定义。
|
||||
|
||||
:::demo{name="select/filterable"}
|
||||
:::
|
||||
|
||||
## 远程搜索
|
||||
|
||||
`remote` 模式不会做本地筛选,全部交给父层:输入触发 `search`,父层把结果写回 `options`;`loading` 时右侧显示加载图标而不是箭头/清除按钮。
|
||||
|
||||
:::demo{name="select/remote"}
|
||||
:::
|
||||
|
||||
## 触底加载更多
|
||||
|
||||
列表滚到底部会持续触发 `loadMore`,配合 `loading` 去重;`#footer` 插槽可以放加载提示。
|
||||
|
||||
:::demo{name="select/load-more"}
|
||||
:::
|
||||
|
||||
## 自定义选项
|
||||
|
||||
不传 `options` 时使用默认插槽,插槽透出 `{ select, close }`,适合做带描述、头像的富选项。
|
||||
|
||||
:::demo{name="select/custom-option"}
|
||||
:::
|
||||
|
||||
## 键盘操作
|
||||
|
||||
| 按键 | 行为 |
|
||||
| --- | --- |
|
||||
| `↓` / `↑` | 打开下拉(若未打开)或移动高亮(跳过分隔项与禁用项) |
|
||||
| `Home` / `End` | 跳到第一个 / 最后一个可选项 |
|
||||
| `Enter` | 选中当前高亮项;若输入文本精确匹配某项 label,优先选中它 |
|
||||
| `Esc` | 关闭下拉并清空筛选词 |
|
||||
| `Tab` | 关闭下拉并把焦点交给下一个控件 |
|
||||
| 直接输入 | 筛选列表(`filterable` 为 true 时) |
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `modelValue` | `string \| number \| boolean \| object \| null \| Array` | — | 双向绑定的值;`multiple` 时为数组 |
|
||||
| `options` | `Array<Record<string, unknown> \| string \| number>` | — | 选项数组。元素为对象时按 `labelKey` / `valueKey` / `disabled` 取值,为原始值时自身即 label 与 value |
|
||||
| `disabled` | `boolean` | `false` | 禁用态 |
|
||||
| `clearable` | `boolean` | `false` | 有值时显示清除按钮(聚焦时出现) |
|
||||
| `multiple` | `boolean` | `false` | 多选模式,`modelValue` 为数组 |
|
||||
| `filterable` | `boolean` | `true` | 是否开启筛选(关闭时输入框只读) |
|
||||
| `filterOption` | `(input: string, option: unknown) => boolean` | — | 自定义筛选函数,`option` 是**原始**选项对象 |
|
||||
| `remote` | `boolean` | `false` | 远程搜索模式:不做本地筛选,输入时触发 `search` |
|
||||
| `loading` | `boolean` | `false` | 加载中:右侧显示加载图标,并阻止触底重复触发 |
|
||||
| `notFoundText` | `string` | 语言包 | 无匹配项时的文案 |
|
||||
| `allowFreeInput` | `boolean` | `false` | 允许自由输入:不在列表里的文本也可以作为值提交 |
|
||||
| `labelKey` | `string` | `'label'` | 选项对象中作为显示文本的字段名 |
|
||||
| `valueKey` | `string` | `'value'` | 选项对象中作为值的字段名 |
|
||||
|
||||
### Events
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `update:modelValue` | `(value: SelectValue \| SelectValue[])` | 选中 / 清除时触发;覆盖多选 |
|
||||
| `change` | `(value, option?)` | 同上,第二个参数是被选中的规范化选项(清除时为 `undefined`) |
|
||||
| `search` | `(query: string)` | `remote` 模式下输入时触发 |
|
||||
| `visibleChange` | `(open: boolean)` | 下拉展开 / 收起。远程型调用方可据此在展开时拉取首页数据 |
|
||||
| `loadMore` | `()` | 列表滚动触底(距底 24px 内),已加载中不重复触发 |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `default` | `{ select: (value) => void; close: () => void }` | 自定义选项(不传 `options` 时生效) |
|
||||
| `footer` | `{ loading: boolean }` | 列表底部内容,如加载更多提示 |
|
||||
|
||||
### Exposed
|
||||
|
||||
| 名称 | 签名 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `openDropdown` | `() => void` | 展开下拉 |
|
||||
| `closeDropdown` | `() => void` | 收起下拉 |
|
||||
|
||||
### 类型定义
|
||||
|
||||
```ts
|
||||
export type SelectValue = string | number | boolean | object | null | undefined
|
||||
|
||||
export interface SelectProps {
|
||||
modelValue?: SelectValue | SelectValue[]
|
||||
options?: Array<Record<string, unknown> | string | number>
|
||||
disabled?: boolean
|
||||
clearable?: boolean
|
||||
multiple?: boolean
|
||||
filterable?: boolean
|
||||
filterOption?: (input: string, option: unknown) => boolean
|
||||
remote?: boolean
|
||||
loading?: boolean
|
||||
notFoundText?: string
|
||||
allowFreeInput?: boolean
|
||||
labelKey?: string
|
||||
valueKey?: string
|
||||
}
|
||||
```
|
||||
|
||||
### 样式变量
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `--g3-select-dropdown-max-height` | `256px` | 下拉最大高度 |
|
||||
| `--g3-select-dropdown-max-width` | `360px` | 下拉宽度上限(触发框更宽时以触发框为准) |
|
||||
| `--g3-control-height` | `32px` | 触发器高度 |
|
||||
| `--g3-color-primary-ring` | 主色 20% | 聚焦环 |
|
||||
|
||||
## 实现说明
|
||||
|
||||
- **触发器就是输入框**:这样「键入筛选」与「键盘导航」共用一套焦点管理,不需要额外的隐藏 input;关闭下拉时会清空筛选词并回显选中项;
|
||||
- **自由输入的提交时机**:输入过程**不**写 `modelValue`(避免把半成品写进表单),只在失焦或关闭下拉时提交;非自由输入模式下,若文本精确匹配某选项 label 则选中该项,否则回退到上次选中值;
|
||||
- **焦点回流**:选中 / 清除后下拉内容会卸载,焦点会掉到 `body`;组件会把焦点主动交还输入框,并用 `suppressNextAutoOpen` 抑制这次「聚焦即展开」,否则下拉会立刻弹回来;
|
||||
- **点击外部关闭**用的浮层基座是 [Popover](/ui/overlay/popover):Teleport 到配置的容器、滚动 / 尺寸变化时重算位置、Esc 关闭;
|
||||
- 下拉宽度取 `max(触发框宽, 内容宽)` 并由 `--g3-select-dropdown-max-width` 封顶,超出上限的选项文案折行而不是把下拉拉出屏幕;
|
||||
- 选项用 `v-memo` 缓存(只在「该选项或它是否是高亮项」变化时重渲染),长列表下键盘连续移动不会卡顿。
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: Switch 开关
|
||||
description: 用于切换单个状态的即时开关
|
||||
---
|
||||
|
||||
# Switch 开关
|
||||
|
||||
`Switch` 表示「开 / 关」两态,通常**立即生效**(不需要点保存)。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 设置项里即时生效的开关(启用通知、公开可见);
|
||||
- 表格行内的状态切换;
|
||||
- 需要注意:`Switch` 不是复选框的替代品。多个选项之间互不排斥、需要一起提交时用 [Checkbox](/ui/form/checkbox)。
|
||||
|
||||
## 基础用法
|
||||
|
||||
`v-model` 绑定 `boolean`;`change` 回传新值与原生事件。
|
||||
|
||||
:::demo{name="switch/basic"}
|
||||
:::
|
||||
|
||||
## 禁用态
|
||||
|
||||
:::demo{name="switch/disabled"}
|
||||
:::
|
||||
|
||||
## 与表单一起使用
|
||||
|
||||
放入 `G3FormItem` 时,label 与校验由表单负责,`Switch` 只提供值。
|
||||
|
||||
:::demo{name="switch/in-form"}
|
||||
:::
|
||||
|
||||
## 关于「异步切换」
|
||||
|
||||
组件本身不做 loading/回滚,因为「乐观切换 + 失败回滚」的业务语义差异很大。推荐在 `change` 里显式处理:
|
||||
|
||||
```ts
|
||||
async function onChange(next: boolean) {
|
||||
const previous = !next
|
||||
try {
|
||||
await api.update(next)
|
||||
} catch {
|
||||
enabled.value = previous // 失败回滚
|
||||
G3Message.error('保存失败')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `modelValue` | `boolean` | `false` | 双向绑定的开关状态 |
|
||||
| `disabled` | `boolean` | `false` | 禁用态 |
|
||||
|
||||
### Events
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `update:modelValue` | `(value: boolean)` | 状态变化 |
|
||||
| `change` | `(value: boolean, event: MouseEvent)` | 同上,附带原生点击事件 |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `default` | — | 开关旁的文案(可点击切换) |
|
||||
|
||||
### 类型定义
|
||||
|
||||
```ts
|
||||
export interface SwitchProps {
|
||||
modelValue?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
### 样式变量
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `--g3-switch-width` | `32px` | 轨道宽 |
|
||||
| `--g3-switch-height` | `18px` | 轨道高 |
|
||||
| `--g3-switch-thumb-size` | `14px` | 滑块直径 |
|
||||
| `--g3-switch-thumb-offset` | `2px` | 滑块内边距 |
|
||||
| `--g3-color-primary` | `#18181b` | 开启态轨道色 |
|
||||
|
||||
## 实现说明
|
||||
|
||||
- 没有原生的 `<input type="switch">`,因此用 `<button type="button" role="switch" aria-checked>` 承载语义:Enter / Space 由原生按钮自动转为 `click`;
|
||||
- `aria-checked` 会跟随 `modelValue` 更新,屏幕阅读器能播报「开 / 关」;
|
||||
- 滑块的位移由固定变量计算(`宽度 - 滑块 - 内边距×2`),因此改 `--g3-switch-*` 变量后滑块位置依然正确,不需要改代码;
|
||||
- 文字部分也在按钮内,因此点击文字同样可以切换(按钮整体是一个目标区域,触屏上更好点)。
|
||||
Reference in new issue
Block a user