Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tree-select): [tree-select] add tree-select component #1683

Merged
merged 2 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions examples/sites/demos/apis/tree-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
export default {
mode: ['pc'],
apis: [
{
name: 'tree-select',
type: 'component',
props: [
{
name: 'clearable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否启用一键清除的功能',
'en-US': 'Whether to display the one click clear button, only applicable to radio selection'
},
mode: ['pc'],
pcDemo: 'filter'
},
{
name: 'filter-method',
type: '(query: string) => void',
defaultValue: '',
desc: {
'zh-CN': '自定义过滤方法',
'en-US': 'Custom filtering method'
},
mode: ['pc'],
pcDemo: 'filter'
},
{
name: 'filterable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否可搜索',
'en-US': 'Is it searchable'
},
mode: ['pc'],
pcDemo: 'filter'
},
{
name: 'modelValue / v-model',
type: 'string | number | Array<string|number>',
defaultValue: '',
desc: {
'zh-CN': '绑定值',
'en-US': 'Bind value'
},
mode: ['pc'],
pcDemo: 'basic-usage'
},
{
name: 'multiple',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否允许选择多个选项',
'en-US': 'Allow multiple options to be selected'
},
mode: ['pc'],
pcDemo: 'multiple'
},
{
name: 'text-field',
type: 'string',
defaultValue: "'label'",
desc: {
'zh-CN': '显示值字段',
'en-US': 'Show Value Fields'
},
mode: ['pc'],
pcDemo: 'map-field'
},
{
name: 'tree-op',
typeAnchorName: 'ITreeOption',
type: 'ITreeOption',
defaultValue: '',
desc: {
'zh-CN': '下拉树时,内置树组件的配置,用法同 Tree 组件。',
'en-US':
'When pulling down a tree, the configuration of the built-in tree component is the same as that of the Tree component. To be used in conjunction with the render type attribute'
},
mode: ['pc'],
pcDemo: 'basic-usage'
},
{
name: 'value-field',
type: 'string',
defaultValue: "'value'",
desc: {
'zh-CN': '绑定值字段',
'en-US': 'Bind Value Field'
},
mode: ['pc'],
pcDemo: 'map-field'
}
]
}
],
types: [
{
name: 'ITreeOption',
type: 'interface',
code: `
interface ITreeNode {
label: string // 默认树节点的文本字段
id: number|string // 树节点唯一标识
children: ITreeNode[] // 子节点
}

interface ITreeOption {
data: ITreeNode[] // 树数据,用法同 Tree
}
`
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<tiny-tree-select v-model="value" :tree-op="treeOp"></tiny-tree-select>
</template>

<script setup>
import { ref } from 'vue'
import { TreeSelect as TinyTreeSelect } from '@opentiny/vue'

const value = ref('')

const treeOp = ref({
data: [
{
value: 1,
label: '一级 1',
children: [
{
value: 4,
label: '二级 1-1',
children: [
{
value: 9,
label: '三级 1-1-1'
},
{
value: 10,
label: '三级 1-1-2'
}
]
}
]
},
{
value: 2,
label: '一级 2',
children: [
{
value: 5,
label: '二级 2-1'
},
{
value: 6,
label: '二级 2-2'
}
]
}
]
})
</script>

<style scoped>
.tiny-tree-select {
width: 280px;
}
</style>
20 changes: 20 additions & 0 deletions examples/sites/demos/pc/app/tree-select/basic-usage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { expect, test } from '@playwright/test'

test('测试基本用法', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('tree-select#basic-usage')

const wrap = page.locator('#basic-usage')
const select = wrap.locator('.tiny-tree-select').nth(0)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const treeNode = dropdown.locator('.tiny-tree-node')

await input.click()
await expect(treeNode).toHaveCount(7)

await treeNode.filter({ hasText: /^二级 2-1$/ }).click()
await expect(input).toHaveValue('二级 2-1')
await input.click()
await expect(treeNode.filter({ hasText: /^二级 2-1$/ })).toHaveClass(/is-current/)
})
61 changes: 61 additions & 0 deletions examples/sites/demos/pc/app/tree-select/basic-usage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<template>
<tiny-tree-select v-model="value" :tree-op="treeOp"></tiny-tree-select>
</template>

<script>
import { TreeSelect } from '@opentiny/vue'

export default {
components: {
TinyTreeSelect: TreeSelect
},
data() {
return {
treeOp: {
data: [
{
value: 1,
label: '一级 1',
children: [
{
value: 4,
label: '二级 1-1',
children: [
{
value: 9,
label: '三级 1-1-1'
},
{
value: 10,
label: '三级 1-1-2'
}
]
}
]
},
{
value: 2,
label: '一级 2',
children: [
{
value: 5,
label: '二级 2-1'
},
{
value: 6,
label: '二级 2-2'
}
]
}
]
}
}
}
}
</script>

<style scoped>
.tiny-tree-select {
width: 280px;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: TreeSelect 树形选择器
---

# TreeSelect 树形选择器

结合了 BaseSelect 和 Tree 组件的选择器,用于从一个下拉树中选择一个或多个选项。
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: TreeSelect
---

# TreeSelect

A selector that combines the BaseSelect and Tree components to select one or more options from a drop-down tree.
18 changes: 18 additions & 0 deletions examples/sites/demos/pc/app/tree-select/webdoc/tree-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default {
column: '2',
owner: '',
demos: [
{
demoId: 'basic-usage',
name: {
'zh-CN': '基本用法',
'en-US': 'Basic Usage'
},
desc: {
'zh-CN': '<p>最基础的用法,通过 <code>tree-op</code> 设置下拉树的数据源,<code>v-model</code> 设置绑定值。</p>',
'en-US': ''
},
codeFiles: ['basic-usage.vue']
}
]
}
8 changes: 7 additions & 1 deletion examples/sites/demos/pc/menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,13 @@ export const cmpMenus = [
{ 'nameCn': '开关', 'name': 'Switch', 'key': 'switch' },
{ 'nameCn': '时间选择器', 'name': 'TimePicker', 'key': 'time-picker' },
{ 'nameCn': '时间选择', 'name': 'TimeSelect', 'key': 'time-select' },
{ 'nameCn': '穿梭框', 'name': 'Transfer', 'key': 'transfer' }
{ 'nameCn': '穿梭框', 'name': 'Transfer', 'key': 'transfer' },
{
'nameCn': '树形选择器',
'name': 'TreeSelect',
'key': 'tree-select',
'mark': { 'type': 'warning', 'text': 'Beta' }
}
]
},
{
Expand Down
13 changes: 13 additions & 0 deletions packages/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -2978,6 +2978,19 @@
"type": "template",
"exclude": false
},
"TreeSelect": {
"path": "vue/src/tree-select/index.ts",
"type": "component",
"exclude": false,
"mode": [
"pc"
]
},
"TreeSelectPc": {
"path": "vue/src/tree-select/src/pc.vue",
"type": "template",
"exclude": false
},
"Upload": {
"path": "vue/src/upload/index.ts",
"type": "component",
Expand Down
38 changes: 38 additions & 0 deletions packages/renderless/src/tree-select/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export const filter =
({ vm }) =>
(value) => {
vm.$refs.treeRef.filter(value)
}

export const nodeClick =
({ props }) =>
(data, { updateSelectedData, hidePanel }) => {
if (!props.multiple) {
updateSelectedData({
...data,
currentLabel: data[props.textField],
value: data[props.valueField],
state: {
currentLabel: data[props.textField]
}
})

hidePanel()
}
}

export const check =
({ props }) =>
(checkedNodes, { updateSelectedData }) => {
if (props.multiple) {
updateSelectedData(
checkedNodes.map((node) => {
return {
...node,
currentLabel: node[props.textField],
value: node[props.valueField]
}
})
)
}
}
21 changes: 21 additions & 0 deletions packages/renderless/src/tree-select/vue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { filter, nodeClick, check } from './index'

export const api = ['state', 'filter', 'nodeClick', 'check']

export const renderless = (props, { reactive }, { vm }) => {
const api = {}

const state = reactive({
value: props.modelValue,
treeData: props.treeOp.data
})

Object.assign(api, {
state,
filter: filter({ vm }),
nodeClick: nodeClick({ props }),
check: check({ props })
})

return api
}
1 change: 1 addition & 0 deletions packages/vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@
"@opentiny/vue-transfer-panel": "workspace:~",
"@opentiny/vue-tree": "workspace:~",
"@opentiny/vue-tree-menu": "workspace:~",
"@opentiny/vue-tree-select": "workspace:~",
"@opentiny/vue-upload": "workspace:~",
"@opentiny/vue-upload-dragger": "workspace:~",
"@opentiny/vue-upload-list": "workspace:~",
Expand Down
Loading
Loading