Skip to content

Commit 54f9dfc

Browse files
authored
feat:下拉表格组件开发 (#3832)
1 parent fd3c1b1 commit 54f9dfc

24 files changed

+1523
-300
lines changed

examples/sites/demos/apis/grid-select.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ export default {
1414
'en-US': 'Whether to display the one click clear button, only applicable to radio selection'
1515
},
1616
mode: ['pc'],
17-
pcDemo: 'clearable',
18-
mfDemo: 'clearable'
17+
pcDemo: 'basic-usage'
1918
},
2019
{
2120
name: 'filterable',
@@ -26,7 +25,7 @@ export default {
2625
'en-US': 'Is it searchable'
2726
},
2827
mode: ['pc'],
29-
pcDemo: 'filter-method'
28+
pcDemo: 'remote'
3029
},
3130
{
3231
name: 'filter-method',
@@ -37,7 +36,7 @@ export default {
3736
'en-US': 'Custom filtering method'
3837
},
3938
mode: ['pc'],
40-
pcDemo: 'filter-method'
39+
pcDemo: 'remote'
4140
},
4241
{
4342
name: 'grid-op',
@@ -71,7 +70,7 @@ export default {
7170
'en-US': 'Allow multiple options to be selected'
7271
},
7372
mode: ['pc'],
74-
pcDemo: 'multiple'
73+
pcDemo: 'remote'
7574
},
7675
{
7776
name: 'radio-config',
@@ -94,7 +93,7 @@ export default {
9493
'en-US': 'Is it a remote search'
9594
},
9695
mode: ['pc'],
97-
pcDemo: 'remote-method'
96+
pcDemo: 'remote'
9897
},
9998
{
10099
name: 'remote-method',
@@ -105,7 +104,7 @@ export default {
105104
'en-US': 'Remote search methods'
106105
},
107106
mode: ['pc'],
108-
pcDemo: 'remote-method'
107+
pcDemo: 'remote'
109108
},
110109
{
111110
name: 'reserve-keyword',
@@ -117,7 +116,7 @@ export default {
117116
'When selecting multiple searchable options, do you still keep the current search keywords after selecting one option'
118117
},
119118
mode: ['pc'],
120-
pcDemo: 'remote-method'
119+
pcDemo: 'remote'
121120
},
122121
{
123122
name: 'select-config',

examples/sites/demos/pc/app/grid-select/basic-usage.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<template>
2-
<tiny-grid-select v-model="value" :grid-op="gridOpSingle" value-field="id" text-field="city"></tiny-grid-select>
2+
<div id="basic-usage">
3+
<tiny-grid-select v-model="value" :grid-op="gridOpSingle" value-field="id" text-field="city"></tiny-grid-select>
4+
</div>
35
</template>
46

57
<script>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { expect, test } from '@playwright/test'
2+
test.use({
3+
viewport: { width: 1920, height: 1080 }
4+
})
5+
test('grid-select 禁用选项', async ({ page }) => {
6+
page.on('pageerror', (exception) => expect(exception).toBeNull())
7+
await page.goto('grid-select#config')
8+
9+
const wrap = page.locator('#config')
10+
const single = wrap.locator('.tiny-grid-select').first()
11+
const singleInput = single.locator('.tiny-input__inner')
12+
13+
await singleInput.click()
14+
const dropdown = page.locator('body > .tiny-select-dropdown')
15+
const rows = dropdown.getByRole('row')
16+
17+
// 第 1 行被禁用,点击后不应该选中
18+
await rows.nth(0).getByRole('cell').nth(1).click()
19+
await expect(singleInput).toHaveValue('')
20+
21+
// 第 2 行可用,点击后可以选中
22+
await page.getByRole('row', { name: '华南区 广东省 深圳市' }).locator('div').first().click()
23+
await expect(singleInput).toHaveValue('深圳市')
24+
})

examples/sites/demos/pc/app/grid-select/config.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div>
2+
<div id="config">
33
<p>场景 1:单选</p>
44
<br />
55
<tiny-grid-select
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<template>
2+
<div class="demo-select">
3+
<div>父选项:</div>
4+
<br />
5+
<tiny-grid-select
6+
v-model="parentValue"
7+
multiple
8+
clearable
9+
filterable
10+
remote
11+
text-field="label"
12+
value-field="id"
13+
:grid-op="gridOpParent"
14+
:init-query="loadParents"
15+
:remote-method="remoteParents"
16+
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
17+
@change="handleParentChange"
18+
></tiny-grid-select>
19+
<br /><br />
20+
<div>子选项:</div>
21+
<br />
22+
<tiny-grid-select
23+
v-model="childValue"
24+
multiple
25+
clearable
26+
filterable
27+
remote
28+
text-field="label"
29+
value-field="id"
30+
:grid-op="gridOpChild"
31+
:extra-query-params="parentValue"
32+
:init-query="loadChildren"
33+
:remote-method="remoteChildren"
34+
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
35+
></tiny-grid-select>
36+
</div>
37+
</template>
38+
39+
<script setup>
40+
import { reactive, ref } from 'vue'
41+
import { TinyGridSelect } from '@opentiny/vue'
42+
43+
const parentValue = ref(['001'])
44+
const childValue = ref(['001'])
45+
46+
const parentOptions = [
47+
{ id: '001', label: '指南' },
48+
{ id: '002', label: '组件' }
49+
]
50+
51+
const childOptions = [
52+
{ id: '001', label: '安装', parent: '001' },
53+
{ id: '002', label: '开发', parent: '001' },
54+
{ id: '004', label: '框架风格', parent: '002' },
55+
{ id: '005', label: '表单组件', parent: '002' },
56+
{ id: '006', label: '数据组件', parent: '002' },
57+
{ id: '007', label: '提示组件', parent: '002' },
58+
{ id: '008', label: '导航组件', parent: '002' },
59+
{ id: '009', label: '其他组件', parent: '002' }
60+
]
61+
62+
const gridOpParent = reactive({
63+
data: [],
64+
height: 260,
65+
columns: [
66+
{ type: 'selection', title: '' },
67+
{ field: 'label', title: '父级' }
68+
]
69+
})
70+
71+
const gridOpChild = reactive({
72+
data: [],
73+
height: 260,
74+
columns: [
75+
{ type: 'selection', title: '' },
76+
{ field: 'label', title: '子级' }
77+
]
78+
})
79+
80+
const loadParents = () => Promise.resolve(parentOptions)
81+
const remoteParents = (keyword) => {
82+
const list = parentOptions.filter((item) => item.label.includes(keyword || ''))
83+
return Promise.resolve(list)
84+
}
85+
86+
const loadChildren = (value, extraQueryParams) => filterChildren(extraQueryParams)
87+
const remoteChildren = (keyword, extraQueryParams) => filterChildren(extraQueryParams, keyword)
88+
89+
const filterChildren = (parentIds, keyword = '') =>
90+
new Promise((resolve) => {
91+
const list = childOptions
92+
.filter((child) => parentIds.includes(child.parent))
93+
.filter((child) => child.label.includes(keyword || ''))
94+
setTimeout(() => resolve(list), 300)
95+
})
96+
97+
const handleParentChange = (parents) => {
98+
const childIds = childOptions
99+
.filter((child) => parents.includes(child.parent))
100+
.map((child) => child.id)
101+
childValue.value = childValue.value.filter((value) => childIds.includes(value))
102+
}
103+
</script>
104+
105+
<style scoped>
106+
.demo-select .tiny-grid-select {
107+
width: 280px;
108+
}
109+
</style>
110+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { expect, test } from '@playwright/test'
2+
3+
test('grid-select 初始化查询传参', async ({ page }) => {
4+
page.on('pageerror', (exception) => expect(exception).toBeNull())
5+
await page.goto('grid-select#extra-query-params')
6+
await page.waitForTimeout(1000)
7+
8+
// 选中指南
9+
await page
10+
.locator('div')
11+
.filter({ hasText: /^$/ })
12+
.click()
13+
await page.getByRole('row', { name: '指南' }).locator('path').nth(2).click()
14+
await page.getByRole('row', { name: '组件' }).locator('path').first().click()
15+
await page.getByRole('textbox').nth(3).click()
16+
await page.waitForTimeout(400)
17+
18+
// 选中框架风格
19+
await page.getByRole('row', { name: '框架风格' }).locator('div').first().click()
20+
await expect(
21+
page
22+
.locator('div')
23+
.filter({ hasText: /^$/ })
24+
.nth(2)
25+
).toBeVisible()
26+
})
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<template>
2+
<div id="extra-query-params" class="demo-select">
3+
<div>父选项:</div>
4+
<br />
5+
<tiny-grid-select
6+
v-model="parentValue"
7+
multiple
8+
clearable
9+
filterable
10+
remote
11+
text-field="label"
12+
value-field="id"
13+
:grid-op="gridOpParent"
14+
:init-query="loadParents"
15+
:remote-method="remoteParents"
16+
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
17+
@change="handleParentChange"
18+
></tiny-grid-select>
19+
<br /><br />
20+
<div>子选项:</div>
21+
<br />
22+
<tiny-grid-select
23+
v-model="childValue"
24+
multiple
25+
clearable
26+
filterable
27+
remote
28+
text-field="label"
29+
value-field="id"
30+
:extra-query-params="parentValue"
31+
:grid-op="gridOpChild"
32+
:init-query="loadChildren"
33+
:remote-method="remoteChildren"
34+
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
35+
></tiny-grid-select>
36+
</div>
37+
</template>
38+
39+
<script>
40+
import { TinyGridSelect } from '@opentiny/vue'
41+
42+
export default {
43+
components: {
44+
TinyGridSelect
45+
},
46+
created() {
47+
this.parentOptions = [
48+
{ id: '001', label: '指南' },
49+
{ id: '002', label: '组件' }
50+
]
51+
this.childOptions = [
52+
{ id: '001', label: '安装', parent: '001' },
53+
{ id: '002', label: '开发', parent: '001' },
54+
{ id: '004', label: '框架风格', parent: '002' },
55+
{ id: '005', label: '表单组件', parent: '002' },
56+
{ id: '006', label: '数据组件', parent: '002' },
57+
{ id: '007', label: '提示组件', parent: '002' },
58+
{ id: '008', label: '导航组件', parent: '002' },
59+
{ id: '009', label: '其他组件', parent: '002' }
60+
]
61+
},
62+
data() {
63+
return {
64+
parentValue: ['001'],
65+
childValue: ['001'],
66+
gridOpParent: {
67+
data: [],
68+
height: 260,
69+
optimization: {
70+
animat: true,
71+
scrollY: { gt: 20 }
72+
},
73+
columns: [
74+
{ type: 'selection', title: '' },
75+
{ field: 'label', title: '父级' }
76+
]
77+
},
78+
gridOpChild: {
79+
data: [],
80+
height: 260,
81+
optimization: {
82+
animat: true,
83+
scrollY: { gt: 20 }
84+
},
85+
columns: [
86+
{ type: 'selection', title: '' },
87+
{ field: 'label', title: '子级' }
88+
]
89+
}
90+
}
91+
},
92+
methods: {
93+
loadParents() {
94+
return Promise.resolve(this.parentOptions)
95+
},
96+
remoteParents(keyword) {
97+
const list = this.parentOptions.filter((item) => item.label.includes(keyword || ''))
98+
return Promise.resolve(list)
99+
},
100+
loadChildren(value, extraQueryParams) {
101+
return this.filterChildren(extraQueryParams)
102+
},
103+
remoteChildren(keyword, extraQueryParams) {
104+
return this.filterChildren(extraQueryParams, keyword)
105+
},
106+
filterChildren(parentIds, keyword = '') {
107+
const list = this.childOptions
108+
.filter((child) => parentIds.includes(child.parent))
109+
.filter((child) => child.label.includes(keyword || ''))
110+
return new Promise((resolve) => {
111+
setTimeout(() => resolve(list), 300)
112+
})
113+
},
114+
handleParentChange(parents) {
115+
const childIds = this.childOptions
116+
.filter((child) => parents.includes(child.parent))
117+
.map((child) => child.id)
118+
this.childValue = this.childValue.filter((value) => childIds.includes(value))
119+
}
120+
}
121+
}
122+
</script>
123+
124+
<style scoped>
125+
.demo-select .tiny-grid-select {
126+
width: 280px;
127+
}
128+
</style>
129+

0 commit comments

Comments
 (0)