Commit 1d45617e4a311e339eb008a629cd342cd673ecf1
1 parent
3f78b5aa
refactor(form): enhanced form customization and dynamic capabilities
Showing
14 changed files
with
364 additions
and
39 deletions
CHANGELOG.zh_CN.md
@@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
4 | 4 | ||
5 | - 重构 hook,引入 `@vueuse`,删除其中已有的`hook`,优化现有的 hook | 5 | - 重构 hook,引入 `@vueuse`,删除其中已有的`hook`,优化现有的 hook |
6 | - `useEvent` 更名->`useEventListener` | 6 | - `useEvent` 更名->`useEventListener` |
7 | +- 表单`ComponentType`删除 `SelectOptGroup`,`SelectOption`,`Transfer`,`Radio`,四个类型。修改`RadioButtonGroup`组件 | ||
7 | 8 | ||
8 | ### ✨ Features | 9 | ### ✨ Features |
9 | 10 | ||
@@ -12,10 +13,15 @@ | @@ -12,10 +13,15 @@ | ||
12 | - 新增菜单及顶栏颜色选择配色 | 13 | - 新增菜单及顶栏颜色选择配色 |
13 | - 增加示例结果页 | 14 | - 增加示例结果页 |
14 | 15 | ||
16 | +### ⚡ Wip | ||
17 | + | ||
18 | +- 上传组件(未完成,测试中...) | ||
19 | + | ||
15 | ### ⚡ Performance Improvements | 20 | ### ⚡ Performance Improvements |
16 | 21 | ||
17 | - 优化 settingDrawer 代码 | 22 | - 优化 settingDrawer 代码 |
18 | - 优化多标签页切换速度 | 23 | - 优化多标签页切换速度 |
24 | +- 增加表单自定义及动态能力 | ||
19 | 25 | ||
20 | ### 🐛 Bug Fixes | 26 | ### 🐛 Bug Fixes |
21 | 27 | ||
@@ -23,6 +29,7 @@ | @@ -23,6 +29,7 @@ | ||
23 | - 修复登录过期后重新登录未跳转原来页面的 | 29 | - 修复登录过期后重新登录未跳转原来页面的 |
24 | - 修复 window 系统动态引入错误 | 30 | - 修复 window 系统动态引入错误 |
25 | - 修复页面类型错误 | 31 | - 修复页面类型错误 |
32 | +- 修复表单 switch 和 checkBox 单独使用报错 | ||
26 | 33 | ||
27 | ## 2.0.0-rc.9 (2020-11-9) | 34 | ## 2.0.0-rc.9 (2020-11-9) |
28 | 35 |
src/components/Form/src/BasicForm.vue
@@ -5,6 +5,7 @@ | @@ -5,6 +5,7 @@ | ||
5 | <template v-for="schema in getSchema" :key="schema.field"> | 5 | <template v-for="schema in getSchema" :key="schema.field"> |
6 | <FormItem | 6 | <FormItem |
7 | :tableAction="tableAction" | 7 | :tableAction="tableAction" |
8 | + :formActionType="formActionType" | ||
8 | :schema="schema" | 9 | :schema="schema" |
9 | :formProps="getProps" | 10 | :formProps="getProps" |
10 | :allDefaultValues="defaultValueRef" | 11 | :allDefaultValues="defaultValueRef" |
@@ -164,7 +165,7 @@ | @@ -164,7 +165,7 @@ | ||
164 | propsRef.value = mergeProps; | 165 | propsRef.value = mergeProps; |
165 | } | 166 | } |
166 | 167 | ||
167 | - const methods: Partial<FormActionType> = { | 168 | + const formActionType: Partial<FormActionType> = { |
168 | getFieldsValue, | 169 | getFieldsValue, |
169 | setFieldsValue, | 170 | setFieldsValue, |
170 | resetFields, | 171 | resetFields, |
@@ -179,7 +180,7 @@ | @@ -179,7 +180,7 @@ | ||
179 | 180 | ||
180 | onMounted(() => { | 181 | onMounted(() => { |
181 | initDefault(); | 182 | initDefault(); |
182 | - emit('register', methods); | 183 | + emit('register', formActionType); |
183 | }); | 184 | }); |
184 | 185 | ||
185 | return { | 186 | return { |
@@ -191,7 +192,8 @@ | @@ -191,7 +192,8 @@ | ||
191 | getProps, | 192 | getProps, |
192 | formElRef, | 193 | formElRef, |
193 | getSchema, | 194 | getSchema, |
194 | - ...methods, | 195 | + formActionType, |
196 | + ...formActionType, | ||
195 | }; | 197 | }; |
196 | }, | 198 | }, |
197 | }); | 199 | }); |
src/components/Form/src/FormItem.tsx
1 | import type { PropType } from 'vue'; | 1 | import type { PropType } from 'vue'; |
2 | -import type { FormProps } from './types/form'; | 2 | +import type { FormActionType, FormProps } from './types/form'; |
3 | import type { FormSchema } from './types/form'; | 3 | import type { FormSchema } from './types/form'; |
4 | import type { ValidationRule } from 'ant-design-vue/lib/form/Form'; | 4 | import type { ValidationRule } from 'ant-design-vue/lib/form/Form'; |
5 | import type { TableActionType } from '/@/components/Table'; | 5 | import type { TableActionType } from '/@/components/Table'; |
@@ -41,6 +41,9 @@ export default defineComponent({ | @@ -41,6 +41,9 @@ export default defineComponent({ | ||
41 | tableAction: { | 41 | tableAction: { |
42 | type: Object as PropType<TableActionType>, | 42 | type: Object as PropType<TableActionType>, |
43 | }, | 43 | }, |
44 | + formActionType: { | ||
45 | + type: Object as PropType<FormActionType>, | ||
46 | + }, | ||
44 | }, | 47 | }, |
45 | setup(props, { slots }) { | 48 | setup(props, { slots }) { |
46 | const itemLabelWidthRef = useItemLabelWidth(toRef(props, 'schema'), toRef(props, 'formProps')); | 49 | const itemLabelWidthRef = useItemLabelWidth(toRef(props, 'schema'), toRef(props, 'formProps')); |
@@ -61,12 +64,12 @@ export default defineComponent({ | @@ -61,12 +64,12 @@ export default defineComponent({ | ||
61 | }); | 64 | }); |
62 | 65 | ||
63 | const getComponentsPropsRef = computed(() => { | 66 | const getComponentsPropsRef = computed(() => { |
64 | - const { schema, tableAction, formModel } = props; | 67 | + const { schema, tableAction, formModel, formActionType } = props; |
65 | const { componentProps = {} } = schema; | 68 | const { componentProps = {} } = schema; |
66 | if (!isFunction(componentProps)) { | 69 | if (!isFunction(componentProps)) { |
67 | return componentProps; | 70 | return componentProps; |
68 | } | 71 | } |
69 | - return componentProps({ schema, tableAction, formModel }) || {}; | 72 | + return componentProps({ schema, tableAction, formModel, formActionType }) || {}; |
70 | }); | 73 | }); |
71 | 74 | ||
72 | const getDisableRef = computed(() => { | 75 | const getDisableRef = computed(() => { |
@@ -179,17 +182,27 @@ export default defineComponent({ | @@ -179,17 +182,27 @@ export default defineComponent({ | ||
179 | } | 182 | } |
180 | 183 | ||
181 | function renderComponent() { | 184 | function renderComponent() { |
182 | - const { renderComponentContent, component, field, changeEvent = 'change' } = props.schema; | 185 | + const { |
186 | + renderComponentContent, | ||
187 | + component, | ||
188 | + field, | ||
189 | + changeEvent = 'change', | ||
190 | + valueField, | ||
191 | + } = props.schema; | ||
183 | 192 | ||
184 | - const isCheck = component && ['Switch'].includes(component); | 193 | + const isCheck = component && ['Switch', 'Checkbox'].includes(component); |
185 | 194 | ||
186 | const eventKey = `on${upperFirst(changeEvent)}`; | 195 | const eventKey = `on${upperFirst(changeEvent)}`; |
196 | + | ||
187 | const on = { | 197 | const on = { |
188 | [eventKey]: (e: any) => { | 198 | [eventKey]: (e: any) => { |
189 | if (propsData[eventKey]) { | 199 | if (propsData[eventKey]) { |
190 | propsData[eventKey](e); | 200 | propsData[eventKey](e); |
191 | } | 201 | } |
192 | - (props.formModel as any)[field] = e && e.target ? e.target.value : e; | 202 | + |
203 | + const target = e ? e.target : null; | ||
204 | + const value = target ? (isCheck ? target.checked : target.value) : e; | ||
205 | + (props.formModel as any)[field] = value; | ||
193 | }, | 206 | }, |
194 | }; | 207 | }; |
195 | const Comp = componentMap.get(component); | 208 | const Comp = componentMap.get(component); |
@@ -215,17 +228,20 @@ export default defineComponent({ | @@ -215,17 +228,20 @@ export default defineComponent({ | ||
215 | propsData.codeField = field; | 228 | propsData.codeField = field; |
216 | propsData.formValues = unref(getValuesRef); | 229 | propsData.formValues = unref(getValuesRef); |
217 | const bindValue = { | 230 | const bindValue = { |
218 | - [isCheck ? 'checked' : 'value']: handleValue(component, field), | 231 | + [valueField || (isCheck ? 'checked' : 'value')]: handleValue(component, field), |
219 | }; | 232 | }; |
220 | if (!renderComponentContent) { | 233 | if (!renderComponentContent) { |
221 | return <Comp {...propsData} {...on} {...bindValue} />; | 234 | return <Comp {...propsData} {...on} {...bindValue} />; |
222 | } | 235 | } |
236 | + const compSlot = isFunction(renderComponentContent) | ||
237 | + ? { ...renderComponentContent(unref(getValuesRef)) } | ||
238 | + : { | ||
239 | + default: () => renderComponentContent, | ||
240 | + }; | ||
223 | 241 | ||
224 | return ( | 242 | return ( |
225 | <Comp {...propsData} {...on} {...bindValue}> | 243 | <Comp {...propsData} {...on} {...bindValue}> |
226 | - {{ | ||
227 | - ...renderComponentContent(unref(getValuesRef)), | ||
228 | - }} | 244 | + {compSlot} |
229 | </Comp> | 245 | </Comp> |
230 | ); | 246 | ); |
231 | } | 247 | } |
@@ -249,7 +265,7 @@ export default defineComponent({ | @@ -249,7 +265,7 @@ export default defineComponent({ | ||
249 | const { colon } = props.formProps; | 265 | const { colon } = props.formProps; |
250 | const getContent = () => { | 266 | const getContent = () => { |
251 | return slot | 267 | return slot |
252 | - ? getSlot(slots, slot) | 268 | + ? getSlot(slots, slot, unref(getValuesRef)) |
253 | : render | 269 | : render |
254 | ? render(unref(getValuesRef)) | 270 | ? render(unref(getValuesRef)) |
255 | : renderComponent(); | 271 | : renderComponent(); |
@@ -276,7 +292,7 @@ export default defineComponent({ | @@ -276,7 +292,7 @@ export default defineComponent({ | ||
276 | const { isIfShow, isShow } = getShow(); | 292 | const { isIfShow, isShow } = getShow(); |
277 | const getContent = () => { | 293 | const getContent = () => { |
278 | return colSlot | 294 | return colSlot |
279 | - ? getSlot(slots, colSlot) | 295 | + ? getSlot(slots, colSlot, unref(getValuesRef)) |
280 | : renderColContent | 296 | : renderColContent |
281 | ? renderColContent(unref(getValuesRef)) | 297 | ? renderColContent(unref(getValuesRef)) |
282 | : renderItem(); | 298 | : renderItem(); |
src/components/Form/src/componentMap.ts
@@ -14,8 +14,8 @@ import { | @@ -14,8 +14,8 @@ import { | ||
14 | Switch, | 14 | Switch, |
15 | TimePicker, | 15 | TimePicker, |
16 | TreeSelect, | 16 | TreeSelect, |
17 | - Transfer, | ||
18 | } from 'ant-design-vue'; | 17 | } from 'ant-design-vue'; |
18 | +import RadioButtonGroup from './components/RadioButtonGroup.vue'; | ||
19 | 19 | ||
20 | import { ComponentType } from './types/index'; | 20 | import { ComponentType } from './types/index'; |
21 | 21 | ||
@@ -30,13 +30,13 @@ componentMap.set('InputNumber', InputNumber); | @@ -30,13 +30,13 @@ componentMap.set('InputNumber', InputNumber); | ||
30 | componentMap.set('AutoComplete', AutoComplete); | 30 | componentMap.set('AutoComplete', AutoComplete); |
31 | 31 | ||
32 | componentMap.set('Select', Select); | 32 | componentMap.set('Select', Select); |
33 | -componentMap.set('SelectOptGroup', Select.OptGroup); | ||
34 | -componentMap.set('SelectOption', Select.Option); | 33 | +// componentMap.set('SelectOptGroup', Select.OptGroup); |
34 | +// componentMap.set('SelectOption', Select.Option); | ||
35 | componentMap.set('TreeSelect', TreeSelect); | 35 | componentMap.set('TreeSelect', TreeSelect); |
36 | -componentMap.set('Transfer', Transfer); | ||
37 | -componentMap.set('Radio', Radio); | 36 | +// componentMap.set('Transfer', Transfer); |
37 | +// componentMap.set('Radio', Radio); | ||
38 | componentMap.set('Switch', Switch); | 38 | componentMap.set('Switch', Switch); |
39 | -componentMap.set('RadioButton', Radio.Button); | 39 | +componentMap.set('RadioButtonGroup', RadioButtonGroup); |
40 | componentMap.set('RadioGroup', Radio.Group); | 40 | componentMap.set('RadioGroup', Radio.Group); |
41 | componentMap.set('Checkbox', Checkbox); | 41 | componentMap.set('Checkbox', Checkbox); |
42 | componentMap.set('CheckboxGroup', Checkbox.Group); | 42 | componentMap.set('CheckboxGroup', Checkbox.Group); |
src/components/Form/src/components/RadioButtonGroup.vue
0 → 100644
1 | +<template> | ||
2 | + <RadioGroup v-bind="$attrs" v-model:value="valueRef" button-style="solid"> | ||
3 | + <template v-for="item in getOptions" :key="`${item.value}`"> | ||
4 | + <RadioButton :value="item.value"> {{ item.label }} </RadioButton> | ||
5 | + </template> | ||
6 | + </RadioGroup> | ||
7 | +</template> | ||
8 | +<script lang="ts"> | ||
9 | + import { defineComponent, ref, PropType, watch, unref, computed } from 'vue'; | ||
10 | + import { Radio } from 'ant-design-vue'; | ||
11 | + import {} from 'ant-design-vue/es/radio/Group'; | ||
12 | + import { isString } from '/@/utils/is'; | ||
13 | + | ||
14 | + type OptionsItem = { label: string; value: string; disabled?: boolean }; | ||
15 | + type RadioItem = string | OptionsItem; | ||
16 | + export default defineComponent({ | ||
17 | + name: 'RadioButtonGroup', | ||
18 | + components: { | ||
19 | + RadioGroup: Radio.Group, | ||
20 | + RadioButton: Radio.Button, | ||
21 | + }, | ||
22 | + props: { | ||
23 | + value: { | ||
24 | + type: String as PropType<string>, | ||
25 | + }, | ||
26 | + options: { | ||
27 | + type: Array as PropType<RadioItem[]>, | ||
28 | + default: () => [], | ||
29 | + }, | ||
30 | + }, | ||
31 | + setup(props, { emit }) { | ||
32 | + const valueRef = ref(''); | ||
33 | + | ||
34 | + watch( | ||
35 | + () => props.value, | ||
36 | + (v = '') => { | ||
37 | + valueRef.value = v; | ||
38 | + }, | ||
39 | + { immediate: true } | ||
40 | + ); | ||
41 | + | ||
42 | + watch( | ||
43 | + () => unref(valueRef), | ||
44 | + () => { | ||
45 | + emit('change', valueRef.value); | ||
46 | + }, | ||
47 | + { immediate: true } | ||
48 | + ); | ||
49 | + | ||
50 | + const getOptions = computed((): OptionsItem[] => { | ||
51 | + const { options } = props; | ||
52 | + if (!options || options.length === 0) return []; | ||
53 | + const isStringArr = options.some((item) => isString(item)); | ||
54 | + if (!isStringArr) return options as OptionsItem[]; | ||
55 | + return options.map((item) => ({ label: item, value: item })) as OptionsItem[]; | ||
56 | + }); | ||
57 | + | ||
58 | + return { valueRef, getOptions }; | ||
59 | + }, | ||
60 | + }); | ||
61 | +</script> |
src/components/Form/src/types/form.ts
@@ -7,6 +7,10 @@ import { TableActionType } from '../../../Table/src/types/table'; | @@ -7,6 +7,10 @@ import { TableActionType } from '../../../Table/src/types/table'; | ||
7 | 7 | ||
8 | export type FieldMapToTime = [string, [string, string], string?][]; | 8 | export type FieldMapToTime = [string, [string, string], string?][]; |
9 | 9 | ||
10 | +export type Rule = RuleObject & { | ||
11 | + trigger?: 'blur' | 'change' | ['change', 'blur']; | ||
12 | +}; | ||
13 | + | ||
10 | export interface RenderCallbackParams { | 14 | export interface RenderCallbackParams { |
11 | schema: FormSchema; | 15 | schema: FormSchema; |
12 | values: any; | 16 | values: any; |
@@ -98,7 +102,10 @@ export interface FormProps { | @@ -98,7 +102,10 @@ export interface FormProps { | ||
98 | export interface FormSchema { | 102 | export interface FormSchema { |
99 | // 字段名 | 103 | // 字段名 |
100 | field: string; | 104 | field: string; |
105 | + // 内部值更改触发的事件名,默认 change | ||
101 | changeEvent?: string; | 106 | changeEvent?: string; |
107 | + // v-model绑定的变量名 默认 value | ||
108 | + valueField?: string; | ||
102 | // 标签名 | 109 | // 标签名 |
103 | label: string; | 110 | label: string; |
104 | // 文本右侧帮助文本 | 111 | // 文本右侧帮助文本 |
@@ -113,13 +120,18 @@ export interface FormSchema { | @@ -113,13 +120,18 @@ export interface FormSchema { | ||
113 | component: ComponentType; | 120 | component: ComponentType; |
114 | // 组件参数 | 121 | // 组件参数 |
115 | componentProps?: | 122 | componentProps?: |
116 | - | ((opt: { schema: FormSchema; tableAction: TableActionType; formModel: any }) => any) | 123 | + | ((opt: { |
124 | + schema: FormSchema; | ||
125 | + tableAction: TableActionType; | ||
126 | + formActionType: FormActionType; | ||
127 | + formModel: any; | ||
128 | + }) => any) | ||
117 | | object; | 129 | | object; |
118 | // 必填 | 130 | // 必填 |
119 | required?: boolean; | 131 | required?: boolean; |
120 | 132 | ||
121 | // 校验规则 | 133 | // 校验规则 |
122 | - rules?: RuleObject[]; | 134 | + rules?: Rule[]; |
123 | // 校验信息是否加入label | 135 | // 校验信息是否加入label |
124 | rulesMessageJoinLabel?: boolean; | 136 | rulesMessageJoinLabel?: boolean; |
125 | 137 | ||
@@ -146,7 +158,11 @@ export interface FormSchema { | @@ -146,7 +158,11 @@ export interface FormSchema { | ||
146 | // 渲染 col内容,需要外层包裹 form-item | 158 | // 渲染 col内容,需要外层包裹 form-item |
147 | renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string; | 159 | renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string; |
148 | 160 | ||
149 | - renderComponentContent?: (renderCallbackParams: RenderCallbackParams) => any; | 161 | + renderComponentContent?: |
162 | + | ((renderCallbackParams: RenderCallbackParams) => any) | ||
163 | + | VNode | ||
164 | + | VNode[] | ||
165 | + | string; | ||
150 | 166 | ||
151 | // 自定义slot, 在 from-item内 | 167 | // 自定义slot, 在 from-item内 |
152 | slot?: string; | 168 | slot?: string; |
@@ -156,7 +172,7 @@ export interface FormSchema { | @@ -156,7 +172,7 @@ export interface FormSchema { | ||
156 | 172 | ||
157 | dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean); | 173 | dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean); |
158 | 174 | ||
159 | - dynamicRules?: (renderCallbackParams: RenderCallbackParams) => RuleObject[]; | 175 | + dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[]; |
160 | } | 176 | } |
161 | export interface HelpComponentProps { | 177 | export interface HelpComponentProps { |
162 | maxWidth: string; | 178 | maxWidth: string; |
src/components/Form/src/types/index.ts
@@ -93,8 +93,8 @@ export type ComponentType = | @@ -93,8 +93,8 @@ export type ComponentType = | ||
93 | | 'SelectOption' | 93 | | 'SelectOption' |
94 | | 'TreeSelect' | 94 | | 'TreeSelect' |
95 | | 'Transfer' | 95 | | 'Transfer' |
96 | - | 'Radio' | ||
97 | - | 'RadioButton' | 96 | + // | 'Radio' |
97 | + | 'RadioButtonGroup' | ||
98 | | 'RadioGroup' | 98 | | 'RadioGroup' |
99 | | 'Checkbox' | 99 | | 'Checkbox' |
100 | | 'CheckboxGroup' | 100 | | 'CheckboxGroup' |
src/components/Table/src/types/table.ts
@@ -87,7 +87,7 @@ export interface GetColumnsParams { | @@ -87,7 +87,7 @@ export interface GetColumnsParams { | ||
87 | export type SizeType = 'default' | 'middle' | 'small' | 'large'; | 87 | export type SizeType = 'default' | 'middle' | 'small' | 'large'; |
88 | 88 | ||
89 | export interface TableActionType { | 89 | export interface TableActionType { |
90 | - reload: (opt?: FetchParams) => Promise<void>; | 90 | + // reload: (opt?: FetchParams) => Promise<void>; |
91 | getSelectRows: () => any[]; | 91 | getSelectRows: () => any[]; |
92 | clearSelectedRowKeys: () => void; | 92 | clearSelectedRowKeys: () => void; |
93 | getSelectRowKeys: () => string[]; | 93 | getSelectRowKeys: () => string[]; |
src/router/menus/modules/demo/comp.ts
@@ -38,10 +38,10 @@ const menu: MenuModule = { | @@ -38,10 +38,10 @@ const menu: MenuModule = { | ||
38 | path: 'strength-meter', | 38 | path: 'strength-meter', |
39 | name: '密码强度组件', | 39 | name: '密码强度组件', |
40 | }, | 40 | }, |
41 | - { | ||
42 | - path: 'upload', | ||
43 | - name: '上传组件', | ||
44 | - }, | 41 | + // { |
42 | + // path: 'upload', | ||
43 | + // name: '上传组件', | ||
44 | + // }, | ||
45 | { | 45 | { |
46 | path: 'scroll', | 46 | path: 'scroll', |
47 | name: '滚动组件', | 47 | name: '滚动组件', |
src/router/menus/modules/demo/form.ts
@@ -4,10 +4,18 @@ const menu: MenuModule = { | @@ -4,10 +4,18 @@ const menu: MenuModule = { | ||
4 | menu: { | 4 | menu: { |
5 | path: '/form', | 5 | path: '/form', |
6 | name: 'Form', | 6 | name: 'Form', |
7 | + tag: { | ||
8 | + type: 'warn', | ||
9 | + dot: true, | ||
10 | + }, | ||
7 | children: [ | 11 | children: [ |
8 | { | 12 | { |
9 | path: 'basic', | 13 | path: 'basic', |
10 | name: '基础表单', | 14 | name: '基础表单', |
15 | + tag: { | ||
16 | + type: 'warn', | ||
17 | + content: 'updated', | ||
18 | + }, | ||
11 | }, | 19 | }, |
12 | { | 20 | { |
13 | path: 'useForm', | 21 | path: 'useForm', |
@@ -24,14 +32,26 @@ const menu: MenuModule = { | @@ -24,14 +32,26 @@ const menu: MenuModule = { | ||
24 | { | 32 | { |
25 | path: 'ruleForm', | 33 | path: 'ruleForm', |
26 | name: '表单校验', | 34 | name: '表单校验', |
35 | + tag: { | ||
36 | + type: 'warn', | ||
37 | + content: 'updated', | ||
38 | + }, | ||
27 | }, | 39 | }, |
28 | { | 40 | { |
29 | path: 'dynamicForm', | 41 | path: 'dynamicForm', |
30 | name: '动态表单', | 42 | name: '动态表单', |
43 | + tag: { | ||
44 | + type: 'warn', | ||
45 | + content: 'updated', | ||
46 | + }, | ||
31 | }, | 47 | }, |
32 | { | 48 | { |
33 | path: 'customerForm', | 49 | path: 'customerForm', |
34 | name: '自定义组件', | 50 | name: '自定义组件', |
51 | + tag: { | ||
52 | + type: 'warn', | ||
53 | + content: 'updated', | ||
54 | + }, | ||
35 | }, | 55 | }, |
36 | ], | 56 | ], |
37 | }, | 57 | }, |
src/views/demo/form/CustomerForm.vue
1 | <template> | 1 | <template> |
2 | <div class="m-4"> | 2 | <div class="m-4"> |
3 | <CollapseContainer title="自定义表单"> | 3 | <CollapseContainer title="自定义表单"> |
4 | - <BasicForm @register="register" @submit="handleSubmit" /> | 4 | + <BasicForm @register="register" @submit="handleSubmit"> |
5 | + <template #f3="{ model, field }"> | ||
6 | + <a-input v-model:value="model[field]" placeholder="自定义slot" /> | ||
7 | + </template> | ||
8 | + </BasicForm> | ||
5 | </CollapseContainer> | 9 | </CollapseContainer> |
6 | </div> | 10 | </div> |
7 | </template> | 11 | </template> |
@@ -15,7 +19,7 @@ | @@ -15,7 +19,7 @@ | ||
15 | { | 19 | { |
16 | field: 'field1', | 20 | field: 'field1', |
17 | component: 'Input', | 21 | component: 'Input', |
18 | - label: '字段1', | 22 | + label: 'render方式', |
19 | colProps: { | 23 | colProps: { |
20 | span: 8, | 24 | span: 8, |
21 | }, | 25 | }, |
@@ -33,7 +37,7 @@ | @@ -33,7 +37,7 @@ | ||
33 | { | 37 | { |
34 | field: 'field2', | 38 | field: 'field2', |
35 | component: 'Input', | 39 | component: 'Input', |
36 | - label: '字段2', | 40 | + label: 'render组件slot', |
37 | colProps: { | 41 | colProps: { |
38 | span: 8, | 42 | span: 8, |
39 | }, | 43 | }, |
@@ -44,6 +48,16 @@ | @@ -44,6 +48,16 @@ | ||
44 | }; | 48 | }; |
45 | }, | 49 | }, |
46 | }, | 50 | }, |
51 | + { | ||
52 | + field: 'field3', | ||
53 | + component: 'Input', | ||
54 | + label: '自定义Slot', | ||
55 | + slot: 'f3', | ||
56 | + colProps: { | ||
57 | + span: 8, | ||
58 | + }, | ||
59 | + rules: [{ required: true }], | ||
60 | + }, | ||
47 | ]; | 61 | ]; |
48 | export default defineComponent({ | 62 | export default defineComponent({ |
49 | components: { BasicForm, CollapseContainer }, | 63 | components: { BasicForm, CollapseContainer }, |
src/views/demo/form/DynamicForm.vue
@@ -9,6 +9,10 @@ | @@ -9,6 +9,10 @@ | ||
9 | <CollapseContainer title="动态表单示例,动态根据表单内其他值改变"> | 9 | <CollapseContainer title="动态表单示例,动态根据表单内其他值改变"> |
10 | <BasicForm @register="register" /> | 10 | <BasicForm @register="register" /> |
11 | </CollapseContainer> | 11 | </CollapseContainer> |
12 | + | ||
13 | + <CollapseContainer class="mt-5" title="componentProps动态改变"> | ||
14 | + <BasicForm @register="register1" /> | ||
15 | + </CollapseContainer> | ||
12 | </div> | 16 | </div> |
13 | </template> | 17 | </template> |
14 | <script lang="ts"> | 18 | <script lang="ts"> |
@@ -120,6 +124,58 @@ | @@ -120,6 +124,58 @@ | ||
120 | }, | 124 | }, |
121 | ]; | 125 | ]; |
122 | 126 | ||
127 | + const schemas1: FormSchema[] = [ | ||
128 | + { | ||
129 | + field: 'f1', | ||
130 | + component: 'Input', | ||
131 | + label: 'F1', | ||
132 | + colProps: { | ||
133 | + span: 12, | ||
134 | + }, | ||
135 | + labelWidth: 200, | ||
136 | + componentProps: ({ formModel }) => { | ||
137 | + return { | ||
138 | + placeholder: '同步f2的值为f1', | ||
139 | + onChange: (e: ChangeEvent) => { | ||
140 | + formModel.f2 = e.target.value; | ||
141 | + }, | ||
142 | + }; | ||
143 | + }, | ||
144 | + }, | ||
145 | + { | ||
146 | + field: 'f2', | ||
147 | + component: 'Input', | ||
148 | + label: 'F2', | ||
149 | + colProps: { | ||
150 | + span: 12, | ||
151 | + }, | ||
152 | + labelWidth: 200, | ||
153 | + componentProps: { disabled: true }, | ||
154 | + }, | ||
155 | + { | ||
156 | + field: 'f3', | ||
157 | + component: 'Input', | ||
158 | + label: 'F3', | ||
159 | + colProps: { | ||
160 | + span: 12, | ||
161 | + }, | ||
162 | + labelWidth: 200, | ||
163 | + // @ts-ignore | ||
164 | + componentProps: ({ formActionType, tableAction }) => { | ||
165 | + return { | ||
166 | + placeholder: '值改变时执行查询,查看控制台', | ||
167 | + onChange: async () => { | ||
168 | + const { validate } = formActionType; | ||
169 | + // tableAction只适用于在表格内开启表单的例子 | ||
170 | + // const { reload } = tableAction; | ||
171 | + const res = await validate(); | ||
172 | + console.log(res); | ||
173 | + }, | ||
174 | + }; | ||
175 | + }, | ||
176 | + }, | ||
177 | + ]; | ||
178 | + | ||
123 | export default defineComponent({ | 179 | export default defineComponent({ |
124 | components: { BasicForm, CollapseContainer }, | 180 | components: { BasicForm, CollapseContainer }, |
125 | setup() { | 181 | setup() { |
@@ -133,6 +189,13 @@ | @@ -133,6 +189,13 @@ | ||
133 | span: 24, | 189 | span: 24, |
134 | }, | 190 | }, |
135 | }); | 191 | }); |
192 | + const [register1] = useForm({ | ||
193 | + labelWidth: 120, | ||
194 | + schemas: schemas1, | ||
195 | + actionColOptions: { | ||
196 | + span: 24, | ||
197 | + }, | ||
198 | + }); | ||
136 | function changeLabel3() { | 199 | function changeLabel3() { |
137 | updateSchema({ | 200 | updateSchema({ |
138 | field: 'field3', | 201 | field: 'field3', |
@@ -170,6 +233,7 @@ | @@ -170,6 +233,7 @@ | ||
170 | } | 233 | } |
171 | return { | 234 | return { |
172 | register, | 235 | register, |
236 | + register1, | ||
173 | schemas, | 237 | schemas, |
174 | setProps, | 238 | setProps, |
175 | changeLabel3, | 239 | changeLabel3, |
src/views/demo/form/RuleForm.vue
@@ -65,7 +65,33 @@ | @@ -65,7 +65,33 @@ | ||
65 | }, | 65 | }, |
66 | ], | 66 | ], |
67 | }, | 67 | }, |
68 | - rules: [{ required: true, message: '请输入aa' }], | 68 | + rules: [ |
69 | + { | ||
70 | + required: true, | ||
71 | + message: '请输入aa', | ||
72 | + }, | ||
73 | + ], | ||
74 | + }, | ||
75 | + { | ||
76 | + field: 'field44', | ||
77 | + component: 'Input', | ||
78 | + label: '自定义校验', | ||
79 | + colProps: { | ||
80 | + span: 8, | ||
81 | + }, | ||
82 | + rules: [ | ||
83 | + { | ||
84 | + required: true, | ||
85 | + // @ts-ignore | ||
86 | + validator: async (rule, value) => { | ||
87 | + if (value === '1') { | ||
88 | + return Promise.reject('值不能为1'); | ||
89 | + } | ||
90 | + return Promise.resolve(); | ||
91 | + }, | ||
92 | + trigger: 'blur', | ||
93 | + }, | ||
94 | + ], | ||
69 | }, | 95 | }, |
70 | { | 96 | { |
71 | field: 'field5', | 97 | field: 'field5', |
src/views/demo/form/index.vue
@@ -11,10 +11,11 @@ | @@ -11,10 +11,11 @@ | ||
11 | </div> | 11 | </div> |
12 | </template> | 12 | </template> |
13 | <script lang="ts"> | 13 | <script lang="ts"> |
14 | - import { defineComponent } from 'vue'; | 14 | + import { defineComponent, ref } from 'vue'; |
15 | import { BasicForm, FormSchema } from '/@/components/Form/index'; | 15 | import { BasicForm, FormSchema } from '/@/components/Form/index'; |
16 | import { CollapseContainer } from '/@/components/Container/index'; | 16 | import { CollapseContainer } from '/@/components/Container/index'; |
17 | import { useMessage } from '/@/hooks/web/useMessage'; | 17 | import { useMessage } from '/@/hooks/web/useMessage'; |
18 | + | ||
18 | const schemas: FormSchema[] = [ | 19 | const schemas: FormSchema[] = [ |
19 | { | 20 | { |
20 | field: 'field1', | 21 | field: 'field1', |
@@ -23,8 +24,11 @@ | @@ -23,8 +24,11 @@ | ||
23 | colProps: { | 24 | colProps: { |
24 | span: 8, | 25 | span: 8, |
25 | }, | 26 | }, |
26 | - defaultValue: '111', | ||
27 | - componentProps: () => { | 27 | + // componentProps:{}, |
28 | + // can func | ||
29 | + componentProps: ({ schema, formModel }) => { | ||
30 | + console.log('form:', schema); | ||
31 | + console.log('formModel:', formModel); | ||
28 | return { | 32 | return { |
29 | placeholder: '自定义placeholder', | 33 | placeholder: '自定义placeholder', |
30 | onChange: (e: any) => { | 34 | onChange: (e: any) => { |
@@ -32,14 +36,26 @@ | @@ -32,14 +36,26 @@ | ||
32 | }, | 36 | }, |
33 | }; | 37 | }; |
34 | }, | 38 | }, |
39 | + renderComponentContent: () => { | ||
40 | + return { | ||
41 | + prefix: () => 'pSlot', | ||
42 | + suffix: () => 'sSlot', | ||
43 | + }; | ||
44 | + }, | ||
35 | }, | 45 | }, |
36 | { | 46 | { |
37 | field: 'field2', | 47 | field: 'field2', |
38 | component: 'Input', | 48 | component: 'Input', |
39 | label: '字段2', | 49 | label: '字段2', |
50 | + defaultValue: '111', | ||
40 | colProps: { | 51 | colProps: { |
41 | span: 8, | 52 | span: 8, |
42 | }, | 53 | }, |
54 | + componentProps: { | ||
55 | + onChange: (e: any) => { | ||
56 | + console.log(e); | ||
57 | + }, | ||
58 | + }, | ||
43 | }, | 59 | }, |
44 | { | 60 | { |
45 | field: 'field3', | 61 | field: 'field3', |
@@ -111,17 +127,100 @@ | @@ -111,17 +127,100 @@ | ||
111 | ], | 127 | ], |
112 | }, | 128 | }, |
113 | }, | 129 | }, |
130 | + { | ||
131 | + field: 'field8', | ||
132 | + component: 'Checkbox', | ||
133 | + label: '字段8', | ||
134 | + colProps: { | ||
135 | + span: 8, | ||
136 | + }, | ||
137 | + renderComponentContent: 'Check', | ||
138 | + }, | ||
139 | + { | ||
140 | + field: 'field9', | ||
141 | + component: 'Switch', | ||
142 | + label: '字段9', | ||
143 | + colProps: { | ||
144 | + span: 8, | ||
145 | + }, | ||
146 | + }, | ||
147 | + { | ||
148 | + field: 'field10', | ||
149 | + component: 'RadioButtonGroup', | ||
150 | + label: '字段10', | ||
151 | + colProps: { | ||
152 | + span: 8, | ||
153 | + }, | ||
154 | + componentProps: { | ||
155 | + options: [ | ||
156 | + { | ||
157 | + label: '选项1', | ||
158 | + value: '1', | ||
159 | + }, | ||
160 | + { | ||
161 | + label: '选项2', | ||
162 | + value: '2', | ||
163 | + }, | ||
164 | + ], | ||
165 | + }, | ||
166 | + }, | ||
167 | + { | ||
168 | + field: 'field11', | ||
169 | + component: 'Cascader', | ||
170 | + label: '字段11', | ||
171 | + colProps: { | ||
172 | + span: 8, | ||
173 | + }, | ||
174 | + componentProps: { | ||
175 | + options: [ | ||
176 | + { | ||
177 | + value: 'zhejiang', | ||
178 | + label: 'Zhejiang', | ||
179 | + children: [ | ||
180 | + { | ||
181 | + value: 'hangzhou', | ||
182 | + label: 'Hangzhou', | ||
183 | + children: [ | ||
184 | + { | ||
185 | + value: 'xihu', | ||
186 | + label: 'West Lake', | ||
187 | + }, | ||
188 | + ], | ||
189 | + }, | ||
190 | + ], | ||
191 | + }, | ||
192 | + { | ||
193 | + value: 'jiangsu', | ||
194 | + label: 'Jiangsu', | ||
195 | + children: [ | ||
196 | + { | ||
197 | + value: 'nanjing', | ||
198 | + label: 'Nanjing', | ||
199 | + children: [ | ||
200 | + { | ||
201 | + value: 'zhonghuamen', | ||
202 | + label: 'Zhong Hua Men', | ||
203 | + }, | ||
204 | + ], | ||
205 | + }, | ||
206 | + ], | ||
207 | + }, | ||
208 | + ], | ||
209 | + }, | ||
210 | + }, | ||
114 | ]; | 211 | ]; |
115 | 212 | ||
116 | export default defineComponent({ | 213 | export default defineComponent({ |
117 | components: { BasicForm, CollapseContainer }, | 214 | components: { BasicForm, CollapseContainer }, |
118 | setup() { | 215 | setup() { |
216 | + const check = ref(null); | ||
119 | const { createMessage } = useMessage(); | 217 | const { createMessage } = useMessage(); |
120 | return { | 218 | return { |
121 | schemas, | 219 | schemas, |
122 | handleSubmit: (values: any) => { | 220 | handleSubmit: (values: any) => { |
123 | createMessage.success('click search,values:' + JSON.stringify(values)); | 221 | createMessage.success('click search,values:' + JSON.stringify(values)); |
124 | }, | 222 | }, |
223 | + check, | ||
125 | }; | 224 | }; |
126 | }, | 225 | }, |
127 | }); | 226 | }); |