Skip to content

Commit

Permalink
perf(form): improve the form function
Browse files Browse the repository at this point in the history
  • Loading branch information
anncwb committed Dec 27, 2020
1 parent 4ff1c40 commit ac1a369
Show file tree
Hide file tree
Showing 23 changed files with 344 additions and 100 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
- 新增主框架外页面示例
- `route.meta` 新增`currentActiveMenu`,`hideTab`,`hideMenu`参数 用于控制详情页面包屑级菜单显示隐藏。
- 新增面包屑导航示例
- form: 新增`suffix`属性,用于配置后缀内容
- form: 新增远程下拉`ApiSelect`及示例
- form: 新增`autoFocusFirstItem`配置。用于配置是否聚焦表单第一个输入框

### 🐛 Bug Fixes

Expand Down
24 changes: 24 additions & 0 deletions mock/demo/select-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { MockMethod } from 'vite-plugin-mock';
import { resultSuccess } from '../_util';

const demoList = (() => {
const result: any[] = [];
for (let index = 0; index < 20; index++) {
result.push({
label: `选项${index}`,
value: `${index}`,
});
}
return result;
})();

export default [
{
url: '/api/select/getDemoOptions',
timeout: 4000,
method: 'get',
response: ({ query }) => {
return resultSuccess(demoList);
},
},
] as MockMethod[];
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"dependencies": {
"@iconify/iconify": "^2.0.0-rc.4",
"@vueuse/core": "^4.0.0",
"@vueuse/core": "^4.0.1",
"ant-design-vue": "^2.0.0-rc.5",
"apexcharts": "^3.23.0",
"axios": "^0.21.1",
Expand All @@ -35,7 +35,7 @@
"path-to-regexp": "^6.2.0",
"qrcode": "^1.4.4",
"sortablejs": "^1.12.0",
"vditor": "^3.7.3",
"vditor": "^3.7.4",
"vue": "^3.0.4",
"vue-i18n": "9.0.0-beta.14",
"vue-router": "^4.0.1",
Expand All @@ -48,7 +48,7 @@
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@iconify/json": "^1.1.276",
"@iconify/json": "^1.1.277",
"@ls-lint/ls-lint": "^1.9.2",
"@purge-icons/generated": "^0.4.1",
"@types/echarts": "^4.9.3",
Expand Down
11 changes: 11 additions & 0 deletions src/api/demo/model/optionsModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { BasicFetchResult } from '/@/api/model/baseModel';

export interface DemoOptionsItem {
label: string;
value: string;
}

/**
* @description: Request list return value
*/
export type DemoOptionsGetResultModel = BasicFetchResult<DemoOptionsItem[]>;
16 changes: 16 additions & 0 deletions src/api/demo/select.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defHttp } from '/@/utils/http/axios';
import { DemoOptionsGetResultModel } from './model/optionsModel';

enum Api {
OPTIONS_LIST = '/select/getDemoOptions',
}

/**
* @description: Get sample options value
*/
export function optionsListApi() {
return defHttp.request<DemoOptionsGetResultModel>({
url: Api.OPTIONS_LIST,
method: 'GET',
});
}
70 changes: 66 additions & 4 deletions src/components/Form/src/BasicForm.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<Form v-bind="{ ...$attrs, ...$props }" ref="formElRef" :model="formModel">
<Row :class="getProps.compact ? 'compact-form-row' : ''" :style="getRowWrapStyle">
<Form v-bind="{ ...$attrs, ...$props }" :class="getFormClass" ref="formElRef" :model="formModel">
<Row :style="getRowWrapStyle">
<slot name="formHeader" />
<template v-for="schema in getSchema" :key="schema.field">
<FormItem
Expand All @@ -18,7 +18,6 @@
</FormItem>
</template>

