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,25 +11,27 @@ | ||
11 | <FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" /> | 11 | <FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" /> |
12 | </div> | 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 | </div> | 33 | </div> |
32 | - </div> | 34 | + </a-spin> |
33 | </div> | 35 | </div> |
34 | </template> | 36 | </template> |
35 | <script lang="ts"> | 37 | <script lang="ts"> |
@@ -48,12 +50,13 @@ | @@ -48,12 +50,13 @@ | ||
48 | import { propTypes } from '/@/utils/propTypes'; | 50 | import { propTypes } from '/@/utils/propTypes'; |
49 | import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is'; | 51 | import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is'; |
50 | import { createPlaceholderMessage } from './helper'; | 52 | import { createPlaceholderMessage } from './helper'; |
51 | - import { omit, set } from 'lodash-es'; | 53 | + import { omit, pick, set } from 'lodash-es'; |
52 | import { treeToList } from '/@/utils/helper/treeHelper'; | 54 | import { treeToList } from '/@/utils/helper/treeHelper'; |
55 | + import { Spin } from 'ant-design-vue'; | ||
53 | 56 | ||
54 | export default defineComponent({ | 57 | export default defineComponent({ |
55 | name: 'EditableCell', | 58 | name: 'EditableCell', |
56 | - components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent }, | 59 | + components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, ASpin: Spin }, |
57 | directives: { | 60 | directives: { |
58 | clickOutside, | 61 | clickOutside, |
59 | }, | 62 | }, |
@@ -80,6 +83,7 @@ | @@ -80,6 +83,7 @@ | ||
80 | const optionsRef = ref<LabelValueOptions>([]); | 83 | const optionsRef = ref<LabelValueOptions>([]); |
81 | const currentValueRef = ref<any>(props.value); | 84 | const currentValueRef = ref<any>(props.value); |
82 | const defaultValueRef = ref<any>(props.value); | 85 | const defaultValueRef = ref<any>(props.value); |
86 | + const spinning = ref<boolean>(false); | ||
83 | 87 | ||
84 | const { prefixCls } = useDesign('editable-cell'); | 88 | const { prefixCls } = useDesign('editable-cell'); |
85 | 89 | ||
@@ -246,6 +250,35 @@ | @@ -246,6 +250,35 @@ | ||
246 | 250 | ||
247 | const dataKey = (dataIndex || key) as string; | 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 | set(record, dataKey, value); | 282 | set(record, dataKey, value); |
250 | //const record = await table.updateTableData(index, dataKey, value); | 283 | //const record = await table.updateTableData(index, dataKey, value); |
251 | needEmit && table.emit?.('edit-end', { record, index, key, value }); | 284 | needEmit && table.emit?.('edit-end', { record, index, key, value }); |
@@ -368,6 +401,7 @@ | @@ -368,6 +401,7 @@ | ||
368 | getValues, | 401 | getValues, |
369 | handleEnter, | 402 | handleEnter, |
370 | handleSubmitClick, | 403 | handleSubmitClick, |
404 | + spinning, | ||
371 | }; | 405 | }; |
372 | }, | 406 | }, |
373 | }); | 407 | }); |
src/components/Table/src/props.ts
@@ -126,4 +126,14 @@ export const basicProps = { | @@ -126,4 +126,14 @@ export const basicProps = { | ||
126 | type: Object as PropType<{ x: number | true; y: number }>, | 126 | type: Object as PropType<{ x: number | true; y: number }>, |
127 | default: null, | 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,6 +363,18 @@ export interface BasicTableProps<T = any> { | ||
363 | transformCellText?: Function; | 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 | * Callback executed when pagination, filters or sorter is changed | 378 | * Callback executed when pagination, filters or sorter is changed |
367 | * @param pagination | 379 | * @param pagination |
368 | * @param filters | 380 | * @param filters |
src/views/demo/table/EditCellTable.vue
@@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
4 | @register="registerTable" | 4 | @register="registerTable" |
5 | @edit-end="handleEditEnd" | 5 | @edit-end="handleEditEnd" |
6 | @edit-cancel="handleEditCancel" | 6 | @edit-cancel="handleEditCancel" |
7 | + :beforeEditSubmit="beforeEditSubmit" | ||
7 | /> | 8 | /> |
8 | </div> | 9 | </div> |
9 | </template> | 10 | </template> |
@@ -14,6 +15,7 @@ | @@ -14,6 +15,7 @@ | ||
14 | 15 | ||
15 | import { demoListApi } from '/@/api/demo/table'; | 16 | import { demoListApi } from '/@/api/demo/table'; |
16 | import { treeOptionsListApi } from '/@/api/demo/tree'; | 17 | import { treeOptionsListApi } from '/@/api/demo/tree'; |
18 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
17 | const columns: BasicColumn[] = [ | 19 | const columns: BasicColumn[] = [ |
18 | { | 20 | { |
19 | title: '输入框', | 21 | title: '输入框', |
@@ -93,7 +95,7 @@ | @@ -93,7 +95,7 @@ | ||
93 | }, | 95 | }, |
94 | { | 96 | { |
95 | title: '远程下拉树', | 97 | title: '远程下拉树', |
96 | - dataIndex: 'name7', | 98 | + dataIndex: 'name71', |
97 | edit: true, | 99 | edit: true, |
98 | editComponent: 'ApiTreeSelect', | 100 | editComponent: 'ApiTreeSelect', |
99 | editRule: false, | 101 | editRule: false, |
@@ -157,8 +159,44 @@ | @@ -157,8 +159,44 @@ | ||
157 | bordered: true, | 159 | bordered: true, |
158 | }); | 160 | }); |
159 | 161 | ||
162 | + const { createMessage } = useMessage(); | ||
163 | + | ||
160 | function handleEditEnd({ record, index, key, value }: Recordable) { | 164 | function handleEditEnd({ record, index, key, value }: Recordable) { |
161 | console.log(record, index, key, value); | 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 | function handleEditCancel() { | 202 | function handleEditCancel() { |
@@ -169,6 +207,7 @@ | @@ -169,6 +207,7 @@ | ||
169 | registerTable, | 207 | registerTable, |
170 | handleEditEnd, | 208 | handleEditEnd, |
171 | handleEditCancel, | 209 | handleEditCancel, |
210 | + beforeEditSubmit, | ||
172 | }; | 211 | }; |
173 | }, | 212 | }, |
174 | }); | 213 | }); |