feat(api-tree-select): add `api` options to tree-select
添加ApiTreeSelect组件
This commit is contained in:
parent
c1178027f0
commit
d81db890df
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
- **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能
|
- **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能
|
||||||
- **MixSider** 优化 Mix 菜单布局时 底部折叠按钮 的样式,与其它菜单布局时的风格保持一致
|
- **MixSider** 优化 Mix 菜单布局时 底部折叠按钮 的样式,与其它菜单布局时的风格保持一致
|
||||||
|
- **ApiTreeSelect** 扩展`antdv`的`TreeSelect`组件,支持远程数据源,用法类似`ApiSelect`
|
||||||
- 可以为不同的用户指定不同的后台首页:
|
- 可以为不同的用户指定不同的后台首页:
|
||||||
- 在`getUserInfo`接口返回的用户信息中增加`homePath`字段(可选)即可为当前用户定制首页路径
|
- 在`getUserInfo`接口返回的用户信息中增加`homePath`字段(可选)即可为当前用户定制首页路径
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { MockMethod } from 'vite-plugin-mock';
|
||||||
|
import { resultSuccess } from '../_util';
|
||||||
|
|
||||||
|
const demoTreeList = (keyword) => {
|
||||||
|
const result = {
|
||||||
|
list: [] as Recordable[],
|
||||||
|
};
|
||||||
|
for (let index = 0; index < 5; index++) {
|
||||||
|
const children: Recordable[] = [];
|
||||||
|
for (let j = 0; j < 3; j++) {
|
||||||
|
children.push({
|
||||||
|
title: `${keyword ?? ''}选项${index}-${j}`,
|
||||||
|
value: `${index}-${j}`,
|
||||||
|
key: `${index}-${j}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
result.list.push({
|
||||||
|
title: `${keyword ?? ''}选项${index}`,
|
||||||
|
value: `${index}`,
|
||||||
|
key: `${index}`,
|
||||||
|
children,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
url: '/basic-api/tree/getDemoOptions',
|
||||||
|
timeout: 1000,
|
||||||
|
method: 'get',
|
||||||
|
response: ({ query }) => {
|
||||||
|
const { keyword } = query;
|
||||||
|
console.log(keyword);
|
||||||
|
return resultSuccess(demoTreeList(keyword));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as MockMethod[];
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
TREE_OPTIONS_LIST = '/tree/getDemoOptions',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: Get sample options value
|
||||||
|
*/
|
||||||
|
export const treeOptionsListApi = (params?: Recordable) =>
|
||||||
|
defHttp.get<Recordable[]>({ url: Api.TREE_OPTIONS_LIST, params });
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
|
|
||||||
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 { 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';
|
||||||
|
|
@ -40,6 +41,7 @@ componentMap.set('AutoComplete', AutoComplete);
|
||||||
componentMap.set('Select', Select);
|
componentMap.set('Select', Select);
|
||||||
componentMap.set('ApiSelect', ApiSelect);
|
componentMap.set('ApiSelect', ApiSelect);
|
||||||
componentMap.set('TreeSelect', TreeSelect);
|
componentMap.set('TreeSelect', TreeSelect);
|
||||||
|
componentMap.set('ApiTreeSelect', ApiTreeSelect);
|
||||||
componentMap.set('Switch', Switch);
|
componentMap.set('Switch', Switch);
|
||||||
componentMap.set('RadioButtonGroup', RadioButtonGroup);
|
componentMap.set('RadioButtonGroup', RadioButtonGroup);
|
||||||
componentMap.set('RadioGroup', Radio.Group);
|
componentMap.set('RadioGroup', Radio.Group);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<template>
<a-tree-select v-bind="getAttrs">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot>
</template>
<template #suffixIcon v-if="loading">
<LoadingOutlined spin />
</template>
</a-tree-select>
</template>
<script lang="ts">
import { computed, defineComponent, watch, ref, onMounted, unref } from 'vue';
import { TreeSelect } from 'ant-design-vue';
import { isArray, isFunction } from '/@/utils/is';
import { get } from 'lodash-es';
import { propTypes } from '/@/utils/propTypes';
import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'ApiTreeSelect',
components: { ATreeSelect: TreeSelect, LoadingOutlined },
props: {
api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
params: { type: Object },
immediate: { type: Boolean, default: true },
resultField: propTypes.string.def(''),
},
emits: ['options-change'],
setup(props, { attrs, emit }) {
const treeData = ref<Recordable[]>([]);
const isFirstLoaded = ref<Boolean>(false);
const loading = ref(false);
const getAttrs = computed(() => {
return {
...(props.api ? { treeData: unref(treeData) } : {}),
...attrs,
};
});
watch([() => props.params, () => props.immediate], () => {
isFirstLoaded.value && fetch();
});
onMounted(() => {
props.immediate && fetch();
});
async function fetch() {
const { api } = props;
if (!api || !isFunction(api)) return;
loading.value = true;
treeData.value = [];
let result;
try {
result = await api(props.params);
} catch (e) {
console.error(e);
}
loading.value = false;
if (!result) return;
if (!isArray(result)) {
result = get(result, props.resultField);
}
treeData.value = (result as Recordable[]) || [];
isFirstLoaded.value = true;
emit('options-change', treeData.value);
}
return { getAttrs, loading };
},
});
</script>
|
||||||
|
|
@ -91,6 +91,7 @@ export type ComponentType =
|
||||||
| 'Select'
|
| 'Select'
|
||||||
| 'ApiSelect'
|
| 'ApiSelect'
|
||||||
| 'TreeSelect'
|
| 'TreeSelect'
|
||||||
|
| 'ApiTreeSelect'
|
||||||
| 'RadioButtonGroup'
|
| 'RadioButtonGroup'
|
||||||
| 'RadioGroup'
|
| 'RadioGroup'
|
||||||
| 'Checkbox'
|
| 'Checkbox'
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
|
|
||||||
import { optionsListApi } from '/@/api/demo/select';
|
import { optionsListApi } from '/@/api/demo/select';
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
import { treeOptionsListApi } from '/@/api/demo/tree';
|
||||||
|
|
||||||
const provincesOptions = [
|
const provincesOptions = [
|
||||||
{
|
{
|
||||||
|
|
@ -347,6 +348,20 @@
|
||||||
},
|
},
|
||||||
defaultValue: '0',
|
defaultValue: '0',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'field33',
|
||||||
|
component: 'ApiTreeSelect',
|
||||||
|
label: '远程下拉树',
|
||||||
|
helpMessage: ['ApiTreeSelect组件', '使用接口提供的数据生成选项'],
|
||||||
|
required: true,
|
||||||
|
componentProps: {
|
||||||
|
api: treeOptionsListApi,
|
||||||
|
resultField: 'list',
|
||||||
|
},
|
||||||
|
colProps: {
|
||||||
|
span: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'field20',
|
field: 'field20',
|
||||||
component: 'InputNumber',
|
component: 'InputNumber',
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,9 @@
|
||||||
editComponent: 'ApiSelect',
|
editComponent: 'ApiSelect',
|
||||||
editComponentProps: {
|
editComponentProps: {
|
||||||
api: optionsListApi,
|
api: optionsListApi,
|
||||||
|
resultField: 'list',
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'id',
|
||||||
},
|
},
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,10 @@
|
||||||
label: 'Option2',
|
label: 'Option2',
|
||||||
value: '2',
|
value: '2',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Option3',
|
||||||
|
value: '3',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
width: 200,
|
width: 200,
|
||||||
|
|
@ -91,6 +95,9 @@
|
||||||
editComponent: 'ApiSelect',
|
editComponent: 'ApiSelect',
|
||||||
editComponentProps: {
|
editComponentProps: {
|
||||||
api: optionsListApi,
|
api: optionsListApi,
|
||||||
|
resultField: 'list',
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'id',
|
||||||
},
|
},
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue