@vue-ui-kit/ant 2.0.3 → 2.0.5
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/README.md +9 -2
- package/dist/cjs/index.js +3 -3
- package/dist/declarations/antProxy.d.ts +21 -1
- package/dist/es/index.js +3233 -2867
- package/dist/packages/components/PCanvasGrid.vue.d.ts +697 -1
- package/dist/packages/components/PCanvasTable.vue.d.ts +4 -1
- package/dist/packages/components/PGrid.vue.d.ts +1559 -2
- package/dist/packages/components/RenderItemSlots.d.ts +2 -2
- package/dist/packages/utils/config.d.ts +4 -0
- package/dist/style.css +1 -1
- package/dist/style.scss +17 -6
- package/package.json +3 -5
- package/src/declarations/antProxy.ts +26 -1
- package/src/packages/components/PCanvasGrid.vue +529 -2
- package/src/packages/components/PCanvasTable.vue +34 -11
- package/src/packages/components/PGrid.vue +2 -2
- package/src/packages/styles/index.scss +17 -6
- package/src/packages/utils/config.ts +16 -1
|
@@ -1,6 +1,533 @@
|
|
|
1
|
-
<script
|
|
1
|
+
<script
|
|
2
|
+
lang="ts"
|
|
3
|
+
setup
|
|
4
|
+
generic="D extends Recordable = Recordable, F extends Recordable = Recordable"
|
|
5
|
+
name="PCanvasGrid"
|
|
6
|
+
>
|
|
2
7
|
import PCanvasTable from './PCanvasTable.vue';
|
|
8
|
+
import {
|
|
9
|
+
computed,
|
|
10
|
+
useAttrs,
|
|
11
|
+
ref,
|
|
12
|
+
Ref,
|
|
13
|
+
reactive,
|
|
14
|
+
onMounted,
|
|
15
|
+
watch,
|
|
16
|
+
toRefs,
|
|
17
|
+
onBeforeUnmount,
|
|
18
|
+
} from 'vue';
|
|
19
|
+
import {
|
|
20
|
+
debounce,
|
|
21
|
+
get,
|
|
22
|
+
isBoolean,
|
|
23
|
+
isFunction,
|
|
24
|
+
isObject,
|
|
25
|
+
isString,
|
|
26
|
+
omit,
|
|
27
|
+
toNumber,
|
|
28
|
+
uniq,
|
|
29
|
+
} from 'xe-utils';
|
|
30
|
+
import {
|
|
31
|
+
CanvasColumnProps,
|
|
32
|
+
PCanvasGridProps,
|
|
33
|
+
PCanvasTableInstance,
|
|
34
|
+
PFormItemProps,
|
|
35
|
+
ResponsePathConfig,
|
|
36
|
+
} from '#/antProxy';
|
|
37
|
+
import { v4 as uuid_v4 } from 'uuid';
|
|
38
|
+
import { isGoodValue } from '@/utils/is';
|
|
39
|
+
import PFormCol from '@/components/PFormCol.vue';
|
|
40
|
+
import { defaultLabelCol } from '@/utils/core';
|
|
41
|
+
import Icon from '@/renders/Icon';
|
|
42
|
+
import { $confirm, $error, $success, $warning } from '@/hooks/useMessage';
|
|
43
|
+
import {
|
|
44
|
+
Button as AButton,
|
|
45
|
+
Form as AForm,
|
|
46
|
+
Row as ARow,
|
|
47
|
+
Spin as ASpin,
|
|
48
|
+
Pagination as APagination,
|
|
49
|
+
} from 'ant-design-vue';
|
|
50
|
+
import { DownOutlined } from '@ant-design/icons-vue';
|
|
51
|
+
import { getCanvasTableDefaults, getGridDefaults } from '@/utils/config';
|
|
52
|
+
import { eachTree } from '@/utils/treeHelper';
|
|
53
|
+
const props = defineProps<PCanvasGridProps<D, F>>();
|
|
54
|
+
const attrs = useAttrs();
|
|
55
|
+
const emit = defineEmits<{
|
|
56
|
+
(
|
|
57
|
+
event: 'toolbarButtonClick',
|
|
58
|
+
data: { data: D[]; code: string; selectedKeys: Array<string | number>; records: D[] },
|
|
59
|
+
): void;
|
|
60
|
+
(
|
|
61
|
+
event: 'toolbarToolClick',
|
|
62
|
+
data: { data: D[]; code: string; selectedKeys: Array<string | number>; records: D[] },
|
|
63
|
+
): void;
|
|
64
|
+
(event: 'pick', data: { row: D; field: string }): void;
|
|
65
|
+
(event: 'resetQuery'): void;
|
|
66
|
+
}>();
|
|
67
|
+
|
|
68
|
+
const { formConfig, pageConfig, columns, toolbarConfig, proxyConfig, config, selectConfig } =
|
|
69
|
+
toRefs(props);
|
|
70
|
+
const gridDefaults = getGridDefaults();
|
|
71
|
+
const canvasTableDefaults = getCanvasTableDefaults();
|
|
72
|
+
const renderFormKey = ref(uuid_v4());
|
|
73
|
+
const queryFormData = ref<Partial<F>>({}) as Ref<Partial<F>>;
|
|
74
|
+
const propsWithDefaults = computed(() => ({
|
|
75
|
+
...props,
|
|
76
|
+
config: {
|
|
77
|
+
...canvasTableDefaults,
|
|
78
|
+
...props.config,
|
|
79
|
+
},
|
|
80
|
+
lazyReset: props.lazyReset ?? gridDefaults.lazyReset ?? false,
|
|
81
|
+
fitCanvasHeight: props.fitHeight ?? gridDefaults.fitCanvasHeight ?? 100,
|
|
82
|
+
}));
|
|
83
|
+
const mode = computed<'list' | 'pagination' | 'bad'>(() =>
|
|
84
|
+
proxyConfig.value && proxyConfig.value.ajax
|
|
85
|
+
? pageConfig.value
|
|
86
|
+
? 'pagination'
|
|
87
|
+
: 'list'
|
|
88
|
+
: 'bad',
|
|
89
|
+
);
|
|
90
|
+
const pg = computed(() =>
|
|
91
|
+
mode.value === 'pagination'
|
|
92
|
+
? {
|
|
93
|
+
current: pagination.page,
|
|
94
|
+
total: totalCount.value,
|
|
95
|
+
pageSize: pagination.size,
|
|
96
|
+
responsive: false,
|
|
97
|
+
showSizeChanger: true,
|
|
98
|
+
showTotal: (total: number) => `共${total}条数据`,
|
|
99
|
+
}
|
|
100
|
+
: false,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const fc = computed(() => omit({ labelCol: defaultLabelCol, ...formConfig.value }, ['items']));
|
|
104
|
+
const loading = reactive({
|
|
105
|
+
toolbar: false,
|
|
106
|
+
form: false,
|
|
107
|
+
table: false,
|
|
108
|
+
});
|
|
109
|
+
const pagination = reactive<IPage>({
|
|
110
|
+
page: 1,
|
|
111
|
+
size: pageConfig.value?.pageSize ?? 10,
|
|
112
|
+
});
|
|
113
|
+
const dataSeed = ref(0);
|
|
114
|
+
const totalCount = ref(0);
|
|
115
|
+
const tableData = ref<D[]>([]) as Ref<D[]>;
|
|
116
|
+
const formEl = ref<InstanceType<typeof AForm>>();
|
|
117
|
+
const canvasTableRef = ref<PCanvasTableInstance<D>>();
|
|
118
|
+
const boxEl = ref<HTMLDivElement>();
|
|
119
|
+
const pFormWrapper = ref<HTMLDivElement>();
|
|
120
|
+
const renderHeight = ref(500);
|
|
121
|
+
/* 分页时缓存的跨页选择数据,风险:数据可能会更新导致串页,读取计算的时候要去重 */
|
|
122
|
+
const pageSelections = ref<{
|
|
123
|
+
[key: number]: D[];
|
|
124
|
+
}>({});
|
|
125
|
+
const handleSelectionChange = (selection: D[]) => {
|
|
126
|
+
pageSelections.value[pagination.page] = selection;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const codeLoadings = reactive<Record<string, boolean>>(
|
|
130
|
+
[
|
|
131
|
+
...(props.toolbarConfig?.buttons?.map((m) => m.code) ?? []).filter((f) => f),
|
|
132
|
+
...(props.toolbarConfig?.tools?.map((m) => m.code) ?? []).filter((f) => f),
|
|
133
|
+
].reduce(
|
|
134
|
+
(acc, cur) => {
|
|
135
|
+
acc[cur!] = false;
|
|
136
|
+
return acc;
|
|
137
|
+
},
|
|
138
|
+
{} as Record<string, boolean>,
|
|
139
|
+
),
|
|
140
|
+
);
|
|
141
|
+
const setBtnLoading = (code: string, value: boolean) => {
|
|
142
|
+
codeLoadings[code] = value;
|
|
143
|
+
};
|
|
144
|
+
const setLoadings = (value: boolean | Record<string, boolean>) => {
|
|
145
|
+
if (isObject(value)) {
|
|
146
|
+
/* 判断value的toolbar和form 有则更新loading对应值,再判断value的table字段,有则canvasTableRef.value.setLoading*/
|
|
147
|
+
if (Object.prototype.hasOwnProperty.call(value, 'toolbar')) {
|
|
148
|
+
loading.toolbar = value.toolbar;
|
|
149
|
+
}
|
|
150
|
+
if (Object.prototype.hasOwnProperty.call(value, 'form')) {
|
|
151
|
+
loading.form = value.form;
|
|
152
|
+
}
|
|
153
|
+
if (Object.prototype.hasOwnProperty.call(value, 'table')) {
|
|
154
|
+
loading.table = value.table;
|
|
155
|
+
}
|
|
156
|
+
} else if (isBoolean(value)) {
|
|
157
|
+
loading.form = value;
|
|
158
|
+
loading.toolbar = value;
|
|
159
|
+
loading.table = value;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const selectedRecords = computed<D[]>(() =>
|
|
163
|
+
uniq(
|
|
164
|
+
Object.values(pageSelections.value).flat(),
|
|
165
|
+
(f) => f[props.config?.ROW_KEY ?? 'id'] as string | number,
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
const handleFormSubmit = () => {
|
|
169
|
+
resetPage();
|
|
170
|
+
};
|
|
171
|
+
const selectedRowKeys = computed<Array<string | number>>(() =>
|
|
172
|
+
selectedRecords.value.map((f) => f[props.config?.ROW_KEY ?? 'id'] as string | number),
|
|
173
|
+
);
|
|
174
|
+
const handleResponse = (response: Recordable, pathConfig?: ResponsePathConfig<D>) =>
|
|
175
|
+
pathConfig
|
|
176
|
+
? {
|
|
177
|
+
list: isString(pathConfig.list)
|
|
178
|
+
? get(response, pathConfig.list)
|
|
179
|
+
: isFunction(pathConfig.list)
|
|
180
|
+
? pathConfig.list(response)
|
|
181
|
+
: undefined,
|
|
182
|
+
total: isString(pathConfig.total)
|
|
183
|
+
? get(response, pathConfig.total)
|
|
184
|
+
: isFunction(pathConfig.total)
|
|
185
|
+
? pathConfig.total(response)
|
|
186
|
+
: undefined,
|
|
187
|
+
result: isString(pathConfig.result)
|
|
188
|
+
? get(response, pathConfig.result)
|
|
189
|
+
: isFunction(pathConfig.result)
|
|
190
|
+
? pathConfig.result(response)
|
|
191
|
+
: undefined,
|
|
192
|
+
message: isString(pathConfig.message)
|
|
193
|
+
? get(response, pathConfig.message)
|
|
194
|
+
: isFunction(pathConfig.message)
|
|
195
|
+
? pathConfig.message(response)
|
|
196
|
+
: undefined,
|
|
197
|
+
}
|
|
198
|
+
: {
|
|
199
|
+
list: response,
|
|
200
|
+
};
|
|
201
|
+
const toolBtnClick = (code: string) => {
|
|
202
|
+
emit('toolbarButtonClick', {
|
|
203
|
+
data: tableData.value,
|
|
204
|
+
code,
|
|
205
|
+
selectedKeys: selectedRowKeys.value,
|
|
206
|
+
records: selectedRecords.value,
|
|
207
|
+
});
|
|
208
|
+
innerToolbarHandler(code);
|
|
209
|
+
};
|
|
210
|
+
const debounceToolBtnClick = debounce(toolBtnClick, 100);
|
|
211
|
+
const toolBtnMenuClick = ({ key }) => debounceToolBtnClick(key);
|
|
212
|
+
const toolToolClick = (code: string) => {
|
|
213
|
+
emit('toolbarToolClick', {
|
|
214
|
+
data: tableData.value,
|
|
215
|
+
code,
|
|
216
|
+
selectedKeys: selectedRowKeys.value,
|
|
217
|
+
records: selectedRecords.value,
|
|
218
|
+
});
|
|
219
|
+
innerToolbarHandler(code);
|
|
220
|
+
};
|
|
221
|
+
const debounceToolToolClick = debounce(toolToolClick, 100);
|
|
222
|
+
|
|
223
|
+
const fetchData = () =>
|
|
224
|
+
new Promise<D[]>((resolve) => {
|
|
225
|
+
if (mode.value !== 'bad') {
|
|
226
|
+
setLoadings({ table: true, form: true });
|
|
227
|
+
const { ajax } = proxyConfig.value!;
|
|
228
|
+
ajax
|
|
229
|
+
.query({
|
|
230
|
+
form: queryFormData.value,
|
|
231
|
+
page: pagination,
|
|
232
|
+
})
|
|
233
|
+
.then((response) => {
|
|
234
|
+
const { list, total, result, message } = handleResponse(
|
|
235
|
+
response,
|
|
236
|
+
proxyConfig.value!.response,
|
|
237
|
+
);
|
|
238
|
+
if (list) {
|
|
239
|
+
tableData.value = list as D[];
|
|
240
|
+
dataSeed.value++;
|
|
241
|
+
} else if (result) {
|
|
242
|
+
tableData.value = result as D[];
|
|
243
|
+
totalCount.value = total ?? result.length;
|
|
244
|
+
dataSeed.value++;
|
|
245
|
+
} else {
|
|
246
|
+
tableData.value = [];
|
|
247
|
+
dataSeed.value++;
|
|
248
|
+
if (message) {
|
|
249
|
+
$warning(message);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
resolve(tableData.value as D[]);
|
|
253
|
+
})
|
|
254
|
+
.catch((e) => {
|
|
255
|
+
console.error('fetchData error', e);
|
|
256
|
+
resolve([] as D[]);
|
|
257
|
+
})
|
|
258
|
+
.finally(() => {
|
|
259
|
+
setLoadings({ table: false, form: false });
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
const handleTableChange = (current: number, pageSize: number) => {
|
|
264
|
+
pagination.page = current;
|
|
265
|
+
pagination.size = pageSize;
|
|
266
|
+
return fetchData();
|
|
267
|
+
};
|
|
268
|
+
const resetPage = () => {
|
|
269
|
+
pagination.page = 1;
|
|
270
|
+
pageSelections.value = {};
|
|
271
|
+
return fetchData();
|
|
272
|
+
};
|
|
273
|
+
const debounceFetchData = debounce(fetchData, 160);
|
|
274
|
+
const passQuery = (params: Partial<F>, lazy?: boolean) => {
|
|
275
|
+
Object.assign(queryFormData.value, params);
|
|
276
|
+
pagination.page = 1;
|
|
277
|
+
return lazy ? Promise.resolve() : debounceFetchData();
|
|
278
|
+
};
|
|
279
|
+
const innerToolbarHandler = (code: string) => {
|
|
280
|
+
const { ajax } = proxyConfig.value!;
|
|
281
|
+
switch (code) {
|
|
282
|
+
case 'multiDelete':
|
|
283
|
+
if (ajax.multiDelete) {
|
|
284
|
+
if (selectedRowKeys.value.length > 0) {
|
|
285
|
+
$confirm({
|
|
286
|
+
title: '警告',
|
|
287
|
+
content: '确认删除选中的数据吗?',
|
|
288
|
+
}).then(() => {
|
|
289
|
+
setLoadings({ table: true, toolbar: true });
|
|
290
|
+
ajax.multiDelete!(selectedRowKeys.value)
|
|
291
|
+
.then(() => {
|
|
292
|
+
$success('删除成功');
|
|
293
|
+
resetPage();
|
|
294
|
+
})
|
|
295
|
+
.catch(() => {
|
|
296
|
+
$error('删除失败');
|
|
297
|
+
})
|
|
298
|
+
.finally(() => {
|
|
299
|
+
setLoadings({ table: false, toolbar: false });
|
|
300
|
+
codeLoadings.multiDelete = false;
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
} else {
|
|
304
|
+
$warning('请选择要删除的数据');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const finalColumns = computed<CanvasColumnProps<D>[]>(() =>
|
|
311
|
+
selectConfig.value?.multiple
|
|
312
|
+
? [
|
|
313
|
+
{
|
|
314
|
+
type: 'selection',
|
|
315
|
+
width: 40,
|
|
316
|
+
widthFillDisable: true,
|
|
317
|
+
title: '',
|
|
318
|
+
},
|
|
319
|
+
...columns.value,
|
|
320
|
+
]
|
|
321
|
+
: columns.value,
|
|
322
|
+
);
|
|
323
|
+
const resetQueryFormData = (lazy?: boolean) => {
|
|
324
|
+
if (formConfig.value && formConfig.value.items.length > 0) {
|
|
325
|
+
if (formConfig.value.customReset) {
|
|
326
|
+
formConfig.value.customReset();
|
|
327
|
+
} else {
|
|
328
|
+
const obj: Partial<F> = {};
|
|
329
|
+
eachTree(formConfig.value.items, (item) => {
|
|
330
|
+
if (item.field && item.itemRender) {
|
|
331
|
+
if (isGoodValue(item.itemRender.defaultValue)) {
|
|
332
|
+
obj[item.field as keyof F] = item.itemRender.defaultValue;
|
|
333
|
+
} else {
|
|
334
|
+
obj[item.field as keyof F] = undefined;
|
|
335
|
+
}
|
|
336
|
+
} else if (item.field && item.slots) {
|
|
337
|
+
if (isGoodValue(item.slots.defaultValue)) {
|
|
338
|
+
obj[item.field as keyof F] = item.slots.defaultValue;
|
|
339
|
+
} else {
|
|
340
|
+
obj[item.field as keyof F] = undefined;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
queryFormData.value = obj;
|
|
345
|
+
}
|
|
346
|
+
emit('resetQuery');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
pagination.page = 1;
|
|
350
|
+
if (!lazy) {
|
|
351
|
+
debounceFetchData();
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
const reload = () => {
|
|
355
|
+
pageSelections.value = {};
|
|
356
|
+
return resetQueryFormData();
|
|
357
|
+
};
|
|
358
|
+
const resizeTable = () => {
|
|
359
|
+
const pNode = boxEl.value?.parentElement;
|
|
360
|
+
const ph = pNode ? window.getComputedStyle(pNode).height : '0px';
|
|
361
|
+
const formOriginHeight = pFormWrapper.value
|
|
362
|
+
? window.getComputedStyle(pFormWrapper.value).height
|
|
363
|
+
: '0px';
|
|
364
|
+
const formHeight = formOriginHeight.includes('px')
|
|
365
|
+
? toNumber(formOriginHeight.replace('px', ''))
|
|
366
|
+
: 0;
|
|
367
|
+
const showCountHeight = selectConfig.value?.showCount ? 22 : 0;
|
|
368
|
+
renderHeight.value =
|
|
369
|
+
toNumber(ph.replace('px', '')) -
|
|
370
|
+
propsWithDefaults.value.fitCanvasHeight -
|
|
371
|
+
(props.toolbarConfig ? 30 : 0) -
|
|
372
|
+
formHeight -
|
|
373
|
+
showCountHeight;
|
|
374
|
+
};
|
|
375
|
+
let observer: MutationObserver;
|
|
376
|
+
onMounted(() => {
|
|
377
|
+
resizeTable();
|
|
378
|
+
window.addEventListener('resize', resizeTable);
|
|
379
|
+
// 还需要在组件根节点由不可见转为可见时重新计算高度(display:none等样式控制时)
|
|
380
|
+
observer = new MutationObserver((mutations) => {
|
|
381
|
+
mutations.forEach((mutation) => {
|
|
382
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
|
|
383
|
+
const el = mutation.target as HTMLElement;
|
|
384
|
+
const style = window.getComputedStyle(el);
|
|
385
|
+
if (style.display !== 'none') {
|
|
386
|
+
resizeTable();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
if (boxEl.value) {
|
|
392
|
+
observer.observe(boxEl.value, { attributes: true, attributeFilter: ['style'] });
|
|
393
|
+
}
|
|
394
|
+
resetQueryFormData(props.manualFetch);
|
|
395
|
+
});
|
|
396
|
+
onBeforeUnmount(() => {
|
|
397
|
+
window.removeEventListener('resize', resizeTable);
|
|
398
|
+
observer.disconnect();
|
|
399
|
+
});
|
|
400
|
+
defineExpose({
|
|
401
|
+
commitProxy: {
|
|
402
|
+
query: debounceFetchData,
|
|
403
|
+
reload,
|
|
404
|
+
reloadPage: resetPage,
|
|
405
|
+
passQuery,
|
|
406
|
+
},
|
|
407
|
+
$canvasTable: computed(() => canvasTableRef.value),
|
|
408
|
+
selectedRowKeys,
|
|
409
|
+
setBtnLoading,
|
|
410
|
+
selectedRecords,
|
|
411
|
+
$form: computed(() => formEl.value),
|
|
412
|
+
getFormData: () => queryFormData.value,
|
|
413
|
+
setLoadings,
|
|
414
|
+
resizeTable,
|
|
415
|
+
});
|
|
3
416
|
</script>
|
|
4
417
|
<template>
|
|
5
|
-
<div>
|
|
418
|
+
<div ref="boxEl" class="h-full p-wrapper flex flex-col gap-8px overflow-y-auto" v-bind="attrs">
|
|
419
|
+
<div v-if="mode === 'bad'">请检查配置</div>
|
|
420
|
+
<template v-else>
|
|
421
|
+
<div
|
|
422
|
+
v-if="formConfig?.items?.some((s: PFormItemProps<F>) => s.field && s.itemRender)"
|
|
423
|
+
class="p-pane p-form-wrapper"
|
|
424
|
+
ref="pFormWrapper"
|
|
425
|
+
>
|
|
426
|
+
<a-spin :spinning="loading.form">
|
|
427
|
+
<a-form
|
|
428
|
+
:key="renderFormKey"
|
|
429
|
+
ref="formEl"
|
|
430
|
+
:model="queryFormData"
|
|
431
|
+
v-bind="fc"
|
|
432
|
+
@submit="handleFormSubmit"
|
|
433
|
+
>
|
|
434
|
+
<a-row :gutter="[6, 12]">
|
|
435
|
+
<p-form-col
|
|
436
|
+
v-for="(item, idx) in formConfig!.items"
|
|
437
|
+
:key="`_col_${item.field || idx}`"
|
|
438
|
+
:form-data="queryFormData"
|
|
439
|
+
:item="item as PFormItemProps<Partial<F>>"
|
|
440
|
+
@reset="resetQueryFormData(propsWithDefaults.lazyReset)"
|
|
441
|
+
/>
|
|
442
|
+
</a-row>
|
|
443
|
+
</a-form>
|
|
444
|
+
</a-spin>
|
|
445
|
+
</div>
|
|
446
|
+
<div
|
|
447
|
+
v-if="toolbarConfig"
|
|
448
|
+
class="p-canvas-toolbar-wrapper flex items-center w-full justify-between p-theme-bg pt-8px px-16px"
|
|
449
|
+
>
|
|
450
|
+
<div class="flex items-center flex-1 gap-4px">
|
|
451
|
+
<template v-if="toolbarConfig.buttons && toolbarConfig.buttons.length > 0">
|
|
452
|
+
<template v-for="(btn, idx) in toolbarConfig.buttons" :key="idx">
|
|
453
|
+
<a-dropdown v-if="btn.dropdowns && btn.dropdowns.length">
|
|
454
|
+
<template #overlay>
|
|
455
|
+
<a-menu @click="toolBtnMenuClick">
|
|
456
|
+
<a-menu-item v-for="sub in btn.dropdowns" :key="sub.code"
|
|
457
|
+
>{{ sub.content }}
|
|
458
|
+
</a-menu-item>
|
|
459
|
+
</a-menu>
|
|
460
|
+
</template>
|
|
461
|
+
<a-button
|
|
462
|
+
:type="btn.type"
|
|
463
|
+
:size="btn.size ?? 'middle'"
|
|
464
|
+
:disabled="toolbarConfig.disabled || btn.disabled"
|
|
465
|
+
:loading="loading.toolbar || (!!btn.code && codeLoadings[btn.code])"
|
|
466
|
+
>
|
|
467
|
+
<Icon v-if="btn.icon" :icon="btn.icon" />
|
|
468
|
+
{{ btn.content }}
|
|
469
|
+
<DownOutlined />
|
|
470
|
+
</a-button>
|
|
471
|
+
</a-dropdown>
|
|
472
|
+
<a-button
|
|
473
|
+
v-else-if="btn.code"
|
|
474
|
+
:type="btn.type"
|
|
475
|
+
:size="btn.size ?? 'middle'"
|
|
476
|
+
:disabled="toolbarConfig.disabled || btn.disabled"
|
|
477
|
+
:loading="loading.toolbar || (!!btn.code && codeLoadings[btn.code])"
|
|
478
|
+
@click="debounceToolBtnClick(btn.code)"
|
|
479
|
+
>
|
|
480
|
+
<Icon v-if="btn.icon" :icon="btn.icon" />
|
|
481
|
+
{{ btn.content }}
|
|
482
|
+
</a-button>
|
|
483
|
+
<div v-else></div>
|
|
484
|
+
</template>
|
|
485
|
+
</template>
|
|
486
|
+
</div>
|
|
487
|
+
<span class="flex items-center gap-4px">
|
|
488
|
+
<template v-if="toolbarConfig.tools && toolbarConfig.tools.length > 0">
|
|
489
|
+
<a-button
|
|
490
|
+
v-for="(tool, idx) in toolbarConfig.tools"
|
|
491
|
+
:key="idx"
|
|
492
|
+
:type="tool.type"
|
|
493
|
+
:size="tool.size ?? 'middle'"
|
|
494
|
+
:disabled="toolbarConfig.disabled || tool.disabled"
|
|
495
|
+
@click="debounceToolToolClick(tool.code)"
|
|
496
|
+
:loading="loading.toolbar || (!!tool.code && codeLoadings[tool.code])"
|
|
497
|
+
>
|
|
498
|
+
<Icon :icon="tool.icon" />
|
|
499
|
+
</a-button>
|
|
500
|
+
</template>
|
|
501
|
+
</span>
|
|
502
|
+
</div>
|
|
503
|
+
<div :class="`p-pane flex-1 h-0 p-inner-scroll`">
|
|
504
|
+
<div
|
|
505
|
+
v-if="selectConfig?.multiple && selectConfig.showCount"
|
|
506
|
+
class="w-full text-slate-5 pl-4"
|
|
507
|
+
>
|
|
508
|
+
已选:{{ selectedRowKeys.length }}
|
|
509
|
+
</div>
|
|
510
|
+
<p-canvas-table
|
|
511
|
+
ref="canvasTableRef"
|
|
512
|
+
:columns="finalColumns"
|
|
513
|
+
:config="{
|
|
514
|
+
...config,
|
|
515
|
+
HEIGHT: renderHeight,
|
|
516
|
+
}"
|
|
517
|
+
@selection-change="handleSelectionChange"
|
|
518
|
+
:data="tableData"
|
|
519
|
+
:loading="loading.table"
|
|
520
|
+
/>
|
|
521
|
+
<a-pagination
|
|
522
|
+
class="p-canvas-pagination"
|
|
523
|
+
v-if="mode === 'pagination'"
|
|
524
|
+
size="small"
|
|
525
|
+
:current="pagination.page"
|
|
526
|
+
:page-size="pagination.size"
|
|
527
|
+
:total="totalCount"
|
|
528
|
+
@change="handleTableChange"
|
|
529
|
+
/>
|
|
530
|
+
</div>
|
|
531
|
+
</template>
|
|
532
|
+
</div>
|
|
6
533
|
</template>
|
|
@@ -20,16 +20,28 @@
|
|
|
20
20
|
import type { CanvasColumnProps, CanvasTableProps, FormatterFunc, CellRender } from '#/antProxy';
|
|
21
21
|
import { v4 as uuidv4 } from 'uuid';
|
|
22
22
|
import { antFormatters } from '@/utils/AFormatters';
|
|
23
|
+
import { getCanvasTableDefaults } from '@/utils/config';
|
|
23
24
|
|
|
24
25
|
const emit = defineEmits<{
|
|
25
26
|
(e: 'change', value: any[]): void; // 需要默认实现change,不能动态绑定
|
|
27
|
+
(e: 'selectionChange', value: T[]): void;
|
|
26
28
|
(e: 'ready', value: EVirtTable): void;
|
|
27
29
|
}>();
|
|
28
|
-
|
|
29
|
-
DISABLED: true,
|
|
30
|
-
};
|
|
30
|
+
|
|
31
31
|
const selectedRecords = ref<T[]>([]);
|
|
32
|
-
const props = defineProps<CanvasTableProps<T, B>>()
|
|
32
|
+
const props = withDefaults(defineProps<CanvasTableProps<T, B>>(), {
|
|
33
|
+
config: () => ({}),
|
|
34
|
+
});
|
|
35
|
+
// 应用默认值 - 参考PGrid的模式
|
|
36
|
+
const canvasTableDefaults = getCanvasTableDefaults();
|
|
37
|
+
const propsWithDefaults = computed(() => ({
|
|
38
|
+
...props,
|
|
39
|
+
config: {
|
|
40
|
+
DISABLED: true, // 内部写死的默认配置
|
|
41
|
+
...canvasTableDefaults, // 全局配置
|
|
42
|
+
...props.config, // 用户传入的配置
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
33
45
|
let eVirtTable: EVirtTable | null = null;
|
|
34
46
|
const attrs = useAttrs();
|
|
35
47
|
const eVirtTableRef = ref(null);
|
|
@@ -129,10 +141,7 @@
|
|
|
129
141
|
return;
|
|
130
142
|
}
|
|
131
143
|
eVirtTable = new EVirtTable(eVirtTableRef.value, {
|
|
132
|
-
config:
|
|
133
|
-
...defaultConfig,
|
|
134
|
-
...props.config,
|
|
135
|
-
},
|
|
144
|
+
config: propsWithDefaults.value.config,
|
|
136
145
|
columns: props.columns.map((col) => parseToEVirtColumn(col)),
|
|
137
146
|
data: props.data,
|
|
138
147
|
footerData: props.footerData,
|
|
@@ -167,6 +176,7 @@
|
|
|
167
176
|
});
|
|
168
177
|
eVirtTable.on('selectionChange', (selection) => {
|
|
169
178
|
selectedRecords.value = selection;
|
|
179
|
+
emit('selectionChange', selection);
|
|
170
180
|
});
|
|
171
181
|
emit('ready', eVirtTable);
|
|
172
182
|
});
|
|
@@ -181,7 +191,7 @@
|
|
|
181
191
|
return document.body;
|
|
182
192
|
};
|
|
183
193
|
watch(
|
|
184
|
-
props.data,
|
|
194
|
+
() => props.data,
|
|
185
195
|
(newValue) => {
|
|
186
196
|
eVirtTable?.loadData(newValue);
|
|
187
197
|
},
|
|
@@ -197,7 +207,7 @@
|
|
|
197
207
|
{ deep: true },
|
|
198
208
|
);
|
|
199
209
|
watch(
|
|
200
|
-
() =>
|
|
210
|
+
() => propsWithDefaults.value.config,
|
|
201
211
|
(newValue) => {
|
|
202
212
|
eVirtTable?.loadConfig(newValue);
|
|
203
213
|
},
|
|
@@ -210,9 +220,21 @@
|
|
|
210
220
|
},
|
|
211
221
|
{ deep: true },
|
|
212
222
|
);
|
|
223
|
+
watch(
|
|
224
|
+
() => props.loading,
|
|
225
|
+
(n, p) => {
|
|
226
|
+
if (n !== p) {
|
|
227
|
+
eVirtTable?.setLoading(n);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{ immediate: true },
|
|
231
|
+
);
|
|
213
232
|
defineExpose({
|
|
214
233
|
$table: eVirtTable,
|
|
215
234
|
selectedRecords: computed(() => selectedRecords.value),
|
|
235
|
+
reloadData: () => {
|
|
236
|
+
eVirtTable?.loadData(props.data);
|
|
237
|
+
},
|
|
216
238
|
});
|
|
217
239
|
</script>
|
|
218
240
|
<template>
|
|
@@ -272,7 +294,7 @@
|
|
|
272
294
|
>
|
|
273
295
|
<div :style="view.style" v-for="view in wrapper.views" :key="view.key">
|
|
274
296
|
<div
|
|
275
|
-
class="cell"
|
|
297
|
+
class="canvas-cell"
|
|
276
298
|
v-for="cell in view.cells"
|
|
277
299
|
:key="`${cell.rowKey}_${cell.key}`"
|
|
278
300
|
:style="cell.style"
|
|
@@ -280,6 +302,7 @@
|
|
|
280
302
|
<component
|
|
281
303
|
v-if="typeof cell.render === 'function'"
|
|
282
304
|
:is="cell.render(cell)"
|
|
305
|
+
:bordered="false"
|
|
283
306
|
></component>
|
|
284
307
|
<template
|
|
285
308
|
v-else-if="typeof cell.render === 'string' && cell.render.startsWith('slot:')"
|
|
@@ -138,8 +138,8 @@
|
|
|
138
138
|
});
|
|
139
139
|
return cols;
|
|
140
140
|
});
|
|
141
|
-
const formEl = ref();
|
|
142
|
-
const tableEl = ref();
|
|
141
|
+
const formEl = ref<InstanceType<typeof AForm>>();
|
|
142
|
+
const tableEl = ref<InstanceType<typeof ATable>>();
|
|
143
143
|
const renderFormKey = ref(uuid_v4());
|
|
144
144
|
const renderTableKey = ref(uuid_v4());
|
|
145
145
|
|