x-next 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI_CODING.md +6 -5
- package/AI_USAGE/catalog.md +106 -105
- package/AI_USAGE/components/alert-api.md +48 -0
- package/AI_USAGE/components/alert.md +222 -3
- package/AI_USAGE/components/bar-chart-api.md +177 -0
- package/AI_USAGE/components/bar-chart.md +297 -3
- package/AI_USAGE/components/boxicons.md +147 -0
- package/AI_USAGE/components/button-api.md +144 -0
- package/AI_USAGE/components/button.md +447 -3
- package/AI_USAGE/components/charts.md +81 -0
- package/AI_USAGE/components/checkbox-api.md +145 -0
- package/AI_USAGE/components/checkbox.md +365 -3
- package/AI_USAGE/components/color-picker-api.md +52 -0
- package/AI_USAGE/components/color-picker.md +211 -3
- package/AI_USAGE/components/config-provider-api.md +38 -0
- package/AI_USAGE/components/config-provider.md +250 -3
- package/AI_USAGE/components/date-picker-api.md +1114 -0
- package/AI_USAGE/components/date-picker.md +378 -3
- package/AI_USAGE/components/dialog-api.md +142 -0
- package/AI_USAGE/components/dialog.md +284 -3
- package/AI_USAGE/components/draggable-api.md +146 -0
- package/AI_USAGE/components/draggable.md +259 -0
- package/AI_USAGE/components/drawer-api.md +78 -0
- package/AI_USAGE/components/drawer.md +227 -0
- package/AI_USAGE/components/dropdown-api.md +529 -0
- package/AI_USAGE/components/dropdown.md +392 -3
- package/AI_USAGE/components/empty-api.md +36 -0
- package/AI_USAGE/components/empty.md +227 -3
- package/AI_USAGE/components/finicon.md +88 -0
- package/AI_USAGE/components/form-api.md +524 -0
- package/AI_USAGE/components/form.md +454 -0
- package/AI_USAGE/components/grid-api.md +527 -0
- package/AI_USAGE/components/grid.md +224 -0
- package/AI_USAGE/components/heatmap-api.md +122 -0
- package/AI_USAGE/components/heatmap.md +220 -0
- package/AI_USAGE/components/icon-api.md +4707 -0
- package/AI_USAGE/components/icon.md +257 -0
- package/AI_USAGE/components/image-api.md +440 -0
- package/AI_USAGE/components/image.md +465 -0
- package/AI_USAGE/components/input-api.md +184 -0
- package/AI_USAGE/components/input-number-api.md +66 -0
- package/AI_USAGE/components/input-number.md +294 -0
- package/AI_USAGE/components/input-tag-api.md +114 -0
- package/AI_USAGE/components/input-tag.md +482 -0
- package/AI_USAGE/components/input.md +293 -3
- package/AI_USAGE/components/layout-api.md +147 -0
- package/AI_USAGE/components/layout.md +230 -0
- package/AI_USAGE/components/line-chart-api.md +197 -0
- package/AI_USAGE/components/line-chart.md +279 -3
- package/AI_USAGE/components/link-api.md +42 -0
- package/AI_USAGE/components/link.md +197 -0
- package/AI_USAGE/components/menu-api.md +516 -0
- package/AI_USAGE/components/menu.md +268 -0
- package/AI_USAGE/components/message-api.md +215 -0
- package/AI_USAGE/components/message-box-api.md +108 -0
- package/AI_USAGE/components/message-box.md +268 -3
- package/AI_USAGE/components/message.md +231 -3
- package/AI_USAGE/components/network-api.md +91 -0
- package/AI_USAGE/components/network.md +378 -0
- package/AI_USAGE/components/notification-api.md +236 -0
- package/AI_USAGE/components/notification.md +275 -3
- package/AI_USAGE/components/pagination-api.md +62 -0
- package/AI_USAGE/components/pagination.md +380 -3
- package/AI_USAGE/components/pie-chart-api.md +109 -0
- package/AI_USAGE/components/pie-chart.md +245 -3
- package/AI_USAGE/components/popconfirm-api.md +75 -0
- package/AI_USAGE/components/popconfirm.md +231 -3
- package/AI_USAGE/components/popover-api.md +75 -0
- package/AI_USAGE/components/popover.md +208 -3
- package/AI_USAGE/components/popup-api.md +75 -0
- package/AI_USAGE/components/popup.md +52 -0
- package/AI_USAGE/components/progress-api.md +44 -0
- package/AI_USAGE/components/progress.md +248 -3
- package/AI_USAGE/components/radio-api.md +165 -0
- package/AI_USAGE/components/radio.md +259 -3
- package/AI_USAGE/components/rate-api.md +54 -0
- package/AI_USAGE/components/rate.md +233 -3
- package/AI_USAGE/components/resize-box-api.md +56 -0
- package/AI_USAGE/components/resize-box.md +231 -0
- package/AI_USAGE/components/scrollbar-api.md +77 -0
- package/AI_USAGE/components/scrollbar.md +218 -0
- package/AI_USAGE/components/select-api.md +758 -0
- package/AI_USAGE/components/select.md +640 -3
- package/AI_USAGE/components/skeleton-api.md +85 -0
- package/AI_USAGE/components/skeleton.md +150 -0
- package/AI_USAGE/components/slider-api.md +51 -0
- package/AI_USAGE/components/slider.md +233 -3
- package/AI_USAGE/components/space-api.md +38 -0
- package/AI_USAGE/components/space.md +145 -0
- package/AI_USAGE/components/spin-api.md +43 -0
- package/AI_USAGE/components/spin.md +205 -0
- package/AI_USAGE/components/steps-api.md +111 -0
- package/AI_USAGE/components/steps.md +201 -3
- package/AI_USAGE/components/switch-api.md +58 -0
- package/AI_USAGE/components/switch.md +290 -3
- package/AI_USAGE/components/table-api.md +2919 -0
- package/AI_USAGE/components/table.md +1414 -0
- package/AI_USAGE/components/tabs-api.md +135 -0
- package/AI_USAGE/components/tabs.md +308 -3
- package/AI_USAGE/components/tag-api.md +124 -0
- package/AI_USAGE/components/tag.md +292 -0
- package/AI_USAGE/components/textarea-api.md +55 -0
- package/AI_USAGE/components/textarea.md +326 -3
- package/AI_USAGE/components/time-picker-api.md +137 -0
- package/AI_USAGE/components/time-picker.md +271 -3
- package/AI_USAGE/components/time-range-selector-api.md +44 -0
- package/AI_USAGE/components/time-range-selector.md +355 -0
- package/AI_USAGE/components/timeline-api.md +93 -0
- package/AI_USAGE/components/timeline.md +312 -0
- package/AI_USAGE/components/tooltip-api.md +77 -0
- package/AI_USAGE/components/tooltip.md +182 -0
- package/AI_USAGE/components/trend-chart-api.md +119 -0
- package/AI_USAGE/components/trend-chart.md +335 -0
- package/AI_USAGE/components/trigger-api.md +124 -0
- package/AI_USAGE/components/trigger.md +185 -0
- package/AI_USAGE/components/upload-api.md +200 -0
- package/AI_USAGE/components/upload.md +711 -3
- package/AI_USAGE/components/watermark-api.md +87 -0
- package/AI_USAGE/components/watermark.md +200 -0
- package/AI_USAGE/core.md +1 -1
- package/AI_USAGE/index.md +35 -7
- package/AI_USAGE/recipes/form-on-demand.md +1 -1
- package/AI_USAGE.md +33651 -5
- package/CHANGELOG.md +18 -0
- package/dist/_utils/inherit-component-type.d.ts +11 -0
- package/dist/components/date-picker/index.d.ts +420 -5
- package/dist/components/form-input/index.d.ts +617 -2
- package/dist/components/layout/index.d.ts +689 -1
- package/dist/components/menu/index.d.ts +786 -1
- package/dist/es/_utils/inherit-component-type.js +6 -0
- package/dist/es/components/date-picker/index.js +18 -17
- package/dist/es/components/form-input/index.js +12 -11
- package/dist/es/components/layout/index.js +10 -9
- package/dist/es/components/menu/hooks/use-level.js +5 -5
- package/dist/es/components/menu/index.js +13 -10
- package/dist/index.cjs +3 -3
- package/dist/index.umd.js +3 -3
- package/package.json +10 -5
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
<!-- Generated by pnpm generate:ai-usage. Edit script/ai-usage/content.md and script/ai-usage/. -->
|
|
2
|
+
# Form AI 使用
|
|
3
|
+
|
|
4
|
+
适用版本:x-next@1.14.0;来源指纹:`014984639d816a2c7e25eb22690c586b0b2e8efba6846bec8a4e424db41a09e9`。
|
|
5
|
+
|
|
6
|
+
先读 [通用接入](../core.md)。公开导出:`Form, FormItem`;主要模型:无。完整 Props、Events、Slots、实例方法与服务签名见 [本地 API](./form-api.md),包含本组件族全部子组件和别名。
|
|
7
|
+
|
|
8
|
+
按需样式:`x-next/style/form`(包含 base.css、form.css、grid.css、trigger.css、tooltip.css)。代码块未单独引样式时在应用入口加载全量样式,或按实际所有组件加载 JS 入口;不要仅因复制一个组件页就漏掉其中的 Button 等组合依赖。
|
|
9
|
+
|
|
10
|
+
## 场景用法
|
|
11
|
+
|
|
12
|
+
以下示例从本地 `src/docs/components/form.md` 显式导入到独立 AI 源,补齐局部注册、状态和样式后进行严格模板检查。上方已核验行为优先;原始 prop 默认值、包装透传和示例业务适配分别查 [完整 API](./form-api.md) 与场景说明。类型通过不代表所有散文和交互已验证。
|
|
13
|
+
|
|
14
|
+
### 用途与选型
|
|
15
|
+
|
|
16
|
+
承载数据录入、校验和提交,是业务系统里最核心的交互容器。
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### 何时使用
|
|
20
|
+
|
|
21
|
+
- 需要把多个输入控件组成一次提交,并统一尺寸、禁用和校验反馈。
|
|
22
|
+
- 需要必填、格式、数值范围或异步业务校验。
|
|
23
|
+
- 需要动态字段、嵌套数据、重置初始值或展示服务端错误。
|
|
24
|
+
|
|
25
|
+
`validate()` / `validateField()` 成功返回 `undefined`,失败返回字段错误对象。通过返回值判断结果,不使用 try/catch 判断业务校验失败。`resetFields()` 恢复字段挂载时的初始快照,`clearValidate()` 只清理状态。
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### 基础用法
|
|
29
|
+
|
|
30
|
+
```vue
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import 'x-next/style.css';
|
|
33
|
+
import { Form, FormItem, Input, Checkbox, Button } from 'x-next';
|
|
34
|
+
import { reactive } from 'vue';
|
|
35
|
+
|
|
36
|
+
const form = reactive({ name: '', post: '', isRead: false });
|
|
37
|
+
const handleSubmit = ({ values, errors }: { values: Record<string, unknown>; errors?: unknown }) => {
|
|
38
|
+
console.log(values, errors);
|
|
39
|
+
};
|
|
40
|
+
</script>
|
|
41
|
+
<template>
|
|
42
|
+
<Form :model="form" @submit="handleSubmit">
|
|
43
|
+
<FormItem field="name" tooltip="用于展示在业务列表中" label="名称" required>
|
|
44
|
+
<Input v-model="form.name" placeholder="请输入名称" />
|
|
45
|
+
</FormItem>
|
|
46
|
+
<FormItem field="post" label="职位">
|
|
47
|
+
<Input v-model="form.post" placeholder="请输入职位" />
|
|
48
|
+
</FormItem>
|
|
49
|
+
<FormItem field="isRead">
|
|
50
|
+
<Checkbox v-model="form.isRead">我已阅读并同意</Checkbox>
|
|
51
|
+
</FormItem>
|
|
52
|
+
<FormItem>
|
|
53
|
+
<Button type="primary" html-type="submit">提交</Button>
|
|
54
|
+
</FormItem>
|
|
55
|
+
</Form>
|
|
56
|
+
</template>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 布局
|
|
60
|
+
|
|
61
|
+
inline 布局的控件列默认宽度为 200px,并在空间不足时换行;可通过 `wrapperColStyle` 调整宽度。
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<script setup lang="ts">
|
|
65
|
+
import 'x-next/style.css';
|
|
66
|
+
import { RadioGroup, Radio, Form, FormItem, Input } from 'x-next';
|
|
67
|
+
import { reactive, ref } from 'vue';
|
|
68
|
+
|
|
69
|
+
const layout = ref<'horizontal' | 'vertical' | 'inline'>('horizontal');
|
|
70
|
+
const form = reactive({ name: '', post: '' });
|
|
71
|
+
</script>
|
|
72
|
+
<template>
|
|
73
|
+
<RadioGroup v-model="layout" type="button">
|
|
74
|
+
<Radio value="horizontal">horizontal</Radio>
|
|
75
|
+
<Radio value="vertical">vertical</Radio>
|
|
76
|
+
<Radio value="inline">inline</Radio>
|
|
77
|
+
</RadioGroup>
|
|
78
|
+
<Form :model="form" :layout="layout">
|
|
79
|
+
<FormItem field="name" label="用户名">
|
|
80
|
+
<Input v-model="form.name" />
|
|
81
|
+
</FormItem>
|
|
82
|
+
<FormItem field="post" label="岗位">
|
|
83
|
+
<Input v-model="form.post" />
|
|
84
|
+
</FormItem>
|
|
85
|
+
</Form>
|
|
86
|
+
</template>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 表单校验
|
|
90
|
+
|
|
91
|
+
```vue
|
|
92
|
+
<script setup lang="ts">
|
|
93
|
+
import 'x-next/style.css';
|
|
94
|
+
import { Form, FormItem, Input, InputNumber, Select, Button } from 'x-next';
|
|
95
|
+
import { reactive, ref } from 'vue';
|
|
96
|
+
|
|
97
|
+
const formRef = ref();
|
|
98
|
+
const form = reactive({ name: '', age: 18, section: '' });
|
|
99
|
+
const rules: Record<string, import('x-next').FieldRule[]> = {
|
|
100
|
+
name: [
|
|
101
|
+
{ required: true, message: '请输入用户名' },
|
|
102
|
+
{ minLength: 3, message: '用户名至少 3 个字符' },
|
|
103
|
+
],
|
|
104
|
+
age: [{ type: 'number', max: 120, message: '年龄不能超过 120' }],
|
|
105
|
+
section: [{ match: /section one/, message: '请选择 Section One' }],
|
|
106
|
+
};
|
|
107
|
+
const sectionOptions = [
|
|
108
|
+
{ label: 'Section One', value: 'section one' },
|
|
109
|
+
{ label: 'Section Two', value: 'section two' },
|
|
110
|
+
];
|
|
111
|
+
const handleSubmit = ({ values, errors }: { values: Record<string, unknown>; errors?: unknown }) => {
|
|
112
|
+
console.log(values, errors);
|
|
113
|
+
};
|
|
114
|
+
</script>
|
|
115
|
+
<template>
|
|
116
|
+
<Form ref="formRef" :model="form" :rules="rules" scroll-to-first-error @submit="handleSubmit">
|
|
117
|
+
<FormItem field="name" label="用户名" :validate-trigger="['change', 'input']">
|
|
118
|
+
<Input v-model="form.name" placeholder="至少 3 个字符" />
|
|
119
|
+
</FormItem>
|
|
120
|
+
<FormItem field="age" label="年龄">
|
|
121
|
+
<InputNumber v-model="form.age" :min="0" :max="200" />
|
|
122
|
+
</FormItem>
|
|
123
|
+
<FormItem field="section" label="部门">
|
|
124
|
+
<Select v-model="form.section" :options="sectionOptions" allow-clear />
|
|
125
|
+
</FormItem>
|
|
126
|
+
<FormItem>
|
|
127
|
+
<Button type="primary" html-type="submit">提交校验</Button>
|
|
128
|
+
<Button @click="formRef?.resetFields()">重置</Button>
|
|
129
|
+
</FormItem>
|
|
130
|
+
</Form>
|
|
131
|
+
</template>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 表单方法
|
|
135
|
+
|
|
136
|
+
```vue
|
|
137
|
+
<script setup lang="ts">
|
|
138
|
+
import 'x-next/style.css';
|
|
139
|
+
import { Form, FormItem, Input, Button } from 'x-next';
|
|
140
|
+
const status = ref<'validating' | 'success' | 'warning' | 'error'>('success');
|
|
141
|
+
|
|
142
|
+
import { reactive, ref } from 'vue';
|
|
143
|
+
|
|
144
|
+
const formRef = ref();
|
|
145
|
+
const form = reactive({ username: '', email: '' });
|
|
146
|
+
const rules: Record<string, import('x-next').FieldRule[]> = {
|
|
147
|
+
username: [{ required: true, message: '请输入用户名' }],
|
|
148
|
+
email: [{ type: 'email', message: '邮箱格式不正确' }],
|
|
149
|
+
};
|
|
150
|
+
const setServerError = () => {
|
|
151
|
+
formRef.value?.setFields({
|
|
152
|
+
email: { status: 'error', message: '服务端返回:邮箱已被占用' },
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
</script>
|
|
156
|
+
<template>
|
|
157
|
+
<Form ref="formRef" :model="form" :rules="rules">
|
|
158
|
+
<FormItem field="username" label="用户名">
|
|
159
|
+
<Input v-model="form.username" />
|
|
160
|
+
</FormItem>
|
|
161
|
+
<FormItem field="email" label="邮箱">
|
|
162
|
+
<Input v-model="form.email" />
|
|
163
|
+
</FormItem>
|
|
164
|
+
<FormItem>
|
|
165
|
+
<Button @click="formRef?.validate()">validate</Button>
|
|
166
|
+
<Button @click="setServerError">setFields</Button>
|
|
167
|
+
<Button @click="formRef?.clearValidate()">clearValidate</Button>
|
|
168
|
+
<Button @click="formRef?.resetFields()">resetFields</Button>
|
|
169
|
+
</FormItem>
|
|
170
|
+
</Form>
|
|
171
|
+
</template>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 状态与反馈
|
|
175
|
+
|
|
176
|
+
### 校验状态的视觉支持范围
|
|
177
|
+
|
|
178
|
+
校验失败时,所有控件都会在 FormItem 上展示错误文案与错误色(`x-form--item-error`);`feedback` 开关控制是否额外展示反馈图标。
|
|
179
|
+
|
|
180
|
+
控件自身的错误描边支持范围如下:
|
|
181
|
+
|
|
182
|
+
| 控件 | 控件级错误描边 | 反馈图标 |
|
|
183
|
+
| --- | --- | --- |
|
|
184
|
+
| Input | 支持 | 支持 |
|
|
185
|
+
| Select / InputTag / InputNumber | 支持 | 支持 |
|
|
186
|
+
| DatePicker / TimePicker(含范围形态) | 支持 | 支持 |
|
|
187
|
+
| Textarea | 支持 | 不支持(右下角为字数统计,无图标位) |
|
|
188
|
+
| Radio / RadioGroup / Checkbox / CheckboxGroup | 不支持(仅 FormItem 文案与错误色) | 不支持 |
|
|
189
|
+
| Slider / Switch | 不支持(仅 FormItem 文案与错误色) | 不支持 |
|
|
190
|
+
|
|
191
|
+
Radio、Checkbox、Slider、Switch 属于无输入边框的控件,参考 Arco Design Vue 的行为,不额外添加控件级错误描边,避免与"选中态"视觉混淆。Textarea 的右下角用于字数统计,同样不放置反馈图标。
|
|
192
|
+
|
|
193
|
+
```vue
|
|
194
|
+
<script setup lang="ts">
|
|
195
|
+
import 'x-next/style.css';
|
|
196
|
+
import { Form, FormItem, Input, InputTag, DatePicker, TimePicker } from 'x-next';
|
|
197
|
+
import { reactive, ref } from 'vue';
|
|
198
|
+
|
|
199
|
+
const status = ref<'validating' | 'success' | 'warning' | 'error'>('success');
|
|
200
|
+
const size = ref<'mini' | 'small' | 'medium' | 'large'>('medium');
|
|
201
|
+
const form = reactive({ name: '', tags: [], date: '2026-09-30', time: '09:30:00' });
|
|
202
|
+
</script>
|
|
203
|
+
<template>
|
|
204
|
+
<Form :model="form" :size="size">
|
|
205
|
+
<FormItem
|
|
206
|
+
field="name"
|
|
207
|
+
label="名称"
|
|
208
|
+
help="当前状态由 validateStatus 手动控制"
|
|
209
|
+
extra="开启 feedback 后展示反馈图标"
|
|
210
|
+
:validate-status="status"
|
|
211
|
+
feedback
|
|
212
|
+
>
|
|
213
|
+
<Input v-model="form.name" />
|
|
214
|
+
</FormItem>
|
|
215
|
+
<FormItem field="tags" label="标签" :validate-status="status" feedback>
|
|
216
|
+
<InputTag v-model="form.tags" />
|
|
217
|
+
</FormItem>
|
|
218
|
+
<FormItem field="date" label="日期" :validate-status="status" feedback>
|
|
219
|
+
<DatePicker v-model="form.date" allow-clear />
|
|
220
|
+
</FormItem>
|
|
221
|
+
<FormItem field="time" label="时间" :validate-status="status" feedback>
|
|
222
|
+
<TimePicker v-model="form.time" allow-clear />
|
|
223
|
+
</FormItem>
|
|
224
|
+
</Form>
|
|
225
|
+
</template>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 全局禁用
|
|
229
|
+
|
|
230
|
+
```vue
|
|
231
|
+
<script setup lang="ts">
|
|
232
|
+
import 'x-next/style.css';
|
|
233
|
+
import { Form, FormItem, Input, Checkbox } from 'x-next';
|
|
234
|
+
import { reactive } from 'vue';
|
|
235
|
+
|
|
236
|
+
const form = reactive({ name: 'Locked user', isRead: true });
|
|
237
|
+
</script>
|
|
238
|
+
<template>
|
|
239
|
+
<Form :model="form" disabled>
|
|
240
|
+
<FormItem field="name" label="用户名">
|
|
241
|
+
<Input v-model="form.name" />
|
|
242
|
+
</FormItem>
|
|
243
|
+
<FormItem field="isRead">
|
|
244
|
+
<Checkbox v-model="form.isRead">已确认</Checkbox>
|
|
245
|
+
</FormItem>
|
|
246
|
+
</Form>
|
|
247
|
+
</template>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 帮助信息和插槽
|
|
251
|
+
|
|
252
|
+
```vue
|
|
253
|
+
<script setup lang="ts">
|
|
254
|
+
import 'x-next/style.css';
|
|
255
|
+
import { Form, FormItem, Input } from 'x-next';
|
|
256
|
+
import { reactive } from 'vue';
|
|
257
|
+
|
|
258
|
+
const form = reactive({ name: '', post: '' });
|
|
259
|
+
</script>
|
|
260
|
+
<template>
|
|
261
|
+
<Form :model="form">
|
|
262
|
+
<FormItem field="name" required validate-trigger="input">
|
|
263
|
+
<template #label>
|
|
264
|
+
<span>登录名</span>
|
|
265
|
+
</template>
|
|
266
|
+
<Input v-model="form.name" />
|
|
267
|
+
<template #extra>
|
|
268
|
+
<span>用于后台账号展示</span>
|
|
269
|
+
</template>
|
|
270
|
+
</FormItem>
|
|
271
|
+
<FormItem field="post" label="岗位" required>
|
|
272
|
+
<Input v-model="form.post" />
|
|
273
|
+
<template #help>
|
|
274
|
+
<span>自定义帮助信息会展示在控件下方</span>
|
|
275
|
+
</template>
|
|
276
|
+
</FormItem>
|
|
277
|
+
</Form>
|
|
278
|
+
</template>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 嵌套字段
|
|
282
|
+
|
|
283
|
+
```vue
|
|
284
|
+
<script setup lang="ts">
|
|
285
|
+
import 'x-next/style.css';
|
|
286
|
+
import { Form, FormItem, Input, Select } from 'x-next';
|
|
287
|
+
import { reactive, ref } from 'vue';
|
|
288
|
+
|
|
289
|
+
const formRef = ref();
|
|
290
|
+
const form = reactive({
|
|
291
|
+
user: { name: '', email: '' },
|
|
292
|
+
members: ['pm'],
|
|
293
|
+
});
|
|
294
|
+
const rules: Record<string, import('x-next').FieldRule[]> = {
|
|
295
|
+
'user.name': [{ required: true, message: '请输入姓名' }],
|
|
296
|
+
'user.email': [{ type: 'email', message: '邮箱格式不正确' }],
|
|
297
|
+
members: [{ type: 'array', minLength: 2, message: '至少选择 2 个成员' }],
|
|
298
|
+
};
|
|
299
|
+
const memberOptions = [
|
|
300
|
+
{ label: '产品', value: 'pm' },
|
|
301
|
+
{ label: '设计', value: 'design' },
|
|
302
|
+
{ label: '前端', value: 'frontend' },
|
|
303
|
+
];
|
|
304
|
+
const handleSubmit = ({ values, errors }: { values: Record<string, unknown>; errors?: unknown }) => {
|
|
305
|
+
console.log(values, errors);
|
|
306
|
+
};
|
|
307
|
+
</script>
|
|
308
|
+
<template>
|
|
309
|
+
<Form ref="formRef" :model="form" :rules="rules" @submit="handleSubmit">
|
|
310
|
+
<FormItem field="user.name" label="姓名">
|
|
311
|
+
<Input v-model="form.user.name" />
|
|
312
|
+
</FormItem>
|
|
313
|
+
<FormItem field="user.email" label="邮箱">
|
|
314
|
+
<Input v-model="form.user.email" />
|
|
315
|
+
</FormItem>
|
|
316
|
+
<FormItem field="members" label="成员">
|
|
317
|
+
<Select v-model="form.members" :options="memberOptions" multiple />
|
|
318
|
+
</FormItem>
|
|
319
|
+
</Form>
|
|
320
|
+
</template>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 异步校验与过期结果
|
|
324
|
+
|
|
325
|
+
输入 `admin` 后失焦会得到占用提示;等待时继续修改并失焦,旧请求不会覆盖最新字段状态。也可以在请求完成前重置。
|
|
326
|
+
|
|
327
|
+
异步 validator 返回 Promise,通过 `addError(message)` 报告错误,再正常完成 Promise。不要把 reject 当作字段错误。`validating` 在等待期间显示;清理状态、重置、切换字段或卸载后,旧请求不会重新写入错误。每次 `validate()` 返回该次校验的结果;等待提交期间修改模型或字段结构会取消旧的提交事件,可重新提交新值。
|
|
328
|
+
|
|
329
|
+
```vue
|
|
330
|
+
<script setup lang="ts">
|
|
331
|
+
import 'x-next/style.css';
|
|
332
|
+
import { Form, FormItem, Input, Button } from 'x-next';
|
|
333
|
+
import { reactive, ref } from 'vue';
|
|
334
|
+
import type { FieldRule, FormInstance } from 'x-next';
|
|
335
|
+
const formRef = ref<FormInstance>();
|
|
336
|
+
const form = reactive({ username: '' });
|
|
337
|
+
const rules: Record<string, FieldRule[]> = {
|
|
338
|
+
username: [
|
|
339
|
+
{ required: true, message: '请输入用户名' },
|
|
340
|
+
{
|
|
341
|
+
validator: async (value, addError) => {
|
|
342
|
+
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
343
|
+
if (value === 'admin') addError('admin 已被占用,请换一个名称');
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
};
|
|
348
|
+
</script>
|
|
349
|
+
<template>
|
|
350
|
+
<Form ref="formRef" :model="form" :rules="rules">
|
|
351
|
+
<FormItem field="username" label="用户名" validate-trigger="blur" feedback>
|
|
352
|
+
<Input v-model="form.username" placeholder="尝试 admin" allow-clear />
|
|
353
|
+
</FormItem>
|
|
354
|
+
<FormItem>
|
|
355
|
+
<Button @click="formRef?.validateField('username')">校验当前字段</Button>
|
|
356
|
+
<Button @click="formRef?.resetFields()">重置</Button>
|
|
357
|
+
</FormItem>
|
|
358
|
+
</Form>
|
|
359
|
+
</template>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### noStyle 与动态字段
|
|
363
|
+
|
|
364
|
+
`noStyle` 子项参与校验,错误展示在外层 FormItem。通过 `v-if` 卸载会注销字段并清理其父级错误;模型中的值仍由业务保存。`v-show` 只隐藏 DOM,字段仍参与校验。
|
|
365
|
+
|
|
366
|
+
```vue
|
|
367
|
+
<script setup lang="ts">
|
|
368
|
+
import 'x-next/style.css';
|
|
369
|
+
import { Checkbox, Form, FormItem, Input, Button } from 'x-next';
|
|
370
|
+
const status = ref<'validating' | 'success' | 'warning' | 'error'>('success');
|
|
371
|
+
|
|
372
|
+
import { reactive, ref } from 'vue';
|
|
373
|
+
const visible = ref(true);
|
|
374
|
+
const formRef = ref();
|
|
375
|
+
const form = reactive({ email: '' });
|
|
376
|
+
const result = ref('等待校验');
|
|
377
|
+
const validate = async () => {
|
|
378
|
+
const errors = await formRef.value.validate();
|
|
379
|
+
result.value = errors ? '邮箱未填写' : '校验通过';
|
|
380
|
+
};
|
|
381
|
+
</script>
|
|
382
|
+
<template>
|
|
383
|
+
<Checkbox v-model="visible" @change="result = visible ? '字段已展开' : '字段已隐藏'">显示邮箱字段</Checkbox>
|
|
384
|
+
<Form ref="formRef" :model="form">
|
|
385
|
+
<FormItem label="联系方式">
|
|
386
|
+
<FormItem v-if="visible" field="email" no-style required>
|
|
387
|
+
<Input v-model="form.email" placeholder="先校验,再隐藏字段" />
|
|
388
|
+
</FormItem>
|
|
389
|
+
<span v-else>当前没有邮箱字段</span>
|
|
390
|
+
</FormItem>
|
|
391
|
+
<FormItem><Button @click="validate">校验</Button></FormItem>
|
|
392
|
+
</Form>
|
|
393
|
+
<p role="status">{{ result }}</p>
|
|
394
|
+
</template>
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### 自动标签、帮助与错误关联
|
|
398
|
+
|
|
399
|
+
单个原生输入自动获得稳定 ID,并与标签的 `for` 关联。多个输入、选择组、评分、滑块和时间区间通过 `aria-labelledby` 共享标签;`noStyle` 字段继承外层标签。帮助、额外说明和当前错误通过 `aria-describedby` 关联,校验失败时同步 `aria-invalid` 和 `aria-errormessage`,清除校验后恢复帮助说明。
|
|
400
|
+
|
|
401
|
+
隐藏标签的字段应显式提供 `aria-label`。自定义控件需要自行绑定原生 ID 和 ARIA 属性;自动关联覆盖 x-next 内置表单控件。
|
|
402
|
+
|
|
403
|
+
```vue
|
|
404
|
+
<script setup lang="ts">
|
|
405
|
+
import 'x-next/style.css';
|
|
406
|
+
import { Form, FormItem, Input, Textarea, Button } from 'x-next';
|
|
407
|
+
import { reactive, ref } from 'vue';
|
|
408
|
+
import type { FormInstance } from 'x-next';
|
|
409
|
+
const form = reactive({ name: '', note: '' });
|
|
410
|
+
const formRef = ref<FormInstance>();
|
|
411
|
+
const result = ref('点击姓名标签可聚焦输入框');
|
|
412
|
+
const validate = async () => {
|
|
413
|
+
result.value = (await formRef.value!.validate()) ? '请补全必填项' : '校验通过';
|
|
414
|
+
};
|
|
415
|
+
</script>
|
|
416
|
+
<template>
|
|
417
|
+
<Form ref="formRef" :model="form">
|
|
418
|
+
<FormItem field="name" label="姓名" required help="填写真实姓名" extra="用于联系和通知">
|
|
419
|
+
<Input v-model="form.name" />
|
|
420
|
+
</FormItem>
|
|
421
|
+
<FormItem field="note" label="备注" help="可选,支持多行">
|
|
422
|
+
<Textarea v-model="form.note" />
|
|
423
|
+
</FormItem>
|
|
424
|
+
<Button @click="validate">校验姓名</Button>
|
|
425
|
+
<p>{{ result }}</p>
|
|
426
|
+
</Form>
|
|
427
|
+
</template>
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### 手动标签与原生输入关联
|
|
431
|
+
|
|
432
|
+
显式 `labelAttrs.for`、原生 `id` 和 ARIA 名称会保留;额外 `aria-describedby` 与 Form 描述合并。重复渲染时请给自定义 ID 使用独立值。Form 的 `id` 前缀用于字段包装层及 `scrollToField`,与原生输入 ID 分开。
|
|
433
|
+
|
|
434
|
+
```vue
|
|
435
|
+
<script setup lang="ts">
|
|
436
|
+
import 'x-next/style.css';
|
|
437
|
+
import { Form, FormItem, Input } from 'x-next';
|
|
438
|
+
import { reactive } from 'vue';
|
|
439
|
+
const form = reactive({ name: '' });
|
|
440
|
+
import type { InputHTMLAttributes } from 'vue';
|
|
441
|
+
const nativeAttrs1 = { "id": "profile-name", "aria-required": "true", "aria-describedby": "profile-name-hint" } satisfies InputHTMLAttributes;
|
|
442
|
+
</script>
|
|
443
|
+
<template>
|
|
444
|
+
<Form :model="form">
|
|
445
|
+
<FormItem field="name" label="姓名" :label-attrs="{ for: 'profile-name' }" required>
|
|
446
|
+
<Input :input-attrs="nativeAttrs1" v-model="form.name" />
|
|
447
|
+
<template #extra><span id="profile-name-hint">请填写用于展示的姓名。</span></template>
|
|
448
|
+
</FormItem>
|
|
449
|
+
</Form>
|
|
450
|
+
</template>
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
返回 [AI 索引](../index.md)。先查本页与 [本地 API](./form-api.md);需要更深类型时查 [公开组件导航](../catalog.md),不默认上网查找本包用法。
|