Skip to content

Commit 0dce36e

Browse files
authored
fix(grid): fix visible columns change wloud delete insert row (#3897)
* fix(grid): fix visible columns change delete insert row * fix(grid): optimize useNormalData composable * fix(grid): optimize useNormalData composable * test(grid): optimize e2e case * fix(grid): fix header border error * fix(grid): fix insert row can't not getRowById
1 parent f018f4f commit 0dce36e

File tree

10 files changed

+122
-33
lines changed

10 files changed

+122
-33
lines changed

examples/sites/demos/pc/app/grid/custom/column-simple.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ test('简化版列设置测试', async ({ page }) => {
44
page.on('pageerror', (exception) => expect(exception).toBeNull())
55
await page.goto('grid-custom#custom-column-simple')
66
await page.locator('.tiny-select > div').click()
7-
await page.getByRole('listitem').filter({ hasText: '员工数' }).click()
7+
await page.locator('.tiny-option-label').filter({ hasText: '员工数' }).click()
88
const thHeader = page.locator('th.tiny-grid-header__column').nth(3)
99
await expect(thHeader).toContainText('地址')
10-
await page.getByRole('listitem').filter({ hasText: '名称' }).click()
10+
await page.locator('.tiny-option-label').filter({ hasText: '名称' }).click()
1111
const thHeader2 = page.locator('th.tiny-grid-header__column').nth(1)
1212

1313
await expect(thHeader2).toContainText('员工数')

examples/sites/demos/pc/app/grid/data-source/undefined-field-defalut-value-composition-api.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
<div>
33
<tiny-button @click="addRow">新增行</tiny-button>
44
<tiny-grid ref="gridRef" :data="tableData" :edit-config="editConfig">
5+
<template #toolbar>
6+
<tiny-grid-toolbar id="undefined-field-defalut-value" :setting="{ storage: 'local' }"></tiny-grid-toolbar>
7+
</template>
58
<tiny-grid-column field="field1" title="所属区域" :editor="{}">
69
<template #edit="{ row }">
710
<tiny-input v-model="row.field1" />
@@ -22,7 +25,7 @@
2225
</template>
2326

2427
<script setup>
25-
import { TinyGrid, TinyGridColumn, TinyInput, TinyButton } from '@opentiny/vue'
28+
import { TinyGrid, TinyGridColumn, TinyInput, TinyButton, TinyGridToolbar } from '@opentiny/vue'
2629
import { ref } from 'vue'
2730
2831
const tableData = ref([

examples/sites/demos/pc/app/grid/data-source/undefined-field-defalut-value.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test('缺省数据默认值', async ({ page }) => {
55
await page.goto('grid-data-source#undefined-field-defalut-value')
66

77
const demo = page.locator('#undefined-field-defalut-value')
8+
const custom = page.locator('.tiny-grid-custom')
89

910
const firstRow = demo.locator('.tiny-grid-body__row:visible').nth(0)
1011

@@ -25,4 +26,27 @@ test('缺省数据默认值', async ({ page }) => {
2526
await firstRow.locator('.tiny-input__inner').click()
2627
await firstRow.locator('.tiny-input__inner').fill('1')
2728
await expect(firstRow.locator('.tiny-input__inner')).toHaveValue('1')
29+
30+
// 测试:新增行后通过个性化面板设置列隐藏,新增的行不应该消失
31+
const rowsBefore = await demo.locator('.tiny-grid-body__row:visible').count()
32+
expect(rowsBefore).toBeGreaterThan(2) // 原始2行 + 新增的1行
33+
34+
await page.locator('.tiny-grid-custom__setting-btn').click()
35+
36+
await custom.getByRole('row', { name: '地址' }).getByTitle('显示').getByRole('img').click()
37+
38+
await custom.getByRole('button', { name: '确定' }).click()
39+
40+
await expect(page.getByRole('cell', { name: '地址' })).not.toBeVisible()
41+
42+
// 6. 验证新增的行仍然存在(行数应该保持不变)
43+
const rowsAfter = await demo.locator('.tiny-grid-body__row:visible').count()
44+
expect(rowsAfter).toBe(rowsBefore)
45+
46+
// 7. 验证新增的行数据仍然可以编辑
47+
const newRow = demo.locator('.tiny-grid-body__row:visible').first()
48+
await newRow.locator('td').nth(0).click() // 点击第一列
49+
await newRow.locator('.tiny-input__inner').click()
50+
await newRow.locator('.tiny-input__inner').fill('test')
51+
await expect(newRow.locator('.tiny-input__inner')).toHaveValue('test')
2852
})

examples/sites/demos/pc/app/grid/data-source/undefined-field-defalut-value.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
<div>
33
<tiny-button @click="addRow">新增行</tiny-button>
44
<tiny-grid ref="grid" :data="tableData" :edit-config="editConfig">
5+
<template #toolbar>
6+
<tiny-grid-toolbar id="undefined-field-defalut-value" :setting="{ storage: 'local' }"></tiny-grid-toolbar>
7+
</template>
58
<tiny-grid-column field="field1" title="所属区域" :editor="{}">
69
<template #edit="{ row }">
710
<tiny-input v-model="row.field1" />
@@ -22,14 +25,15 @@
2225
</template>
2326

2427
<script>
25-
import { TinyGrid, TinyGridColumn, TinyInput, TinyButton } from '@opentiny/vue'
28+
import { TinyGrid, TinyGridColumn, TinyInput, TinyButton, TinyGridToolbar } from '@opentiny/vue'
2629
2730
export default {
2831
components: {
2932
TinyGrid,
3033
TinyGridColumn,
3134
TinyInput,
32-
TinyButton
35+
TinyButton,
36+
TinyGridToolbar
3337
},
3438
data() {
3539
return {

examples/sites/demos/pc/app/grid/editor/custom-editor-select.spec.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { test, expect } from '@playwright/test'
33
test('引入组件作为编辑器', async ({ page }) => {
44
page.on('pageerror', (exception) => expect(exception).toBeNull())
55
await page.goto('grid-editor#editor-custom-editor-select')
6-
await page.getByText('华中区').first().click()
7-
await page.getByRole('textbox', { name: '请选择' }).click()
8-
await page.getByRole('list').getByText('西南区').click()
9-
await page.getByRole('cell', { name: '创建时间' }).click()
6+
const demo = page.locator('#editor-custom-editor-select')
7+
await demo.getByText('华中区').first().click()
8+
await demo.locator('.tiny-input__inner').click()
9+
await page.locator('.tiny-option-label').filter({ hasText: '西南区' }).click()
10+
await demo.locator('.tiny-grid__body thead').click()
1011
await expect(page.locator('.tiny-grid-body__row').first()).toContainText('西南区')
1112
})

packages/theme/src/grid/header.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
color: var(--tv-Grid-header-text-color);
3838

3939
// 去掉最后一个竖线
40-
&:nth-last-of-type(2) {
40+
&:nth-last-of-type(1) {
4141
.@{grid-prefix-cls}-resizable.is__line:before {
4242
width: 0;
4343
}

packages/vue/src/grid/src/composable/useNormalData.ts

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,72 @@
11
import { hooks } from '@opentiny/vue-common'
22

3-
export const useNormalData = ({ props, visibleColumn }) => {
3+
export const useNormalData = ({ props, tableFullColumn }) => {
44
const $table = hooks.getCurrentInstance()?.proxy
55
// 原始数据
66
const rawData = hooks.ref([])
77
// 原始数据版本
88
const rawDataVersion = hooks.ref(0)
9+
// 所有的可编辑列
10+
const editorColumns = hooks.ref([])
11+
const editorColumnKey = hooks.ref('')
912

10-
hooks.watch([rawData, () => visibleColumn.value], ([_data, _visibleColumn]) => {
11-
if (props.editConfig && Array.isArray(_data) && _data.length > 0 && _visibleColumn.length > 0) {
12-
// 对于可编辑表格,如果编辑器对应数据行字段不存在,通过编辑器修改字段时会动态添加字段
13-
// 对于旧数据处理:watch -> handleDataChange -> loadTableData -> updateCache
14-
// 在Vue2,这种动态添加字段会触发选项式watch data,更新cache,最终导致脏数据状态不显示
15-
// 在Vue3,不会触发选项式watch data 和更新cache,不会导致脏数据状态不显示
16-
// 这里修复这种数据处理过程的不一致性:在数据改变后,先规范化数据,再更新数据缓存,再处理数据改变
17-
_data.forEach((row) => $table.defineField(row))
18-
rawDataVersion.value += 1
13+
hooks.watch(tableFullColumn, (_tableFullColumn) => {
14+
let columns = []
15+
let columnKeys = []
16+
17+
for (let i = 0; i < _tableFullColumn.length; i++) {
18+
const column = _tableFullColumn[i]
19+
20+
if (column.property && column.editor) {
21+
columns.push(column)
22+
columnKeys.push(column.propety)
23+
}
24+
}
25+
26+
// 在显示隐藏或者拖动可编辑列时,不触发editorColumns响应性更新
27+
// 在初始化时和在增加可编辑列时触发editorColumns响应性更新
28+
const keyString = columnKeys.sort().join()
29+
30+
if (editorColumnKey.value !== keyString) {
31+
editorColumnKey.value = keyString
32+
editorColumns.value = columns
1933
}
2034
})
2135

36+
// 在可编辑列改变时,尝试规范化可编辑字段,包括原始数据和临时插入数据
37+
hooks.watch(editorColumns, (_editorColumns) => {
38+
if (props.editConfig && _editorColumns.length > 0) {
39+
const _insertData = $table.editStore.insertList || []
40+
const _rawData = rawData.value || []
41+
const _data = [..._insertData, ..._rawData]
42+
43+
if (Array.isArray(_data)) {
44+
// 在增加可编辑列时,同步修改原始备份,保证字段存在
45+
_data.forEach((row) => $table.defineField(row, false, _editorColumns, true))
46+
}
47+
}
48+
})
49+
50+
// 在原始数据改变时,尝试规范化可编辑字段。不处理临时插入数据,因为后续会被清理掉。
51+
hooks.watch(rawData, (_rawData) => {
52+
const _editorColumns = editorColumns.value
53+
54+
if (props.editConfig && _editorColumns.length > 0) {
55+
const _data = _rawData || []
56+
57+
if (Array.isArray(_data)) {
58+
_data.forEach((row) => $table.defineField(row, false, _editorColumns))
59+
}
60+
}
61+
62+
// 触发表格数据刷新
63+
rawDataVersion.value += 1
64+
})
65+
2266
hooks.watch(rawDataVersion, () => {
2367
// 设置数据查找缓存,对数据进行备份,深度克隆
2468
$table.updateCache(true, props.saveSource === 'deep')
25-
// 处理数据改变
69+
// 处理表格数据刷新
2670
$table.handleDataChange()
2771
})
2872

packages/vue/src/grid/src/edit/src/methods.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,14 @@ export default {
252252
* 如果还额外传了field则还原指定单元格。
253253
*/
254254
_revertData(rows, field) {
255-
let { tableSynchData } = this
255+
let { rawData } = this
256256

257257
if (arguments.length && rows && !isArray(rows)) {
258258
rows = [rows]
259259
}
260260

261261
if (!arguments.length) {
262-
rows = tableSynchData || []
262+
rows = rawData || []
263263
}
264264

265265
for (let i = 0; i < rows.length; i++) {
@@ -281,7 +281,7 @@ export default {
281281
return this.$nextTick()
282282
}
283283

284-
return this.reloadData(tableSynchData)
284+
return this.reloadData(rawData || [])
285285
},
286286

287287
/**

packages/vue/src/grid/src/table/src/methods.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,8 @@ const Methods = {
296296
editStore.insertList = []
297297
editStore.insertMap = new Map()
298298
editStore.removeList = []
299-
// 设置全量数据,原始数据,行虚滚标记
300-
Object.assign(this, { tableFullData, tableSynchData: datas, scrollYLoad })
299+
// 设置全量数据,行虚滚标记
300+
Object.assign(this, { tableFullData, scrollYLoad })
301301

302302
if (scrollYLoad && !(height || maxHeight)) {
303303
error('ui.grid.error.scrollYHeight')
@@ -585,28 +585,41 @@ const Methods = {
585585
hasIndexColumn(column) {
586586
return column?.type === 'index'
587587
},
588-
defineField(row, copy) {
588+
defineField(row, copy, editorColumns, cache) {
589589
if (!row || typeof row !== 'object') return row
590590

591591
if (copy) {
592592
row = clone(row, true)
593593
}
594594

595595
const rowKey = getRowkey(this)
596+
const columns = editorColumns || this.visibleColumn
596597

597-
this.visibleColumn.forEach(({ property, editor }) => {
598+
columns.forEach((column) => {
599+
const { property, editor } = column
598600
const propNotExist = property && !has(row, property)
601+
599602
// 对于可编辑表格,如果编辑器对应数据行字段不存在,就修复这种字段为值 editor.defaultValue 或者 null
600603
const propDefaultValue = editor && !isUndefined(editor.defaultValue) ? editor.defaultValue : null
601604

602605
if (propNotExist) {
606+
// 在增加可编辑列时,同步修改原始备份,保证字段存在
607+
if (cache) {
608+
const originRow = this.getOriginRow(row)
609+
610+
if (originRow) {
611+
originRow[property] = propDefaultValue
612+
}
613+
}
614+
603615
if (isVue2) {
604616
this.$set(row, property, propDefaultValue)
605617
} else {
606618
set(row, property, propDefaultValue)
607619
}
608620
}
609621
})
622+
610623
// 如果行数据的唯一主键不存在,则生成
611624
const rowId = get(row, rowKey)
612625
if (isNull(rowId) || rowId === '') {
@@ -751,7 +764,7 @@ const Methods = {
751764
},
752765
// 获取表格所有数据
753766
getData(rowIndex) {
754-
const allRows = this.rawData || this.tableSynchData
767+
const allRows = this.rawData || []
755768

756769
if (!arguments.length) {
757770
return allRows.slice(0)
@@ -883,9 +896,9 @@ const Methods = {
883896
return this.scrollLoad ? this.scrollLoad.pageSize || 10 : this._graphInfo?.graphed.length || 0
884897
},
885898
getRowById(rowid) {
886-
let { fullDataRowIdData } = this
887-
let rowCache = fullDataRowIdData[rowid]
888-
return rowCache ? rowCache.row : null
899+
let { fullDataRowIdData, editStore } = this
900+
let rowCache = fullDataRowIdData[rowid]?.row || editStore?.insertMap?.get(rowid)
901+
return rowCache || null
889902
},
890903
// 获取处理后的表格数据
891904
getTableData() {

packages/vue/src/grid/src/table/src/table.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ export default defineComponent({
708708
// 模板引用
709709
const tableWrapper = hooks.ref()
710710

711-
const { rawData, rawDataVersion } = useNormalData({ props, visibleColumn })
711+
const { rawData, rawDataVersion } = useNormalData({ props, tableFullColumn })
712712

713713
// TINY主题变量
714714
const tinyTheme = hooks.ref(resolveTheme(props, context))

0 commit comments

Comments
 (0)