mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 16:46:19 +08:00
feat(ApiCascader): add asynchronous cascader component (#1321)
This commit is contained in:
325
mock/demo/api-cascader.ts
Normal file
325
mock/demo/api-cascader.ts
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
import { MockMethod } from 'vite-plugin-mock';
|
||||||
|
import { resultSuccess } from '../_util';
|
||||||
|
|
||||||
|
const areaList: any[] = [
|
||||||
|
{
|
||||||
|
id: '530825900854620160',
|
||||||
|
code: '430000',
|
||||||
|
parentCode: '100000',
|
||||||
|
levelType: 1,
|
||||||
|
name: '湖南省',
|
||||||
|
province: '湖南省',
|
||||||
|
city: null,
|
||||||
|
district: null,
|
||||||
|
town: null,
|
||||||
|
village: null,
|
||||||
|
parentPath: '430000',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 16:33:42',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530825900883980288',
|
||||||
|
code: '430100',
|
||||||
|
parentCode: '430000',
|
||||||
|
levelType: 2,
|
||||||
|
name: '长沙市',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: null,
|
||||||
|
town: null,
|
||||||
|
village: null,
|
||||||
|
parentPath: '430000,430100',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 16:33:42',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530825900951089152',
|
||||||
|
code: '430102',
|
||||||
|
parentCode: '430100',
|
||||||
|
levelType: 3,
|
||||||
|
name: '芙蓉区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '芙蓉区',
|
||||||
|
town: null,
|
||||||
|
village: null,
|
||||||
|
parentPath: '430000,430100,430102',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 16:33:42',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530825901014003712',
|
||||||
|
code: '430104',
|
||||||
|
parentCode: '430100',
|
||||||
|
levelType: 3,
|
||||||
|
name: '岳麓区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '岳麓区',
|
||||||
|
town: null,
|
||||||
|
village: null,
|
||||||
|
parentPath: '430000,430100,430104',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 16:33:42',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530825900988837888',
|
||||||
|
code: '430103',
|
||||||
|
parentCode: '430100',
|
||||||
|
levelType: 3,
|
||||||
|
name: '天心区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: null,
|
||||||
|
village: null,
|
||||||
|
parentPath: '430000,430100,430103',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 16:33:42',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530826672489115648',
|
||||||
|
code: '430103002',
|
||||||
|
parentCode: '430103',
|
||||||
|
levelType: 4,
|
||||||
|
name: '坡子街街道',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: null,
|
||||||
|
parentPath: '430000,430100,430103,430103002',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-12-14 15:26:43',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241171607552',
|
||||||
|
code: '430103002001',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '八角亭社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '八角亭社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002001',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2021-01-20 14:07:23',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241200967680',
|
||||||
|
code: '430103002002',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '西牌楼社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '西牌楼社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002002',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241230327808',
|
||||||
|
code: '430103002003',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '太平街社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '太平街社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002003',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241259687936',
|
||||||
|
code: '430103002005',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '坡子街社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '坡子街社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002005',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241284853760',
|
||||||
|
code: '430103002006',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '青山祠社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '青山祠社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002006',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241310019584',
|
||||||
|
code: '430103002007',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '沙河社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '沙河社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002007',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241381322752',
|
||||||
|
code: '430103002008',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '碧湘社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '碧湘社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002008',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241410682880',
|
||||||
|
code: '430103002009',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '创远社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '创远社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002009',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241431654400',
|
||||||
|
code: '430103002010',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '楚湘社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '楚湘社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002010',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241465208832',
|
||||||
|
code: '430103002011',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '西湖社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '西湖社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002011',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241502957568',
|
||||||
|
code: '430103002012',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '登仁桥社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '登仁桥社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002012',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '530840241553289216',
|
||||||
|
code: '430103002013',
|
||||||
|
parentCode: '430103002',
|
||||||
|
levelType: 5,
|
||||||
|
name: '文庙坪社区',
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '天心区',
|
||||||
|
town: '坡子街街道',
|
||||||
|
village: '文庙坪社区',
|
||||||
|
parentPath: '430000,430100,430103,430103002,430103002013',
|
||||||
|
createTime: '2020-11-30 15:47:31',
|
||||||
|
updateTime: '2020-11-30 17:30:41',
|
||||||
|
customized: false,
|
||||||
|
usable: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
url: '/basic-api/cascader/getAreaRecord',
|
||||||
|
timeout: 1000,
|
||||||
|
method: 'post',
|
||||||
|
response: ({ body }) => {
|
||||||
|
const { parentCode } = body || {};
|
||||||
|
if (!parentCode) {
|
||||||
|
return resultSuccess(areaList.filter((it) => it.code === '430000'));
|
||||||
|
}
|
||||||
|
return resultSuccess(areaList.filter((it) => it.parentCode === parentCode));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as MockMethod[];
|
9
src/api/demo/cascader.ts
Normal file
9
src/api/demo/cascader.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { AreaModel, AreaParams } from '/@/api/demo/model/areaModel';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
AREA_RECORD = '/cascader/getAreaRecord',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const areaRecord = (data: AreaParams) =>
|
||||||
|
defHttp.post<AreaModel>({ url: Api.AREA_RECORD, data });
|
12
src/api/demo/model/areaModel.ts
Normal file
12
src/api/demo/model/areaModel.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export interface AreaModel {
|
||||||
|
id: string;
|
||||||
|
code: string;
|
||||||
|
parentCode: string;
|
||||||
|
name: string;
|
||||||
|
levelType: number;
|
||||||
|
[key: string]: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AreaParams {
|
||||||
|
parentCode: string;
|
||||||
|
}
|
@@ -10,5 +10,6 @@ export { default as ApiSelect } from './src/components/ApiSelect.vue';
|
|||||||
export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
|
export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
|
||||||
export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
|
export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
|
||||||
export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
|
export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
|
||||||
|
export { default as ApiCascader } from './src/components/ApiCascader.vue';
|
||||||
|
|
||||||
export { BasicForm };
|
export { BasicForm };
|
||||||
|
@@ -25,6 +25,7 @@ import ApiRadioGroup from './components/ApiRadioGroup.vue';
|
|||||||
import RadioButtonGroup from './components/RadioButtonGroup.vue';
|
import RadioButtonGroup from './components/RadioButtonGroup.vue';
|
||||||
import ApiSelect from './components/ApiSelect.vue';
|
import ApiSelect from './components/ApiSelect.vue';
|
||||||
import ApiTreeSelect from './components/ApiTreeSelect.vue';
|
import ApiTreeSelect from './components/ApiTreeSelect.vue';
|
||||||
|
import ApiCascader from './components/ApiCascader.vue';
|
||||||
import { BasicUpload } from '/@/components/Upload';
|
import { BasicUpload } from '/@/components/Upload';
|
||||||
import { StrengthMeter } from '/@/components/StrengthMeter';
|
import { StrengthMeter } from '/@/components/StrengthMeter';
|
||||||
import { IconPicker } from '/@/components/Icon';
|
import { IconPicker } from '/@/components/Icon';
|
||||||
@@ -50,6 +51,7 @@ componentMap.set('RadioButtonGroup', RadioButtonGroup);
|
|||||||
componentMap.set('RadioGroup', Radio.Group);
|
componentMap.set('RadioGroup', Radio.Group);
|
||||||
componentMap.set('Checkbox', Checkbox);
|
componentMap.set('Checkbox', Checkbox);
|
||||||
componentMap.set('CheckboxGroup', Checkbox.Group);
|
componentMap.set('CheckboxGroup', Checkbox.Group);
|
||||||
|
componentMap.set('ApiCascader', ApiCascader);
|
||||||
componentMap.set('Cascader', Cascader);
|
componentMap.set('Cascader', Cascader);
|
||||||
componentMap.set('Slider', Slider);
|
componentMap.set('Slider', Slider);
|
||||||
componentMap.set('Rate', Rate);
|
componentMap.set('Rate', Rate);
|
||||||
|
197
src/components/Form/src/components/ApiCascader.vue
Normal file
197
src/components/Form/src/components/ApiCascader.vue
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
<template>
|
||||||
|
<a-cascader
|
||||||
|
v-model:value="state"
|
||||||
|
:options="options"
|
||||||
|
:load-data="loadData"
|
||||||
|
change-on-select
|
||||||
|
@change="handleChange"
|
||||||
|
:displayRender="handleRenderDisplay"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</a-cascader>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType, ref, unref, watch, watchEffect } from 'vue';
|
||||||
|
import { Cascader } from 'ant-design-vue';
|
||||||
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
import { isFunction } from '/@/utils/is';
|
||||||
|
import { get, omit } from 'lodash-es';
|
||||||
|
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
loading?: boolean;
|
||||||
|
isLeaf?: boolean;
|
||||||
|
children?: Option[];
|
||||||
|
}
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ApiCascader',
|
||||||
|
components: {
|
||||||
|
LoadingOutlined,
|
||||||
|
[Cascader.name]: Cascader,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
type: Function as PropType<(arg?: Recordable) => Promise<Option[]>>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
numberToString: propTypes.bool,
|
||||||
|
resultField: propTypes.string.def(''),
|
||||||
|
labelField: propTypes.string.def('label'),
|
||||||
|
valueField: propTypes.string.def('value'),
|
||||||
|
childrenField: propTypes.string.def('children'),
|
||||||
|
asyncFetchParamKey: propTypes.string.def('parentCode'),
|
||||||
|
immediate: propTypes.bool.def(true),
|
||||||
|
// init fetch params
|
||||||
|
initFetchParams: {
|
||||||
|
type: Object as PropType<Recordable>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
// 是否有下级,默认是
|
||||||
|
isLeaf: {
|
||||||
|
type: Function as PropType<(arg: Recordable) => boolean>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
displayRenderArray: {
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['change', 'defaultChange'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const apiData = ref<any[]>([]);
|
||||||
|
const options = ref<Option[]>([]);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
const emitData = ref<any[]>([]);
|
||||||
|
const isFirstLoad = ref(true);
|
||||||
|
|
||||||
|
// Embedded in the form, just use the hook binding to perform form verification
|
||||||
|
const [state] = useRuleFormItem(props, 'value', 'change', emitData);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
apiData,
|
||||||
|
(data) => {
|
||||||
|
const opts = generatorOptions(data);
|
||||||
|
options.value = opts;
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
function generatorOptions(options: any[]): Option[] {
|
||||||
|
const { labelField, valueField, numberToString, childrenField, isLeaf } = props;
|
||||||
|
return options.reduce((prev, next: Recordable) => {
|
||||||
|
if (next) {
|
||||||
|
const value = next[valueField];
|
||||||
|
const item = {
|
||||||
|
...omit(next, [labelField, valueField]),
|
||||||
|
label: next[labelField],
|
||||||
|
value: numberToString ? `${value}` : value,
|
||||||
|
isLeaf: isLeaf && typeof isLeaf === 'function' ? isLeaf(next) : false,
|
||||||
|
};
|
||||||
|
const children = Reflect.get(next, childrenField);
|
||||||
|
if (children) {
|
||||||
|
Reflect.set(item, childrenField, generatorOptions(children));
|
||||||
|
}
|
||||||
|
prev.push(item);
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, [] as Option[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initialFetch() {
|
||||||
|
const api = props.api;
|
||||||
|
if (!api || !isFunction(api)) return;
|
||||||
|
apiData.value = [];
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await api(props.initFetchParams);
|
||||||
|
if (Array.isArray(res)) {
|
||||||
|
apiData.value = res;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (props.resultField) {
|
||||||
|
apiData.value = get(res, props.resultField) || [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadData(selectedOptions: Option[]) {
|
||||||
|
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||||
|
targetOption.loading = true;
|
||||||
|
|
||||||
|
const api = props.api;
|
||||||
|
if (!api || !isFunction(api)) return;
|
||||||
|
try {
|
||||||
|
const res = await api({
|
||||||
|
[props.asyncFetchParamKey]: Reflect.get(targetOption, 'value'),
|
||||||
|
});
|
||||||
|
if (Array.isArray(res)) {
|
||||||
|
const children = generatorOptions(res);
|
||||||
|
targetOption.children = children;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (props.resultField) {
|
||||||
|
const children = generatorOptions(get(res, props.resultField) || []);
|
||||||
|
targetOption.children = children;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
} finally {
|
||||||
|
targetOption.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
props.immediate && initialFetch();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.initFetchParams,
|
||||||
|
() => {
|
||||||
|
!unref(isFirstLoad) && initialFetch();
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleChange(keys, args) {
|
||||||
|
emitData.value = keys;
|
||||||
|
emit('defaultChange', keys, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRenderDisplay({ labels, selectedOptions }) {
|
||||||
|
if (unref(emitData).length === selectedOptions.length) {
|
||||||
|
return labels.join(' / ');
|
||||||
|
}
|
||||||
|
if (props.displayRenderArray) {
|
||||||
|
return props.displayRenderArray.join(' / ');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
options,
|
||||||
|
loading,
|
||||||
|
handleChange,
|
||||||
|
loadData,
|
||||||
|
handleRenderDisplay,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@@ -98,6 +98,7 @@ export type ComponentType =
|
|||||||
| 'Checkbox'
|
| 'Checkbox'
|
||||||
| 'CheckboxGroup'
|
| 'CheckboxGroup'
|
||||||
| 'AutoComplete'
|
| 'AutoComplete'
|
||||||
|
| 'ApiCascader'
|
||||||
| 'Cascader'
|
| 'Cascader'
|
||||||
| 'DatePicker'
|
| 'DatePicker'
|
||||||
| 'MonthPicker'
|
| 'MonthPicker'
|
||||||
|
@@ -52,6 +52,7 @@
|
|||||||
>
|
>
|
||||||
修改查询按钮
|
修改查询按钮
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button @click="handleLoad" class="mr-2"> 联动回显 </a-button>
|
||||||
</div>
|
</div>
|
||||||
<CollapseContainer title="useForm示例">
|
<CollapseContainer title="useForm示例">
|
||||||
<BasicForm @register="register" @submit="handleSubmit" />
|
<BasicForm @register="register" @submit="handleSubmit" />
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
import { CollapseContainer } from '/@/components/Container/index';
|
import { CollapseContainer } from '/@/components/Container/index';
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import { PageWrapper } from '/@/components/Page';
|
import { PageWrapper } from '/@/components/Page';
|
||||||
|
import { areaRecord } from '/@/api/demo/cascader';
|
||||||
|
|
||||||
const schemas: FormSchema[] = [
|
const schemas: FormSchema[] = [
|
||||||
{
|
{
|
||||||
@@ -166,6 +168,48 @@
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'field8',
|
||||||
|
component: 'ApiCascader',
|
||||||
|
label: '联动',
|
||||||
|
colProps: {
|
||||||
|
span: 8,
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
api: areaRecord,
|
||||||
|
apiParamKey: 'parentCode',
|
||||||
|
dataField: 'data',
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'code',
|
||||||
|
initFetchParams: {
|
||||||
|
parentCode: '',
|
||||||
|
},
|
||||||
|
isLeaf: (record) => {
|
||||||
|
return !(record.levelType < 3);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'field9',
|
||||||
|
component: 'ApiCascader',
|
||||||
|
label: '联动回显',
|
||||||
|
colProps: {
|
||||||
|
span: 8,
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
api: areaRecord,
|
||||||
|
apiParamKey: 'parentCode',
|
||||||
|
dataField: 'data',
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'code',
|
||||||
|
initFetchParams: {
|
||||||
|
parentCode: '',
|
||||||
|
},
|
||||||
|
isLeaf: (record) => {
|
||||||
|
return !(record.levelType < 3);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -173,7 +217,7 @@
|
|||||||
setup() {
|
setup() {
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
|
|
||||||
const [register, { setProps }] = useForm({
|
const [register, { setProps, setFieldsValue, updateSchema }] = useForm({
|
||||||
labelWidth: 120,
|
labelWidth: 120,
|
||||||
schemas,
|
schemas,
|
||||||
actionColOptions: {
|
actionColOptions: {
|
||||||
@@ -181,6 +225,35 @@
|
|||||||
},
|
},
|
||||||
fieldMapToTime: [['fieldTime', ['startTime', 'endTime'], 'YYYY-MM']],
|
fieldMapToTime: [['fieldTime', ['startTime', 'endTime'], 'YYYY-MM']],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function handleLoad() {
|
||||||
|
const promiseFn = function () {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({
|
||||||
|
field9: ['430000', '430100', '430102'],
|
||||||
|
province: '湖南省',
|
||||||
|
city: '长沙市',
|
||||||
|
district: '岳麓区',
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const item = await promiseFn();
|
||||||
|
|
||||||
|
const { field9, province, city, district } = item as any;
|
||||||
|
await updateSchema({
|
||||||
|
field: 'field9',
|
||||||
|
componentProps: {
|
||||||
|
displayRenderArray: [province, city, district],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await setFieldsValue({
|
||||||
|
field9,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
register,
|
register,
|
||||||
schemas,
|
schemas,
|
||||||
@@ -188,6 +261,7 @@
|
|||||||
createMessage.success('click search,values:' + JSON.stringify(values));
|
createMessage.success('click search,values:' + JSON.stringify(values));
|
||||||
},
|
},
|
||||||
setProps,
|
setProps,
|
||||||
|
handleLoad,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user