Skip to content

Commit 317b23b

Browse files
committed
feat(grid-select): add single/multiple select
1 parent 4e74df8 commit 317b23b

File tree

10 files changed

+241
-29
lines changed

10 files changed

+241
-29
lines changed

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

+51-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,50 @@ export default {
1616
},
1717
mode: ['pc'],
1818
pcDemo: 'basic-usage'
19+
},
20+
{
21+
name: 'modelValue / v-model',
22+
type: 'string | number | Array<string|number>',
23+
defaultValue: '',
24+
desc: {
25+
'zh-CN': '绑定值',
26+
'en-US': 'Bind value'
27+
},
28+
mode: ['pc'],
29+
pcDemo: 'basic-usage'
30+
},
31+
{
32+
name: 'multiple',
33+
type: 'boolean',
34+
defaultValue: 'false',
35+
desc: {
36+
'zh-CN': '是否允许选择多个选项',
37+
'en-US': 'Allow multiple options to be selected'
38+
},
39+
mode: ['pc'],
40+
pcDemo: 'multiple'
41+
},
42+
{
43+
name: 'text-field',
44+
type: 'string',
45+
defaultValue: "''",
46+
desc: {
47+
'zh-CN': '显示值字段',
48+
'en-US': 'Show Value Fields'
49+
},
50+
mode: ['pc'],
51+
pcDemo: 'basic-usage'
52+
},
53+
{
54+
name: 'value-field',
55+
type: 'string',
56+
defaultValue: "''",
57+
desc: {
58+
'zh-CN': '绑定值字段',
59+
'en-US': 'Bind Value Field'
60+
},
61+
mode: ['pc'],
62+
pcDemo: 'basic-usage'
1963
}
2064
]
2165
}
@@ -25,14 +69,14 @@ export default {
2569
name: 'IGridOption',
2670
type: 'interface',
2771
code: `
28-
interface ITreeNode {
29-
label: string // 默认树节点的文本字段
30-
id: number|string // 树节点唯一标识
31-
children: ITreeNode[] // 子节点
32-
}
33-
3472
interface IGridOption {
35-
data: ITreeNode[] // 树数据,用法同 Tree
73+
data: Record<string, any>
74+
columns: {
75+
type: string
76+
field: string
77+
title: string
78+
width: number
79+
}[] // 表格列数据,用法同 Grid
3680
}
3781
`
3882
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<template>
2-
<tiny-grid-select v-model="value" :grid-op="gridOp"></tiny-grid-select>
2+
<tiny-grid-select v-model="value" :grid-op="gridOpSingle" value-field="id" text-field="city"></tiny-grid-select>
33
</template>
44

55
<script setup>
66
import { ref, reactive } from 'vue'
7-
import { GridSelect as TinyGridSelect } from '@opentiny/vue'
7+
import { TinyGridSelect } from '@opentiny/vue'
88
99
const value = ref('')
1010
11-
const gridOp = reactive({
11+
const gridOpSingle = reactive({
1212
data: [
13-
{ id: '001', area: '华南区', province: '广东省', city: '深圳1' },
13+
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
1414
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
1515
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
1616
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
import { expect, test } from '@playwright/test'
22

3-
test('测试基本用法', async ({ page }) => {
3+
test('测试下拉表格单选', async ({ page }) => {
44
page.on('pageerror', (exception) => expect(exception).toBeNull())
5-
await page.goto('tree-select#basic-usage')
5+
await page.goto('grid-select#basic-usage')
66

77
const wrap = page.locator('#basic-usage')
8-
const select = wrap.locator('.tiny-tree-select').nth(0)
8+
const select = wrap.locator('.tiny-grid-select').nth(0)
99
const input = select.locator('.tiny-input__inner')
1010
const dropdown = page.locator('body > .tiny-select-dropdown')
11-
const treeNode = dropdown.locator('.tiny-tree-node')
11+
const suffixSvg = select.locator('.tiny-base-select__caret')
12+
const row = dropdown.getByRole('row')
13+
14+
await expect(suffixSvg).toHaveCount(1)
15+
await expect(suffixSvg).toBeVisible()
1216

1317
await input.click()
14-
await expect(treeNode).toHaveCount(7)
18+
await expect(dropdown).toBeVisible()
19+
await expect(row).toHaveCount(6)
1520

16-
await treeNode.filter({ hasText: /^ 2-1$/ }).click()
17-
await expect(input).toHaveValue('二级 2-1')
21+
await row.nth(1).getByRole('cell').first().click()
22+
await expect(input).toHaveValue('广州市')
1823
await input.click()
19-
await expect(treeNode.filter({ hasText: /^ 2-1$/ })).toHaveClass(/is-current/)
24+
await expect(row.filter({ hasText: '广州市' })).toHaveClass(/tiny-grid-body__row row__radio/)
2025
})

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
<template>
2-
<tiny-grid-select v-model="value" :grid-op="gridOp"></tiny-grid-select>
2+
<tiny-grid-select v-model="value" :grid-op="gridOpSingle" value-field="id" text-field="city"></tiny-grid-select>
33
</template>
44

55
<script>
6-
import { GridSelect } from '@opentiny/vue'
6+
import { TinyGridSelect } from '@opentiny/vue'
77
88
export default {
99
components: {
10-
TinyGridSelect: GridSelect
10+
TinyGridSelect
1111
},
1212
data() {
1313
return {
1414
value: '',
15-
treeOp: {
15+
gridOpSingle: {
1616
data: [
17-
{ id: '001', area: '华南区', province: '广东省', city: '深圳1' },
17+
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
1818
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
1919
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
2020
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<template>
2+
<tiny-grid-select
3+
v-model="value"
4+
multiple
5+
:grid-op="gridOpMulti"
6+
value-field="id"
7+
text-field="city"
8+
></tiny-grid-select>
9+
</template>
10+
11+
<script setup>
12+
import { ref, reactive } from 'vue'
13+
import { GridSelect as TinyGridSelect } from '@opentiny/vue'
14+
15+
const value = ref('')
16+
17+
const gridOpMulti = reactive({
18+
data: [
19+
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
20+
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
21+
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
22+
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
23+
{ id: '005', area: '华南区', province: '广东省', city: '中山市' }
24+
],
25+
columns: [
26+
{ type: 'selection', title: '' },
27+
{ field: 'area', title: '区域', width: 90 },
28+
{ field: 'province', title: '省份', width: 60 },
29+
{ field: 'city', title: '城市', width: 60 }
30+
]
31+
})
32+
</script>
33+
34+
<style scoped>
35+
.tiny-grid-select {
36+
width: 280px;
37+
}
38+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<template>
2+
<tiny-grid-select
3+
v-model="value"
4+
multiple
5+
:grid-op="gridOpMulti"
6+
value-field="id"
7+
text-field="city"
8+
></tiny-grid-select>
9+
</template>
10+
11+
<script>
12+
import { TinyGridSelect } from '@opentiny/vue'
13+
14+
export default {
15+
components: {
16+
TinyGridSelect
17+
},
18+
data() {
19+
return {
20+
value: '',
21+
treeOp: {
22+
data: [
23+
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
24+
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
25+
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
26+
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
27+
{ id: '005', area: '华南区', province: '广东省', city: '中山市' }
28+
],
29+
columns: [
30+
{ type: 'selection', title: '' },
31+
{ field: 'area', title: '区域', width: 90 },
32+
{ field: 'province', title: '省份', width: 60 },
33+
{ field: 'city', title: '城市', width: 60 }
34+
]
35+
}
36+
}
37+
}
38+
}
39+
</script>
40+
41+
<style scoped>
42+
.tiny-grid-select {
43+
width: 280px;
44+
}
45+
</style>

examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ export default {
1717
'en-US': ''
1818
},
1919
codeFiles: ['basic-usage.vue']
20+
},
21+
{
22+
demoId: 'multiple',
23+
name: {
24+
'zh-CN': '多选',
25+
'en-US': 'Multiple'
26+
},
27+
desc: {
28+
'zh-CN':
29+
'<p>通过 <code>multiple</code> 属性启用多选功能,此时 <code>v-model</code> 的值为当前选中值所组成的数组,默认选中值会以标签形式展示。</p>',
30+
'en-US': ''
31+
},
32+
codeFiles: ['multiple.vue']
2033
}
2134
]
2235
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export const radioChange =
2+
({ props, vm, emit }) =>
3+
({ row }) => {
4+
if (!props.multiple) {
5+
vm.$refs.baseSelectRef.updateSelectedData({
6+
...row,
7+
currentLabel: row[props.textField],
8+
value: row[props.valueField],
9+
state: {
10+
currentLabel: row[props.textField]
11+
}
12+
})
13+
14+
vm.$refs.baseSelectRef.hidePanel()
15+
16+
emit('update:modelValue', row)
17+
emit('change', row)
18+
}
19+
}
20+
21+
export const selectChange =
22+
({ props, vm, emit }) =>
23+
({ $table, selection, checked, row }) => {
24+
if (props.multiple) {
25+
vm.$refs.baseSelectRef.updateSelectedData(
26+
selection.map((node) => {
27+
return {
28+
...node,
29+
currentLabel: node[props.textField],
30+
value: node[props.valueField]
31+
}
32+
})
33+
)
34+
35+
emit('update:modelValue', selection)
36+
emit('change', selection)
37+
}
38+
}

packages/renderless/src/grid-select/vue.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
export const api = ['state']
1+
import { radioChange, selectChange } from './index'
22

3-
export const renderless = (props, { reactive }, { vm }) => {
3+
export const api = ['state', 'radioChange', 'selectChange']
4+
5+
export const renderless = (props, { reactive }, { vm, emit }) => {
46
const api = {}
57

68
const state = reactive({
@@ -9,7 +11,9 @@ export const renderless = (props, { reactive }, { vm }) => {
911
})
1012

1113
Object.assign(api, {
12-
state
14+
state,
15+
radioChange: radioChange({ props, vm, emit }),
16+
selectChange: selectChange({ props, vm, emit })
1317
})
1418

1519
return api

packages/vue/src/grid-select/src/pc.vue

+27-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
<template>
2-
<tiny-base-select ref="baseSelectRef" class="tiny-grid-select" v-model="state.value">
2+
<tiny-base-select ref="baseSelectRef" class="tiny-grid-select" v-model="state.value" :multiple="multiple">
33
<template #panel>
4-
<tiny-grid ref="gridRef" v-bind="state.gridData"></tiny-grid>
4+
<tiny-grid
5+
ref="gridRef"
6+
auto-resize
7+
:row-id="valueField"
8+
:highlight-current-row="true"
9+
:columns="state.gridData.columns"
10+
:data="state.gridData"
11+
@select-all="selectChange"
12+
@select-change="selectChange"
13+
@radio-change="radioChange"
14+
@mousedown.stop
15+
v-bind="state.gridData"
16+
></tiny-grid>
517
</template>
618
</tiny-base-select>
719
</template>
@@ -19,9 +31,22 @@ export default defineComponent({
1931
TinyBaseSelect: BaseSelect
2032
},
2133
props: {
34+
clearable: Boolean,
35+
filterable: Boolean,
36+
filterMethod: Function,
2237
gridOp: {
2338
type: Object,
2439
default: () => ({})
40+
},
41+
modelValue: {},
42+
multiple: Boolean,
43+
textField: {
44+
type: String,
45+
default: 'label'
46+
},
47+
valueField: {
48+
type: String,
49+
default: 'value'
2550
}
2651
},
2752
setup(props, context) {

0 commit comments

Comments
 (0)