<!-- -->
<FormAction
v-bind="{ ...getProps, ...advanceState }"
@toggle-advanced="handleToggleAdvanced"
Expand Down Expand Up @@ -46,8 +45,10 @@
import useAdvanced from './hooks/useAdvanced';
import { useFormEvents } from './hooks/useFormEvents';
import { createFormContext } from './hooks/useFormContext';
import { useAutoFocus } from './hooks/useAutoFocus';
import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'BasicForm',
Expand All @@ -71,13 +72,24 @@
const schemaRef = ref<Nullable<FormSchema[]>>(null);
const formElRef = ref<Nullable<FormActionType>>(null);
const { prefixCls } = useDesign('basic-form');
// Get the basic configuration of the form
const getProps = computed(
(): FormProps => {
return deepMerge(cloneDeep(props), unref(propsRef));
}
);
const getFormClass = computed(() => {
return [
prefixCls,
{
[`${prefixCls}--compact`]: unref(getProps).compact,
},
];
});
// Get uniform row style
const getRowWrapStyle = computed(
(): CSSProperties => {
Expand Down Expand Up @@ -115,7 +127,7 @@
defaultValueRef,
});
const { transformDateFunc, fieldMapToTime } = toRefs(props);
const { transformDateFunc, fieldMapToTime, autoFocusFirstItem } = toRefs(props);
const { handleFormValues, initDefault } = useFormValues({
transformDateFuncRef: transformDateFunc,
Expand All @@ -125,6 +137,13 @@
formModel,
});
useAutoFocus({
getSchema,
autoFocusFirstItem,
isInitedDefault: isInitedDefaultRef,
formElRef: formElRef as Ref<FormActionType>,
});
const {
handleSubmit,
setFieldsValue,
Expand Down Expand Up @@ -217,8 +236,51 @@
getSchema,
formActionType,
setFormModel,
prefixCls,
getFormClass,
...formActionType,
};
},
});
</script>
<style lang="less">
@import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-basic-form';
.@{prefix-cls} {
.ant-form-item {
&-label label::after {
margin: 0 6px 0 2px;
}
&-with-help {
margin-bottom: 0;
}
&:not(.ant-form-item-with-help) {
margin-bottom: 20px;
}
&.suffix-item {
.ant-form-item-children {
display: flex;
}
.suffix {
display: inline-block;
padding-left: 6px;
}
}
}
.ant-form-explain {
font-size: 14px;
}
&--compact {
.ant-form-item {
margin-bottom: 8px;
}
}
}
</style>
2 changes: 2 additions & 0 deletions src/components/Form/src/componentMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from 'ant-design-vue';

import RadioButtonGroup from './components/RadioButtonGroup.vue';
import ApiSelect from './components/ApiSelect.vue';
import { BasicUpload } from '/@/components/Upload';

const componentMap = new Map<ComponentType, Component>();
Expand All @@ -32,6 +33,7 @@ componentMap.set('InputNumber', InputNumber);
componentMap.set('AutoComplete', AutoComplete);

componentMap.set('Select', Select);
componentMap.set('ApiSelect', ApiSelect);
// componentMap.set('SelectOptGroup', Select.OptGroup);
// componentMap.set('SelectOption', Select.Option);
componentMap.set('TreeSelect', TreeSelect);
Expand Down
89 changes: 89 additions & 0 deletions src/components/Form/src/components/ApiSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<template>
<Select v-bind="attrs" :options="options" v-model:value="state">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data" />
</template>
<template #suffixIcon v-if="loading">
<LoadingOutlined spin />
</template>
<template #notFoundContent v-if="loading">
<span>
<LoadingOutlined spin class="mr-1" />
{{ t('component.form.apiSelectNotFound') }}
</span>
</template>
</Select>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, watchEffect } from 'vue';
import { Select } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { get } from 'lodash-es';
import { LoadingOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
type OptionsItem = { label: string; value: string; disabled?: boolean };
export default defineComponent({
name: 'RadioButtonGroup',
components: {
Select,
LoadingOutlined,
},
props: {
value: {
type: String as PropType<string>,
},
api: {
type: Function as PropType<(arg: Recordable) => Promise<OptionsItem[]>>,
default: null,
},
params: {
type: Object as PropType<Recordable>,
default: () => {},
},
resultField: {
type: String as PropType<string>,
default: '',
},
},
setup(props) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
const attrs = useAttrs();
const { t } = useI18n();
// Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props);
watchEffect(() => {
fetch();
});
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) return;
try {
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
}
}
return { state, attrs, options, loading, t };
},
});
</script>
Loading

0 comments on commit ac1a369

Please sign in to comment.