@vue-ui-kit/ant 2.0.2 → 2.0.4

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.
@@ -1,6 +1,533 @@
1
- <script lang="ts" setup>
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>todo</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
- const defaultConfig = {
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,12 +141,10 @@
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,
147
+ footerData: props.footerData,
138
148
  emptyElement: eVirtTableEmptyRef.value || undefined,
139
149
  overlayerElement: eVirtTableOverlayerRef.value || undefined,
140
150
  editorElement: eVirtTableEditorRef.value || undefined,
@@ -166,6 +176,7 @@
166
176
  });
167
177
  eVirtTable.on('selectionChange', (selection) => {
168
178
  selectedRecords.value = selection;
179
+ emit('selectionChange', selection);
169
180
  });
170
181
  emit('ready', eVirtTable);
171
182
  });
@@ -180,7 +191,7 @@
180
191
  return document.body;
181
192
  };
182
193
  watch(
183
- props.data,
194
+ () => props.data,
184
195
  (newValue) => {
185
196
  eVirtTable?.loadData(newValue);
186
197
  },
@@ -196,7 +207,7 @@
196
207
  { deep: true },
197
208
  );
198
209
  watch(
199
- () => props.config,
210
+ () => propsWithDefaults.value.config,
200
211
  (newValue) => {
201
212
  eVirtTable?.loadConfig(newValue);
202
213
  },
@@ -209,9 +220,21 @@
209
220
  },
210
221
  { deep: true },
211
222
  );
223
+ watch(
224
+ () => props.loading,
225
+ (n, p) => {
226
+ if (n !== p) {
227
+ eVirtTable?.setLoading(n);
228
+ }
229
+ },
230
+ { immediate: true },
231
+ );
212
232
  defineExpose({
213
233
  $table: eVirtTable,
214
234
  selectedRecords: computed(() => selectedRecords.value),
235
+ reloadData: () => {
236
+ eVirtTable?.loadData(props.data);
237
+ },
215
238
  });
216
239
  </script>
217
240
  <template>
@@ -271,7 +294,7 @@
271
294
  >
272
295
  <div :style="view.style" v-for="view in wrapper.views" :key="view.key">
273
296
  <div
274
- class="cell"
297
+ class="canvas-cell"
275
298
  v-for="cell in view.cells"
276
299
  :key="`${cell.rowKey}_${cell.key}`"
277
300
  :style="cell.style"
@@ -279,6 +302,7 @@
279
302
  <component
280
303
  v-if="typeof cell.render === 'function'"
281
304
  :is="cell.render(cell)"
305
+ :bordered="false"
282
306
  ></component>
283
307
  <template
284
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