@ethan1979/jf-lib 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/extensions.json +3 -0
- package/README.md +3 -0
- package/babel.config.js +3 -0
- package/dist/App.vue.d.ts +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/v-table/components/content/components/operations.vue.d.ts +47 -0
- package/dist/components/v-table/components/content/index.vue.d.ts +13 -0
- package/dist/components/v-table/components/modal-form.vue.d.ts +30 -0
- package/dist/components/v-table/components/search.vue.d.ts +12 -0
- package/dist/components/v-table/components/toolbar.vue.d.ts +11 -0
- package/dist/components/v-table/get-render-function.d.ts +6 -0
- package/dist/components/v-table/hooks.d.ts +80 -0
- package/dist/components/v-table/index.vue.d.ts +17 -0
- package/dist/components/v-table/util.d.ts +2 -0
- package/dist/favicon.ico +0 -0
- package/dist/jf-lib.es.js +6553 -0
- package/dist/jf-lib.umd.js +27 -0
- package/dist/main.d.ts +1 -0
- package/dist/style.css +1 -0
- package/dist/types/global.d.ts +43 -0
- package/dist/utils/utils.d.ts +11 -0
- package/docs/superpowers/plans/2026-09-19-collapsible-table-search.md +194 -0
- package/docs/superpowers/specs/2026-09-19-collapsible-table-search-design.md +80 -0
- package/index.html +13 -0
- package/package.json +31 -0
- package/public/favicon.ico +0 -0
- package/scripts/check-collapsible-search.mjs +67 -0
- package/src/App.vue +98 -0
- package/src/assets/logo.png +0 -0
- package/src/components/index.ts +18 -0
- package/src/components/v-table/components/content/components/operations.vue +119 -0
- package/src/components/v-table/components/content/index.vue +295 -0
- package/src/components/v-table/components/modal-form.vue +111 -0
- package/src/components/v-table/components/search.vue +181 -0
- package/src/components/v-table/components/toolbar.vue +121 -0
- package/src/components/v-table/get-render-function.ts +15 -0
- package/src/components/v-table/hooks.ts +128 -0
- package/src/components/v-table/index.vue +338 -0
- package/src/components/v-table/typings.d.ts +164 -0
- package/src/components/v-table/util.ts +14 -0
- package/src/env.d.ts +12 -0
- package/src/main.ts +13 -0
- package/src/types/global.ts +48 -0
- package/src/utils/utils.ts +77 -0
- package/tsconfig.json +38 -0
- package/tsconfig.node.json +8 -0
- package/vite.config.ts +47 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<a-modal
|
|
3
|
+
v-model:visible="_visible"
|
|
4
|
+
modal-class="table-modal"
|
|
5
|
+
unmount-on-close
|
|
6
|
+
@before-ok="handleOk"
|
|
7
|
+
@close="handleClose"
|
|
8
|
+
>
|
|
9
|
+
<template #title> {{ props.title }} </template>
|
|
10
|
+
<div>
|
|
11
|
+
<a-form
|
|
12
|
+
ref="formRef"
|
|
13
|
+
:model="formData"
|
|
14
|
+
:style="{ width: '100%' }"
|
|
15
|
+
:label-col-props="{ span: 6 }"
|
|
16
|
+
:wrapper-col-props="{ span: 18 }"
|
|
17
|
+
:rules="rules"
|
|
18
|
+
>
|
|
19
|
+
<a-row>
|
|
20
|
+
<a-form-item
|
|
21
|
+
v-for="{
|
|
22
|
+
formItem: { dataIndex, title: label, tooltip },
|
|
23
|
+
render,
|
|
24
|
+
} in form || []"
|
|
25
|
+
:key="dataIndex"
|
|
26
|
+
:span="6"
|
|
27
|
+
:field="dataIndex"
|
|
28
|
+
:label="label"
|
|
29
|
+
>
|
|
30
|
+
<a-col :span="24">
|
|
31
|
+
<component
|
|
32
|
+
:is="getRenderFunction(render, props.renderParams)"
|
|
33
|
+
v-model="formData[dataIndex]"
|
|
34
|
+
:style="{
|
|
35
|
+
width: hasTooltip ? '94%' : '100%',
|
|
36
|
+
}"
|
|
37
|
+
/>
|
|
38
|
+
<a-tooltip v-if="tooltip" :content="tooltip">
|
|
39
|
+
<div
|
|
40
|
+
style="display: inline-block; width: 6%; text-align: right"
|
|
41
|
+
>
|
|
42
|
+
<icon-info-circle />
|
|
43
|
+
</div>
|
|
44
|
+
</a-tooltip>
|
|
45
|
+
</a-col>
|
|
46
|
+
<!-- <a-col
|
|
47
|
+
:span="24"
|
|
48
|
+
style="font-size: 12px; color: var(--color-text-3)"
|
|
49
|
+
>
|
|
50
|
+
123
|
|
51
|
+
</a-col> -->
|
|
52
|
+
</a-form-item>
|
|
53
|
+
</a-row>
|
|
54
|
+
</a-form>
|
|
55
|
+
</div>
|
|
56
|
+
</a-modal>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<script setup lang="ts">
|
|
60
|
+
import { Form } from '@arco-design/web-vue'
|
|
61
|
+
import { cloneDeep } from 'lodash'
|
|
62
|
+
import { computed, inject, ref } from 'vue'
|
|
63
|
+
import { BaseObj } from '../../../types/global'
|
|
64
|
+
import getRenderFunction from '../get-render-function'
|
|
65
|
+
import { useTableForm } from '../hooks'
|
|
66
|
+
import { TableConfig } from '../typings'
|
|
67
|
+
|
|
68
|
+
const props = defineProps<{
|
|
69
|
+
type: 'Add' | 'Update'
|
|
70
|
+
visible: boolean
|
|
71
|
+
title: string
|
|
72
|
+
renderParams?: BaseObj
|
|
73
|
+
}>()
|
|
74
|
+
const emit = defineEmits(['update:visible', 'okEvent'])
|
|
75
|
+
|
|
76
|
+
const config = inject<TableConfig>('config')
|
|
77
|
+
const { form, formData, rules, hasTooltip, resetFormData } = useTableForm(
|
|
78
|
+
config?.columns,
|
|
79
|
+
props.type
|
|
80
|
+
)
|
|
81
|
+
const _visible = computed<boolean>({
|
|
82
|
+
get() {
|
|
83
|
+
return props.visible
|
|
84
|
+
},
|
|
85
|
+
set(val) {
|
|
86
|
+
emit('update:visible', val)
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
const formRef = ref<InstanceType<typeof Form>>()
|
|
90
|
+
const handleOk = (done: (closed: boolean) => void) => {
|
|
91
|
+
formRef.value?.validate().then((errors) => {
|
|
92
|
+
if (errors) {
|
|
93
|
+
done(false)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
emit('okEvent', cloneDeep(formData.value))
|
|
97
|
+
done(true)
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
const handleClose = () => {
|
|
101
|
+
resetFormData()
|
|
102
|
+
formRef?.value?.resetFields()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
defineExpose({
|
|
106
|
+
formData,
|
|
107
|
+
resetFormData,
|
|
108
|
+
})
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<style scoped></style>
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="form.length > 0" class="search-form-wrapper">
|
|
4
|
+
<a-row>
|
|
5
|
+
<a-col :flex="1">
|
|
6
|
+
<a-form
|
|
7
|
+
ref="formRef"
|
|
8
|
+
:model="formData"
|
|
9
|
+
:style="{ width: '100%' }"
|
|
10
|
+
label-align="left"
|
|
11
|
+
>
|
|
12
|
+
<a-row :gutter="16">
|
|
13
|
+
<a-col
|
|
14
|
+
v-for="{
|
|
15
|
+
formItem: { dataIndex, title },
|
|
16
|
+
render,
|
|
17
|
+
searchColSpan,
|
|
18
|
+
} in visibleForm"
|
|
19
|
+
:key="dataIndex"
|
|
20
|
+
:span="getSpan(searchColSpan)"
|
|
21
|
+
>
|
|
22
|
+
<a-form-item
|
|
23
|
+
:field="dataIndex"
|
|
24
|
+
:label="title"
|
|
25
|
+
:label-col-props="{
|
|
26
|
+
span: getFormItemSpan(searchColSpan, 'label'),
|
|
27
|
+
}"
|
|
28
|
+
:wrapper-col-props="{
|
|
29
|
+
span: getFormItemSpan(searchColSpan, 'wrapper'),
|
|
30
|
+
}"
|
|
31
|
+
>
|
|
32
|
+
<component
|
|
33
|
+
:is="getRenderFunction(render)"
|
|
34
|
+
@change="handleSearchParamsChange"
|
|
35
|
+
v-model="formData[dataIndex]"
|
|
36
|
+
style="width: 100%"
|
|
37
|
+
/>
|
|
38
|
+
</a-form-item>
|
|
39
|
+
</a-col>
|
|
40
|
+
</a-row>
|
|
41
|
+
</a-form>
|
|
42
|
+
</a-col>
|
|
43
|
+
<a-divider
|
|
44
|
+
:style="{ height: isVertical ? '84px' : '32px' }"
|
|
45
|
+
direction="vertical"
|
|
46
|
+
/>
|
|
47
|
+
<a-col :flex="'86px'" style="text-align: right">
|
|
48
|
+
<a-space
|
|
49
|
+
:direction="isVertical ? 'vertical' : 'horizontal'"
|
|
50
|
+
:size="18"
|
|
51
|
+
>
|
|
52
|
+
<a-button type="primary" @click="search">
|
|
53
|
+
<template #icon>
|
|
54
|
+
<icon-search />
|
|
55
|
+
</template>
|
|
56
|
+
搜索
|
|
57
|
+
</a-button>
|
|
58
|
+
<a-button v-if="!config?.search?.hiddenReset" @click="reset">
|
|
59
|
+
<template #icon>
|
|
60
|
+
<icon-refresh />
|
|
61
|
+
</template>
|
|
62
|
+
重置
|
|
63
|
+
</a-button>
|
|
64
|
+
<a-button
|
|
65
|
+
v-if="hasExpandableFields"
|
|
66
|
+
type="text"
|
|
67
|
+
:aria-expanded="expanded"
|
|
68
|
+
@click="expanded = !expanded"
|
|
69
|
+
>
|
|
70
|
+
<template #icon>
|
|
71
|
+
<icon-up v-if="expanded" />
|
|
72
|
+
<icon-down v-else />
|
|
73
|
+
</template>
|
|
74
|
+
{{ expanded ? '收起' : '展开' }}
|
|
75
|
+
</a-button>
|
|
76
|
+
</a-space>
|
|
77
|
+
</a-col>
|
|
78
|
+
</a-row>
|
|
79
|
+
</div>
|
|
80
|
+
<!-- <div class="search-btn-wrapper"></div> -->
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
83
|
+
|
|
84
|
+
<script setup lang="ts">
|
|
85
|
+
import { computed, nextTick, onMounted, PropType, ref } from 'vue'
|
|
86
|
+
import Form from '@arco-design/web-vue/es/form'
|
|
87
|
+
import { SearchColSpan, TableConfig } from '../typings'
|
|
88
|
+
import getRenderFunction from '../get-render-function'
|
|
89
|
+
import { useTableForm } from '../hooks'
|
|
90
|
+
|
|
91
|
+
const props = defineProps({
|
|
92
|
+
config: Object as PropType<TableConfig>,
|
|
93
|
+
})
|
|
94
|
+
const emit = defineEmits(['search'])
|
|
95
|
+
|
|
96
|
+
const { form, formData, resetFormData } = useTableForm(
|
|
97
|
+
props.config?.columns,
|
|
98
|
+
'Search'
|
|
99
|
+
)
|
|
100
|
+
const formRef = ref<InstanceType<typeof Form>>()
|
|
101
|
+
const expanded = ref(false)
|
|
102
|
+
const defaultVisibleFields = computed(
|
|
103
|
+
() => props.config?.search?.defaultVisibleFields || []
|
|
104
|
+
)
|
|
105
|
+
const hasExpandableFields = computed(
|
|
106
|
+
() =>
|
|
107
|
+
props.config?.search?.collapsible === true &&
|
|
108
|
+
defaultVisibleFields.value.length > 0 &&
|
|
109
|
+
form.value.some(
|
|
110
|
+
({ formItem: { dataIndex } }) =>
|
|
111
|
+
!defaultVisibleFields.value.includes(dataIndex)
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
const visibleForm = computed(() => {
|
|
115
|
+
if (!hasExpandableFields.value || expanded.value) return form.value
|
|
116
|
+
return form.value.filter(({ formItem: { dataIndex } }) =>
|
|
117
|
+
defaultVisibleFields.value.includes(dataIndex)
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
const search = () => {
|
|
121
|
+
emit('search', formData.value)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const handleSearchParamsChange=()=>{
|
|
125
|
+
if(props.config?.search?.searchOnchange!==false) search()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const reset = () => {
|
|
129
|
+
resetFormData()
|
|
130
|
+
search()
|
|
131
|
+
// formRef?.value?.resetFields()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const getSpan = (config: SearchColSpan | undefined) => {
|
|
135
|
+
if (!config) return 6
|
|
136
|
+
if (typeof config === 'number') return config
|
|
137
|
+
return config.span ?? 6
|
|
138
|
+
}
|
|
139
|
+
const formItemSpanSize = {
|
|
140
|
+
label: 7,
|
|
141
|
+
wrapper: 17,
|
|
142
|
+
}
|
|
143
|
+
const getFormItemSpan = (
|
|
144
|
+
config: SearchColSpan | undefined,
|
|
145
|
+
type: 'label' | 'wrapper'
|
|
146
|
+
) => {
|
|
147
|
+
const size = formItemSpanSize[type]
|
|
148
|
+
if (!config || typeof config === 'number' || !config.formItem) {
|
|
149
|
+
return size
|
|
150
|
+
}
|
|
151
|
+
return config?.formItem[`${type}ColSpan`] || size
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const isVertical = computed(() => {
|
|
155
|
+
let colSpans = 0
|
|
156
|
+
visibleForm.value.forEach(({ searchColSpan }) => {
|
|
157
|
+
colSpans += getSpan(searchColSpan)
|
|
158
|
+
})
|
|
159
|
+
return colSpans > 24
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
onMounted(()=>{
|
|
163
|
+
nextTick(()=>{
|
|
164
|
+
search()
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
defineExpose({
|
|
169
|
+
reset,
|
|
170
|
+
search,
|
|
171
|
+
})
|
|
172
|
+
</script>
|
|
173
|
+
|
|
174
|
+
<style lang="less">
|
|
175
|
+
.search-form-wrapper{
|
|
176
|
+
margin-top: 16px;
|
|
177
|
+
.arco-form-item{
|
|
178
|
+
margin-bottom: 16px;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
</style>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="table-toolbar">
|
|
3
|
+
<a-divider
|
|
4
|
+
style="margin-top: 0; margin-bottom: 16px"
|
|
5
|
+
divider-style
|
|
6
|
+
:style="{
|
|
7
|
+
...dividerStyle,
|
|
8
|
+
borderBottom: `${
|
|
9
|
+
searchForm.length > 0 ? 1 : 0
|
|
10
|
+
}px solid var(--color-neutral-3)`,
|
|
11
|
+
}"
|
|
12
|
+
/>
|
|
13
|
+
<a-row
|
|
14
|
+
style="padding-bottom: 16px"
|
|
15
|
+
v-if="config?.batchDelete || form.length > 0 || onBeforeAddModalOpen"
|
|
16
|
+
>
|
|
17
|
+
<a-col :span="16" class="toolbar-left">
|
|
18
|
+
<a-space>
|
|
19
|
+
<slot name="toolbar-left">
|
|
20
|
+
<a-button
|
|
21
|
+
v-if="form.length > 0 || onBeforeAddModalOpen"
|
|
22
|
+
type="primary"
|
|
23
|
+
@click="handleClickAdd"
|
|
24
|
+
>
|
|
25
|
+
<template #icon>
|
|
26
|
+
<icon-plus />
|
|
27
|
+
</template>
|
|
28
|
+
新增
|
|
29
|
+
</a-button>
|
|
30
|
+
<a-popconfirm
|
|
31
|
+
v-if="config?.batchDelete"
|
|
32
|
+
:content="`是否删除选中的${selectedRowKeys?.length}项内容`"
|
|
33
|
+
:disabled="selectedRowKeys?.length === 0"
|
|
34
|
+
:ok-button-props="{
|
|
35
|
+
status: 'danger',
|
|
36
|
+
}"
|
|
37
|
+
type="error"
|
|
38
|
+
position="bl"
|
|
39
|
+
@ok="emit('batchDelete')"
|
|
40
|
+
>
|
|
41
|
+
<a-button
|
|
42
|
+
type="primary"
|
|
43
|
+
status="danger"
|
|
44
|
+
:disabled="selectedRowKeys?.length === 0"
|
|
45
|
+
>
|
|
46
|
+
<template #icon>
|
|
47
|
+
<icon-minus />
|
|
48
|
+
</template>
|
|
49
|
+
删除
|
|
50
|
+
</a-button>
|
|
51
|
+
</a-popconfirm>
|
|
52
|
+
</slot>
|
|
53
|
+
<slot name="toolbar-left-extend"></slot>
|
|
54
|
+
</a-space>
|
|
55
|
+
</a-col>
|
|
56
|
+
<a-col :span="8" class="toolbar-right">
|
|
57
|
+
<slot name="toolbar-right"></slot>
|
|
58
|
+
</a-col>
|
|
59
|
+
</a-row>
|
|
60
|
+
<template v-if="form.length > 0">
|
|
61
|
+
<modal-form
|
|
62
|
+
v-model:visible="visible"
|
|
63
|
+
title="新增"
|
|
64
|
+
type="Add"
|
|
65
|
+
@ok-event="(data) => emit('add', data)"
|
|
66
|
+
/>
|
|
67
|
+
</template>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script setup lang="ts">
|
|
72
|
+
import { PropType, Ref, inject, ref, computed } from "vue";
|
|
73
|
+
import { useTableForm } from "../hooks";
|
|
74
|
+
import { TableConfig } from "../typings";
|
|
75
|
+
|
|
76
|
+
import ModalForm from "./modal-form.vue";
|
|
77
|
+
|
|
78
|
+
const props = defineProps({
|
|
79
|
+
config: Object as PropType<TableConfig>,
|
|
80
|
+
});
|
|
81
|
+
const emit = defineEmits(["add", "batchDelete"]);
|
|
82
|
+
|
|
83
|
+
const selectedRowKeys = inject<Ref<number[] | string[]>>("selectedRowKeys");
|
|
84
|
+
|
|
85
|
+
const visible = ref(false);
|
|
86
|
+
const { form } = useTableForm(props.config?.columns, "Add");
|
|
87
|
+
const onBeforeAddModalOpen = computed(() => {
|
|
88
|
+
return props.config?.toolbar?.onBeforeAddModalOpen;
|
|
89
|
+
});
|
|
90
|
+
const handleClickAdd = async () => {
|
|
91
|
+
if (typeof onBeforeAddModalOpen.value === "function") {
|
|
92
|
+
const result = await onBeforeAddModalOpen.value();
|
|
93
|
+
if (result === false) return;
|
|
94
|
+
}
|
|
95
|
+
visible.value = true;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const { form: searchForm } = useTableForm(props.config?.columns, "Search");
|
|
99
|
+
const dividerStyle = computed(() => {
|
|
100
|
+
return props.config?.card
|
|
101
|
+
? {
|
|
102
|
+
left: "-16px",
|
|
103
|
+
width: "calc(100% + 32px)",
|
|
104
|
+
minWidth: "calc(100% + 32px)",
|
|
105
|
+
}
|
|
106
|
+
: {};
|
|
107
|
+
});
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<style lang="less">
|
|
111
|
+
.table-toolbar {
|
|
112
|
+
// padding-bottom: 16px;
|
|
113
|
+
.arco-space {
|
|
114
|
+
width: 100%;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.toolbar-right {
|
|
118
|
+
text-align: right;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { h, getCurrentInstance } from 'vue'
|
|
2
|
+
import { toPascalCase } from '../../utils/utils'
|
|
3
|
+
import { FormRender } from './typings'
|
|
4
|
+
import { BaseObj } from '../../types/global'
|
|
5
|
+
|
|
6
|
+
export default (render: FormRender, renderParams?: BaseObj | undefined) => {
|
|
7
|
+
if (typeof render === 'function') {
|
|
8
|
+
return render(renderParams)
|
|
9
|
+
}
|
|
10
|
+
const component =
|
|
11
|
+
getCurrentInstance()?.appContext.components[toPascalCase(render.type)] ||
|
|
12
|
+
render.type
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
return h(component as any, render.props)
|
|
15
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { FieldRule } from '@arco-design/web-vue/es/form/interface'
|
|
2
|
+
import { cloneDeep } from 'lodash'
|
|
3
|
+
import { ref, toRefs, watchEffect } from 'vue'
|
|
4
|
+
import { BaseObj } from '../../types/global'
|
|
5
|
+
import { Columns, FormRender, SearchColSpan } from './typings'
|
|
6
|
+
|
|
7
|
+
const defineRender = (title: string) => ({
|
|
8
|
+
type: 'a-input',
|
|
9
|
+
props: {
|
|
10
|
+
placeholder: `请输入${title}`,
|
|
11
|
+
allowClear: true,
|
|
12
|
+
maxLength: 100,
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
type FormType = 'Search' | 'Update' | 'Add'
|
|
17
|
+
|
|
18
|
+
const getIndexAndConvert = (column: Columns, type: FormType) => {
|
|
19
|
+
const { form } = column
|
|
20
|
+
if (typeof form !== 'object') return 0
|
|
21
|
+
const _key = type.toLocaleLowerCase() as 'add' | 'update' | 'search'
|
|
22
|
+
// console.log('_key', _key, form[`${_key}Config`], form.defaultValue=11111)
|
|
23
|
+
if (form[`${_key}Config`]) {
|
|
24
|
+
column.form = {
|
|
25
|
+
...form,
|
|
26
|
+
...form[`${_key}Config`],
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return form?.yIndex || 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const getForm = (columns: Array<Columns> | undefined, type: FormType) => {
|
|
33
|
+
// const formData: BaseObj = {}
|
|
34
|
+
const defaultValues: BaseObj = {}
|
|
35
|
+
const rules: Record<string, FieldRule[]> = {}
|
|
36
|
+
let hasTooltip = false
|
|
37
|
+
const addRule = (dataIndex: string, rule: FieldRule | FieldRule[]) => {
|
|
38
|
+
if (!rules[dataIndex]) rules[dataIndex] = []
|
|
39
|
+
if (rule instanceof Array) {
|
|
40
|
+
rules[dataIndex].push(...rule)
|
|
41
|
+
} else {
|
|
42
|
+
rules[dataIndex].push(rule)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const _columns = cloneDeep(columns?.filter((column) => column.form))
|
|
46
|
+
_columns?.sort((a, b) => {
|
|
47
|
+
return getIndexAndConvert(b, type) - getIndexAndConvert(a, type)
|
|
48
|
+
})
|
|
49
|
+
const form = _columns
|
|
50
|
+
?.map(({ form, title, dataIndex }) => {
|
|
51
|
+
let formItem: BaseObj = {
|
|
52
|
+
title,
|
|
53
|
+
dataIndex,
|
|
54
|
+
}
|
|
55
|
+
if (typeof form !== 'object') {
|
|
56
|
+
defaultValues[dataIndex as string] = ''
|
|
57
|
+
const render =
|
|
58
|
+
typeof form === 'boolean' ? defineRender(title as string) : form
|
|
59
|
+
return {
|
|
60
|
+
formItem,
|
|
61
|
+
render,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (form[`hidden${type}`]) return false
|
|
65
|
+
const { searchColSpan, tooltip } = form
|
|
66
|
+
if (tooltip) hasTooltip = true
|
|
67
|
+
formItem = {
|
|
68
|
+
...formItem,
|
|
69
|
+
tooltip,
|
|
70
|
+
}
|
|
71
|
+
if (form.title) formItem.title = form.title
|
|
72
|
+
// console.log('form.defaultValue', form.defaultValue, form)
|
|
73
|
+
defaultValues[dataIndex as string] = form.defaultValue ?? ''
|
|
74
|
+
if (form.required)
|
|
75
|
+
addRule(dataIndex as string, {
|
|
76
|
+
required: true,
|
|
77
|
+
message: `${title}不能为空`,
|
|
78
|
+
})
|
|
79
|
+
if (form.rule) addRule(dataIndex as string, form.rule)
|
|
80
|
+
return {
|
|
81
|
+
formItem,
|
|
82
|
+
render: form.render || defineRender(title as string),
|
|
83
|
+
searchColSpan,
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.filter((item) => item)
|
|
87
|
+
return {
|
|
88
|
+
form: form as Array<{
|
|
89
|
+
formItem: {
|
|
90
|
+
title: string
|
|
91
|
+
dataIndex: string
|
|
92
|
+
tooltip?: string
|
|
93
|
+
}
|
|
94
|
+
render: FormRender
|
|
95
|
+
searchColSpan?: SearchColSpan
|
|
96
|
+
}>,
|
|
97
|
+
formData: cloneDeep(defaultValues),
|
|
98
|
+
defaultValues,
|
|
99
|
+
rules,
|
|
100
|
+
hasTooltip,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function useTableForm(
|
|
105
|
+
columns: Array<Columns> | undefined,
|
|
106
|
+
type: 'Search' | 'Update' | 'Add'
|
|
107
|
+
) {
|
|
108
|
+
const form = ref(getForm(columns, type))
|
|
109
|
+
watchEffect(() => {
|
|
110
|
+
// form.value = getForm(columns, type)
|
|
111
|
+
const {
|
|
112
|
+
form: _form,
|
|
113
|
+
formData,
|
|
114
|
+
rules,
|
|
115
|
+
defaultValues,
|
|
116
|
+
} = getForm(columns, type)
|
|
117
|
+
form.value.form = _form
|
|
118
|
+
form.value.formData = formData
|
|
119
|
+
form.value.rules = rules
|
|
120
|
+
form.value.defaultValues = defaultValues
|
|
121
|
+
})
|
|
122
|
+
return {
|
|
123
|
+
...toRefs(form.value),
|
|
124
|
+
resetFormData() {
|
|
125
|
+
form.value.formData = cloneDeep(form.value.defaultValues)
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
}
|