Showing
4 changed files
with
202 additions
and
31 deletions
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | import { ReloadOutlined } from '@ant-design/icons-vue'; | 2 | import { ReloadOutlined } from '@ant-design/icons-vue'; |
3 | - import { Button, List, Tooltip } from 'ant-design-vue'; | 3 | + import { Button, List, Space, Tooltip } from 'ant-design-vue'; |
4 | import { reactive } from 'vue'; | 4 | import { reactive } from 'vue'; |
5 | import { ref } from 'vue'; | 5 | import { ref } from 'vue'; |
6 | + import { BasicForm, useForm } from '../../Form'; | ||
7 | + import { basicListProps } from './props'; | ||
8 | + import { ExtractPropTypes } from 'vue'; | ||
9 | + defineProps<ExtractPropTypes<typeof basicListProps>>(); | ||
10 | + | ||
11 | + const [registerForm] = useForm({ | ||
12 | + schemas: [ | ||
13 | + { | ||
14 | + field: '123', | ||
15 | + label: 'test', | ||
16 | + component: 'Input', | ||
17 | + }, | ||
18 | + ], | ||
19 | + labelWidth: 100, | ||
20 | + layout: 'inline', | ||
21 | + baseColProps: { span: 8 }, | ||
22 | + showAdvancedButton: true, | ||
23 | + compact: true, | ||
24 | + }); | ||
6 | 25 | ||
7 | const listElRef = ref<Nullable<ComponentElRef>>(null); | 26 | const listElRef = ref<Nullable<ComponentElRef>>(null); |
8 | 27 | ||
9 | - const pagination = reactive({}); | 28 | + const pagination = reactive({ size: 'small' }); |
10 | 29 | ||
11 | const loading = ref(false); | 30 | const loading = ref(false); |
12 | 31 | ||
@@ -16,31 +35,48 @@ | @@ -16,31 +35,48 @@ | ||
16 | </script> | 35 | </script> |
17 | 36 | ||
18 | <template> | 37 | <template> |
19 | - <section class="bg-light-50 my-4 p-4 x dark:text-gray-300 dark:bg-dark-900"> | ||
20 | - <List | ||
21 | - ref="listElRef" | ||
22 | - :dataSource="dataSource" | ||
23 | - :pagination="pagination" | ||
24 | - :grid="{ gutter: 16, xs: 1, sm: 1, md: 1, lg: 2, xl: 2, xxl: 3, column: 3 }" | ||
25 | - :loading="loading" | ||
26 | - > | ||
27 | - <template #header> | ||
28 | - <section class="flex justify-between gap-4 min-h-12 items-center"> | ||
29 | - <div class="text-lg font-semibold"> | ||
30 | - <span>任务列表</span> | ||
31 | - </div> | ||
32 | - <Tooltip title="刷新"> | ||
33 | - <Button type="primary" @click="getDataSource"> | ||
34 | - <ReloadOutlined :spin="loading" /> | ||
35 | - </Button> | ||
36 | - </Tooltip> | ||
37 | - </section> | ||
38 | - </template> | ||
39 | - <template #renderItem="{ item }"> | ||
40 | - <List.Item :key="item.id"> | ||
41 | - <slot name="item" :item="item"></slot> | ||
42 | - </List.Item> | ||
43 | - </template> | ||
44 | - </List> | 38 | + <section class="basic-list-container"> |
39 | + <section class="mb-4 bg-light-50 p-2 x dark:text-gray-300 dark:bg-dark-900"> | ||
40 | + <BasicForm @register="registerForm" /> | ||
41 | + </section> | ||
42 | + <section class="bg-light-50 p-2 x dark:text-gray-300 dark:bg-dark-900"> | ||
43 | + <List | ||
44 | + ref="listElRef" | ||
45 | + :dataSource="dataSource" | ||
46 | + :pagination="pagination" | ||
47 | + :grid="{ gutter: 16, xs: 1, sm: 1, md: 1, lg: 2, xl: 2, xxl: 3, column: 3 }" | ||
48 | + :loading="loading" | ||
49 | + > | ||
50 | + <template #header> | ||
51 | + <section class="flex px-5 justify-between gap-4 min-h-12 items-center"> | ||
52 | + <div class="text-lg font-semibold"> | ||
53 | + <span>任务列表</span> | ||
54 | + </div> | ||
55 | + <Space> | ||
56 | + <slot name="toolbar"></slot> | ||
57 | + <Tooltip title="刷新"> | ||
58 | + <Button type="primary" @click="getDataSource"> | ||
59 | + <ReloadOutlined :spin="loading" /> | ||
60 | + </Button> | ||
61 | + </Tooltip> | ||
62 | + </Space> | ||
63 | + </section> | ||
64 | + </template> | ||
65 | + <template #renderItem="{ item }"> | ||
66 | + <List.Item :key="item.id"> | ||
67 | + <slot name="item" :item="item"></slot> | ||
68 | + </List.Item> | ||
69 | + </template> | ||
70 | + </List> | ||
71 | + </section> | ||
45 | </section> | 72 | </section> |
46 | </template> | 73 | </template> |
74 | + | ||
75 | +<style lang="less" scoped> | ||
76 | + .basic-list-container { | ||
77 | + :deep(.ant-list-header) { | ||
78 | + padding-top: 0; | ||
79 | + padding-bottom: 8px; | ||
80 | + } | ||
81 | + } | ||
82 | +</style> |
src/components/List/src/hooks/useListForm.ts
0 → 100644
1 | +import type { ComputedRef, Slots } from 'vue'; | ||
2 | +import type { BasicTableProps, FetchParams } from '../types/table'; | ||
3 | +import { unref, computed } from 'vue'; | ||
4 | +import type { FormProps } from '/@/components/Form'; | ||
5 | +import { isFunction } from '/@/utils/is'; | ||
6 | + | ||
7 | +export function useTableForm( | ||
8 | + propsRef: ComputedRef<BasicTableProps>, | ||
9 | + slots: Slots, | ||
10 | + fetch: (opt?: FetchParams | undefined) => Promise<void>, | ||
11 | + getLoading: ComputedRef<boolean | undefined> | ||
12 | +) { | ||
13 | + const getFormProps = computed((): Partial<FormProps> => { | ||
14 | + const { formConfig } = unref(propsRef); | ||
15 | + const { submitButtonOptions } = formConfig || {}; | ||
16 | + return { | ||
17 | + showAdvancedButton: true, | ||
18 | + ...formConfig, | ||
19 | + submitButtonOptions: { loading: unref(getLoading), ...submitButtonOptions }, | ||
20 | + compact: true, | ||
21 | + }; | ||
22 | + }); | ||
23 | + | ||
24 | + const getFormSlotKeys: ComputedRef<string[]> = computed(() => { | ||
25 | + const keys = Object.keys(slots); | ||
26 | + return keys | ||
27 | + .map((item) => (item.startsWith('form-') ? item : null)) | ||
28 | + .filter((item) => !!item) as string[]; | ||
29 | + }); | ||
30 | + | ||
31 | + function replaceFormSlotKey(key: string) { | ||
32 | + if (!key) return ''; | ||
33 | + return key?.replace?.(/form\-/, '') ?? ''; | ||
34 | + } | ||
35 | + | ||
36 | + function handleSearchInfoChange(info: Recordable) { | ||
37 | + const { handleSearchInfoFn } = unref(propsRef); | ||
38 | + if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) { | ||
39 | + info = handleSearchInfoFn(info) || info; | ||
40 | + } | ||
41 | + fetch({ searchInfo: info, page: 1 }); | ||
42 | + } | ||
43 | + | ||
44 | + return { | ||
45 | + getFormProps, | ||
46 | + replaceFormSlotKey, | ||
47 | + getFormSlotKeys, | ||
48 | + handleSearchInfoChange, | ||
49 | + }; | ||
50 | +} |
1 | -import { ComponentPropsOptions } from 'vue'; | 1 | +import { FormProps } from '../../Form'; |
2 | 2 | ||
3 | -export const props = { | 3 | +export const basicListProps = { |
4 | + immediate: { | ||
5 | + type: Boolean, | ||
6 | + default: true, | ||
7 | + }, | ||
8 | + searchInfo: { | ||
9 | + type: Object as PropType<Recordable>, | ||
10 | + }, | ||
11 | + formConfig: { | ||
12 | + type: Object as PropType<Partial<FormProps>>, | ||
13 | + default: null, | ||
14 | + }, | ||
4 | title: { | 15 | title: { |
5 | type: String, | 16 | type: String, |
6 | }, | 17 | }, |
7 | -} as ComponentPropsOptions; | 18 | + titleHelpMessage: { |
19 | + type: [String, Array] as PropType<string | string[]>, | ||
20 | + }, | ||
21 | + autoCreateKey: { | ||
22 | + type: Boolean, | ||
23 | + default: true, | ||
24 | + }, | ||
25 | + api: { | ||
26 | + type: Function as PropType<Fn<any, Promise<any>>>, | ||
27 | + }, | ||
28 | + beforeFetch: { | ||
29 | + type: Function as PropType<Fn>, | ||
30 | + }, | ||
31 | + afterFetch: { | ||
32 | + type: Function as PropType<Fn>, | ||
33 | + }, | ||
34 | + handleSearchInfoFn: { | ||
35 | + type: Function as PropType<Fn>, | ||
36 | + }, | ||
37 | +}; |
src/components/List/src/types/list.ts
0 → 100644
1 | +import { FormProps } from 'ant-design-vue/es/form/Form'; | ||
2 | + | ||
3 | +export interface BasicListProps { | ||
4 | + /** | ||
5 | + * @description 立即执行 | ||
6 | + */ | ||
7 | + immediate?: boolean; | ||
8 | + | ||
9 | + /** | ||
10 | + * @description 额外的请求参数 | ||
11 | + */ | ||
12 | + searchInfo?: Recordable; | ||
13 | + | ||
14 | + /** | ||
15 | + * @description 搜索表单 | ||
16 | + */ | ||
17 | + formConfig?: Partial<FormProps>; | ||
18 | + | ||
19 | + /** | ||
20 | + * @description 列表名 | ||
21 | + */ | ||
22 | + title?: string; | ||
23 | + | ||
24 | + /** | ||
25 | + * @description 列表标题帮助信息 | ||
26 | + */ | ||
27 | + titleHelpMessage?: string | string[]; | ||
28 | + | ||
29 | + /** | ||
30 | + * @description 自动创建key | ||
31 | + */ | ||
32 | + autoCreateKey?: boolean; | ||
33 | + | ||
34 | + /** | ||
35 | + * @description 请求接口 | ||
36 | + * @param args | ||
37 | + * @returns | ||
38 | + */ | ||
39 | + api?: (...args: any) => Promise<any>; | ||
40 | + | ||
41 | + /** | ||
42 | + * @description 请求前对参数处理 | ||
43 | + */ | ||
44 | + beforeFetch?: Fn; | ||
45 | + | ||
46 | + /** | ||
47 | + * @description 请求后对结果处理 | ||
48 | + */ | ||
49 | + afterFetch?: Fn; | ||
50 | + | ||
51 | + /** | ||
52 | + * @description 请求前处理搜索参数 | ||
53 | + */ | ||
54 | + handleSearchInfoFn?: Fn; | ||
55 | +} |