Commit d81db890dfeb533d60f378ddb86f8ac50a31252b

Authored by 无木
1 parent c1178027

feat(api-tree-select): add `api` options to tree-select

添加ApiTreeSelect组件
CHANGELOG.zh_CN.md
... ... @@ -2,6 +2,7 @@
2 2  
3 3 - **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能
4 4 - **MixSider** 优化 Mix 菜单布局时 底部折叠按钮 的样式,与其它菜单布局时的风格保持一致
  5 +- **ApiTreeSelect** 扩展`antdv`的`TreeSelect`组件,支持远程数据源,用法类似`ApiSelect`
5 6 - 可以为不同的用户指定不同的后台首页:
6 7 - 在`getUserInfo`接口返回的用户信息中增加`homePath`字段(可选)即可为当前用户定制首页路径
7 8  
... ...
mock/demo/tree-demo.ts 0 → 100644
  1 +import { MockMethod } from 'vite-plugin-mock';
  2 +import { resultSuccess } from '../_util';
  3 +
  4 +const demoTreeList = (keyword) => {
  5 + const result = {
  6 + list: [] as Recordable[],
  7 + };
  8 + for (let index = 0; index < 5; index++) {
  9 + const children: Recordable[] = [];
  10 + for (let j = 0; j < 3; j++) {
  11 + children.push({
  12 + title: `${keyword ?? ''}选项${index}-${j}`,
  13 + value: `${index}-${j}`,
  14 + key: `${index}-${j}`,
  15 + });
  16 + }
  17 + result.list.push({
  18 + title: `${keyword ?? ''}选项${index}`,
  19 + value: `${index}`,
  20 + key: `${index}`,
  21 + children,
  22 + });
  23 + }
  24 + return result;
  25 +};
  26 +
  27 +export default [
  28 + {
  29 + url: '/basic-api/tree/getDemoOptions',
  30 + timeout: 1000,
  31 + method: 'get',
  32 + response: ({ query }) => {
  33 + const { keyword } = query;
  34 + console.log(keyword);
  35 + return resultSuccess(demoTreeList(keyword));
  36 + },
  37 + },
  38 +] as MockMethod[];
... ...
src/api/demo/tree.ts 0 → 100644
  1 +import { defHttp } from '/@/utils/http/axios';
  2 +
  3 +enum Api {
  4 + TREE_OPTIONS_LIST = '/tree/getDemoOptions',
  5 +}
  6 +
  7 +/**
  8 + * @description: Get sample options value
  9 + */
  10 +export const treeOptionsListApi = (params?: Recordable) =>
  11 + defHttp.get<Recordable[]>({ url: Api.TREE_OPTIONS_LIST, params });
... ...
src/components/Form/src/componentMap.ts
... ... @@ -22,6 +22,7 @@ import {
22 22  
23 23 import RadioButtonGroup from './components/RadioButtonGroup.vue';
24 24 import ApiSelect from './components/ApiSelect.vue';
  25 +import ApiTreeSelect from './components/ApiTreeSelect.vue';
25 26 import { BasicUpload } from '/@/components/Upload';
26 27 import { StrengthMeter } from '/@/components/StrengthMeter';
27 28 import { IconPicker } from '/@/components/Icon';
... ... @@ -40,6 +41,7 @@ componentMap.set(&#39;AutoComplete&#39;, AutoComplete);
40 41 componentMap.set('Select', Select);
41 42 componentMap.set('ApiSelect', ApiSelect);
42 43 componentMap.set('TreeSelect', TreeSelect);
  44 +componentMap.set('ApiTreeSelect', ApiTreeSelect);
43 45 componentMap.set('Switch', Switch);
44 46 componentMap.set('RadioButtonGroup', RadioButtonGroup);
45 47 componentMap.set('RadioGroup', Radio.Group);
... ...
src/components/Form/src/components/ApiTreeSelect.vue 0 → 100644
  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>
0 2 \ No newline at end of file
... ...
src/components/Form/src/types/index.ts
... ... @@ -91,6 +91,7 @@ export type ComponentType =
91 91 | 'Select'
92 92 | 'ApiSelect'
93 93 | 'TreeSelect'
  94 + | 'ApiTreeSelect'
94 95 | 'RadioButtonGroup'
95 96 | 'RadioGroup'
96 97 | 'Checkbox'
... ...
src/views/demo/form/index.vue
... ... @@ -46,6 +46,7 @@
46 46  
47 47 import { optionsListApi } from '/@/api/demo/select';
48 48 import { useDebounceFn } from '@vueuse/core';
  49 + import { treeOptionsListApi } from '/@/api/demo/tree';
49 50  
50 51 const provincesOptions = [
51 52 {
... ... @@ -348,6 +349,20 @@
348 349 defaultValue: '0',
349 350 },
350 351 {
  352 + field: 'field33',
  353 + component: 'ApiTreeSelect',
  354 + label: '远程下拉树',
  355 + helpMessage: ['ApiTreeSelect组件', '使用接口提供的数据生成选项'],
  356 + required: true,
  357 + componentProps: {
  358 + api: treeOptionsListApi,
  359 + resultField: 'list',
  360 + },
  361 + colProps: {
  362 + span: 8,
  363 + },
  364 + },
  365 + {
351 366 field: 'field20',
352 367 component: 'InputNumber',
353 368 label: '字段20',
... ...
src/views/demo/table/EditCellTable.vue
... ... @@ -84,6 +84,9 @@
84 84 editComponent: 'ApiSelect',
85 85 editComponentProps: {
86 86 api: optionsListApi,
  87 + resultField: 'list',
  88 + labelField: 'name',
  89 + valueField: 'id',
87 90 },
88 91 width: 200,
89 92 },
... ...
src/views/demo/table/EditRowTable.vue
... ... @@ -80,6 +80,10 @@
80 80 label: 'Option2',
81 81 value: '2',
82 82 },
  83 + {
  84 + label: 'Option3',
  85 + value: '3',
  86 + },
83 87 ],
84 88 },
85 89 width: 200,
... ... @@ -91,6 +95,9 @@
91 95 editComponent: 'ApiSelect',
92 96 editComponentProps: {
93 97 api: optionsListApi,
  98 + resultField: 'list',
  99 + labelField: 'name',
  100 + valueField: 'id',
94 101 },
95 102 width: 200,
96 103 },
... ...