Skip to content

Commit 4047778

Browse files
authored
fix(grid): fix undefined field can't be marked change at grid edit (#3860)
* fix(grid): fix undefined field can't be marked change at grid edit * fix(grid): fix undefined field can't be marked change at grid edit * fix(form-item): fix input append slot wrap in form item
1 parent a5c404e commit 4047778

File tree

12 files changed

+299
-59
lines changed

12 files changed

+299
-59
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<template>
2+
<tiny-grid :data="tableData" :edit-config="editConfig">
3+
<tiny-grid-column field="field1" title="所属区域" :editor="{}">
4+
<template #edit="{ row }">
5+
<tiny-input v-model="row.field1" />
6+
</template>
7+
</tiny-grid-column>
8+
<tiny-grid-column field="field2" title="地址" :editor="{ defaultValue: '' }">
9+
<template #edit="{ row }">
10+
<tiny-input v-model="row.field2" />
11+
</template>
12+
</tiny-grid-column>
13+
<tiny-grid-column field="field3" title="邮编" :editor="{ defaultValue: '' }">
14+
<template #edit="{ row }">
15+
<tiny-input v-model="row.field3" />
16+
</template>
17+
</tiny-grid-column>
18+
</tiny-grid>
19+
</template>
20+
21+
<script setup>
22+
import { TinyGrid, TinyGridColumn, TinyInput } from '@opentiny/vue'
23+
import { ref } from 'vue'
24+
25+
const tableData = ref([
26+
{
27+
field1: 'field1'
28+
},
29+
{
30+
field1: 'field1'
31+
}
32+
])
33+
34+
const editConfig = ref({
35+
trigger: 'click',
36+
mode: 'cell',
37+
autofocus: 'input'
38+
})
39+
</script>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { test, expect } from '@playwright/test'
2+
3+
test('缺省数据默认值', async ({ page }) => {
4+
page.on('pageerror', (exception) => expect(exception).toBeNull())
5+
await page.goto('grid-data-source#undefined-field-defalut-value')
6+
7+
const demo = page.locator('#undefined-field-defalut-value')
8+
9+
const firstRow = demo.locator('.tiny-grid-body__row:visible').nth(0)
10+
11+
await firstRow.locator('td').nth(1).click()
12+
await firstRow.locator('.tiny-input__inner').click()
13+
await firstRow.locator('.tiny-input__inner').fill('1')
14+
await firstRow.locator('td').nth(2).click()
15+
await firstRow.locator('.tiny-input__inner').click()
16+
await expect(firstRow.locator('td').nth(1)).toHaveClass(/col__valid-success/)
17+
await firstRow.locator('.tiny-input__inner').fill('2')
18+
await firstRow.locator('td').nth(1).click()
19+
await expect(firstRow.locator('td').nth(2)).toHaveClass(/col__valid-success/)
20+
})
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<tiny-grid :data="tableData" :edit-config="editConfig">
3+
<tiny-grid-column field="field1" title="所属区域" :editor="{}">
4+
<template #edit="{ row }">
5+
<tiny-input v-model="row.field1" />
6+
</template>
7+
</tiny-grid-column>
8+
<tiny-grid-column field="field2" title="地址" :editor="{ defaultValue: '' }">
9+
<template #edit="{ row }">
10+
<tiny-input v-model="row.field2" />
11+
</template>
12+
</tiny-grid-column>
13+
<tiny-grid-column field="field3" title="邮编" :editor="{ defaultValue: '' }">
14+
<template #edit="{ row }">
15+
<tiny-input v-model="row.field3" />
16+
</template>
17+
</tiny-grid-column>
18+
</tiny-grid>
19+
</template>
20+
21+
<script>
22+
import { TinyGrid, TinyGridColumn, TinyInput } from '@opentiny/vue'
23+
24+
export default {
25+
components: {
26+
TinyGrid,
27+
TinyGridColumn,
28+
TinyInput
29+
},
30+
data() {
31+
return {
32+
tableData: [
33+
{
34+
field1: 'field1'
35+
},
36+
{
37+
field1: 'field1'
38+
}
39+
],
40+
editConfig: {
41+
trigger: 'click',
42+
mode: 'cell',
43+
autofocus: 'input'
44+
}
45+
}
46+
}
47+
}
48+
</script>

