Commit 3549043f372e8d80a2aa7cc747bd58a8308d4f52
1 parent
a305e591
wip(table): perf table
Showing
24 changed files
with
521 additions
and
388 deletions
CHANGELOG.zh_CN.md
... | ... | @@ -12,10 +12,13 @@ |
12 | 12 | - form: 新增远程下拉`ApiSelect`及示例 |
13 | 13 | - form: 新增`autoFocusFirstItem`配置。用于配置是否聚焦表单第一个输入框 |
14 | 14 | - useForm: 支持动态改变参数。可以传入`Ref`类型与`Computed`类型进行动态更改 |
15 | +- table: 新增`clickToRowSelect`属性。用于控制点击行是否选中勾选狂 | |
16 | +- table: 监听行点击事件 | |
15 | 17 | |
16 | 18 | ### ⚡ Performance Improvements |
17 | 19 | |
18 | 20 | - 优化`modal`与`drawer`滚动条组件 |
21 | +- table: 移除 `isTreeTable`属性 | |
19 | 22 | |
20 | 23 | ### 🎫 Chores |
21 | 24 | ... | ... |
src/components/Drawer/src/BasicDrawer.vue
src/components/Icon/src/index.vue
... | ... | @@ -43,7 +43,7 @@ |
43 | 43 | if (el) { |
44 | 44 | await nextTick(); |
45 | 45 | const icon = unref(getIconRef); |
46 | - | |
46 | + if (!icon) return; | |
47 | 47 | const svg = Iconify.renderSVG(icon, {}); |
48 | 48 | |
49 | 49 | if (svg) { |
... | ... | @@ -74,7 +74,7 @@ |
74 | 74 | } |
75 | 75 | ); |
76 | 76 | |
77 | - watch(() => props.icon, update, { flush: 'post' }); | |
77 | + // watch(() => props.icon, update, { flush: 'post' }); | |
78 | 78 | |
79 | 79 | onMounted(update); |
80 | 80 | ... | ... |
src/components/Modal/src/index.less
src/components/Table/src/BasicTable.vue
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | :submitOnReset="true" |
12 | 12 | v-bind="getFormProps" |
13 | 13 | v-if="getBindValues.useSearchForm" |
14 | - :submitButtonOptions="{ loading }" | |
14 | + :submitButtonOptions="{ loading: getLoading }" | |
15 | 15 | :tableAction="tableAction" |
16 | 16 | @register="registerForm" |
17 | 17 | @submit="handleSearchInfoChange" |
... | ... | @@ -35,18 +35,10 @@ |
35 | 35 | </div> |
36 | 36 | </template> |
37 | 37 | <script lang="ts"> |
38 | - import type { | |
39 | - BasicTableProps, | |
40 | - FetchParams, | |
41 | - GetColumnsParams, | |
42 | - TableActionType, | |
43 | - SizeType, | |
44 | - SorterResult, | |
45 | - TableCustomRecord, | |
46 | - } from './types/table'; | |
38 | + import type { BasicTableProps, TableActionType, SizeType, SorterResult } from './types/table'; | |
47 | 39 | import { PaginationProps } from './types/pagination'; |
48 | 40 | |
49 | - import { defineComponent, ref, computed, unref, watch, nextTick, toRaw } from 'vue'; | |
41 | + import { defineComponent, ref, computed, unref, watch, nextTick } from 'vue'; | |
50 | 42 | import { Table } from 'ant-design-vue'; |
51 | 43 | import renderTitle from './components/renderTitle'; |
52 | 44 | import renderFooter from './components/renderFooter'; |
... | ... | @@ -64,51 +56,64 @@ |
64 | 56 | import { useRowSelection } from './hooks/useRowSelection'; |
65 | 57 | import { useTableScroll } from './hooks/useTableScroll'; |
66 | 58 | import { provideTable } from './hooks/useProvinceTable'; |
59 | + import { useCustomRow } from './hooks/useCustomRow'; | |
60 | + import { useTableStyle } from './hooks/useTableStyle'; | |
67 | 61 | |
68 | 62 | import { useEventListener } from '/@/hooks/event/useEventListener'; |
69 | 63 | import { basicProps } from './props'; |
70 | - import { ROW_KEY } from './const'; | |
71 | 64 | import { useExpose } from '/@/hooks/core/useExpose'; |
72 | 65 | |
73 | 66 | import './style/index.less'; |
74 | 67 | export default defineComponent({ |
75 | 68 | props: basicProps, |
76 | 69 | components: { Table, BasicForm }, |
77 | - emits: ['fetch-success', 'fetch-error', 'selection-change', 'register'], | |
70 | + emits: [ | |
71 | + 'fetch-success', | |
72 | + 'fetch-error', | |
73 | + 'selection-change', | |
74 | + 'register', | |
75 | + 'row-click', | |
76 | + 'row-dbClick', | |
77 | + 'row-contextmenu', | |
78 | + 'row-mouseenter', | |
79 | + 'row-mouseleave', | |
80 | + ], | |
78 | 81 | setup(props, { attrs, emit, slots }) { |
79 | 82 | const tableElRef = ref<ComponentRef>(null); |
83 | + | |
80 | 84 | const wrapRef = ref<Nullable<HTMLDivElement>>(null); |
81 | 85 | const innerPropsRef = ref<Partial<BasicTableProps>>(); |
86 | + | |
82 | 87 | const [registerForm, { getFieldsValue }] = useForm(); |
83 | 88 | |
84 | - const getMergeProps = computed(() => { | |
85 | - return { | |
86 | - ...props, | |
87 | - ...unref(innerPropsRef), | |
88 | - } as BasicTableProps; | |
89 | + const getProps = computed(() => { | |
90 | + return { ...props, ...unref(innerPropsRef) } as BasicTableProps; | |
89 | 91 | }); |
90 | 92 | |
91 | - // const getProps = computed( | |
92 | - // (): FormProps => { | |
93 | - // return deepMerge(toRaw(props), unref(innerPropsRef)); | |
94 | - // } | |
95 | - // ); | |
96 | - | |
97 | - const { loadingRef } = useLoading(getMergeProps); | |
98 | - const { getPaginationRef, setPagination } = usePagination(getMergeProps); | |
99 | - const { getColumnsRef, setColumns } = useColumns(getMergeProps, getPaginationRef); | |
100 | - const { getDataSourceRef, setTableData, fetch, getAutoCreateKey } = useDataSource( | |
101 | - getMergeProps, | |
93 | + const { getLoading, setLoading } = useLoading(getProps); | |
94 | + const { getPaginationInfo, getPagination, setPagination } = usePagination(getProps); | |
95 | + const { getColumnsRef, getColumns, setColumns } = useColumns(getProps, getPaginationInfo); | |
96 | + const { | |
97 | + getDataSourceRef, | |
98 | + getDataSource, | |
99 | + setTableData, | |
100 | + fetch, | |
101 | + getRowKey, | |
102 | + reload, | |
103 | + getAutoCreateKey, | |
104 | + } = useDataSource( | |
105 | + getProps, | |
102 | 106 | { |
103 | - getPaginationRef, | |
104 | - loadingRef, | |
107 | + getPaginationInfo, | |
108 | + setLoading, | |
105 | 109 | setPagination, |
106 | 110 | getFieldsValue, |
107 | 111 | }, |
108 | 112 | emit |
109 | 113 | ); |
110 | 114 | |
111 | - const { getScrollRef, redoHeight } = useTableScroll(getMergeProps, tableElRef); | |
115 | + const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef); | |
116 | + | |
112 | 117 | const { |
113 | 118 | getRowSelectionRef, |
114 | 119 | getSelectRows, |
... | ... | @@ -116,55 +121,58 @@ |
116 | 121 | getSelectRowKeys, |
117 | 122 | deleteSelectRowByKey, |
118 | 123 | setSelectedRowKeys, |
119 | - } = useRowSelection(getMergeProps, emit); | |
120 | - | |
121 | - const getRowKey = computed(() => { | |
122 | - const { rowKey } = unref(getMergeProps); | |
124 | + } = useRowSelection(getProps, emit); | |
123 | 125 | |
124 | - return unref(getAutoCreateKey) ? ROW_KEY : rowKey; | |
126 | + const { customRow } = useCustomRow(getProps, { | |
127 | + setSelectedRowKeys, | |
128 | + getSelectRowKeys, | |
129 | + clearSelectedRowKeys, | |
130 | + getAutoCreateKey, | |
131 | + emit, | |
125 | 132 | }); |
126 | 133 | |
134 | + const { getRowClassName } = useTableStyle(getProps); | |
135 | + | |
136 | + const getTitleProps = computed( | |
137 | + (): Recordable => { | |
138 | + const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(getProps); | |
139 | + const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting; | |
140 | + if (hideTitle && !isString(title)) { | |
141 | + return {}; | |
142 | + } | |
143 | + return { | |
144 | + title: hideTitle | |
145 | + ? null | |
146 | + : renderTitle.bind( | |
147 | + null, | |
148 | + title, | |
149 | + titleHelpMessage, | |
150 | + slots, | |
151 | + showTableSetting, | |
152 | + tableSetting | |
153 | + ), | |
154 | + }; | |
155 | + } | |
156 | + ); | |
157 | + | |
127 | 158 | const getBindValues = computed(() => { |
128 | - const { title, titleHelpMessage, showSummary, showTableSetting, tableSetting } = unref( | |
129 | - getMergeProps | |
130 | - ); | |
131 | - const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting; | |
132 | - const titleData: Recordable = | |
133 | - hideTitle && !isString(title) | |
134 | - ? {} | |
135 | - : { | |
136 | - title: hideTitle | |
137 | - ? null | |
138 | - : renderTitle.bind( | |
139 | - null, | |
140 | - title, | |
141 | - titleHelpMessage, | |
142 | - slots, | |
143 | - showTableSetting, | |
144 | - tableSetting | |
145 | - ), | |
146 | - }; | |
147 | - const pagination = unref(getPaginationRef); | |
148 | - const rowSelection = unref(getRowSelectionRef); | |
149 | - const scroll = unref(getScrollRef); | |
150 | - const loading = unref(loadingRef); | |
151 | - const rowKey = unref(getRowKey); | |
152 | - const columns = unref(getColumnsRef); | |
153 | - const dataSource = unref(getDataSourceRef); | |
154 | - let propsData = { | |
159 | + const { showSummary } = unref(getProps); | |
160 | + | |
161 | + let propsData: Recordable = { | |
155 | 162 | size: 'middle', |
156 | 163 | ...(slots.expandedRowRender ? { expandIcon: renderExpandIcon() } : {}), |
157 | 164 | ...attrs, |
158 | - ...unref(getMergeProps), | |
159 | - ...titleData, | |
160 | - scroll, | |
161 | - loading, | |
165 | + customRow, | |
166 | + ...unref(getProps), | |
167 | + ...unref(getTitleProps), | |
168 | + scroll: unref(getScrollRef), | |
169 | + loading: unref(getLoading), | |
162 | 170 | tableLayout: 'fixed', |
163 | - rowSelection, | |
164 | - rowKey, | |
165 | - columns, | |
166 | - pagination, | |
167 | - dataSource, | |
171 | + rowSelection: unref(getRowSelectionRef), | |
172 | + rowKey: unref(getRowKey), | |
173 | + columns: unref(getColumnsRef), | |
174 | + pagination: unref(getPaginationInfo), | |
175 | + dataSource: unref(getDataSourceRef), | |
168 | 176 | }; |
169 | 177 | if (slots.expandedRowRender) { |
170 | 178 | propsData = omit(propsData, 'scroll'); |
... | ... | @@ -173,7 +181,7 @@ |
173 | 181 | propsData.footer = renderFooter.bind(null, { |
174 | 182 | scroll: scroll as any, |
175 | 183 | columnsRef: getColumnsRef, |
176 | - summaryFunc: unref(getMergeProps).summaryFunc, | |
184 | + summaryFunc: unref(getProps).summaryFunc, | |
177 | 185 | dataSourceRef: getDataSourceRef, |
178 | 186 | rowSelectionRef: getRowSelectionRef, |
179 | 187 | }); |
... | ... | @@ -182,17 +190,17 @@ |
182 | 190 | }); |
183 | 191 | |
184 | 192 | const getFormProps = computed(() => { |
185 | - const { formConfig } = unref(getBindValues); | |
186 | - const formProps: FormProps = { | |
193 | + const { formConfig } = unref(getProps); | |
194 | + const formProps: Partial<FormProps> = { | |
187 | 195 | showAdvancedButton: true, |
188 | - ...(formConfig as FormProps), | |
196 | + ...formConfig, | |
189 | 197 | compact: true, |
190 | 198 | }; |
191 | 199 | return formProps; |
192 | 200 | }); |
193 | 201 | |
194 | 202 | const getEmptyDataIsShowTable = computed(() => { |
195 | - const { emptyDataIsShowTable, useSearchForm } = unref(getMergeProps); | |
203 | + const { emptyDataIsShowTable, useSearchForm } = unref(getProps); | |
196 | 204 | if (emptyDataIsShowTable || !useSearchForm) { |
197 | 205 | return true; |
198 | 206 | } |
... | ... | @@ -207,17 +215,8 @@ |
207 | 215 | { immediate: true } |
208 | 216 | ); |
209 | 217 | |
210 | - function getRowClassName(record: TableCustomRecord, index: number) { | |
211 | - const { striped, rowClassName } = unref(getMergeProps); | |
212 | - if (!striped) return; | |
213 | - if (rowClassName && isFunction(rowClassName)) { | |
214 | - return rowClassName(record); | |
215 | - } | |
216 | - return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : ''; | |
217 | - } | |
218 | - | |
219 | 218 | function handleSearchInfoChange(info: any) { |
220 | - const { handleSearchInfoFn } = unref(getMergeProps); | |
219 | + const { handleSearchInfoFn } = unref(getProps); | |
221 | 220 | if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) { |
222 | 221 | info = handleSearchInfoFn(info) || info; |
223 | 222 | } |
... | ... | @@ -230,7 +229,7 @@ |
230 | 229 | filters: Partial<Recordable<string[]>>, |
231 | 230 | sorter: SorterResult |
232 | 231 | ) { |
233 | - const { clearSelectOnPageChange, sortFn } = unref(getMergeProps); | |
232 | + const { clearSelectOnPageChange, sortFn } = unref(getProps); | |
234 | 233 | if (clearSelectOnPageChange) { |
235 | 234 | clearSelectedRowKeys(); |
236 | 235 | } |
... | ... | @@ -245,7 +244,7 @@ |
245 | 244 | } |
246 | 245 | |
247 | 246 | function handleSummary() { |
248 | - if (unref(getMergeProps).showSummary) { | |
247 | + if (unref(getProps).showSummary) { | |
249 | 248 | nextTick(() => { |
250 | 249 | const tableEl = unref(tableElRef); |
251 | 250 | if (!tableEl) return; |
... | ... | @@ -273,9 +272,7 @@ |
273 | 272 | } |
274 | 273 | |
275 | 274 | const tableAction: TableActionType = { |
276 | - reload: async (opt?: FetchParams) => { | |
277 | - await fetch(opt); | |
278 | - }, | |
275 | + reload, | |
279 | 276 | getSelectRows, |
280 | 277 | clearSelectedRowKeys, |
281 | 278 | getSelectRowKeys, |
... | ... | @@ -285,27 +282,11 @@ |
285 | 282 | redoHeight, |
286 | 283 | setSelectedRowKeys, |
287 | 284 | setColumns, |
288 | - getPaginationRef: () => { | |
289 | - return unref(getPaginationRef); | |
290 | - }, | |
291 | - getColumns: (opt?: GetColumnsParams) => { | |
292 | - const { ignoreIndex, ignoreAction } = opt || {}; | |
293 | - let columns = toRaw(unref(getColumnsRef)); | |
294 | - if (ignoreIndex) { | |
295 | - columns = columns.filter((item) => item.flag !== 'INDEX'); | |
296 | - } | |
297 | - if (ignoreAction) { | |
298 | - columns = columns.filter((item) => item.flag !== 'ACTION'); | |
299 | - } | |
300 | - return columns; | |
301 | - }, | |
302 | - getDataSource: () => { | |
303 | - return unref(getDataSourceRef); | |
304 | - }, | |
305 | - setLoading: (loading: boolean) => { | |
306 | - loadingRef.value = loading; | |
307 | - }, | |
285 | + setLoading, | |
286 | + getDataSource, | |
308 | 287 | setProps, |
288 | + getPaginationRef: getPagination, | |
289 | + getColumns, | |
309 | 290 | getSize: () => { |
310 | 291 | return unref(getBindValues).size as SizeType; |
311 | 292 | }, |
... | ... | @@ -323,7 +304,7 @@ |
323 | 304 | return { |
324 | 305 | tableElRef, |
325 | 306 | getBindValues, |
326 | - loading: loadingRef, | |
307 | + getLoading, | |
327 | 308 | registerForm, |
328 | 309 | handleSearchInfoChange, |
329 | 310 | getFormProps, | ... | ... |
src/components/Table/src/const.ts
src/components/Table/src/hooks/useColumns.ts
1 | -import { BasicColumn, BasicTableProps } from '../types/table'; | |
1 | +import { BasicColumn, BasicTableProps, GetColumnsParams } from '../types/table'; | |
2 | 2 | import { PaginationProps } from '../types/pagination'; |
3 | 3 | import { unref, ComputedRef, Ref, computed, watchEffect, ref, toRaw } from 'vue'; |
4 | 4 | import { isBoolean, isArray, isObject } from '/@/utils/is'; |
5 | -import { PAGE_SIZE } from '../const'; | |
6 | -import { useProps } from './useProps'; | |
5 | +import { DEFAULT_ALIGN, PAGE_SIZE, INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG } from '../const'; | |
7 | 6 | import { useI18n } from '/@/hooks/web/useI18n'; |
8 | 7 | |
9 | 8 | const { t } = useI18n(); |
9 | + | |
10 | +function handleItem(item: BasicColumn, ellipsis: boolean) { | |
11 | + const { key, dataIndex, children } = item; | |
12 | + item.align = item.align || DEFAULT_ALIGN; | |
13 | + if (ellipsis) { | |
14 | + if (!key) { | |
15 | + item.key = dataIndex; | |
16 | + } | |
17 | + if (!isBoolean(item.ellipsis)) { | |
18 | + Object.assign(item, { | |
19 | + ellipsis, | |
20 | + }); | |
21 | + } | |
22 | + } | |
23 | + if (children && children.length) { | |
24 | + handleChildren(children, !!ellipsis); | |
25 | + } | |
26 | +} | |
27 | + | |
28 | +function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) { | |
29 | + if (!children) return; | |
30 | + children.forEach((item) => { | |
31 | + const { children } = item; | |
32 | + handleItem(item, ellipsis); | |
33 | + handleChildren(children, ellipsis); | |
34 | + }); | |
35 | +} | |
36 | + | |
37 | +function handleIndexColumn( | |
38 | + propsRef: ComputedRef<BasicTableProps>, | |
39 | + getPaginationRef: ComputedRef<boolean | PaginationProps>, | |
40 | + columns: BasicColumn[] | |
41 | +) { | |
42 | + const { showIndexColumn, indexColumnProps, ellipsis } = unref(propsRef); | |
43 | + | |
44 | + let pushIndexColumns = false; | |
45 | + columns.forEach((item) => { | |
46 | + const { children } = item; | |
47 | + handleItem(item, !!ellipsis); | |
48 | + const isTreeTable = children && children.length; | |
49 | + | |
50 | + const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG); | |
51 | + | |
52 | + if (showIndexColumn && !isTreeTable) { | |
53 | + pushIndexColumns = indIndex === -1; | |
54 | + } else if (!showIndexColumn && !isTreeTable && indIndex !== -1) { | |
55 | + columns.splice(indIndex, 1); | |
56 | + } | |
57 | + }); | |
58 | + | |
59 | + if (!pushIndexColumns) return; | |
60 | + | |
61 | + const isFixedLeft = columns.some((item) => item.fixed === 'left'); | |
62 | + | |
63 | + columns.unshift({ | |
64 | + flag: INDEX_COLUMN_FLAG, | |
65 | + width: 50, | |
66 | + title: t('component.table.index'), | |
67 | + align: 'center', | |
68 | + customRender: ({ index }) => { | |
69 | + const getPagination = unref(getPaginationRef); | |
70 | + if (isBoolean(getPagination)) { | |
71 | + return `${index + 1}`; | |
72 | + } | |
73 | + const { current = 1, pageSize = PAGE_SIZE } = getPagination; | |
74 | + const currentIndex = (current - 1) * pageSize + index + 1; | |
75 | + return currentIndex; | |
76 | + }, | |
77 | + ...(isFixedLeft | |
78 | + ? { | |
79 | + fixed: 'left', | |
80 | + } | |
81 | + : {}), | |
82 | + ...indexColumnProps, | |
83 | + }); | |
84 | +} | |
85 | + | |
86 | +function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) { | |
87 | + const { actionColumn } = unref(propsRef); | |
88 | + if (!actionColumn) return; | |
89 | + | |
90 | + const hasIndex = columns.findIndex((column) => column.flag === ACTION_COLUMN_FLAG); | |
91 | + if (hasIndex === -1) { | |
92 | + columns.push({ | |
93 | + ...columns[hasIndex], | |
94 | + fixed: 'right', | |
95 | + ...actionColumn, | |
96 | + flag: ACTION_COLUMN_FLAG, | |
97 | + }); | |
98 | + } | |
99 | +} | |
100 | + | |
10 | 101 | export function useColumns( |
11 | - refProps: ComputedRef<BasicTableProps>, | |
12 | - getPaginationRef: ComputedRef<false | PaginationProps> | |
102 | + propsRef: ComputedRef<BasicTableProps>, | |
103 | + getPaginationRef: ComputedRef<boolean | PaginationProps> | |
13 | 104 | ) { |
14 | - const { propsRef } = useProps(refProps); | |
15 | 105 | const columnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>; |
16 | - const cacheColumnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>; | |
106 | + let cacheColumns = unref(propsRef).columns; | |
17 | 107 | |
18 | 108 | const getColumnsRef = computed(() => { |
19 | - const props = unref(propsRef); | |
20 | - const { showIndexColumn, indexColumnProps, ellipsis, actionColumn, isTreeTable } = props; | |
21 | - | |
22 | 109 | const columns = unref(columnsRef); |
23 | 110 | if (!columns) { |
24 | 111 | return []; |
25 | 112 | } |
26 | - let pushIndexColumns = false; | |
27 | - columns.forEach((item) => { | |
28 | - const { children } = item; | |
29 | - handleItem(item, !!ellipsis); | |
30 | - | |
31 | - handleChildren(children, !!ellipsis); | |
32 | - | |
33 | - const indIndex = columns.findIndex((column) => column.flag === 'INDEX'); | |
34 | - if (showIndexColumn && !isTreeTable) { | |
35 | - pushIndexColumns = indIndex === -1; | |
36 | - } else if (!showIndexColumn && !isTreeTable && indIndex !== -1) { | |
37 | - columns.splice(indIndex, 1); | |
38 | - } | |
39 | - }); | |
40 | 113 | |
41 | - if (pushIndexColumns) { | |
42 | - const isFixedLeft = columns.some((item) => item.fixed === 'left'); | |
43 | - | |
44 | - columns.unshift({ | |
45 | - flag: 'INDEX', | |
46 | - width: 50, | |
47 | - title: t('component.table.index'), | |
48 | - align: 'center', | |
49 | - customRender: ({ index }) => { | |
50 | - const getPagination = unref(getPaginationRef); | |
51 | - if (isBoolean(getPagination)) { | |
52 | - return `${index + 1}`; | |
53 | - } | |
54 | - const { current = 1, pageSize = PAGE_SIZE } = getPagination; | |
55 | - const currentIndex = (current - 1) * pageSize + index + 1; | |
56 | - return currentIndex; | |
57 | - }, | |
58 | - ...(isFixedLeft | |
59 | - ? { | |
60 | - fixed: 'left', | |
61 | - } | |
62 | - : {}), | |
63 | - ...indexColumnProps, | |
64 | - }); | |
65 | - } | |
66 | - if (actionColumn) { | |
67 | - const hasIndex = columns.findIndex((column) => column.flag === 'ACTION'); | |
68 | - if (hasIndex === -1) { | |
69 | - columns.push({ | |
70 | - ...columns[hasIndex], | |
71 | - fixed: 'right', | |
72 | - ...actionColumn, | |
73 | - flag: 'ACTION', | |
74 | - }); | |
75 | - } | |
76 | - } | |
114 | + handleIndexColumn(propsRef, getPaginationRef, columns); | |
115 | + handleActionColumn(propsRef, columns); | |
116 | + | |
77 | 117 | return columns; |
78 | 118 | }); |
79 | 119 | |
80 | 120 | watchEffect(() => { |
81 | 121 | const columns = toRaw(unref(propsRef).columns); |
82 | 122 | columnsRef.value = columns; |
83 | - cacheColumnsRef.value = columns; | |
123 | + cacheColumns = columns; | |
84 | 124 | }); |
85 | 125 | |
86 | - function handleItem(item: BasicColumn, ellipsis: boolean) { | |
87 | - const { key, dataIndex } = item; | |
88 | - item.align = item.align || 'center'; | |
89 | - if (ellipsis) { | |
90 | - if (!key) { | |
91 | - item.key = dataIndex; | |
92 | - } | |
93 | - if (!isBoolean(item.ellipsis)) { | |
94 | - Object.assign(item, { | |
95 | - ellipsis, | |
96 | - }); | |
97 | - } | |
98 | - } | |
99 | - } | |
100 | - | |
101 | - function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) { | |
102 | - if (!children) return; | |
103 | - children.forEach((item) => { | |
104 | - const { children } = item; | |
105 | - handleItem(item, ellipsis); | |
106 | - handleChildren(children, ellipsis); | |
107 | - }); | |
108 | - } | |
109 | - | |
110 | - function setColumns(columns: BasicColumn[] | string[]) { | |
126 | + /** | |
127 | + * set columns | |
128 | + * @param columns key|column | |
129 | + */ | |
130 | + function setColumns(columns: Partial<BasicColumn>[] | string[]) { | |
111 | 131 | if (!isArray(columns)) return; |
112 | 132 | |
113 | 133 | if (columns.length <= 0) { |
... | ... | @@ -116,15 +136,30 @@ export function useColumns( |
116 | 136 | } |
117 | 137 | |
118 | 138 | const firstColumn = columns[0]; |
139 | + | |
119 | 140 | if (isObject(firstColumn)) { |
120 | - columnsRef.value = columns as any; | |
141 | + columnsRef.value = columns as BasicColumn[]; | |
121 | 142 | } else { |
122 | - const newColumns = unref(cacheColumnsRef).filter((item) => | |
123 | - (columns as string[]).includes(`${item.key}`! || item.dataIndex!) | |
143 | + const newColumns = cacheColumns.filter( | |
144 | + (item) => | |
145 | + (item.dataIndex || `${item.key}`) && | |
146 | + (columns as string[]).includes(`${item.key}`! || item.dataIndex!) | |
124 | 147 | ); |
125 | 148 | columnsRef.value = newColumns; |
126 | 149 | } |
127 | 150 | } |
128 | 151 | |
129 | - return { getColumnsRef, setColumns }; | |
152 | + function getColumns(opt?: GetColumnsParams) { | |
153 | + const { ignoreIndex, ignoreAction } = opt || {}; | |
154 | + let columns = toRaw(unref(getColumnsRef)); | |
155 | + if (ignoreIndex) { | |
156 | + columns = columns.filter((item) => item.flag !== INDEX_COLUMN_FLAG); | |
157 | + } | |
158 | + if (ignoreAction) { | |
159 | + columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG); | |
160 | + } | |
161 | + return columns; | |
162 | + } | |
163 | + | |
164 | + return { getColumnsRef, getColumns, setColumns }; | |
130 | 165 | } | ... | ... |
src/components/Table/src/hooks/useCustomRow.ts
0 → 100644
1 | +import type { ComputedRef } from 'vue'; | |
2 | +import type { BasicTableProps } from '../types/table'; | |
3 | +import { unref } from 'vue'; | |
4 | +import { ROW_KEY } from '../const'; | |
5 | +import { isString, isFunction } from '/@/utils/is'; | |
6 | + | |
7 | +interface Options { | |
8 | + setSelectedRowKeys: (keys: string[]) => void; | |
9 | + getSelectRowKeys: () => string[]; | |
10 | + clearSelectedRowKeys: () => void; | |
11 | + emit: EmitType; | |
12 | + getAutoCreateKey: ComputedRef<boolean | undefined>; | |
13 | +} | |
14 | + | |
15 | +function getKey( | |
16 | + record: Recordable, | |
17 | + rowKey: string | ((record: Record<string, any>) => string) | undefined, | |
18 | + autoCreateKey?: boolean | |
19 | +) { | |
20 | + if (!rowKey || autoCreateKey) { | |
21 | + return record[ROW_KEY]; | |
22 | + } | |
23 | + if (isString(rowKey)) { | |
24 | + return record[rowKey]; | |
25 | + } | |
26 | + if (isFunction(rowKey)) { | |
27 | + return record[rowKey(record)]; | |
28 | + } | |
29 | + return null; | |
30 | +} | |
31 | + | |
32 | +export function useCustomRow( | |
33 | + propsRef: ComputedRef<BasicTableProps>, | |
34 | + { setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options | |
35 | +) { | |
36 | + const customRow = (record: Recordable, index: number) => { | |
37 | + return { | |
38 | + onClick: (e: Event) => { | |
39 | + emit('row-click', record, index, e); | |
40 | + e?.stopPropagation(); | |
41 | + const { rowSelection, rowKey, clickToRowSelect } = unref(propsRef); | |
42 | + if (!rowSelection || !clickToRowSelect) return; | |
43 | + const keys = getSelectRowKeys(); | |
44 | + const key = getKey(record, rowKey, unref(getAutoCreateKey)); | |
45 | + if (!key) return; | |
46 | + | |
47 | + const isCheckbox = rowSelection.type === 'checkbox'; | |
48 | + | |
49 | + if (isCheckbox) { | |
50 | + if (!keys.includes(key)) { | |
51 | + setSelectedRowKeys([...keys, key]); | |
52 | + return; | |
53 | + } | |
54 | + const keyIndex = keys.findIndex((item) => item === key); | |
55 | + keys.splice(keyIndex, 1); | |
56 | + setSelectedRowKeys(keys); | |
57 | + return; | |
58 | + } | |
59 | + | |
60 | + const isRadio = rowSelection.type === 'radio'; | |
61 | + if (isRadio) { | |
62 | + if (!keys.includes(key)) { | |
63 | + if (keys.length) { | |
64 | + clearSelectedRowKeys(); | |
65 | + } | |
66 | + setSelectedRowKeys([key]); | |
67 | + return; | |
68 | + } | |
69 | + clearSelectedRowKeys(); | |
70 | + } | |
71 | + }, | |
72 | + onDblclick: (event: Event) => { | |
73 | + emit('row-dbClick', record, index, event); | |
74 | + }, | |
75 | + onContextmenu: (event: Event) => { | |
76 | + emit('row-contextmenu', record, index, event); | |
77 | + }, | |
78 | + onMouseenter: (event: Event) => { | |
79 | + emit('row-mouseenter', record, index, event); | |
80 | + }, | |
81 | + onMouseleave: (event: Event) => { | |
82 | + emit('row-mouseleave', record, index, event); | |
83 | + }, | |
84 | + }; | |
85 | + }; | |
86 | + | |
87 | + return { | |
88 | + customRow, | |
89 | + }; | |
90 | +} | ... | ... |
src/components/Table/src/hooks/useDataSource.ts
1 | 1 | import type { BasicTableProps, FetchParams } from '../types/table'; |
2 | 2 | import type { PaginationProps } from '../types/pagination'; |
3 | 3 | |
4 | -import { watch, ref, unref, ComputedRef, computed, onMounted, Ref } from 'vue'; | |
4 | +import { ref, unref, ComputedRef, computed, onMounted, watchEffect } from 'vue'; | |
5 | 5 | |
6 | 6 | import { useTimeoutFn } from '/@/hooks/core/useTimeout'; |
7 | 7 | |
... | ... | @@ -9,39 +9,28 @@ import { buildUUID } from '/@/utils/uuid'; |
9 | 9 | import { isFunction, isBoolean } from '/@/utils/is'; |
10 | 10 | import { get } from 'lodash-es'; |
11 | 11 | |
12 | -import { useProps } from './useProps'; | |
12 | +import { FETCH_SETTING, ROW_KEY, PAGE_SIZE } from '../const'; | |
13 | 13 | |
14 | -import { FETCH_SETTING, ROW_KEY } from '../const'; | |
15 | 14 | interface ActionType { |
16 | - getPaginationRef: ComputedRef<false | PaginationProps>; | |
15 | + getPaginationInfo: ComputedRef<boolean | PaginationProps>; | |
17 | 16 | setPagination: (info: Partial<PaginationProps>) => void; |
18 | - loadingRef: Ref<boolean | undefined>; | |
19 | - getFieldsValue: () => { | |
20 | - [field: string]: any; | |
21 | - }; | |
17 | + setLoading: (loading: boolean) => void; | |
18 | + getFieldsValue: () => Recordable; | |
22 | 19 | } |
23 | 20 | export function useDataSource( |
24 | - refProps: ComputedRef<BasicTableProps>, | |
25 | - { getPaginationRef, setPagination, loadingRef, getFieldsValue }: ActionType, | |
21 | + propsRef: ComputedRef<BasicTableProps>, | |
22 | + { getPaginationInfo, setPagination, setLoading, getFieldsValue }: ActionType, | |
26 | 23 | emit: EmitType |
27 | 24 | ) { |
28 | - const { propsRef } = useProps(refProps); | |
29 | - | |
30 | - const dataSourceRef = ref<any[]>([]); | |
25 | + const dataSourceRef = ref<Recordable[]>([]); | |
31 | 26 | |
32 | - watch( | |
33 | - () => unref(propsRef).dataSource, | |
34 | - (data: any[]) => { | |
35 | - const { api } = unref(propsRef); | |
36 | - !api && (dataSourceRef.value = data); | |
37 | - }, | |
38 | - { immediate: true } | |
39 | - ); | |
27 | + watchEffect(() => { | |
28 | + const { dataSource, api } = unref(propsRef); | |
29 | + !api && dataSource && (dataSourceRef.value = dataSource); | |
30 | + }); | |
40 | 31 | |
41 | 32 | function setTableKey(items: any[]) { |
42 | - if (!items || !Array.isArray(items)) { | |
43 | - return; | |
44 | - } | |
33 | + if (!items || !Array.isArray(items)) return; | |
45 | 34 | items.forEach((item) => { |
46 | 35 | if (!item[ROW_KEY]) { |
47 | 36 | item[ROW_KEY] = buildUUID(); |
... | ... | @@ -51,10 +40,16 @@ export function useDataSource( |
51 | 40 | } |
52 | 41 | }); |
53 | 42 | } |
43 | + | |
54 | 44 | const getAutoCreateKey = computed(() => { |
55 | 45 | return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey; |
56 | 46 | }); |
57 | 47 | |
48 | + const getRowKey = computed(() => { | |
49 | + const { rowKey } = unref(propsRef); | |
50 | + return unref(getAutoCreateKey) ? ROW_KEY : rowKey; | |
51 | + }); | |
52 | + | |
58 | 53 | const getDataSourceRef = computed(() => { |
59 | 54 | const dataSource = unref(dataSourceRef); |
60 | 55 | if (!dataSource || dataSource.length === 0) { |
... | ... | @@ -86,20 +81,20 @@ export function useDataSource( |
86 | 81 | ); |
87 | 82 | if (!api || !isFunction(api)) return; |
88 | 83 | try { |
89 | - loadingRef.value = true; | |
84 | + setLoading(true); | |
90 | 85 | const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING; |
91 | - let pageParams: any = {}; | |
92 | - | |
93 | - const { current, pageSize } = unref(getPaginationRef) as PaginationProps; | |
94 | - | |
95 | - if (isBoolean(getPaginationRef)) { | |
86 | + let pageParams: Recordable = {}; | |
87 | + | |
88 | + const { current = 1, pageSize = PAGE_SIZE } = unref(getPaginationInfo) as PaginationProps; | |
89 | + | |
90 | + if (isBoolean(getPaginationInfo)) { | |
96 | 91 | pageParams = {}; |
97 | 92 | } else { |
98 | 93 | pageParams[pageField] = (opt && opt.page) || current; |
99 | 94 | pageParams[sizeField] = pageSize; |
100 | 95 | } |
101 | 96 | |
102 | - let params: any = { | |
97 | + let params: Recordable = { | |
103 | 98 | ...pageParams, |
104 | 99 | ...(useSearchForm ? getFieldsValue() : {}), |
105 | 100 | ...searchInfo, |
... | ... | @@ -112,18 +107,21 @@ export function useDataSource( |
112 | 107 | } |
113 | 108 | |
114 | 109 | const res = await api(params); |
115 | - let resultItems: any[] = get(res, listField); | |
116 | - const resultTotal: number = get(res, totalField); | |
117 | - | |
110 | + | |
111 | + const isArrayResult = Array.isArray(res); | |
112 | + | |
113 | + let resultItems: Recordable[] = isArrayResult ? res : get(res, listField); | |
114 | + const resultTotal: number = isArrayResult ? 0 : get(res, totalField); | |
115 | + | |
118 | 116 | // 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行 |
119 | - var currentTotalPage = Math.ceil(resultTotal / pageSize); | |
117 | + const currentTotalPage = Math.ceil(resultTotal / pageSize); | |
120 | 118 | if (current > currentTotalPage) { |
121 | - setPagination({ | |
122 | - current: currentTotalPage, | |
123 | - }); | |
124 | - fetch(opt); | |
119 | + setPagination({ | |
120 | + current: currentTotalPage, | |
121 | + }); | |
122 | + fetch(opt); | |
125 | 123 | } |
126 | - | |
124 | + | |
127 | 125 | if (afterFetch && isFunction(afterFetch)) { |
128 | 126 | resultItems = afterFetch(resultItems) || resultItems; |
129 | 127 | } |
... | ... | @@ -147,20 +145,35 @@ export function useDataSource( |
147 | 145 | total: 0, |
148 | 146 | }); |
149 | 147 | } finally { |
150 | - loadingRef.value = false; | |
151 | - // setSearchFormLoading(false); | |
148 | + setLoading(false); | |
152 | 149 | } |
153 | 150 | } |
154 | 151 | |
155 | - function setTableData(values: any[]) { | |
152 | + function setTableData<T = Recordable>(values: T[]) { | |
156 | 153 | dataSourceRef.value = values; |
157 | 154 | } |
155 | + | |
156 | + function getDataSource<T = Recordable>() { | |
157 | + return getDataSourceRef.value as T[]; | |
158 | + } | |
159 | + | |
160 | + async function reload(opt?: FetchParams) { | |
161 | + await fetch(opt); | |
162 | + } | |
163 | + | |
158 | 164 | onMounted(() => { |
159 | - // 转异步任务 | |
160 | 165 | useTimeoutFn(() => { |
161 | 166 | unref(propsRef).immediate && fetch(); |
162 | 167 | }, 0); |
163 | 168 | }); |
164 | 169 | |
165 | - return { getDataSourceRef, setTableData, getAutoCreateKey, fetch: fetch }; | |
170 | + return { | |
171 | + getDataSourceRef, | |
172 | + getDataSource, | |
173 | + getRowKey, | |
174 | + setTableData, | |
175 | + getAutoCreateKey, | |
176 | + fetch, | |
177 | + reload, | |
178 | + }; | |
166 | 179 | } | ... | ... |
src/components/Table/src/hooks/useLoading.ts
1 | -import { watch, ref, ComputedRef, unref } from 'vue'; | |
2 | -import { BasicTableProps } from '../types/table'; | |
3 | -import { useProps } from './useProps'; | |
4 | -export function useLoading(refProps: ComputedRef<BasicTableProps>) { | |
5 | - const { propsRef } = useProps(refProps); | |
6 | - | |
7 | - const loadingRef = ref(unref(propsRef).loading); | |
8 | - watch( | |
9 | - () => unref(propsRef).loading, | |
10 | - (v: boolean) => { | |
11 | - loadingRef.value = v; | |
12 | - } | |
13 | - ); | |
14 | - return { loadingRef }; | |
1 | +import { ref, ComputedRef, unref, computed, watchEffect } from 'vue'; | |
2 | +import type { BasicTableProps } from '../types/table'; | |
3 | + | |
4 | +export function useLoading(props: ComputedRef<BasicTableProps>) { | |
5 | + const loadingRef = ref(unref(props).loading); | |
6 | + | |
7 | + watchEffect(() => { | |
8 | + loadingRef.value = unref(props).loading; | |
9 | + }); | |
10 | + | |
11 | + const getLoading = computed(() => { | |
12 | + return unref(loadingRef); | |
13 | + }); | |
14 | + | |
15 | + function setLoading(loading: boolean) { | |
16 | + loadingRef.value = loading; | |
17 | + } | |
18 | + | |
19 | + return { getLoading, setLoading }; | |
15 | 20 | } | ... | ... |
src/components/Table/src/hooks/usePagination.tsx
... | ... | @@ -7,16 +7,30 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; |
7 | 7 | import { isBoolean } from '/@/utils/is'; |
8 | 8 | |
9 | 9 | import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const'; |
10 | -import { useProps } from './useProps'; | |
11 | 10 | import { useI18n } from '/@/hooks/web/useI18n'; |
12 | 11 | |
13 | -const { t } = useI18n(); | |
12 | +interface ItemRender { | |
13 | + page: number; | |
14 | + type: 'page' | 'prev' | 'next'; | |
15 | + originalElement: any; | |
16 | +} | |
17 | + | |
18 | +function itemRender({ page, type, originalElement }: ItemRender) { | |
19 | + if (type === 'prev') { | |
20 | + return page === 0 ? null : <LeftOutlined />; | |
21 | + } else if (type === 'next') { | |
22 | + return page === 1 ? null : <RightOutlined />; | |
23 | + } | |
24 | + return originalElement; | |
25 | +} | |
26 | + | |
14 | 27 | export function usePagination(refProps: ComputedRef<BasicTableProps>) { |
15 | 28 | const configRef = ref<PaginationProps>({}); |
16 | - const { propsRef } = useProps(refProps); | |
17 | 29 | |
18 | - const getPaginationRef = computed((): PaginationProps | false => { | |
19 | - const { pagination } = unref(propsRef); | |
30 | + const { t } = useI18n(); | |
31 | + const getPaginationInfo = computed((): PaginationProps | boolean => { | |
32 | + const { pagination } = unref(refProps); | |
33 | + | |
20 | 34 | if (isBoolean(pagination) && !pagination) { |
21 | 35 | return false; |
22 | 36 | } |
... | ... | @@ -28,20 +42,7 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { |
28 | 42 | showTotal: (total) => t('component.table.total', { total }), |
29 | 43 | showSizeChanger: true, |
30 | 44 | pageSizeOptions: PAGE_SIZE_OPTIONS, |
31 | - itemRender: ({ page, type, originalElement }) => { | |
32 | - if (type === 'prev') { | |
33 | - if (page === 0) { | |
34 | - return null; | |
35 | - } | |
36 | - return <LeftOutlined />; | |
37 | - } else if (type === 'next') { | |
38 | - if (page === 1) { | |
39 | - return null; | |
40 | - } | |
41 | - return <RightOutlined />; | |
42 | - } | |
43 | - return originalElement; | |
44 | - }, | |
45 | + itemRender: itemRender, | |
45 | 46 | showQuickJumper: true, |
46 | 47 | ...(isBoolean(pagination) ? {} : pagination), |
47 | 48 | ...unref(configRef), |
... | ... | @@ -49,10 +50,15 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { |
49 | 50 | }); |
50 | 51 | |
51 | 52 | function setPagination(info: Partial<PaginationProps>) { |
53 | + const paginationInfo = unref(getPaginationInfo); | |
52 | 54 | configRef.value = { |
53 | - ...unref(getPaginationRef), | |
55 | + ...(!isBoolean(paginationInfo) ? paginationInfo : {}), | |
54 | 56 | ...info, |
55 | 57 | }; |
56 | 58 | } |
57 | - return { getPaginationRef, setPagination }; | |
59 | + | |
60 | + function getPagination() { | |
61 | + return unref(getPaginationInfo); | |
62 | + } | |
63 | + return { getPagination, getPaginationInfo, setPagination }; | |
58 | 64 | } | ... | ... |
src/components/Table/src/hooks/useProps.ts deleted
100644 → 0
1 | -import { Ref, ref, watch, unref } from 'vue'; | |
2 | - | |
3 | -import type { BasicTableProps } from '../types/table'; | |
4 | - | |
5 | -/** | |
6 | - * @description: | |
7 | - * @Date: 2020-05-12 13:20:37 | |
8 | - */ | |
9 | -export function useProps(props: Readonly<Ref<BasicTableProps>>) { | |
10 | - const propsRef = (ref<BasicTableProps>(unref(props)) as unknown) as Ref<BasicTableProps>; | |
11 | - watch( | |
12 | - () => props.value, | |
13 | - (v) => { | |
14 | - propsRef.value = unref(v); | |
15 | - }, | |
16 | - { | |
17 | - immediate: false, | |
18 | - } | |
19 | - ); | |
20 | - return { propsRef }; | |
21 | -} |
src/components/Table/src/hooks/useRowSelection.ts
1 | 1 | import type { BasicTableProps, TableRowSelection } from '../types/table'; |
2 | 2 | |
3 | 3 | import { computed, ref, unref, ComputedRef } from 'vue'; |
4 | -import { useProps } from './useProps'; | |
5 | 4 | |
6 | 5 | /* eslint-disable */ |
7 | -export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: EmitType) { | |
8 | - const { propsRef } = useProps(refProps); | |
9 | - | |
6 | +export function useRowSelection(propsRef: ComputedRef<BasicTableProps>, emit: EmitType) { | |
10 | 7 | const selectedRowKeysRef = ref<string[]>([]); |
11 | - const selectedRowRef = ref<any[]>([]); | |
8 | + const selectedRowRef = ref<Recordable[]>([]); | |
12 | 9 | |
13 | 10 | const getRowSelectionRef = computed((): TableRowSelection | null => { |
14 | - const rowSelection = unref(propsRef).rowSelection; | |
11 | + const { rowSelection } = unref(propsRef); | |
15 | 12 | if (!rowSelection) { |
16 | 13 | return null; |
17 | 14 | } |
... | ... | @@ -46,11 +43,14 @@ export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: Em |
46 | 43 | unref(selectedRowKeysRef).splice(index, 1); |
47 | 44 | } |
48 | 45 | } |
46 | + | |
49 | 47 | function getSelectRowKeys() { |
50 | 48 | return unref(selectedRowKeysRef); |
51 | 49 | } |
52 | - function getSelectRows() { | |
53 | - return unref(selectedRowRef); | |
50 | + | |
51 | + function getSelectRows<T = Recordable>() { | |
52 | + // const ret = toRaw(unref(selectedRowRef)).map((item) => toRaw(item)); | |
53 | + return unref(selectedRowRef) as T[]; | |
54 | 54 | } |
55 | 55 | |
56 | 56 | return { | ... | ... |
src/components/Table/src/hooks/useTable.ts
... | ... | @@ -14,10 +14,11 @@ export function useTable( |
14 | 14 | const loadedRef = ref<Nullable<boolean>>(false); |
15 | 15 | |
16 | 16 | function register(instance: TableActionType) { |
17 | - onUnmounted(() => { | |
18 | - tableRef.value = null; | |
19 | - loadedRef.value = null; | |
20 | - }); | |
17 | + isProdMode() && | |
18 | + onUnmounted(() => { | |
19 | + tableRef.value = null; | |
20 | + loadedRef.value = null; | |
21 | + }); | |
21 | 22 | |
22 | 23 | if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) { |
23 | 24 | return; | ... | ... |
src/components/Table/src/hooks/useTableScroll.ts
1 | 1 | import type { BasicTableProps } from '../types/table'; |
2 | -import { computed, Ref, onMounted, unref, ref, nextTick, ComputedRef, watch } from 'vue'; | |
2 | +import type { Ref, ComputedRef } from 'vue'; | |
3 | +import { computed, unref, ref, nextTick, watchEffect } from 'vue'; | |
3 | 4 | |
4 | 5 | import { getViewportOffset } from '/@/utils/domUtils'; |
5 | 6 | import { isBoolean } from '/@/utils/is'; |
6 | 7 | |
7 | 8 | import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; |
8 | -import { useProps } from './useProps'; | |
9 | 9 | import { useModalContext } from '/@/components/Modal'; |
10 | 10 | |
11 | -export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRef: Ref<any>) { | |
12 | - const { propsRef } = useProps(refProps); | |
13 | - | |
14 | - const tableHeightRef: Ref<number | null> = ref(null); | |
11 | +export function useTableScroll( | |
12 | + propsRef: ComputedRef<BasicTableProps>, | |
13 | + tableElRef: Ref<ComponentRef> | |
14 | +) { | |
15 | + const tableHeightRef: Ref<Nullable<number>> = ref(null); | |
15 | 16 | |
16 | 17 | const modalFn = useModalContext(); |
17 | 18 | |
18 | - watch( | |
19 | - () => unref(propsRef).canResize, | |
20 | - () => { | |
21 | - redoHeight(); | |
22 | - } | |
23 | - ); | |
19 | + const getCanResize = computed(() => { | |
20 | + const { canResize, scroll } = unref(propsRef); | |
21 | + return canResize && !(scroll || {}).y; | |
22 | + }); | |
24 | 23 | |
25 | - function redoHeight() { | |
26 | - const { canResize } = unref(propsRef); | |
24 | + watchEffect(() => { | |
25 | + redoHeight(); | |
26 | + }); | |
27 | 27 | |
28 | - if (!canResize) return; | |
29 | - calcTableHeight(); | |
28 | + function redoHeight() { | |
29 | + if (unref(getCanResize)) { | |
30 | + nextTick(() => { | |
31 | + calcTableHeight(); | |
32 | + }); | |
33 | + } | |
30 | 34 | } |
31 | 35 | |
36 | + // No need to repeat queries | |
32 | 37 | let paginationEl: HTMLElement | null; |
33 | 38 | let footerEl: HTMLElement | null; |
39 | + | |
34 | 40 | async function calcTableHeight() { |
35 | - const { canResize, resizeHeightOffset, pagination, maxHeight } = unref(propsRef); | |
36 | - if (!canResize) return; | |
41 | + const { resizeHeightOffset, pagination, maxHeight } = unref(propsRef); | |
42 | + if (!unref(getCanResize)) return; | |
37 | 43 | |
38 | 44 | await nextTick(); |
39 | - const table = unref(tableElRef) as any; | |
45 | + const table = unref(tableElRef); | |
40 | 46 | if (!table) return; |
41 | 47 | |
42 | 48 | const tableEl: Element = table.$el; |
43 | 49 | if (!tableEl) return; |
50 | + | |
44 | 51 | const headEl = tableEl.querySelector('.ant-table-thead '); |
45 | 52 | if (!headEl) return; |
46 | 53 | |
47 | - // 表格距离底部高度 | |
54 | + // Table height from bottom | |
48 | 55 | const { bottomIncludeBody } = getViewportOffset(headEl); |
49 | - // 表格高度+距离底部高度-自定义偏移量 | |
56 | + // Table height from bottom height-custom offset | |
50 | 57 | |
51 | 58 | const paddingHeight = 32; |
52 | 59 | const borderHeight = 2 * 2; |
53 | - // 分页器高度 | |
54 | - | |
60 | + // Pager height | |
55 | 61 | let paginationHeight = 2; |
56 | 62 | if (!isBoolean(pagination)) { |
57 | 63 | if (!paginationEl) { |
... | ... | @@ -61,7 +67,7 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe |
61 | 67 | const offsetHeight = paginationEl.offsetHeight; |
62 | 68 | paginationHeight += offsetHeight || 0; |
63 | 69 | } else { |
64 | - // TODO 先固定24 | |
70 | + // TODO First fix 24 | |
65 | 71 | paginationHeight += 24; |
66 | 72 | } |
67 | 73 | } |
... | ... | @@ -75,11 +81,13 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe |
75 | 81 | footerHeight += offsetHeight || 0; |
76 | 82 | } |
77 | 83 | } |
84 | + | |
78 | 85 | let headerHeight = 0; |
79 | 86 | if (headEl) { |
80 | 87 | headerHeight = (headEl as HTMLElement).offsetHeight; |
81 | 88 | } |
82 | - tableHeightRef.value = | |
89 | + | |
90 | + const height = | |
83 | 91 | bottomIncludeBody - |
84 | 92 | (resizeHeightOffset || 0) - |
85 | 93 | paddingHeight - |
... | ... | @@ -89,27 +97,14 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe |
89 | 97 | headerHeight; |
90 | 98 | |
91 | 99 | setTimeout(() => { |
92 | - tableHeightRef.value = | |
93 | - tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value; | |
94 | - // 解决表格放modal内的时候,modal自适应高度计算问题 | |
100 | + tableHeightRef.value = (height > maxHeight! ? (maxHeight as number) : height) ?? height; | |
101 | + // Solve the problem of modal adaptive height calculation when the form is placed in the modal | |
95 | 102 | modalFn?.redoModalHeight?.(); |
96 | - }, 16); | |
103 | + }, 0); | |
97 | 104 | } |
98 | 105 | |
99 | - const getCanResize = computed(() => { | |
100 | - const { canResize, scroll } = unref(propsRef); | |
101 | - return canResize && !(scroll || {}).y; | |
102 | - }); | |
103 | - | |
104 | 106 | useWindowSizeFn(calcTableHeight, 100); |
105 | 107 | |
106 | - onMounted(() => { | |
107 | - if (unref(getCanResize)) { | |
108 | - nextTick(() => { | |
109 | - calcTableHeight(); | |
110 | - }); | |
111 | - } | |
112 | - }); | |
113 | 108 | const getScrollRef = computed(() => { |
114 | 109 | const tableHeight = unref(tableHeightRef); |
115 | 110 | const { canResize, scroll } = unref(propsRef); |
... | ... | @@ -121,5 +116,6 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe |
121 | 116 | ...scroll, |
122 | 117 | }; |
123 | 118 | }); |
119 | + | |
124 | 120 | return { getScrollRef, redoHeight }; |
125 | 121 | } | ... | ... |
src/components/Table/src/hooks/useTableStyle.ts
0 → 100644
1 | +import type { ComputedRef } from 'vue'; | |
2 | +import type { BasicTableProps, TableCustomRecord } from '../types/table'; | |
3 | +import { unref } from 'vue'; | |
4 | +import { isFunction } from '/@/utils/is'; | |
5 | +export function useTableStyle(propsRef: ComputedRef<BasicTableProps>) { | |
6 | + function getRowClassName(record: TableCustomRecord, index: number) { | |
7 | + const { striped, rowClassName } = unref(propsRef); | |
8 | + if (!striped) return; | |
9 | + if (rowClassName && isFunction(rowClassName)) { | |
10 | + return rowClassName(record); | |
11 | + } | |
12 | + return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : ''; | |
13 | + } | |
14 | + | |
15 | + return { | |
16 | + getRowClassName, | |
17 | + }; | |
18 | +} | ... | ... |
src/components/Table/src/props.ts
... | ... | @@ -14,6 +14,7 @@ import { propTypes } from '/@/utils/propTypes'; |
14 | 14 | |
15 | 15 | // 注释看 types/table |
16 | 16 | export const basicProps = { |
17 | + clickToRowSelect: propTypes.bool.def(true), | |
17 | 18 | tableSetting: { |
18 | 19 | type: Object as PropType<TableSetting>, |
19 | 20 | }, |
... | ... | @@ -34,7 +35,6 @@ export const basicProps = { |
34 | 35 | }, |
35 | 36 | |
36 | 37 | canColDrag: propTypes.bool.def(true), |
37 | - isTreeTable: propTypes.bool, | |
38 | 38 | api: { |
39 | 39 | type: Function as PropType<(...arg: any[]) => Promise<any>>, |
40 | 40 | default: null, | ... | ... |
src/components/Table/src/style/index.less
... | ... | @@ -167,20 +167,6 @@ |
167 | 167 | } |
168 | 168 | } |
169 | 169 | |
170 | - .ant-radio { | |
171 | - &-inner { | |
172 | - border-color: @text-color-base; | |
173 | - } | |
174 | - } | |
175 | - | |
176 | - .ant-checkbox { | |
177 | - &:not(.ant-checkbox-checked) { | |
178 | - .ant-checkbox-inner { | |
179 | - border-color: @text-color-base; | |
180 | - } | |
181 | - } | |
182 | - } | |
183 | - | |
184 | 170 | .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th, |
185 | 171 | .ant-table-tbody > tr > td { |
186 | 172 | word-break: break-word; | ... | ... |
src/components/Table/src/types/table.ts
... | ... | @@ -124,6 +124,8 @@ export interface TableSetting { |
124 | 124 | } |
125 | 125 | |
126 | 126 | export interface BasicTableProps<T = any> { |
127 | + // 点击行选中 | |
128 | + clickToRowSelect?: boolean; | |
127 | 129 | // 自定义排序方法 |
128 | 130 | sortFn?: (sortInfo: SorterResult) => any; |
129 | 131 | // 取消表格的默认padding |
... | ... | @@ -141,8 +143,6 @@ export interface BasicTableProps<T = any> { |
141 | 143 | showSummary?: boolean; |
142 | 144 | // 是否可拖拽列 |
143 | 145 | canColDrag?: boolean; |
144 | - // 是否树表 | |
145 | - isTreeTable?: boolean; | |
146 | 146 | // 接口请求对象 |
147 | 147 | api?: (...arg: any) => Promise<any>; |
148 | 148 | // 请求之前处理参数 |
... | ... | @@ -158,7 +158,7 @@ export interface BasicTableProps<T = any> { |
158 | 158 | // 在开起搜索表单的时候,如果没有数据是否显示表格 |
159 | 159 | emptyDataIsShowTable?: boolean; |
160 | 160 | // 额外的请求参数 |
161 | - searchInfo?: any; | |
161 | + searchInfo?: Recordable; | |
162 | 162 | // 使用搜索表单 |
163 | 163 | useSearchForm?: boolean; |
164 | 164 | // 表单配置 |
... | ... | @@ -180,9 +180,9 @@ export interface BasicTableProps<T = any> { |
180 | 180 | // 在分页改变的时候清空选项 |
181 | 181 | clearSelectOnPageChange?: boolean; |
182 | 182 | // |
183 | - rowKey?: string | ((record: any) => string); | |
183 | + rowKey?: string | ((record: Recordable) => string); | |
184 | 184 | // 数据 |
185 | - dataSource?: any[]; | |
185 | + dataSource?: Recordable[]; | |
186 | 186 | // 标题右侧提示 |
187 | 187 | titleHelpMessage?: string | string[]; |
188 | 188 | // 表格滚动最大高度 | ... | ... |
src/views/demo/table/Basic.vue
... | ... | @@ -9,7 +9,8 @@ |
9 | 9 | :loading="loading" |
10 | 10 | :striped="striped" |
11 | 11 | :bordered="border" |
12 | - :pagination="{ pageSize: 20 }" | |
12 | + showTableSetting | |
13 | + :pagination="pagination" | |
13 | 14 | > |
14 | 15 | <template #toolbar> |
15 | 16 | <a-button type="primary" @click="toggleCanResize"> |
... | ... | @@ -38,6 +39,7 @@ |
38 | 39 | const loading = ref(false); |
39 | 40 | const striped = ref(true); |
40 | 41 | const border = ref(true); |
42 | + const pagination = ref<any>(false); | |
41 | 43 | function toggleCanResize() { |
42 | 44 | canResize.value = !canResize.value; |
43 | 45 | } |
... | ... | @@ -48,6 +50,7 @@ |
48 | 50 | loading.value = true; |
49 | 51 | setTimeout(() => { |
50 | 52 | loading.value = false; |
53 | + pagination.value = { pageSize: 20 }; | |
51 | 54 | }, 3000); |
52 | 55 | } |
53 | 56 | function toggleBorder() { |
... | ... | @@ -64,6 +67,7 @@ |
64 | 67 | toggleCanResize, |
65 | 68 | toggleLoading, |
66 | 69 | toggleBorder, |
70 | + pagination, | |
67 | 71 | }; |
68 | 72 | }, |
69 | 73 | }); | ... | ... |
src/views/demo/table/FixedColumn.vue
... | ... | @@ -74,10 +74,10 @@ |
74 | 74 | slots: { customRender: 'action' }, |
75 | 75 | }, |
76 | 76 | }); |
77 | - function handleDelete(record: any) { | |
77 | + function handleDelete(record: Recordable) { | |
78 | 78 | console.log('点击了删除', record); |
79 | 79 | } |
80 | - function handleOpen(record: any) { | |
80 | + function handleOpen(record: Recordable) { | |
81 | 81 | console.log('点击了启用', record); |
82 | 82 | } |
83 | 83 | return { | ... | ... |
src/views/demo/table/TreeTable.vue
src/views/demo/table/UseTable.vue
... | ... | @@ -44,12 +44,13 @@ |
44 | 44 | clearSelectedRowKeys, |
45 | 45 | }, |
46 | 46 | ] = useTable({ |
47 | - canResize: false, | |
47 | + canResize: true, | |
48 | 48 | title: 'useTable示例', |
49 | 49 | titleHelpMessage: '使用useTable调用表格内方法', |
50 | 50 | api: demoListApi, |
51 | 51 | columns: getBasicColumns(), |
52 | 52 | rowKey: 'id', |
53 | + showTableSetting: true, | |
53 | 54 | rowSelection: { |
54 | 55 | type: 'checkbox', |
55 | 56 | }, | ... | ... |
src/views/demo/table/tableData.tsx
... | ... | @@ -5,13 +5,13 @@ export function getBasicColumns(): BasicColumn[] { |
5 | 5 | return [ |
6 | 6 | { |
7 | 7 | title: 'ID', |
8 | - width: 150, | |
9 | 8 | dataIndex: 'id', |
9 | + width: 150, | |
10 | 10 | }, |
11 | 11 | { |
12 | 12 | title: '姓名', |
13 | 13 | dataIndex: 'name', |
14 | - width: 120, | |
14 | + width: 150, | |
15 | 15 | }, |
16 | 16 | { |
17 | 17 | title: '地址', |
... | ... | @@ -20,14 +20,16 @@ export function getBasicColumns(): BasicColumn[] { |
20 | 20 | { |
21 | 21 | title: '编号', |
22 | 22 | dataIndex: 'no', |
23 | - width: 80, | |
23 | + width: 150, | |
24 | 24 | }, |
25 | 25 | { |
26 | 26 | title: '开始时间', |
27 | + width: 120, | |
27 | 28 | dataIndex: 'beginTime', |
28 | 29 | }, |
29 | 30 | { |
30 | 31 | title: '结束时间', |
32 | + width: 120, | |
31 | 33 | sorter: true, |
32 | 34 | dataIndex: 'endTime', |
33 | 35 | }, | ... | ... |