Commit 2c867b3d636d57cdc526a4ca600af7d747b7d833
1 parent
fb43fad5
feat(table): add `beforeEditSubmit` for editable cell
单元格编辑功能新增提交回调
Showing
5 changed files
with
117 additions
and
21 deletions
CHANGELOG.zh_CN.md
src/components/Table/src/components/editable/EditableCell.vue
... | ... | @@ -11,25 +11,27 @@ |
11 | 11 | <FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" /> |
12 | 12 | </div> |
13 | 13 | |
14 | - <div v-if="isEdit" :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside"> | |
15 | - <CellComponent | |
16 | - v-bind="getComponentProps" | |
17 | - :component="getComponent" | |
18 | - :style="getWrapperStyle" | |
19 | - :popoverVisible="getRuleVisible" | |
20 | - :rule="getRule" | |
21 | - :ruleMessage="ruleMessage" | |
22 | - :class="getWrapperClass" | |
23 | - ref="elRef" | |
24 | - @change="handleChange" | |
25 | - @options-change="handleOptionsChange" | |
26 | - @pressEnter="handleEnter" | |
27 | - /> | |
28 | - <div :class="`${prefixCls}__action`" v-if="!getRowEditable"> | |
29 | - <CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" /> | |
30 | - <CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" /> | |
14 | + <a-spin v-if="isEdit" :spinning="spinning"> | |
15 | + <div :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside"> | |
16 | + <CellComponent | |
17 | + v-bind="getComponentProps" | |
18 | + :component="getComponent" | |
19 | + :style="getWrapperStyle" | |
20 | + :popoverVisible="getRuleVisible" | |
21 | + :rule="getRule" | |
22 | + :ruleMessage="ruleMessage" | |
23 | + :class="getWrapperClass" | |
24 | + ref="elRef" | |
25 | + @change="handleChange" | |
26 | + @options-change="handleOptionsChange" | |
27 | + @pressEnter="handleEnter" | |
28 | + /> | |
29 | + <div :class="`${prefixCls}__action`" v-if="!getRowEditable"> | |
30 | + <CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" /> | |
31 | + <CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" /> | |
32 | + </div> | |
31 | 33 | </div> |
32 | - </div> | |
34 | + </a-spin> | |
33 | 35 | </div> |
34 | 36 | </template> |
35 | 37 | <script lang="ts"> |
... | ... | @@ -48,12 +50,13 @@ |
48 | 50 | import { propTypes } from '/@/utils/propTypes'; |
49 | 51 | import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is'; |
50 | 52 | import { createPlaceholderMessage } from './helper'; |
51 | - import { omit, set } from 'lodash-es'; | |
53 | + import { omit, pick, set } from 'lodash-es'; | |
52 | 54 | import { treeToList } from '/@/utils/helper/treeHelper'; |
55 | + import { Spin } from 'ant-design-vue'; | |
53 | 56 | |
54 | 57 | export default defineComponent({ |
55 | 58 | name: 'EditableCell', |
56 | - components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent }, | |
59 | + components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, ASpin: Spin }, | |
57 | 60 | directives: { |
58 | 61 | clickOutside, |
59 | 62 | }, |
... | ... | @@ -80,6 +83,7 @@ |
80 | 83 | const optionsRef = ref<LabelValueOptions>([]); |
81 | 84 | const currentValueRef = ref<any>(props.value); |
82 | 85 | const defaultValueRef = ref<any>(props.value); |
86 | + const spinning = ref<boolean>(false); | |
83 | 87 | |
84 | 88 | const { prefixCls } = useDesign('editable-cell'); |
85 | 89 | |
... | ... | @@ -246,6 +250,35 @@ |
246 | 250 | |
247 | 251 | const dataKey = (dataIndex || key) as string; |
248 | 252 | |
253 | + if (!record.editable) { | |
254 | + const { getBindValues } = table; | |
255 | + | |
256 | + const { beforeEditSubmit, columns } = unref(getBindValues); | |
257 | + | |
258 | + if (beforeEditSubmit && isFunction(beforeEditSubmit)) { | |
259 | + spinning.value = true; | |
260 | + const keys: string[] = columns | |
261 | + .map((_column) => _column.dataIndex) | |
262 | + .filter((field) => !!field) as string[]; | |
263 | + let result: any = true; | |
264 | + try { | |
265 | + result = await beforeEditSubmit({ | |
266 | + record: pick(record, keys), | |
267 | + index, | |
268 | + key, | |
269 | + value, | |
270 | + }); | |
271 | + } catch (e) { | |
272 | + result = false; | |
273 | + } finally { | |
274 | + spinning.value = false; | |
275 | + } | |
276 | + if (result === false) { | |
277 | + return; | |
278 | + } | |
279 | + } | |
280 | + } | |
281 | + | |
249 | 282 | set(record, dataKey, value); |
250 | 283 | //const record = await table.updateTableData(index, dataKey, value); |
251 | 284 | needEmit && table.emit?.('edit-end', { record, index, key, value }); |
... | ... | @@ -368,6 +401,7 @@ |
368 | 401 | getValues, |
369 | 402 | handleEnter, |
370 | 403 | handleSubmitClick, |
404 | + spinning, | |
371 | 405 | }; |
372 | 406 | }, |
373 | 407 | }); | ... | ... |
src/components/Table/src/props.ts
... | ... | @@ -126,4 +126,14 @@ export const basicProps = { |
126 | 126 | type: Object as PropType<{ x: number | true; y: number }>, |
127 | 127 | default: null, |
128 | 128 | }, |
129 | + beforeEditSubmit: { | |
130 | + type: Function as PropType< | |
131 | + (data: { | |
132 | + record: Recordable; | |
133 | + index: number; | |
134 | + key: string | number; | |
135 | + value: any; | |
136 | + }) => Promise<any> | |
137 | + >, | |
138 | + }, | |
129 | 139 | }; | ... | ... |
src/components/Table/src/types/table.ts
... | ... | @@ -363,6 +363,18 @@ export interface BasicTableProps<T = any> { |
363 | 363 | transformCellText?: Function; |
364 | 364 | |
365 | 365 | /** |
366 | + * Callback executed before editable cell submit value, not for row-editor | |
367 | + * | |
368 | + * The cell will not submit data while callback return false | |
369 | + */ | |
370 | + beforeEditSubmit?: (data: { | |
371 | + record: Recordable; | |
372 | + index: number; | |
373 | + key: string | number; | |
374 | + value: any; | |
375 | + }) => Promise<any>; | |
376 | + | |
377 | + /** | |
366 | 378 | * Callback executed when pagination, filters or sorter is changed |
367 | 379 | * @param pagination |
368 | 380 | * @param filters | ... | ... |
src/views/demo/table/EditCellTable.vue
... | ... | @@ -4,6 +4,7 @@ |
4 | 4 | @register="registerTable" |
5 | 5 | @edit-end="handleEditEnd" |
6 | 6 | @edit-cancel="handleEditCancel" |
7 | + :beforeEditSubmit="beforeEditSubmit" | |
7 | 8 | /> |
8 | 9 | </div> |
9 | 10 | </template> |
... | ... | @@ -14,6 +15,7 @@ |
14 | 15 | |
15 | 16 | import { demoListApi } from '/@/api/demo/table'; |
16 | 17 | import { treeOptionsListApi } from '/@/api/demo/tree'; |
18 | + import { useMessage } from '/@/hooks/web/useMessage'; | |
17 | 19 | const columns: BasicColumn[] = [ |
18 | 20 | { |
19 | 21 | title: '输入框', |
... | ... | @@ -93,7 +95,7 @@ |
93 | 95 | }, |
94 | 96 | { |
95 | 97 | title: '远程下拉树', |
96 | - dataIndex: 'name7', | |
98 | + dataIndex: 'name71', | |
97 | 99 | edit: true, |
98 | 100 | editComponent: 'ApiTreeSelect', |
99 | 101 | editRule: false, |
... | ... | @@ -157,8 +159,44 @@ |
157 | 159 | bordered: true, |
158 | 160 | }); |
159 | 161 | |
162 | + const { createMessage } = useMessage(); | |
163 | + | |
160 | 164 | function handleEditEnd({ record, index, key, value }: Recordable) { |
161 | 165 | console.log(record, index, key, value); |
166 | + return false; | |
167 | + } | |
168 | + | |
169 | + // 模拟将指定数据保存 | |
170 | + function feakSave({ value, key, id }) { | |
171 | + createMessage.loading({ | |
172 | + content: `正在模拟保存${key}`, | |
173 | + key: '_save_fake_data', | |
174 | + duration: 0, | |
175 | + }); | |
176 | + return new Promise((resolve) => { | |
177 | + setTimeout(() => { | |
178 | + if (value === '') { | |
179 | + createMessage.error({ | |
180 | + content: '保存失败:不能为空', | |
181 | + key: '_save_fake_data', | |
182 | + duration: 2, | |
183 | + }); | |
184 | + resolve(false); | |
185 | + } else { | |
186 | + createMessage.success({ | |
187 | + content: `记录${id}的${key}已保存`, | |
188 | + key: '_save_fake_data', | |
189 | + duration: 2, | |
190 | + }); | |
191 | + resolve(true); | |
192 | + } | |
193 | + }, 2000); | |
194 | + }); | |
195 | + } | |
196 | + | |
197 | + async function beforeEditSubmit({ record, index, key, value }) { | |
198 | + console.log('单元格数据正在准备提交', { record, index, key, value }); | |
199 | + return await feakSave({ id: record.id, key, value }); | |
162 | 200 | } |
163 | 201 | |
164 | 202 | function handleEditCancel() { |
... | ... | @@ -169,6 +207,7 @@ |
169 | 207 | registerTable, |
170 | 208 | handleEditEnd, |
171 | 209 | handleEditCancel, |
210 | + beforeEditSubmit, | |
172 | 211 | }; |
173 | 212 | }, |
174 | 213 | }); | ... | ... |