Commit 40071529d276bfdbff7610adf01aa4af36ff3f77
Committed by
GitHub
1 parent
6f5711b0
添加 Form ApiTransfer , 修复标签页切换灰屏不显示内容问题 (#2052)
* Table BasicColumn 添加 editDynamicDisabled Co-authored-by: Cyrus Zhou <6802207@qq.com> 使用方式同 Form FormSchema dynamicDisabled ``` export const Columns: BasicColumn[] = [ { title: 'Title', dataIndex: 'Title', editRow: true, editComponent: 'Select', editDynamicDisabled: ({ record }) => record.isDisabled, }, * editComponentProps onChange 功能恢复 Co-authored-by: Cyrus Zhou <6802207@qq.com> 说明: ...omit(compProps, 'onChange') 这会忽略 onChange ,导致 editComponentProps onChange 被取消 如下功能将不支持: ``` editComponentProps: ({ record }) => { return { options: effectTypeData, onChange: () => { }, }; }, ``` * tableData == null 报错 * ApiSelect 第一次选择触发required错误提示问题 * 恢复 虽然可以解决第一次选择提示报错问题,但是会导致 onChange: (e: any, options: any) => 无法获得 options 的值 * 修复标签页切换灰屏不显示内容问题 Co-authored-by: Cyrus Zhou <6802207@qq.com> 问题描述页面没有用 div 包括 会提示 Component inside <Transition> renders non-element root node that cannot be animated , 导致页灰屏必须刷新页面才可以显示内容 * 添加 Form ApiTransfer ## 使用方式 api 方式: ``` ...... component: 'ApiTransfer', componentProps: { api: sysUserSelector, labelField: 'name', valueField: 'id', }, ..... ``` 数据方式: ``` .... componentProps: { dataSource: [ { title: 'Test01', key: '0', disabled: false, description: 'description 01' }, { title: 'Test02', key: '1', disabled: false, description: 'description 02' }, { title: 'Test03', key: '2', disabled: false, description: 'description 03' }, { title: 'Test04', key: '3', disabled: false, description: 'description 04' }, { title: 'Test05', key: '4', disabled: false, description: 'description 05' }, ], }, .... ```
Showing
5 changed files
with
146 additions
and
3 deletions
src/components/Form/index.ts
... | ... | @@ -12,5 +12,6 @@ export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; |
12 | 12 | export { default as ApiTree } from './src/components/ApiTree.vue'; |
13 | 13 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; |
14 | 14 | export { default as ApiCascader } from './src/components/ApiCascader.vue'; |
15 | +export { default as ApiTransfer } from './src/components/ApiTransfer.vue'; | |
15 | 16 | |
16 | 17 | export { BasicForm }; | ... | ... |
src/components/Form/src/componentMap.ts
... | ... | @@ -27,6 +27,7 @@ import ApiSelect from './components/ApiSelect.vue'; |
27 | 27 | import ApiTree from './components/ApiTree.vue'; |
28 | 28 | import ApiTreeSelect from './components/ApiTreeSelect.vue'; |
29 | 29 | import ApiCascader from './components/ApiCascader.vue'; |
30 | +import ApiTransfer from './components/ApiTransfer.vue'; | |
30 | 31 | import { BasicUpload } from '/@/components/Upload'; |
31 | 32 | import { StrengthMeter } from '/@/components/StrengthMeter'; |
32 | 33 | import { IconPicker } from '/@/components/Icon'; |
... | ... | @@ -57,6 +58,7 @@ componentMap.set('ApiCascader', ApiCascader); |
57 | 58 | componentMap.set('Cascader', Cascader); |
58 | 59 | componentMap.set('Slider', Slider); |
59 | 60 | componentMap.set('Rate', Rate); |
61 | +componentMap.set('ApiTransfer', ApiTransfer); | |
60 | 62 | |
61 | 63 | componentMap.set('DatePicker', DatePicker); |
62 | 64 | componentMap.set('MonthPicker', DatePicker.MonthPicker); | ... | ... |
src/components/Form/src/components/ApiTransfer.vue
0 → 100644
1 | +<template> | |
2 | + <Transfer | |
3 | + :data-source="getdataSource" | |
4 | + show-search | |
5 | + :filter-option="filterOption" | |
6 | + :render="(item) => item.title" | |
7 | + :showSelectAll="showSelectAll" | |
8 | + :selectedKeys="selectedKeys" | |
9 | + :targetKeys="getTargetKeys" | |
10 | + :showSearch="showSearch" | |
11 | + @change="handleChange" | |
12 | + /> | |
13 | +</template> | |
14 | + | |
15 | +<script lang="ts"> | |
16 | + import { computed, defineComponent, watch, ref, unref, watchEffect } from 'vue'; | |
17 | + import { Transfer } from 'ant-design-vue'; | |
18 | + import { isFunction } from '/@/utils/is'; | |
19 | + import { get, omit } from 'lodash-es'; | |
20 | + import { propTypes } from '/@/utils/propTypes'; | |
21 | + import { useI18n } from '/@/hooks/web/useI18n'; | |
22 | + import { TransferDirection, TransferItem } from 'ant-design-vue/lib/transfer'; | |
23 | + export default defineComponent({ | |
24 | + name: 'ApiTransfer', | |
25 | + components: { Transfer }, | |
26 | + props: { | |
27 | + value: { type: Array<string> }, | |
28 | + api: { | |
29 | + type: Function as PropType<(arg?: Recordable) => Promise<TransferItem[]>>, | |
30 | + default: null, | |
31 | + }, | |
32 | + params: { type: Object }, | |
33 | + dataSource: { type: Array<TransferItem> }, | |
34 | + immediate: propTypes.bool.def(true), | |
35 | + alwaysLoad: propTypes.bool.def(false), | |
36 | + afterFetch: { type: Function as PropType<Fn> }, | |
37 | + resultField: propTypes.string.def(''), | |
38 | + labelField: propTypes.string.def('title'), | |
39 | + valueField: propTypes.string.def('key'), | |
40 | + showSearch: { type: Boolean, default: false }, | |
41 | + disabled: { type: Boolean, default: false }, | |
42 | + filterOption: { | |
43 | + type: Function as PropType<(inputValue: string, item: TransferItem) => boolean>, | |
44 | + }, | |
45 | + selectedKeys: { type: Array<string> }, | |
46 | + showSelectAll: { type: Boolean, default: false }, | |
47 | + targetKeys: { type: Array<string> }, | |
48 | + }, | |
49 | + emits: ['options-change', 'change'], | |
50 | + setup(props, { attrs, emit }) { | |
51 | + const _dataSource = ref<TransferItem[]>([]); | |
52 | + const _targetKeys = ref<string[]>([]); | |
53 | + const { t } = useI18n(); | |
54 | + | |
55 | + const getAttrs = computed(() => { | |
56 | + return { | |
57 | + ...(!props.api ? { dataSource: unref(_dataSource) } : {}), | |
58 | + ...attrs, | |
59 | + }; | |
60 | + }); | |
61 | + const getdataSource = computed(() => { | |
62 | + const { labelField, valueField } = props; | |
63 | + | |
64 | + return unref(_dataSource).reduce((prev, next: Recordable) => { | |
65 | + if (next) { | |
66 | + prev.push({ | |
67 | + ...omit(next, [labelField, valueField]), | |
68 | + title: next[labelField], | |
69 | + key: next[valueField], | |
70 | + }); | |
71 | + } | |
72 | + return prev; | |
73 | + }, [] as TransferItem[]); | |
74 | + }); | |
75 | + const getTargetKeys = computed<string[]>(() => { | |
76 | + if (unref(_targetKeys).length > 0) { | |
77 | + return unref(_targetKeys); | |
78 | + } | |
79 | + if (Array.isArray(props.value)) { | |
80 | + return props.value; | |
81 | + } | |
82 | + return []; | |
83 | + }); | |
84 | + | |
85 | + function handleChange(keys: string[], direction: TransferDirection, moveKeys: string[]) { | |
86 | + _targetKeys.value = keys; | |
87 | + console.log(direction); | |
88 | + console.log(moveKeys); | |
89 | + emit('change', keys); | |
90 | + } | |
91 | + | |
92 | + watchEffect(() => { | |
93 | + props.immediate && !props.alwaysLoad && fetch(); | |
94 | + }); | |
95 | + | |
96 | + watch( | |
97 | + () => props.params, | |
98 | + () => { | |
99 | + fetch(); | |
100 | + }, | |
101 | + { deep: true }, | |
102 | + ); | |
103 | + | |
104 | + async function fetch() { | |
105 | + const api = props.api; | |
106 | + if (!api || !isFunction(api)) { | |
107 | + if (Array.isArray(props.dataSource)) { | |
108 | + _dataSource.value = props.dataSource; | |
109 | + } | |
110 | + return; | |
111 | + } | |
112 | + _dataSource.value = []; | |
113 | + try { | |
114 | + const res = await api(props.params); | |
115 | + if (Array.isArray(res)) { | |
116 | + _dataSource.value = res; | |
117 | + emitChange(); | |
118 | + return; | |
119 | + } | |
120 | + if (props.resultField) { | |
121 | + _dataSource.value = get(res, props.resultField) || []; | |
122 | + } | |
123 | + emitChange(); | |
124 | + } catch (error) { | |
125 | + console.warn(error); | |
126 | + } finally { | |
127 | + } | |
128 | + } | |
129 | + function emitChange() { | |
130 | + emit('options-change', unref(getdataSource)); | |
131 | + } | |
132 | + return { getTargetKeys, getdataSource, t, getAttrs, handleChange }; | |
133 | + }, | |
134 | + }); | |
135 | +</script> | ... | ... |
src/components/Form/src/types/index.ts
src/layouts/page/index.vue
... | ... | @@ -15,9 +15,13 @@ |
15 | 15 | appear |
16 | 16 | > |
17 | 17 | <keep-alive v-if="openCache" :include="getCaches"> |
18 | - <component :is="Component" :key="route.fullPath" /> | |
18 | + <div :key="route.name"> | |
19 | + <component :is="Component" :key="route.fullPath" /> | |
20 | + </div> | |
19 | 21 | </keep-alive> |
20 | - <component v-else :is="Component" :key="route.fullPath" /> | |
22 | + <div v-else :key="route.name"> | |
23 | + <component :is="Component" :key="route.fullPath" /> | |
24 | + </div> | |
21 | 25 | </transition> |
22 | 26 | </template> |
23 | 27 | </RouterView> | ... | ... |