examples/sites/demos/pc/app/grid/editor/mutil-render.spec.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ test('Grid-编辑器 - 下拉多选', async ({ page }) => {
77
width: 1600,
88
height: 1200
99
})
10-
await page.getByText('华中区;华南区').click()
10+
const demo = page.locator('#editor-mutil-render')
11+
const firstRow = demo.locator('.tiny-grid-body__row:visible').first()
12+
await firstRow.locator('td').nth(2).click()
13+
await page.waitForTimeout(2500)
1114
await page.locator('.tiny-input__suffix-inner > .tiny-svg').click()
1215
await page.locator('li').filter({ hasText: '华东区' }).click()
1316
await page.getByRole('cell', { name: '创建时间' }).click()

examples/sites/demos/pc/app/grid/webdoc/grid-data-source.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ export default {
6262
'en-US': ''
6363
},
6464
codeFiles: ['data-source/defslot-protochain-fetch.vue']
65+
},
66+
{
67+
demoId: 'undefined-field-defalut-value',
68+
name: { 'zh-CN': '缺省数据的默认值', 'en-US': '' },
69+
desc: {
70+
'zh-CN': `
71+
<p>在可编辑表格组件中,当编辑器检测到当前数据行缺少对应字段时,会自动创建该字段。字段的初始化值遵循以下规则:</p>
72+
<p>1. 若配置了 <code>editor.defaultValue</code>,则使用该值作为初始值</p>
73+
<p>2.若未配置 <code>editor.defaultValue</code>,则默认使用 <code>null</code>作为初始值</p>`,
74+
'en-US': ''
75+
},
76+
codeFiles: ['data-source/undefined-field-defalut-value.vue']
6577
}
6678
],
6779
apis: [{ name: 'grid-data-source', 'type': 'component', 'props': [], 'events': [], 'slots': [] }]

packages/renderless/src/grid/static/array/eachTree.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const eachTreeItem = ({ parent, obj, iterate, context, path, node, parseChildren
3535
if (item && parseChildren) {
3636
paths.push(parseChildren)
3737
eachTreeItem({
38+
parent: item,
3839
item,
3940
obj: item[parseChildren],
4041
context,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from './useData'
55
export * from './useHeader'
66
export * from './useCellEvent'
77
export * from './useCellSpan'
8+
export * from './useNormalData'

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export const useData = (props) => {
153153

154154
hooks.watch([() => props.data, () => getTiledLength(props)], ([_, length]) => {
155155
tiledLength.value = length
156-
$table?.handleDataChange()
156+
$table.updateRawData(props.data)
157157
})
158158

159159
return { tiledLength }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { hooks } from '@opentiny/vue-common'
2+
3+
export const useNormalData = ({ props, visibleColumn }) => {
4+
const $table = hooks.getCurrentInstance()?.proxy
5+
// 原始数据
6+
const rawData = hooks.ref(null)
7+
// 原始数据版本
8+
const rawDataVersion = hooks.ref(0)
9+
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
19+
}
20+
})
21+
22+
hooks.watch(rawDataVersion, () => {
23+
// 设置数据查找缓存,对数据进行备份,深度克隆
24+
$table.updateCache(true, props.saveSource === 'deep')
25+
// 处理数据改变
26+
$table.handleDataChange()
27+
})
28+
29+
return { rawData, rawDataVersion }
30+
}

packages/vue/src/grid/src/dragger/src/rowDrop.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const createHandlerOnEnd = ({ _vm, refresh }) => {
4040
let prevTrElem =
4141
prevEl && prevEl.classList.contains('tiny-grid-body__row') ? prevEl : prevEl && prevEl.previousElementSibling
4242
// 这里优先使用用户通过props传递过来的表格数据,所以拖拽后会改变原始数据
43-
const tableTreeData = _vm.data || _vm.tableData
43+
const tableTreeData = _vm.rawData || _vm.tableData
4444
const selfRow = _vm.getRowNode(targetTrElem).item
4545
const selfNode = findTree(tableTreeData, (row) => row === selfRow, options)
4646
selfRow._isDraging = true
@@ -78,7 +78,7 @@ export const createHandlerOnEnd = ({ _vm, refresh }) => {
7878

7979
// 如果变动了树层级,需要刷新数据
8080
_vm.$emit('row-drop-end', event, _vm, _vm.scrollYLoad ? tableTreeData : _vm.tableFullData)
81-
refresh && _vm.data && _vm.refreshData(_vm.data)
81+
refresh && _vm.rawData && _vm.refreshData(_vm.rawData)
8282
}
8383
}
8484

0 commit comments

Comments
 (0)