feat(base-select): [base-select,select] add base-select (#1632)

* refactor(base-select): add base-select

* refactor(base-select): remove tree/grid and e2e tests

* refactor(base-select): remove tree/grid in the base-select/pc.vue

* refactor(base-select): remove tree/grid in the renderless/index.ts|vue.ts

* docs(base-select): remove tree/grid demos
This commit is contained in:
Kagol 2024-05-21 15:31:50 +08:00 committed by GitHub
parent f3a912871a
commit 0778e60d0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
135 changed files with 12932 additions and 0 deletions

View File

@ -0,0 +1,886 @@
export default {
mode: ['pc', 'mobile-first'],
apis: [
{
name: 'base-select',
type: 'component',
props: [
{
name: 'all-text',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '当下拉中显示全部时,自定义全部的显示文本。不指定时,则默认显示"全部"',
'en-US':
'When all is displayed in the drop-down list, you can customize the display text of all. If this parameter is not specified, All is displayed by default.'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'all-text',
mfDemo: 'all-text'
},
{
name: 'allow-copy',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否允许复制输入框的内容,适用单选可搜索场景',
'en-US':
'Is it allowed to copy the content of the input box, applicable to single choice searchable scenarios'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'copy-single',
mfDemo: 'copy-single'
},
{
name: 'allow-create',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否允许创建新条目,需配合 filterable 使用。若搜索字段不在选项列表中,可创建为新的选项',
'en-US':
'Is it allowed to create new entries? It needs to be used in conjunction with filterable. If the search field is not in the option list, it can be created as a new option'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'allow-create',
mfDemo: 'allow-create'
},
{
name: 'autocomplete',
type: 'string',
defaultValue: "'off'",
desc: {
'zh-CN': '输入框的原生 autocomplete 属性',
'en-US': 'The native autocomplete attribute of the input box'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'native-properties',
mfDemo: 'native-properties'
},
{
name: 'cache-op',
typeAnchorName: 'ICacheOp',
type: 'ICacheOp',
defaultValue:
"<pre>\n{\n key: '',\n sortBy: 'frequency',\n sort: 'desc',\n dataKey: 'value',\n highlightClass: \n 'memorize-highlight',\n highlightNum: Infinity,\n cacheNum: Infinity,\n serialize: JSON.stringify\n deserialize: JSON.parse\n}\n</pre>",
desc: {
'zh-CN': '启用本地缓存已选项的功能配置(根据用户点击选择的次数、最后时间继续存储排序)',
'en-US': 'Set the component type when Grid or Tree is embedded in the drop-down list box.'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'cache-usage',
mfDemo: 'cache-usage'
},
{
name: 'clear-no-match-value',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否自动清空无法在 options 中找到匹配项的值',
'en-US': 'Automatically clear values that cannot find matching items in options'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'clear-no-match-value',
mfDemo: 'clear-no-match-value'
},
{
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', 'mobile-first'],
pcDemo: 'clearable',
mfDemo: 'clearable'
},
{
name: 'click-expend',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '点击可展开或收起显示不全的选项。仅用于多选',
'en-US': 'Click to expand or collapse options. Only applicable to multiple selections'
}
},
{
name: 'collapse-tags',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否将多个标签折叠显示。仅适用多选',
'en-US': 'Whether to collapse multiple labels for display. Only applicable to multiple selections'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'collapse-tags',
mfDemo: 'collapse-tags'
},
{
name: 'copyable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否启用一键复制的功能。点击复制按钮一键复制所有标签的文本内容并以逗号分隔,仅适用于多选',
'en-US':
'Is the one click copy function enabled. Click the copy button to copy the text content of all labels with one click, separated by commas, only applicable to multiple selections'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'copy-multi',
mfDemo: 'copy-multi'
},
{
name: 'default-first-option',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否启用按 Enter 键选择第一个匹配项的功能。需配合 filterable 或 remote 使用',
'en-US':
'Whether to enable the function of pressing the Enter key to select the first match. Must be used in conjunction with filterable or remote'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'allow-create',
mfDemo: 'allow-create'
},
{
name: 'disabled',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否禁用',
'en-US': 'Is it disabled'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'disabled',
mfDemo: 'disabled'
},
{
name: 'dropdown-icon',
type: 'Component',
defaultValue: '',
desc: {
'zh-CN': '自定义下拉图标',
'en-US': 'Custom drop-down icon'
},
mode: ['pc'],
pcDemo: 'multiple'
},
{
name: 'dropdown-style',
type: 'String',
defaultValue: '',
desc: {
'zh-CN': '自定义下拉选项样式',
'en-US': 'Custom drop-down options style'
},
mode: ['pc'],
pcDemo: 'multiple'
},
{
name: 'filter-method',
type: '(query: string) => void',
defaultValue: '',
desc: {
'zh-CN': '自定义过滤方法',
'en-US': 'Custom filtering method'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'filter-method',
mfDemo: 'filter-method'
},
{
name: 'filterable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否可搜索',
'en-US': 'Is it searchable'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'filter-method',
mfDemo: 'filter-method'
},
{
name: 'input-box-type',
type: "'input' | 'underline'",
defaultValue: "'input'",
desc: {
'zh-CN': '输入框的显示类型',
'en-US': 'Display type of input box'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'input-box-type',
mfDemo: 'input-box-type'
},
{
name: 'is-drop-inherit-width',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '下拉弹框的宽度是否跟输入框保持一致。默认超出输入框宽度时由内容撑开',
'en-US':
'Is the width of the dropdown box consistent with the input box. By default, when the width of the input box is exceeded, it is supported by the content'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'is-drop-inherit-width',
mfDemo: 'is-drop-inherit-width'
},
{
name: 'loading',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否加载中,适用于远程搜索场景',
'en-US': 'Loading or not, suitable for remote search scenarios'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'remote-method',
mfDemo: 'remote-method'
},
{
name: 'loading-text',
type: 'string',
defaultValue: "'加载中'",
desc: {
'zh-CN': '远程加载时显示的文本',
'en-US': 'Text displayed during remote loading'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'remote-method',
mfDemo: 'remote-method'
},
{
name: 'max-visible-rows',
type: 'number',
defaultValue: '1',
desc: {
'zh-CN': '多行默认最大显示行数,超出后选项自动隐藏',
'en-US':
'Default maximum display lines for multiple lines, with automatic hiding option for exceeding lines'
},
mode: ['pc'],
pcDemo: 'collapse-tags'
},
{
name: 'modelValue / v-model',
type: 'string | number | Array<string|number>',
defaultValue: '',
desc: {
'zh-CN': '绑定值',
'en-US': 'Bind value'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'multiple',
mfDemo: 'multiple'
},
{
name: 'multiple',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否允许选择多个选项',
'en-US': 'Allow multiple options to be selected'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'multiple',
mfDemo: 'multiple'
},
{
name: 'multiple-limit',
type: 'number',
defaultValue: '0',
desc: {
'zh-CN': '多选时最多可选择的个数,默认为 0 不限制',
'en-US':
'When selecting multiple options, the maximum number of options available is 0, with no limit by default'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'multiple-limit',
mfDemo: 'multiple-limit'
},
{
name: 'name',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '输入框的原生 name 属性',
'en-US': 'The native name attribute of the input box'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'native-properties',
mfDemo: 'native-properties'
},
{
name: 'no-data-text',
type: 'string',
defaultValue: "'暂无相关数据'",
desc: {
'zh-CN': '选项列表为空时显示的文本,也可以使用 empty 插槽设置',
'en-US': 'The text displayed when the option list is empty can also be set using empty slots'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'no-data-text',
mfDemo: 'no-data-text'
},
{
name: 'no-match-text',
type: 'string',
defaultValue: "'无匹配数据'",
desc: {
'zh-CN': '搜索条件无匹配时显示的文本,也可以使用 empty 插槽设置',
'en-US':
'The text displayed when there is no match for the search criteria can also be set using empty slots'
},
mode: ['pc'],
pcDemo: 'filter-method'
},
{
name: 'options',
typeAnchorName: 'IOption',
type: 'IOption[]',
defaultValue: '',
desc: {
'zh-CN': '选项列表配置,使用后不需要再配置 tiny-option',
'en-US': 'Option list configuration, no need to configure tiny options after use'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'map-field',
mfDemo: 'map-field'
},
{
name: 'placeholder',
type: 'string',
defaultValue: "'请选择'",
desc: {
'zh-CN': '占位符',
'en-US': 'Placeholder'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'native-properties',
mfDemo: 'native-properties'
},
{
name: 'placement',
typeAnchorName: 'IPlacement',
type: 'IPlacement',
defaultValue: "'bottom-start'",
desc: {
'zh-CN': '下拉弹框相对于触发源的弹出位置',
'en-US': 'The pop-up position of the pull-down pop-up box relative to the trigger source'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'popup-style-position',
mfDemo: 'popup-style-position'
},
{
name: 'popper-append-to-body',
type: 'boolean',
defaultValue: 'true',
desc: {
'zh-CN': '是否将弹出框的 dom 元素插入至 body 元素',
'en-US': 'Whether to insert the dom element of the pop-up box into the body element'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'popup-style-position',
mfDemo: 'popup-style-position'
},
{
name: 'popper-class',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '自定义下拉框的类名,用于自定义样式',
'en-US': 'The class name of the custom dropdown box, used for customizing styles'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'popup-style-position',
mfDemo: 'popup-style-position'
},
{
name: 'remote',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否为远程搜索',
'en-US': 'Is it a remote search'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'remote-method',
mfDemo: 'remote-method'
},
{
name: 'remote-method',
type: '(query:string) => void',
defaultValue: '',
desc: {
'zh-CN': '远程搜索的方法',
'en-US': 'Remote search methods'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'remote-method',
mfDemo: 'remote-method'
},
{
name: 'reserve-keyword',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '多选可搜索时,是否在选中一个选项后仍然保留当前的搜索关键词',
'en-US':
'When selecting multiple searchable options, do you still keep the current search keywords after selecting one option'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'remote-method',
mfDemo: 'remote-method'
},
{
name: 'searchable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否启用下拉面板搜索',
'en-US': 'Whether to allow users to create new items. This parameter must be used together with filterable.'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'searchable',
mfDemo: 'searchable'
},
{
name: 'show-alloption',
type: 'boolean',
defaultValue: 'true',
desc: {
'zh-CN': '是否展示 “全选” 选项',
'en-US': 'Whether to display the "Select All" option'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'show-alloption',
mfDemo: 'show-alloption'
},
{
name: 'show-empty-image',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否显示空数据图片',
'en-US': 'Display empty data image'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'no-data-text',
mfDemo: 'no-data-text'
},
{
name: 'size',
type: "'medium' | 'small' | 'mini'",
defaultValue: '',
desc: {
'zh-CN': '输入框尺寸。',
'en-US': 'Text box size'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'size',
mfDemo: 'size'
},
{
name: 'tag-selectable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '输入框中的标签是否可通过鼠标选中复制',
'en-US': 'Can the label in the input box be copied by selecting it with the mouse'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'copy-multi',
mfDemo: 'copy-multi'
},
{
name: 'tag-type',
type: "'success' | 'info' | 'warning' | 'danger'",
defaultValue: '',
desc: {
'zh-CN': '标签类型,仅多选适用。使用 aurora 主题时设置该属性为 info',
'en-US':
'Label type, only applicable for multiple choices. Set this property to info when using the aurora theme'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'tag-type',
mfDemo: 'tag-type'
},
{
name: 'text-field',
type: 'string',
defaultValue: "'label'",
desc: {
'zh-CN': '显示值字段',
'en-US': 'Show Value Fields'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'map-field',
mfDemo: 'map-field'
},
{
name: 'text-split',
type: 'string',
defaultValue: "','",
desc: {
'zh-CN': '自定义复制文本的分隔符,需结合 copyable 属性使用',
'en-US': 'The separator for custom copied text needs to be used in conjunction with the copyable attribute'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'copy-multi',
mfDemo: 'copy-multi'
},
{
name: 'top-create',
type: 'boolean',
defaultValue: '',
desc: {
'zh-CN': '是否显示下拉框顶部新增按钮,点击按钮会抛出一个 top-create-click 事件,可以在事件中自定义一些行为',
'en-US':
'Indicates whether to display a new button on the top of the drop-down list box. When a button is clicked, a top-create-click event is thrown. You can customize some behaviors in the event'
},
mode: ['pc'],
pcDemo: 'allow-create'
},
{
name: 'value-field',
type: 'string',
defaultValue: "'value'",
desc: {
'zh-CN': '绑定值字段',
'en-US': 'Bind Value Field'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'map-field',
mfDemo: 'map-field'
},
{
name: 'value-key',
type: 'string',
defaultValue: "'value'",
desc: {
'zh-CN': '作为 value 唯一标识的键名,绑定值为对象类型时必填',
'en-US':
'The key name that uniquely identifies the value must be filled in when the binding value is of object type'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'binding-obj',
mfDemo: 'binding-obj'
},
{
name: 'show-proportion',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否展示多选框选中条数和总条数的占比的文字提示',
'en-US':
'Display the proportion of the number of selected items and the total number of items in the multiple-choice box'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'collapse-tags',
mfDemo: 'collapse-tags'
},
{
name: 'show-limit-text',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN':
'是否展示多选框开启多选限制选择数量时,选中条数和限制总条数的占比的文字提示。 该属性的优先级大于<code>show-proportion</code> 属性,同时设置只',
'en-US':
'Display the proportion of the number of selected items and the total number of items in the multiple-choice box'
},
mode: ['pc'],
pcDemo: 'multiple'
}
],
events: [
{
name: 'blur',
type: '(event:MouseEvent) => void',
defaultValue: '',
desc: {
'zh-CN': '监听输入框失去焦点事件',
'en-US': 'Listening for input box lose focus event'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'events',
mfDemo: 'events'
},
{
name: 'change',
type: '(value:string|number|Array<string|number>, list:Array<IOption|ITreeNode>) => void',
defaultValue: '',
desc: {
'zh-CN': '监听绑定值变更事件',
'en-US': 'Listening for binding value change events'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'events',
mfDemo: 'events'
},
{
name: 'clear',
type: '() => void',
defaultValue: '',
desc: {
'zh-CN': '监听一键清除事件',
'en-US': 'Listening for one click clear events'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'events',
mfDemo: 'events'
},
{
name: 'focus',
type: '(event:MouseEvent) => void',
defaultValue: '',
desc: {
'zh-CN': '监听输入框获取焦点事件',
'en-US': 'Listening to input boxes to obtain focus events'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'events',
mfDemo: 'events'
},
{
name: 'remove-tag',
type: '(tag:string|number) => void',
defaultValue: '',
desc: {
'zh-CN': '监听多选时移除标签事件',
'en-US': 'Remove label events when listening for multiple selections'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'events',
mfDemo: 'events'
},
{
name: 'top-create-click',
type: '() => void',
defaultValue: '',
desc: {
'zh-CN': '监听顶部新增按钮点击事件,同 top-create 属性一起使用',
'en-US':
'Listens to the click event of a new button on the top. This parameter is used together with the top-create attribute'
},
mode: ['pc'],
pcDemo: 'events'
},
{
name: 'visible-change',
type: '(status:boolean) => void',
defaultValue: '',
desc: {
'zh-CN': '监听下拉弹框的显示隐藏状态',
'en-US': 'Monitor the display and hidden status of dropdown pop ups'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'events',
mfDemo: 'events'
}
],
methods: [
{
name: 'blur',
type: '() => void',
defaultValue: '',
desc: {
'zh-CN': '使输入框失去焦点',
'en-US': 'Causes the input box to lose focus'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'manual-focus-blur',
mfDemo: 'manual-focus-blur'
},
{
name: 'focus',
type: '() => void',
defaultValue: '',
desc: {
'zh-CN': '使输入框获取焦点',
'en-US': 'Bring the input box to focus'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'manual-focus-blur',
mfDemo: 'manual-focus-blur'
}
],
slots: [
{
name: 'default',
type: '',
defaultValue: '',
desc: {
'zh-CN': '选项默认插槽',
'en-US': 'Option default slot'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'slot-default',
mfDemo: 'slot-default'
},
{
name: 'empty',
type: '',
defaultValue: '',
desc: {
'zh-CN': '空数据插槽',
'en-US': 'Empty data slot'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'slot-empty',
mfDemo: 'slot-empty'
},
{
name: 'footer',
type: '',
defaultValue: '',
desc: {
'zh-CN': '下拉弹框底部插槽',
'en-US': 'Pull down the bottom slot of the pop-up box'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'slot-footer',
mfDemo: 'slot-footer'
},
{
name: 'prefix',
type: '',
defaultValue: '',
desc: {
'zh-CN': '输入框前缀插槽',
'en-US': 'Input box prefix slot'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'slot-prefix',
mfDemo: 'slot-prefix'
},
{
name: 'reference',
type: '',
defaultValue: '',
desc: {
'zh-CN': '触发源插槽',
'en-US': 'Trigger Source Slot'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'slot-reference',
mfDemo: 'slot-reference'
}
]
},
{
name: 'option',
type: 'component',
props: [
{
name: 'disabled',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '选项是否禁用',
'en-US': 'Is the option disabled'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'disabled',
mfDemo: 'disabled'
},
{
name: 'icon',
type: 'Component',
defaultValue: '',
desc: {
'zh-CN': '自定义选项的图标',
'en-US': 'Customize icons for options'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'basic-usage',
mfDemo: 'basic-usage'
},
{
name: 'label',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '选项的显示文本',
'en-US': 'Display text for option'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'basic-usage',
mfDemo: 'basic-usage'
},
{
name: 'required',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '选项是否必选',
'en-US': 'Is it mandatory to select an option'
},
mode: ['pc', 'mobile-first'],
pcDemo: '',
mfDemo: ''
},
{
name: 'value',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '选项的值',
'en-US': 'Value for option'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'basic-usage',
mfDemo: 'basic-usage'
}
],
events: [],
methods: [],
slots: []
}
],
types: [
{
name: 'IOption',
type: 'interface',
code: `
interface IOption {
value?: string | number
label?: string
disabled?: boolean
icon?: Component
required?:boolean
}
`
},
{
name: 'ICacheOp',
type: 'interface',
code: `
interface ICacheItem {
frequency: number
key: string
time: number
}
interface ICacheOp {
key: string // 本地缓存的唯一 key 值
sortBy?: 'frequency' | 'time' // 排序的字段,默认 frequency (频次)
sort?: 'desc' | 'asc' // 排序方式,默认 desc (降序)
dataKey?: string // 数据中的唯一标识的 key 名称,默认 value
highlightClass?: string // 个性化高亮 class 名称默认memorize-highlight
highlightNum?: number // 高亮个性化的条数,默认Infinity
cacheNum?: number // 存储个性化的条数,默认Infinity
serialize?: ()=> string // 本地存储序列化方法,默认JSON.stringify
deserialize?: ()=> ICacheItem[] // 本地存储序反列化方法默认JSON.parse
}
`
},
{
name: 'IPlacement',
type: 'type',
code: `
type IPlacement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end'
`
}
]
}

View File

@ -0,0 +1,28 @@
<template>
<div>
<tiny-base-select v-model="value" multiple all-text="全部小吃">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶' },
{ value: '选项3', label: '蚵仔煎' },
{ value: '选项4', label: '龙须面' },
{ value: '选项6', label: '螺蛳粉' }
])
const value = ref([])
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,13 @@
import { expect, test } from '@playwright/test'
test('多选时自定义全部的文本', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#all-text')
const wrap = page.locator('#all-text')
const select = wrap.locator('.tiny-base-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await select.locator('.tiny-input__suffix').click()
await expect(option.filter({ hasText: '全部小吃' })).toHaveCount(1)
})

View File

@ -0,0 +1,36 @@
<template>
<div>
<tiny-base-select v-model="value" multiple all-text="全部小吃">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
value: [],
options: [
{ value: '选项1', label: '黄金糕' },
{ value: '选项2', label: '双皮奶' },
{ value: '选项3', label: '蚵仔煎' },
{ value: '选项4', label: '龙须面' },
{ value: '选项6', label: '螺蛳粉' }
]
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,102 @@
<template>
<div>
<div>场景1allow-create + filterable点击创建条目</div>
<br />
<tiny-base-select v-model="value" allow-create filterable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2allow-create + filterable + default-first-optionEnter 键创建条目</div>
<br />
<tiny-base-select v-model="value" allow-create filterable default-first-option>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3下拉框显示新增按钮</div>
<br />
<tiny-base-select
v-model="value"
placeholder="请选择"
@top-create-click="handleAddOption"
automatic-dropdown
top-create
ref="selectDom"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<tiny-dialog-box :visible="boxVisibility" @update:visible="boxVisibility = $event" title="新建" width="30%">
<div>
<div>
<span>label</span>
<br />
<br />
<tiny-input v-model="optionLabel"></tiny-input>
</div>
<br />
<div>
<span>value</span>
<br />
<br />
<tiny-input v-model="optionValue"></tiny-input>
</div>
</div>
<template #footer>
<tiny-button @click="boxVisibility = false">取消</tiny-button>
<tiny-button type="primary" @click="handleConfirm"> 确定 </tiny-button>
</template>
</tiny-dialog-box>
</div>
</template>
<script setup>
import { ref } from 'vue'
import {
BaseSelect as TinyBaseSelect,
Option as TinyOption,
Input as TinyInput,
Button as TinyButton,
DialogBox as TinyDialogBox,
Modal
} from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const selectDom = ref(null)
const value = ref('')
const boxVisibility = ref(false)
const optionLabel = ref('')
const optionValue = ref('')
const handleAddOption = () => {
optionValue.value = ''
optionLabel.value = ''
boxVisibility.value = true
}
const handleConfirm = () => {
if (!optionLabel.value || !optionValue.value) {
Modal.message({ message: '选项不能为空!', status: 'warning' })
return
}
boxVisibility.value = false
options.value.unshift({
value: optionValue,
label: optionLabel
})
selectDom.value.focus()
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,50 @@
import { test, expect } from '@playwright/test'
test('点击选中', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.waitForTimeout(300)
await page.goto('base-select#allow-create')
const wrap = page.locator('#allow-create')
const select = wrap.locator('.tiny-base-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const input = select.locator('.tiny-input__inner')
await input.click()
await input.fill('测试allow-create')
const KeyboardEvent = await page.evaluateHandle(() => new KeyboardEvent('keyup'))
await input.dispatchEvent('keyup', { KeyboardEvent })
await expect(input).toHaveValue('测试allow-create')
await dropdown.getByRole('listitem').filter({ hasText: '测试allow-create' }).click()
await expect(input).toHaveValue('测试allow-create')
await input.click()
await expect(input).toHaveValue('')
await expect(dropdown.getByRole('listitem').filter({ hasText: '测试allow-create' })).toHaveClass(/selected/)
})
test('enter 选中', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.waitForTimeout(300)
await page.goto('base-select#allow-create')
const wrap = page.locator('#allow-create')
const select = wrap.locator('.tiny-base-select').nth(1)
const dropdown = page.locator('body > .tiny-select-dropdown')
const input = select.locator('.tiny-input__inner')
await input.click()
await input.press('a')
await input.press('b')
await page.waitForTimeout(300)
await input.press('Enter')
await expect(dropdown).toBeHidden()
await expect(input).toHaveValue('ab')
await input.click()
await expect(input).toHaveValue('')
await expect(dropdown.getByRole('listitem').filter({ hasText: 'ab' })).toHaveClass(/selected/)
})

View File

@ -0,0 +1,105 @@
<template>
<div>
<div>场景1allow-create + filterable点击创建条目</div>
<br />
<tiny-base-select v-model="value" allow-create filterable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2allow-create + filterable + default-first-optionEnter 键创建条目</div>
<br />
<tiny-base-select v-model="value" allow-create filterable default-first-option>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3下拉框显示新增按钮</div>
<br />
<tiny-base-select
v-model="value"
placeholder="请选择"
@top-create-click="handleAddOption"
automatic-dropdown
top-create
ref="selectDom"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<tiny-dialog-box :visible="boxVisibility" @update:visible="boxVisibility = $event" title="新建" width="30%">
<div>
<div>
<span>label</span>
<br />
<br />
<tiny-input v-model="optionLabel"></tiny-input>
</div>
<br />
<div>
<span>value</span>
<br />
<br />
<tiny-input v-model="optionValue"></tiny-input>
</div>
</div>
<template #footer>
<tiny-button @click="boxVisibility = false">取消</tiny-button>
<tiny-button type="primary" @click="handleConfirm"> 确定 </tiny-button>
</template>
</tiny-dialog-box>
</div>
</template>
<script>
import { BaseSelect, Option, Input, Button, DialogBox, Modal } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyInput: Input,
TinyButton: Button,
TinyDialogBox: DialogBox
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: '',
boxVisibility: false,
optionLabel: '',
optionValue: ''
}
},
methods: {
handleAddOption() {
this.optionValue = ''
this.optionLabel = ''
this.boxVisibility = true
},
handleConfirm() {
if (!this.optionLabel || !this.optionValue) {
Modal.message({ message: '选项不能为空!', status: 'warning' })
return
}
this.boxVisibility = false
this.options.unshift({
value: this.optionValue,
label: this.optionLabel
})
this.$refs.selectDom.focus()
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,51 @@
<template>
<div>
<p>场景1默认不可搜索时获取焦点不下拉</p>
<tiny-button @click="handleFocus1"> 点击获取焦点 </tiny-button>
<tiny-base-select v-model="value" ref="selectOnlyFocusRef">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2设置不可搜索时获取焦点并自动下拉</p>
<tiny-button @click="handleFocus2"> 点击获取焦点 </tiny-button>
<tiny-base-select v-model="value" ref="selectAutoDropRef" automatic-dropdown>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption, Button as TinyButton } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
const selectOnlyFocusRef = ref()
const selectAutoDropRef = ref()
const handleFocus1 = () => {
selectOnlyFocusRef.value.focus()
}
const handleFocus2 = () => {
selectAutoDropRef.value.focus()
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
.tiny-button {
margin-right: 10px;
}
</style>

View File

@ -0,0 +1,31 @@
import { test, expect } from '@playwright/test'
test('不可搜索时,获取焦点不下拉', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#automatic-dropdown')
const wrap = page.locator('#automatic-dropdown')
const input = wrap.locator('.tiny-input__inner').first()
const dropdown = page.locator('.tiny-select-dropdown').first()
await wrap.getByRole('button').first().click()
// 聚焦高亮
await expect(input).toHaveCSS('border-color', 'rgb(94, 124, 224)')
// 不下拉
await expect(dropdown).toBeHidden()
})
test('可搜索时,获取焦点自动下拉', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#automatic-dropdown')
const wrap = page.locator('#automatic-dropdown')
const input = wrap.locator('.tiny-input__inner').nth(1)
const dropdown = page.locator('.tiny-select-dropdown').nth(1)
await wrap.getByRole('button').nth(1).click()
// 聚焦下拉
await dropdown.getByRole('listitem').filter({ hasText: '上海' }).click()
await expect(input).toHaveValue('上海')
// 验证选中
await input.click()
await expect(page.getByRole('listitem').filter({ hasText: '上海' })).toHaveClass(/selected/)
})

View File

@ -0,0 +1,59 @@
<template>
<div>
<p>场景1默认不可搜索时获取焦点不下拉</p>
<tiny-button @click="handleFocus1"> 点击获取焦点 </tiny-button>
<tiny-base-select v-model="value" ref="selectOnlyFocusRef">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2设置不可搜索时获取焦点并自动下拉</p>
<tiny-button @click="handleFocus2"> 点击获取焦点 </tiny-button>
<tiny-base-select v-model="value" ref="selectAutoDropRef" automatic-dropdown>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option, Button } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyButton: Button
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
},
methods: {
handleFocus1() {
this.$refs.selectOnlyFocusRef.focus()
},
handleFocus2() {
this.$refs.selectAutoDropRef.focus()
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
.tiny-button {
margin-right: 10px;
}
</style>

View File

@ -0,0 +1,38 @@
<template>
<div>
<div>选中的值为 {{ value }}</div>
<br />
<div>场景1标签式</div>
<br />
<tiny-base-select v-model="value">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" :icon="item.icon">
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2配置式</div>
<br />
<tiny-base-select v-model="value" :options="options"> </tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
import { iconFile } from '@opentiny/vue-icon'
const options = ref([
{ value: '选项1', label: '北京', icon: iconFile() },
{ value: '选项2', label: '上海', icon: iconFile() },
{ value: '选项3', label: '天津', icon: iconFile() },
{ value: '选项4', label: '重庆', icon: iconFile() },
{ value: '选项5', label: '深圳', icon: iconFile() }
])
const value = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,47 @@
import { test, expect } from '@playwright/test'
test('基础用法标签式', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#basic-usage')
const wrap = page.locator('#basic-usage')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await input.click()
await option.filter({ hasText: '天津' }).click()
await expect(input).toHaveValue('天津')
await select.locator('.tiny-input__suffix svg').click()
await expect(page.getByRole('listitem').filter({ hasText: '天津' })).toHaveClass(/selected/)
await option.filter({ hasText: '深圳' }).click()
await expect(input).toHaveValue('深圳')
await input.click()
await expect(option.filter({ hasText: '深圳' })).toHaveClass(/selected/)
await expect(option.locator('.tiny-option__icon')).toHaveCount(5)
await option.nth(0).click()
await expect(dropdown).toBeHidden()
})
test('基础用法配置式', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#basic-usage')
const wrap = page.locator('#basic-usage')
const select = wrap.locator('.tiny-base-select').nth(1)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await input.click()
await option.filter({ hasText: '天津' }).click()
await expect(input).toHaveValue('天津')
await select.locator('.tiny-input__suffix svg').click()
await expect(page.getByRole('listitem').filter({ hasText: '天津' })).toHaveClass(/selected/)
await option.filter({ hasText: '深圳' }).click()
await expect(input).toHaveValue('深圳')
await input.click()
await expect(option.filter({ hasText: '深圳' })).toHaveClass(/selected/)
await expect(option.locator('.tiny-option__icon')).toHaveCount(5)
await option.nth(0).click()
await expect(dropdown).toBeHidden()
})

View File

@ -0,0 +1,47 @@
<template>
<div>
<div>选中的值为 {{ value }}</div>
<br />
<div>场景1标签式</div>
<br />
<tiny-base-select v-model="value">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" :icon="item.icon">
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2配置式</div>
<br />
<tiny-base-select v-model="value" :options="options"> </tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
import { iconFile } from '@opentiny/vue-icon'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京', icon: iconFile() },
{ value: '选项2', label: '上海', icon: iconFile() },
{ value: '选项3', label: '天津', icon: iconFile() },
{ value: '选项4', label: '重庆', icon: iconFile() },
{ value: '选项5', label: '深圳', icon: iconFile() }
],
value: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,34 @@
<template>
<div>
<tiny-base-select v-model="value" value-key="val">
<tiny-option v-for="(item, index) in options" :key="index" :label="item.text" :value="item.obj"> </tiny-option>
</tiny-base-select>
<p class="value">
{{ value }}
</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ obj: { val: '选项1', id: 1 }, text: '北京' },
{ obj: { val: '选项2', id: 2 }, text: '上海' },
{ obj: { val: '选项3', id: 3 }, text: '天津' },
{ obj: { val: '选项4', id: 4 }, text: '重庆' },
{ obj: { val: '选项5', id: 5 }, text: '深圳' }
])
const value = ref({ val: '选项3', id: 3 })
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,21 @@
import { test, expect } from '@playwright/test'
test('binding-obj', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#binding-obj')
const wrap = page.locator('#binding-obj')
const input = wrap.locator('.tiny-input__inner')
const dropdown = page.locator('.tiny-select-dropdown')
const valueLocator = wrap.locator('.value')
await input.click()
await dropdown.getByRole('listitem').filter({ hasText: '重庆' }).click()
await expect(input).toHaveValue('重庆')
await expect(valueLocator).toHaveText('{ "val": "选项4", "id": 4 }')
await input.click()
await dropdown.getByRole('listitem').filter({ hasText: '天津' }).click()
await expect(input).toHaveValue('天津')
await expect(valueLocator).toHaveText('{ "val": "选项3", "id": 3 }')
})

View File

@ -0,0 +1,43 @@
<template>
<div>
<tiny-base-select v-model="value" value-key="val">
<tiny-option v-for="(item, index) in options" :key="index" :label="item.text" :value="item.obj"> </tiny-option>
</tiny-base-select>
<p class="value">
{{ value }}
</p>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ obj: { val: '选项1', id: 1 }, text: '北京' },
{ obj: { val: '选项2', id: 2 }, text: '上海' },
{ obj: { val: '选项3', id: 3 }, text: '天津' },
{ obj: { val: '选项4', id: 4 }, text: '重庆' },
{ obj: { val: '选项5', id: 5 }, text: '深圳' }
],
value: { val: '选项3', id: 3 }
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,45 @@
<template>
<div>
<tiny-base-select
v-model="value"
clearable
:options="options"
:cache-op="cacheOp"
@change="cacheChange"
></tiny-base-select>
<p class="cache-value">
{{ cacheValue }}
</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect } from '@opentiny/vue'
const cacheOp = ref({
key: 'test'
})
const cacheValue = ref('')
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('选项3')
const cacheChange = () => {
cacheValue.value = window.localStorage.getItem(`tiny_memorize_${cacheOp.value.key}`)
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,19 @@
import { expect, test } from '@playwright/test'
test('cache-op', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#cache-usage')
const wrap = page.locator('#cache-usage')
const dropdown = page.locator('.tiny-select-dropdown')
const input = wrap.locator('.tiny-input__inner')
const cacheValue = wrap.locator('.cache-value')
await input.click()
await dropdown.getByRole('listitem').filter({ hasText: '北京' }).click()
await expect(cacheValue).toContainText(['选项1'])
await input.click()
await dropdown.getByRole('listitem').filter({ hasText: '上海' }).click()
await expect(cacheValue).toContainText(['选项2'])
})

View File

@ -0,0 +1,55 @@
<template>
<div>
<tiny-base-select
v-model="value"
clearable
:options="options"
:cache-op="cacheOp"
@change="cacheChange"
></tiny-base-select>
<p class="cache-value">
{{ cacheValue }}
</p>
</div>
</template>
<script>
import { BaseSelect } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect
},
data() {
return {
cacheOp: {
key: 'test'
},
cacheValue: '',
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: '选项3'
}
},
methods: {
cacheChange() {
this.cacheValue = window.localStorage.getItem(`tiny_memorize_${this.cacheOp.key}`)
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<div>
<p>
场景1单选val 找不到匹配值val为 ,<span class="val">{{ val }}</span>
</p>
<tiny-base-select v-model="val" :clear-no-match-value="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>
场景2多选multiVal 找不到匹配值multiVal <span class="multi-val">{{ multiVal }}</span>
</p>
<tiny-base-select v-model="multiVal" :clear-no-match-value="true" multiple>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const val = ref('11')
const multiVal = ref(['选项2', '11'])
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,24 @@
import { test, expect } from '@playwright/test'
test('单选找不到匹配值', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#clear-no-match-value')
const wrap = page.locator('#clear-no-match-value')
const input = wrap.locator('.tiny-input__inner').nth(0)
// 验证是否清空不匹配的值
await expect(input).toHaveValue('')
await expect(wrap.locator('.val')).toHaveText('')
})
test('多选找不到匹配值', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#clear-no-match-value')
const wrap = page.locator('#clear-no-match-value')
const tag = wrap.locator('.tiny-base-select').nth(1).locator('.tiny-tag')
// 验证是否清空不匹配的值
await expect(tag).toHaveCount(1)
await expect(tag).toHaveText('上海')
await expect(wrap.locator('.multi-val')).toHaveText('[ "选项2" ]')
})

View File

@ -0,0 +1,51 @@
<template>
<div>
<p>
场景1单选val 找不到匹配值val为 ,<span class="val">{{ val }}</span>
</p>
<tiny-base-select v-model="val" :clear-no-match-value="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>
场景2多选multiVal 找不到匹配值multiVal <span class="multi-val">{{ multiVal }}</span>
</p>
<tiny-base-select v-model="multiVal" :clear-no-match-value="true" multiple>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
val: '11',
multiVal: ['选项2', '11']
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<tiny-base-select v-model="value" clearable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('选项3')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,21 @@
import { test, expect } from '@playwright/test'
test('clearable', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#clearable')
const wrap = page.locator('#clearable')
const dropdown = page.locator('.tiny-select-dropdown')
const input = wrap.locator('.tiny-input__inner')
const icon = wrap.locator('.tiny-input__suffix')
// 验证默认值
await expect(input).toHaveValue('天津')
// 验证清空
await input.hover()
await icon.click()
await expect(input).toHaveValue('')
// 验证选中
await icon.click()
await dropdown.getByRole('listitem').filter({ hasText: '上海' }).click()
await expect(input).toHaveValue('上海')
})

View File

@ -0,0 +1,34 @@
<template>
<tiny-base-select v-model="value" clearable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: '选项3'
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,48 @@
<template>
<div>
<br />
<div>场景1 collapse-tags折叠</div>
<br />
<tiny-base-select v-model="value1" :show-proportion="true" multiple collapse-tags>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2 hover-expand 折叠</div>
<br />
<tiny-base-select v-model="value2" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景3 click-expand 折叠</div>
<br />
<tiny-base-select v-model="value3" :max-visible-rows="2" multiple click-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆超长超长超长超长超长超长超长超长超长' },
{ value: '选项5', label: '深圳' }
])
const value1 = ref(['选项1', '选项2'])
const value2 = ref(['选项1', '选项2', '选项3', '选项4'])
const value3 = ref(['选项1', '选项2', '选项3', '选项4', '选项5'])
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,32 @@
import { test, expect } from '@playwright/test'
test('collapse-tags', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#collapse-tags')
const wrap = page.locator('#collapse-tags')
const select = wrap.locator('.tiny-base-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const tag = select.locator('.tiny-tag')
const option = dropdown.locator('.tiny-option')
// 验证默认值的折叠标签显示
await expect(tag).toHaveCount(2)
await expect(tag.filter({ hasText: '北京' })).toBeVisible()
await expect(tag.filter({ hasText: '+ 1' })).toBeVisible()
// 点击下拉后选中效果
await tag.first().click()
await expect(option.filter({ hasText: '北京' })).toHaveClass(/selected/)
await expect(option.filter({ hasText: '上海' })).toHaveClass(/selected/)
// 取消选中一个
await option.filter({ hasText: '北京' }).locator('span').nth(2).click()
await expect(tag.filter({ hasText: '+ 1' })).toBeHidden()
await expect(tag).toHaveCount(1)
// 再选中2个
await option.filter({ hasText: '天津' }).locator('span').nth(2).click()
await option.filter({ hasText: '深圳' }).locator('span').nth(2).click()
await expect(tag.filter({ hasText: '+ 2' })).toBeVisible()
await expect(tag).toHaveCount(2)
})

View File

@ -0,0 +1,56 @@
<template>
<div>
<br />
<div>场景1 collapse-tags折叠</div>
<br />
<tiny-base-select v-model="value1" :show-proportion="true" multiple collapse-tags>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2 hover-expand 折叠</div>
<br />
<tiny-base-select v-model="value2" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景3 click-expand 折叠</div>
<br />
<tiny-base-select v-model="value3" :max-visible-rows="2" multiple click-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆超长超长超长超长超长超长超长超长超长' },
{ value: '选项5', label: '深圳' }
],
value1: ['选项1', '选项2'],
value2: ['选项1', '选项2', '选项3', '选项4'],
value3: ['选项1', '选项2', '选项3', '选项4', '选项5']
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<div>
<p>场景1多选复制单个标签</p>
<tiny-base-select ref="selectTagSelectable" v-model="value1" multiple tag-selectable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2多选一键复制所有标签</p>
<tiny-base-select ref="selectCopyable" v-model="value2" multiple copyable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景3多选设置复制文本分隔符</p>
<tiny-base-select ref="selectCopyable" v-model="value2" multiple copyable text-split="/">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>复制到此处</p>
<tiny-input v-model="copyValue" class="copy-value" type="text"></tiny-input>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption, Input as TinyInput } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value1 = ref(['选项1'])
const value2 = ref(['选项1', '选项2'])
const copyValue = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,63 @@
import { test, expect } from '@playwright/test'
test('多选复制单个标签', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#copy-multi')
const wrap = page.locator('#copy-multi')
const select = wrap.locator('.tiny-base-select').nth(0)
const tag = select.locator('.tiny-tag').nth(0)
await expect(tag).toContainText('北京')
await page.waitForTimeout(200)
const tagBox = await tag.locator('span').boundingBox()
const x = tagBox.x + tagBox.width
const y = tagBox.y + tagBox.height - 5
await page.mouse.move(tagBox.x, tagBox.y)
await page.waitForTimeout(200)
await page.mouse.down()
await page.waitForTimeout(200)
await page.mouse.move(x, y)
await page.waitForTimeout(200)
await page.mouse.up()
await page.keyboard.press('Control+C')
await page.waitForTimeout(200)
const valueInput = page.locator('.copy-value .tiny-input__inner')
await expect(valueInput).toHaveValue('')
await valueInput.focus()
await page.keyboard.press('Control+V')
await page.waitForTimeout(200)
await expect(valueInput).toHaveValue('北京')
})
test('多选一键复制所有标签', async ({ page }) => {
await page.goto('base-select#copy-multi')
const wrap = page.locator('#copy-multi')
const select = wrap.locator('.tiny-base-select').nth(1)
const copyValueInput = wrap.locator('.copy-value .tiny-input__inner')
await page.waitForTimeout(200)
await select.hover()
await select.locator('.tiny-base-select__copy > .tiny-svg').click()
await copyValueInput.press('Control+V')
await expect(copyValueInput).toHaveValue('北京,上海')
})
test('多选设置复制文本分隔符', async ({ page }) => {
await page.goto('base-select#copy-multi')
const wrap = page.locator('#copy-multi')
const select = wrap.locator('.tiny-base-select').nth(2)
const copyValueInput = wrap.locator('.copy-value .tiny-input__inner')
await page.waitForTimeout(200)
await select.hover()
await select.locator('.tiny-base-select__copy > .tiny-svg').click()
await copyValueInput.press('Control+V')
await expect(copyValueInput).toHaveValue('北京/上海')
})

View File

@ -0,0 +1,54 @@
<template>
<div>
<p>场景1多选复制单个标签</p>
<tiny-base-select ref="selectTagSelectable" v-model="value1" multiple tag-selectable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2多选一键复制所有标签</p>
<tiny-base-select ref="selectCopyable" v-model="value2" multiple copyable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景3多选设置复制文本分隔符</p>
<tiny-base-select ref="selectCopyable" v-model="value2" multiple copyable text-split="/">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>复制到此处</p>
<tiny-input v-model="copyValue" class="copy-value" type="text"></tiny-input>
</div>
</template>
<script>
import { BaseSelect, Option, Input } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyInput: Input
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value1: ['选项1'],
value2: ['选项1', '选项2'],
copyValue: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,150 @@
<template>
<div>
<p>场景1单选无需配置可复制</p>
<tiny-base-select v-model="value1" :options="options1"> </tiny-base-select>
<p>场景2单选可搜索配置 allow-copy 可复制</p>
<tiny-base-select
ref="selectRef"
v-model="value1"
:options="options1"
filterable
:filter-method="filter"
allow-copy
>
</tiny-base-select>
<p>场景3单选远程搜索配置 allow-copy 可复制</p>
<tiny-base-select
v-model="value2"
filterable
remote
placeholder="请输入关键词"
:remote-method="remoteMethod"
:loading="loading"
loading-text="Loading..."
allow-copy
>
<tiny-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p class="font-style">粘贴至此处</p>
<tiny-input v-model="inputVal" type="text" class="custom"></tiny-input>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption, Input as TinyInput } from '@opentiny/vue'
const options1 = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const options2 = ref([])
const value1 = ref('选项1')
const value2 = ref('Alabama')
const inputVal = ref('')
const selectRef = ref(null)
const loading = ref(false)
const list = ref([])
const filter = (value) => {
const select = selectRef.value
if (value) {
select.state.cachedOptions.forEach((item) => {
item.state.visible = item.label.includes(value)
})
} else {
select.state.cachedOptions.forEach((item) => {
item.state.visible = true
})
}
}
const remoteMethod = (query) => {
if (query !== undefined) {
loading.value = true
setTimeout(() => {
loading.value = false
options2.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
options2.value = []
}
}
onMounted(() => {
const states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
]
list.value = states.map((item) => {
return { value: item, label: item }
})
})
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,76 @@
import { test, expect } from '@playwright/test'
test('单选无需配置可复制', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#copy-single')
const wrap = page.locator('#copy-single')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input__inner')
const valueInput = wrap.locator('.custom .tiny-input__inner')
await page.waitForTimeout(200)
const inputBox = await input.boundingBox()
await page.mouse.move(inputBox.x + inputBox?.width / 2, inputBox.y + inputBox?.height / 2)
await page.mouse.down()
await page.mouse.move(inputBox.x - 2, inputBox.y + inputBox?.height / 2)
await page.mouse.up()
await page.keyboard.press('Control+C')
await expect(valueInput).toHaveValue('')
await valueInput.focus()
await page.keyboard.press('Control+V')
await page.waitForTimeout(200)
await expect(valueInput).toHaveValue('北京')
})
test('单选可搜索配置 allow-copy 可复制', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#copy-single')
const wrap = page.locator('#copy-single')
const select = wrap.locator('.tiny-base-select').nth(1)
const input = select.locator('.tiny-input__inner')
const valueInput = wrap.locator('.custom .tiny-input__inner')
await expect(input).toHaveValue('北京')
await page.waitForTimeout(200)
const inputBox = await input.boundingBox()
await page.mouse.move(inputBox.x + inputBox?.width / 2, inputBox.y + inputBox?.height / 2)
await page.mouse.down()
await page.mouse.move(inputBox.x - 2, inputBox.y + inputBox?.height / 2)
await page.mouse.up()
await page.waitForTimeout(800)
await page.keyboard.press('Control+C')
await expect(valueInput).toHaveValue('')
await valueInput.click()
await page.keyboard.press('Control+V')
await page.waitForTimeout(200)
await expect(valueInput).toHaveValue('北京')
})
test('单选远程搜索配置 allow-copy 可复制', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#copy-single')
const wrap = page.locator('#copy-single')
const select = wrap.locator('.tiny-base-select').nth(2)
const input = select.locator('.tiny-input__inner')
const valueInput = wrap.locator('.custom .tiny-input__inner')
await page.waitForTimeout(100)
const inputBox = await input.boundingBox()
await page.mouse.move(inputBox.x + inputBox?.width / 2, inputBox.y + inputBox?.height / 2)
await page.mouse.down()
await page.mouse.move(inputBox.x - 2, inputBox.y + inputBox?.height / 2)
await page.mouse.up()
await page.keyboard.press('Control+C')
await expect(valueInput).toHaveValue('')
await valueInput.focus()
await page.keyboard.press('Control+V')
await page.waitForTimeout(200)
await expect(valueInput).toHaveValue('Alabama')
})

View File

@ -0,0 +1,156 @@
<template>
<div>
<p>场景1单选无需配置可复制</p>
<tiny-base-select v-model="value1" :options="options1"> </tiny-base-select>
<p>场景2单选可搜索配置 allow-copy 可复制</p>
<tiny-base-select
ref="selectRef"
v-model="value1"
:options="options1"
filterable
:filter-method="filter"
allow-copy
>
</tiny-base-select>
<p>场景3单选远程搜索配置 allow-copy 可复制</p>
<tiny-base-select
v-model="value2"
filterable
remote
placeholder="请输入关键词"
:remote-method="remoteMethod"
:loading="loading"
loading-text="Loading..."
allow-copy
>
<tiny-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p class="font-style">粘贴至此处</p>
<tiny-input v-model="inputVal" type="text" class="custom"></tiny-input>
</div>
</template>
<script>
import { BaseSelect, Option, Input } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyInput: Input
},
data() {
return {
options1: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
options2: [],
value1: '选项1',
value2: 'Alabama',
inputVal: '',
loading: false,
list: []
}
},
mounted() {
const states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
]
this.list = states.map((item) => {
return { value: item, label: item }
})
},
methods: {
filter(value) {
const select = this.$refs.selectRef
if (value) {
select.state.cachedOptions.forEach((item) => {
item.state.visible = item.label.includes(value)
})
} else {
select.state.cachedOptions.forEach((item) => {
item.state.visible = true
})
}
},
remoteMethod(query) {
if (query !== undefined) {
this.loading = true
setTimeout(() => {
this.loading = false
this.options2 = this.list.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
this.options2 = []
}
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,100 @@
<template>
<div>
<br />
<div>场景1 下拉禁用</div>
<br />
<tiny-base-select v-model="value1" disabled autocomplete>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2多选 + 下拉某项禁用</div>
<br />
<tiny-base-select v-model="value2" multiple>
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3单选 + 下拉某项禁用</div>
<br />
<tiny-base-select v-model="value3">
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景4多选 + 禁用项默认选中</div>
<br />
<tiny-base-select v-model="value4" multiple>
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景5多选 hover-expand 模式下禁用</div>
<br />
<tiny-base-select
v-model="value5"
placeholder="请选择"
multiple
disabled
hover-expand
disabled-tooltip-content="我是自定义内容"
>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options1 = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆重庆超长超长超长超长超长超长超长超长超长' },
{ value: '选项5', label: '深圳' }
])
const options2 = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海', disabled: true },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆', disabled: true },
{ value: '选项5', label: '深圳' }
])
const value1 = ref('')
const value2 = ref(['选项2'])
const value3 = ref('')
const value4 = ref(['选项2', '选项3'])
const value5 = ref(['选项1', '选项2', '选项3', '选项4', '选项5'])
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,88 @@
import { test, expect } from '@playwright/test'
test('下拉禁用', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#disabled')
const wrap = page.locator('#disabled')
const input = wrap.locator('.tiny-input__inner').first()
const hasDisabled = await input.evaluate((input) => input.hasAttribute('disabled'))
await expect(hasDisabled).toBe(true)
})
test('多选某项禁用', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#disabled')
const wrap = page.locator('#disabled')
const select = wrap.locator('.tiny-base-select').nth(1)
const dropdown = page.locator('body > .tiny-select-dropdown')
const tag = select.locator('.tiny-tag')
const option = dropdown.locator('.tiny-option')
await expect(tag).toHaveCount(1)
await select.click()
await expect(option.filter({ hasText: '上海' })).toHaveClass(/is-disabled/)
await option.filter({ hasText: '上海' }).click()
await expect(tag).toHaveCount(1)
await option.filter({ hasText: '北京' }).click()
await expect(tag).toHaveCount(2)
await expect(tag.filter({ hasText: '北京' })).toHaveCount(1)
})
test('单选某项禁用', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#disabled')
const wrap = page.locator('#disabled')
const select = wrap.locator('.tiny-base-select').nth(2)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await select.click()
await expect(option.filter({ hasText: '全部' })).toHaveCount(0)
await expect(option.filter({ hasText: '上海' })).toHaveClass(/is-disabled/)
await option.filter({ hasText: '上海' }).click()
await expect(dropdown).toBeVisible()
await expect(input).toHaveValue('')
await option.filter({ hasText: '北京' }).click()
await expect(dropdown).toBeHidden()
await expect(input).toHaveValue('北京')
})
test('多选,禁用项默认选中', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#disabled')
const wrap = page.locator('#disabled')
const select = wrap.locator('.tiny-base-select').nth(3)
const dropdown = page.locator('body > .tiny-select-dropdown')
const tag = select.locator('.tiny-tag')
const option = dropdown.locator('.tiny-option')
// 默认值显示tag数
await expect(tag).toHaveCount(2)
// 禁用项默认选中不显示关闭图标
await expect(tag.filter({ hasText: '上海' }).locator('svg')).toHaveCount(0)
// 非禁用项显示关闭图标
await expect(tag.filter({ hasText: '天津' }).locator('svg')).toHaveCount(1)
// 下拉禁用和默认选中的效果
await select.click()
await expect(option.filter({ hasText: '全部' })).toHaveCount(1)
await expect(option.filter({ hasText: '上海' })).toHaveClass(/is-disabled/)
await expect(option.filter({ hasText: '上海' })).toHaveClass(/selected/)
await expect(option.filter({ hasText: '天津' })).toHaveClass(/selected/)
// 点击禁用项不取消选中
await option.filter({ hasText: '上海' }).click()
await expect(dropdown).toBeVisible()
await expect(tag).toHaveCount(2)
// 选中其他项
await option.filter({ hasText: '北京' }).click()
await expect(dropdown).toBeVisible()
await expect(tag).toHaveCount(3)
})

View File

@ -0,0 +1,107 @@
<template>
<div>
<br />
<div>场景1 下拉禁用</div>
<br />
<tiny-base-select v-model="value1" disabled autocomplete>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2多选 + 下拉某项禁用</div>
<br />
<tiny-base-select v-model="value2" multiple>
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3单选 + 下拉某项禁用</div>
<br />
<tiny-base-select v-model="value3">
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景4多选 + 禁用项默认选中</div>
<br />
<tiny-base-select v-model="value4" multiple>
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景5多选 hover-expand 模式下禁用</div>
<br />
<tiny-base-select
v-model="value5"
placeholder="请选择"
multiple
disabled
hover-expand
disabled-tooltip-content="我是自定义内容"
>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options1: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆重庆超长超长超长超长超长超长超长超长超长' },
{ value: '选项5', label: '深圳' }
],
options2: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海', disabled: true },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆', disabled: true },
{ value: '选项5', label: '深圳' }
],
value1: '',
value2: ['选项2'],
value3: '',
value4: ['选项2', '选项3'],
value5: ['选项1', '选项2', '选项3', '选项4', '选项5']
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,106 @@
<template>
<div>
<p>单选</p>
<tiny-base-select
v-model="value1"
clearable
@change="change"
@blur="blur"
@focus="focus"
@visible-change="visibleChange"
@clear="clear"
@dropdown-click="dropdownClick"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>多选</p>
<tiny-base-select
v-model="value2"
multiple
clearable
@change="change"
@blur="blur"
@focus="focus"
@visible-change="visibleChange"
@remove-tag="removeTag"
@dropdown-click="dropdownClick"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption, Modal } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value1 = ref('')
const value2 = ref([])
const change = () => {
Modal.message({
message: '触发 change 事件',
status: 'info'
})
}
const clear = () => {
Modal.message({
message: '触发 clear 事件',
status: 'info'
})
}
const focus = () => {
Modal.message({
message: '触发 focus 事件',
status: 'info'
})
}
const blur = () => {
Modal.message({
message: '触发 blur 事件',
status: 'info'
})
}
const removeTag = () => {
Modal.message({
message: '触发 remove-tag 事件',
status: 'info'
})
}
const visibleChange = () => {
Modal.message({
message: '触发 visible-change 事件',
status: 'info'
})
}
const dropdownClick = () => {
Modal.message({
message: '触发 dropdown-click 事件',
status: 'info'
})
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,72 @@
import { test, expect } from '@playwright/test'
test('单选事件', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#events')
const wrap = page.locator('#events')
const select = wrap.locator('.tiny-base-select').first()
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const model = page.locator('.tiny-modal')
await input.click()
await page.waitForTimeout(500)
await expect(model.filter({ hasText: '触发 focus 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 visible-change 事件' })).toHaveCount(1)
await option.first().click()
await expect(input).toHaveValue('北京')
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 visible-change 事件' })).toHaveCount(2)
await page.waitForTimeout(500)
await wrap.click()
await expect(model.filter({ hasText: '触发 blur 事件' })).toHaveCount(1)
await page.waitForTimeout(200)
await input.hover()
await select.locator('.tiny-base-select__caret.icon-close').click()
await page.waitForTimeout(500)
await expect(input).toHaveValue('')
await expect(model.filter({ hasText: '触发 clear 事件' })).toHaveCount(1)
})
test('多选事件', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#events')
const wrap = page.locator('#events')
const select = wrap.locator('.tiny-base-select').nth(1)
const tag = wrap.locator('.tiny-tag')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const model = page.locator('.tiny-modal')
await page.waitForTimeout(500)
await select.click()
await expect(model.filter({ hasText: '触发 visible-change 事件' })).toHaveCount(1)
await option.nth(1).click()
await expect(tag).toHaveCount(1)
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1)
await option.nth(0).click()
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(2)
await expect(tag).toHaveCount(5)
await page.waitForTimeout(500)
await tag.first().locator('.tiny-tag__close').click()
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1)
await expect(model.filter({ hasText: '触发 remove-tag 事件' })).toHaveCount(1)
await expect(tag).toHaveCount(4)
await wrap.click()
await expect(model.filter({ hasText: '触发 visible-change 事件' })).toHaveCount(1)
await page.waitForTimeout(200)
await select.hover()
await select.locator('.tiny-base-select__caret.icon-close').click()
await expect(tag).toHaveCount(0)
await expect(model.filter({ hasText: '触发 change 事件' })).toHaveCount(1)
})

View File

@ -0,0 +1,112 @@
<template>
<div>
<div>单选</div>
<br />
<tiny-base-select
v-model="value1"
clearable
@change="change"
@blur="blur"
@focus="focus"
@visible-change="visibleChange"
@clear="clear"
@dropdown-click="dropdownClick"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>多选</div>
<br />
<tiny-base-select
v-model="value2"
multiple
clearable
@change="change"
@blur="blur"
@focus="focus"
@visible-change="visibleChange"
@remove-tag="removeTag"
@dropdown-click="dropdownClick"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option, Modal } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value1: '',
value2: []
}
},
methods: {
change() {
Modal.message({
message: '触发 change 事件',
status: 'info'
})
},
clear() {
Modal.message({
message: '触发 clear 事件',
status: 'info'
})
},
focus() {
Modal.message({
message: '触发 focus 事件'
})
},
blur() {
Modal.message({
message: '触发 blur 事件',
status: 'info'
})
},
removeTag() {
Modal.message({
message: '触发 remove-tag 事件',
status: 'info'
})
},
visibleChange() {
Modal.message({
message: '触发 visible-change 事件',
status: 'info'
})
},
dropdownClick() {
Modal.message({
message: '触发 dropdown-click 事件',
status: 'info'
})
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,72 @@
<template>
<div>
<p>场景1组件默认过滤</p>
<tiny-base-select v-model="value1" ref="selectRef" filterable no-match-text="No Match">
<tiny-option
v-for="item in options"
v-show="!item.filter"
:key="item.value"
:label="item.label"
:value="item.value"
>
</tiny-option>
</tiny-base-select>
<p>场景2自定义过滤</p>
<tiny-base-select
v-model="value2"
ref="customFilterRef"
filterable
:filter-method="customFilterMethod"
clearable
no-match-text="No Match"
>
<tiny-option
v-for="item in options"
v-show="!item.filter"
:key="item.value"
:label="item.label"
:value="item.value"
>
</tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value1 = ref('')
const value2 = ref('')
const selectRef = ref()
const customFilterRef = ref()
const customFilterMethod = (searchValue) => {
if (searchValue) {
customFilterRef.value?.state.cachedOptions.forEach((item) => {
item.state.visible = item.label.includes(searchValue)
})
} else {
customFilterRef.value?.state.cachedOptions.forEach((item) => {
item.state.visible = true
})
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,89 @@
import { expect, test } from '@playwright/test'
test('默认搜索', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#filter-method')
const wrap = page.locator('#filter-method')
const select = wrap.locator('.tiny-base-select').first()
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
// 1.1 没有过滤到内容
await input.click()
// 1.1.1 验证 no-match-text
await expect(page.getByText('No Match')).toBeHidden()
await input.press('1')
await expect(input).toHaveValue('1')
await input.press('Enter')
await expect(page.getByText('No Match')).toBeVisible()
await page.waitForTimeout(500)
let allListItems = await option.all()
allListItems.forEach(async (item) => {
await expect(item).toHaveCSS('display', 'none')
})
// 1.2 过滤到内容
await input.fill('上海')
await expect(input).toHaveValue('上海')
await input.press('Enter')
await page.waitForTimeout(200)
allListItems.forEach(async (item) => {
const isVisibleItem = (await item.innerText()) === '上海'
if (isVisibleItem) {
await expect(item).toHaveCSS('display', 'flex')
} else {
await expect(item).toHaveCSS('display', 'none')
}
})
await expect(option.filter({ hasText: '上海' })).toBeVisible()
await option.filter({ hasText: '上海' }).click()
await expect(input).toHaveValue('上海')
})
test('自定义过滤', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#filter-method')
const wrap = page.locator('#filter-method')
const select = wrap.locator('.tiny-base-select').nth(1)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
// 1.1 没有过滤到内容
await input.click()
// 1.1.1 验证 no-match-text
await expect(page.getByText('No Match')).toBeHidden()
await input.press('1')
await expect(input).toHaveValue('1')
await input.press('Enter')
await expect(page.getByText('No Match')).toBeVisible()
await page.waitForTimeout(500)
let allListItems = await option.all()
allListItems.forEach(async (item) => {
await expect(item).toHaveCSS('display', 'none')
})
// 1.2 过滤到内容
await input.fill('上海')
await expect(input).toHaveValue('上海')
await input.press('Enter')
await page.waitForTimeout(200)
allListItems.forEach(async (item) => {
const isVisibleItem = (await item.innerText()) === '上海'
if (isVisibleItem) {
await expect(item).toHaveCSS('display', 'flex')
} else {
await expect(item).toHaveCSS('display', 'none')
}
})
await expect(option.filter({ hasText: '上海' })).toBeVisible()
await option.filter({ hasText: '上海' }).click()
await expect(input).toHaveValue('上海')
})

View File

@ -0,0 +1,82 @@
<template>
<div>
<p>场景1组件默认过滤</p>
<tiny-base-select v-model="value1" ref="selectRef" filterable no-match-text="No Match">
<tiny-option
v-for="item in options"
v-show="!item.filter"
:key="item.value"
:label="item.label"
:value="item.value"
>
</tiny-option>
</tiny-base-select>
<p>场景2自定义过滤</p>
<tiny-base-select
v-model="value2"
ref="customFilterRef"
filterable
:filter-method="customFilterMethod"
clearable
no-match-text="No Match"
>
<tiny-option
v-for="item in options"
v-show="!item.filter"
:key="item.value"
:label="item.label"
:value="item.value"
>
</tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value1: '',
value2: ''
}
},
methods: {
customFilterMethod(searchValue) {
const customFilterRef = this.$refs.customFilterRef
if (searchValue) {
customFilterRef.state.cachedOptions.forEach((item) => {
item.state.visible = item.label.includes(searchValue)
})
} else {
customFilterRef.state.cachedOptions.forEach((item) => {
item.state.visible = true
})
}
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,42 @@
<template>
<div>
<div>场景1过滤器模式(默认)</div>
<br />
<tiny-base-select
v-model="value"
label="地区名称"
shape="filter"
:drop-style="{ width: '200px', 'min-width': '200px' }"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景2过滤器模式(透明)</div>
<br />
<tiny-base-select
v-model="value"
label="地区名称"
shape="filter"
:drop-style="{ width: '200px', 'min-width': '200px' }"
blank
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
</script>

View File

@ -0,0 +1,51 @@
<template>
<div>
<div>场景1过滤器模式(默认)</div>
<br />
<tiny-base-select
v-model="value"
label="地区名称"
shape="filter"
:drop-style="{ width: '200px', 'min-width': '200px' }"
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景2过滤器模式(透明)</div>
<br />
<tiny-base-select
v-model="value"
label="地区名称"
shape="filter"
:drop-style="{ width: '200px', 'min-width': '200px' }"
blank
>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
}
}
</script>

View File

@ -0,0 +1,25 @@
<template>
<tiny-base-select v-model="value" :hide-drop="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,12 @@
import { test, expect } from '@playwright/test'
test('hidedrop', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#hide-drop')
const wrap = page.locator('#hide-drop')
const select = wrap.locator('.tiny-base-select')
const dropdown = page.locator('body > .tiny-select-dropdown')
await select.click()
await expect(dropdown).toBeHidden()
})

View File

@ -0,0 +1,34 @@
<template>
<tiny-base-select v-model="value" :hide-drop="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<div>
<p>设置为下划线类型</p>
<p class="font-style">场景1下划线默认</p>
<tiny-base-select v-model="value1" input-box-type="underline" clearable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p class="font-style">场景2下划线禁用</p>
<tiny-base-select v-model="value2" input-box-type="underline" disabled>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p class="font-style">场景3下划线多选</p>
<tiny-base-select v-model="value3" input-box-type="underline" multiple>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value1 = ref('')
const value2 = ref('')
const value3 = ref([])
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,72 @@
import { test, expect } from '@playwright/test'
test('下划线默认', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#input-box-type')
const wrap = page.locator('#input-box-type')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await expect(select).toHaveClass(/tiny-base-select__underline/)
await expect(input).toHaveCSS('border-top-width', '0px')
await expect(input).toHaveCSS('border-left-width', '0px')
await expect(input).toHaveCSS('border-right-width', '0px')
await expect(input).toHaveCSS('border-color', 'rgb(173, 176, 184)')
await expect(select.locator('svg')).toHaveCSS('fill', 'rgb(87, 93, 108)')
await select.click()
await option.first().click()
await expect(dropdown).toBeHidden()
await expect(input).toHaveValue('北京')
})
test('下划线禁用', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#input-box-type')
const wrap = page.locator('#input-box-type')
const select = wrap.locator('.tiny-base-select').nth(1)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
await expect(select).toHaveClass(/tiny-base-select__underline/)
await expect(input).toHaveCSS('border-top-width', '0px')
await expect(input).toHaveCSS('border-left-width', '0px')
await expect(input).toHaveCSS('border-right-width', '0px')
await expect(input).toHaveCSS('border-color', 'rgb(223, 225, 230)')
await expect(input).toHaveCSS('cursor', 'not-allowed')
await expect(select.locator('svg')).toHaveCSS('fill', 'rgb(173, 176, 184)')
const hasDisabled = await input.evaluate((input) => input.hasAttribute('disabled'))
await expect(hasDisabled).toBe(true)
await select.click()
await expect(dropdown).toBeHidden()
await expect(input).toHaveValue('')
})
test('下划线多选', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#input-box-type')
const wrap = page.locator('#input-box-type')
const select = wrap.locator('.tiny-base-select').nth(2)
const input = select.locator('.tiny-input__inner')
const tag = wrap.locator('.tiny-tag')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await expect(select).toHaveClass(/tiny-base-select__underline/)
await expect(input).toHaveCSS('border-top-width', '0px')
await expect(input).toHaveCSS('border-left-width', '0px')
await expect(input).toHaveCSS('border-right-width', '0px')
await expect(input).toHaveCSS('border-color', 'rgb(173, 176, 184)')
await expect(select.locator('.tiny-base-select__caret')).toHaveCSS('fill', 'rgb(87, 93, 108)')
await select.click()
await expect(dropdown).toBeVisible()
await option.first().click()
await expect(tag).toHaveCount(5)
await expect(select).toHaveClass(/tiny-base-select__underline/)
await expect(select).toHaveClass(/tiny-base-select__multiple/)
})

View File

@ -0,0 +1,52 @@
<template>
<div>
<p>设置为下划线类型</p>
<p>场景1下划线默认</p>
<tiny-base-select v-model="value1" input-box-type="underline" clearable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2下划线禁用</p>
<tiny-base-select v-model="value2" input-box-type="underline" disabled>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景3下划线多选</p>
<tiny-base-select v-model="value3" input-box-type="underline" multiple>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value1: '',
value2: '',
value3: []
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<div>
<p>场景1默认下拉弹框宽度由内容撑开</p>
<tiny-base-select v-model="value">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2下拉弹框宽度与输入框一致</p>
<tiny-base-select v-model="value" is-drop-inherit-width>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{
value: '选项1',
label: '北京北京北京北京北京北京北京北京北京北京北京北京北京北京北京北京北京'
},
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,34 @@
import { expect, test } from '@playwright/test'
test('默认下拉弹框宽度由内容撑开', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#is-drop-inherit-width')
const wrap = page.locator('#is-drop-inherit-width')
const select = wrap.locator('.tiny-base-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const input = select.locator('.tiny-input__inner')
const option = dropdown.locator('.tiny-option')
await select.click()
const inputBox = await input.boundingBox()
const listitemBox = await option.first().boundingBox()
const result = listitemBox.width > inputBox.width
await expect(result).toBe(true)
})
test('下拉弹框宽度与输入框一致', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#is-drop-inherit-width')
const wrap = page.locator('#is-drop-inherit-width')
const select = wrap.locator('.tiny-base-select').nth(1)
const dropdown = page.locator('body > .tiny-select-dropdown')
const input = select.locator('.tiny-input__inner')
await select.click()
const inputBox = await input.boundingBox()
const dropdownBox = await dropdown.boundingBox()
const result = dropdownBox.width - inputBox.width
await expect(result < 1).toBe(true)
})

View File

@ -0,0 +1,48 @@
<template>
<div>
<p>场景1默认下拉弹框宽度由内容撑开</p>
<tiny-base-select v-model="value">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2下拉弹框宽度与输入框一致</p>
<tiny-base-select v-model="value" is-drop-inherit-width>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{
value: '选项1',
label: '北京北京北京北京北京北京北京北京北京北京北京北京北京北京北京北京北京'
},
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,88 @@
<template>
<div>
<div class="demo1">
<div>场景1默认 focus() 后仅聚焦不下拉</div>
<br />
<tiny-button @click="handleFocus1"> 点击获取焦点 </tiny-button>
<tiny-button @click="handleBlur1"> 点击失去焦点 </tiny-button>
<br />
<tiny-base-select v-model="value" ref="drop1">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
<br />
<div class="demo2">
<div>场景2配置 filterablefocus() 后聚焦并自动下拉</div>
<br />
<tiny-button @click="handleFocus2"> 点击获取焦点 </tiny-button>
<tiny-button @click="handleBlur2"> 点击失去焦点 </tiny-button>
<br />
<tiny-base-select v-model="value" ref="drop2" filterable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
<br />
<div class="demo3">
<div>场景2配置 automaticDropdownfocus() 后聚焦并自动下拉</div>
<br />
<tiny-button @click="handleFocus3"> 点击获取焦点 </tiny-button>
<tiny-button @click="handleBlur3"> 点击失去焦点 </tiny-button>
<br />
<tiny-base-select v-model="value" ref="drop3" automatic-dropdown>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption, Button as TinyButton } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
const drop1 = ref(null)
const drop2 = ref(null)
const drop3 = ref(null)
const handleFocus1 = () => {
drop1.value.focus()
}
const handleBlur1 = () => {
drop1.value.blur()
}
const handleFocus2 = () => {
drop2.value.focus()
}
const handleBlur2 = () => {
drop2.value.blur()
}
const handleFocus3 = () => {
drop3.value.focus()
}
const handleBlur3 = () => {
drop3.value.blur()
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
.tiny-button {
max-width: unset;
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,18 @@
import { test, expect } from '@playwright/test'
test('可搜索+手动聚焦失焦', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#manual-focus-blur')
const wrap = page.locator('#manual-focus-blur')
const dropdown = page.locator('body > .tiny-select-dropdown')
const button = wrap.locator('.demo2 .tiny-button')
await page.waitForTimeout(2000)
// 手动聚焦下拉
await button.nth(0).click()
await expect(dropdown).toBeVisible()
// 手动失焦收起
await button.nth(1).click()
await expect(dropdown).toBeHidden()
})

View File

@ -0,0 +1,90 @@
<template>
<div>
<div class="demo1">
<div>场景1默认 focus() 后仅聚焦不下拉</div>
<br />
<tiny-button @click="handleFocus1"> 点击获取焦点 </tiny-button>
<tiny-button @click="handleBlur1"> 点击失去焦点 </tiny-button>
<br />
<tiny-base-select v-model="value" ref="drop1">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
<br />
<div class="demo2">
<div>场景2配置 filterablefocus() 后聚焦并自动下拉</div>
<br />
<tiny-button @click="handleFocus2"> 点击获取焦点 </tiny-button>
<tiny-button @click="handleBlur2"> 点击失去焦点 </tiny-button>
<br />
<tiny-base-select v-model="value" ref="drop2" filterable>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
<br />
<div class="demo3">
<div>场景2配置 automaticDropdownfocus() 后聚焦并自动下拉</div>
<br />
<tiny-button @click="handleFocus3"> 点击获取焦点 </tiny-button>
<tiny-button @click="handleBlur3"> 点击失去焦点 </tiny-button>
<br />
<tiny-base-select v-model="value" ref="drop3" automatic-dropdown>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</div>
</template>
<script>
import { BaseSelect, Option, Button } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyButton: Button
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
},
methods: {
handleFocus1() {
this.$refs.drop1.focus()
},
handleBlur1() {
this.$refs.drop1.blur()
},
handleFocus2() {
this.$refs.drop2.focus()
},
handleBlur2() {
this.$refs.drop2.blur()
},
handleFocus3() {
this.$refs.drop3.focus()
},
handleBlur3() {
this.$refs.drop3.blur()
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
.tiny-button {
max-width: unset;
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,33 @@
<template>
<div>
<p>场景1配置式配置映射字段</p>
<tiny-select v-model="value1" multiple :options="options" value-field="val" text-field="text"> </tiny-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Select as TinySelect } from '@opentiny/vue'
const value1 = ref(['选项1', '选项2'])
const options = ref([
{ val: '选项1', text: '北京' },
{ val: '选项2', text: '上海' },
{ val: '选项3', text: '天津' },
{ val: '选项4', text: '重庆' },
{ val: '选项5', text: '深圳' },
{ val: '选项6', text: '南京' },
{ val: '选项7', text: '成都' }
])
</script>
<style lang="less" scoped>
.tiny-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,21 @@
import { expect, test } from '@playwright/test'
test('配置式配置映射字段', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('select#map-field')
const wrap = page.locator('#map-field')
const select = wrap.locator('.tiny-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const suffix = select.locator('.tiny-input__suffix')
const tag = select.locator('.tiny-tag')
await expect(tag).toHaveCount(2)
await expect(tag.first()).toHaveText('北京')
await expect(tag.nth(1)).toHaveText('上海')
await suffix.click()
await page.waitForTimeout(500)
await expect(dropdown).toBeVisible()
await expect(option.filter({ hasText: '北京' })).toHaveClass(/selected/)
await expect(option.filter({ hasText: '上海' })).toHaveClass(/selected/)
})

View File

@ -0,0 +1,65 @@
<template>
<div>
<p>场景1配置式配置映射字段</p>
<tiny-select v-model="value1" multiple :options="options" value-field="val" text-field="text"> </tiny-select>
<p>场景2嵌套表格配置映射字段</p>
<tiny-select
v-model="value2"
multiple
value-field="id"
text-field="city"
render-type="grid"
:grid-op="gridOp"
></tiny-select>
</div>
</template>
<script>
import { Select } from '@opentiny/vue'
export default {
components: {
TinySelect: Select
},
data() {
return {
value1: ['选项1', '选项2'],
value2: ['001', '002'],
gridOp: {
data: [
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
{ id: '005', area: '华南区', province: '广东省', city: '中山市' }
],
columns: [
{ type: 'selection', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
},
options: [
{ val: '选项1', text: '北京' },
{ val: '选项2', text: '上海' },
{ val: '选项3', text: '天津' },
{ val: '选项4', text: '重庆' },
{ val: '选项5', text: '深圳' },
{ val: '选项6', text: '南京' },
{ val: '选项7', text: '成都' }
]
}
}
}
</script>
<style lang="less" scoped>
.tiny-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,51 @@
<template>
<div>
<tiny-base-select v-model="value">
<tiny-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
@click.native="clickItem(item.value)"
:highlight-class="item._highlightClass"
></tiny-option>
</tiny-base-select>
<p class="cache-value">
{{ cacheValue }}
</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
import Memorize from '@opentiny/vue-renderless/common/deps/memorize'
const MemorizeInstance = new Memorize({ key: 'test456' })
const options = MemorizeInstance.assemble([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('选项3')
const cacheValue = ref([])
const clickItem = (value) => {
MemorizeInstance.updateByKey(value)
cacheValue.value = window.localStorage.getItem('tiny_memorize_test456')
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,19 @@
import { expect, test } from '@playwright/test'
test('手动缓存', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#memoize-usage')
const wrap = page.locator('#memoize-usage')
const select = wrap.locator('.tiny-base-select')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const cacheValue = wrap.locator('.cache-value')
await select.click()
await option.filter({ hasText: '北京' }).click()
await expect(cacheValue).toContainText(['选项1'])
await select.click()
await option.filter({ hasText: '上海' }).click()
await expect(cacheValue).toContainText(['选项2'])
})

View File

@ -0,0 +1,65 @@
<template>
<div>
<tiny-base-select v-model="value">
<tiny-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
@click.native="clickItem(item.value)"
:highlight-class="item._highlightClass"
></tiny-option>
</tiny-base-select>
<p class="cache-value">
{{ cacheValue }}
</p>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
import Memorize from '@opentiny/vue-renderless/common/deps/memorize'
const MemorizeInstance = new Memorize({ key: 'test456' })
const options = MemorizeInstance.assemble([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options,
value: '选项3',
cacheValue: []
}
},
methods: {
clickItem(value) {
MemorizeInstance.updateByKey(value)
this.cacheValue = window.localStorage.getItem('tiny_memorize_test456')
}
},
mounted() {
window.localStorage.setItem('tiny_memorize_test456', '')
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<div>
<br />
<div>场景1多选</div>
<br />
<tiny-base-select v-model="value1" multiple>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景2必选</div>
<br />
<tiny-base-select v-model="value2" multiple>
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:required="item.required"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景3配置式必选</div>
<br />
<tiny-base-select v-model="value3" multiple :options="options2"> </tiny-base-select>
<br />
<br />
<br />
<div>场景4多选个数限制</div>
<br />
<tiny-base-select v-model="value4" multiple :multiple-limit="2" show-limit-text>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景5自定义图标 + 自定义样式</div>
<br />
<tiny-base-select
v-model="value4"
multiple
:dropdown-icon="tinyIconPopup"
:drop-style="{ width: '200px', 'min-width': '200px' }"
>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
import { iconPopup } from '@opentiny/vue-icon'
const options1 = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' },
{ value: '选项6', label: '南京' },
{ value: '选项7', label: '成都' }
])
const options2 = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳', required: true },
{ value: '选项6', label: '南京' },
{ value: '选项7', label: '成都' }
])
const value1 = ref(['选项1', '选项2'])
const value2 = ref(['选项1', '选项2'])
const value3 = ref([])
const value4 = ref([])
const tinyIconPopup = iconPopup()
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<div class="demo-box">
<tiny-button @click="displayOnly = !displayOnly">display-only/{{ displayOnly }}</tiny-button>
<p>边框为表单范围</p>
<tiny-form class="custom-form" :inline="inline" label-position="top" :display-only="displayOnly">
<tiny-form-item label="label 垂直布局">
<div>
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</tiny-option>
</tiny-base-select>
<br />
<br />
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</tiny-option>
</tiny-base-select>
</div>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="form 超出隐藏">
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
</tiny-form>
<br />
<tiny-form class="custom-form visible-form" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="自定义-超出显示">
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
</tiny-form>
<br />
<tiny-form class="custom-form" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="默认尺寸">
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" size="mini" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="mini" size="mini">
<tiny-base-select v-model="formData.select1" size="mini" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" size="small" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="small" size="small">
<tiny-base-select v-model="formData.select1" size="small" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" size="medium" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="medium" size="medium">
<tiny-base-select v-model="formData.select1" size="medium" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import {
BaseSelect as TinyBaseSelect,
Option as TinyOption,
Form as TinyForm,
FormItem as TinyFormItem,
Button as TinyButton
} from '@opentiny/vue'
const options = ref([
{
value: '选项1',
label: '北京'
},
{
value: '选项2',
label: '上海'
},
{
value: '选项3',
label: '天津'
},
{
value: '选项4',
label: '重庆'
},
{
value: '选项5',
label: '深圳'
},
{
value: '选项6',
label: '杭州'
},
{
value: '选项7',
label: '宇宙无敌老面小馒头'
}
])
const displayOnly = ref(true)
const top = ref(false)
const inline = ref(false)
const size = ref('')
const formData = reactive({
select1: ['选项1', '选项2', '选项3', '选项4', '选项5', '选项6', '选项7']
})
</script>
<style lang="less" scoped>
.demo-box {
width: 350px;
.custom-form {
border: 1px solid #fdf4f4;
padding: 10px;
}
.visible-form {
overflow: visible;
}
}
</style>

View File

@ -0,0 +1,152 @@
<template>
<div class="demo-box">
<tiny-button @click="displayOnly = !displayOnly">display-only/{{ displayOnly }}</tiny-button>
<p>边框为表单范围</p>
<tiny-form class="custom-form" :inline="inline" label-position="top" :display-only="displayOnly">
<tiny-form-item label="label 垂直布局">
<div>
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</tiny-option>
</tiny-base-select>
<br />
<br />
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</tiny-option>
</tiny-base-select>
</div>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="form 超出隐藏">
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
</tiny-form>
<br />
<tiny-form class="custom-form visible-form" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="自定义-超出显示">
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
</tiny-form>
<br />
<tiny-form class="custom-form" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="默认尺寸">
<tiny-base-select v-model="formData.select1" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" size="mini" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="mini" size="mini">
<tiny-base-select v-model="formData.select1" size="mini" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" size="small" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="small" size="small">
<tiny-base-select v-model="formData.select1" size="small" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
<br />
<br />
<tiny-form class="custom-form" size="medium" :inline="inline" :display-only="displayOnly">
<tiny-form-item label="medium" size="medium">
<tiny-base-select v-model="formData.select1" size="medium" multiple hover-expand>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</tiny-form-item>
<p>&nbsp;</p>
</tiny-form>
</div>
</template>
<script>
import { Form, FormItem, Option, Button } from '@opentiny/vue'
export default {
components: {
TinyForm: Form,
TinyButton: Button,
TinyFormItem: FormItem,
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
displayOnly: true,
top: false,
inline: false,
size: '',
options: [
{
value: '选项1',
label: '北京'
},
{
value: '选项2',
label: '上海'
},
{
value: '选项3',
label: '天津'
},
{
value: '选项4',
label: '重庆'
},
{
value: '选项5',
label: '深圳'
},
{
value: '选项6',
label: '杭州'
},
{
value: '选项7',
label: '宇宙无敌老面小馒头'
}
],
formData: {
select1: ['选项1', '选项2', '选项3', '选项4', '选项5', '选项6', '选项7']
}
}
}
}
</script>
<style lang="less" scoped>
.demo-box {
width: 350px;
.custom-form {
border: 1px solid #fdf4f4;
padding: 10px;
}
.visible-form {
overflow: visible;
}
}
</style>

View File

@ -0,0 +1,49 @@
import { expect, test } from '@playwright/test'
test('多选时取远端数据与当前已选数据的并集', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#multiple')
const wrap = page.locator('#multiple')
const select = wrap.locator('.tiny-base-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const tag = select.locator('.tiny-tag')
await expect(tag).toHaveCount(2)
await select.locator('.tiny-input__suffix').click()
await option.filter({ hasText: '全部' }).click()
await expect(tag).toHaveCount(7)
await option.filter({ hasText: '全部' }).click()
await expect(tag).toHaveCount(0)
await option.filter({ hasText: '北京' }).click()
await expect(tag).toHaveCount(1)
await option.filter({ hasText: '上海' }).click()
await expect(tag).toHaveCount(2)
await tag.filter({ hasText: '上海' }).locator('.tiny-tag__close').click()
await expect(tag).toHaveCount(1)
})
test('multiple-limit', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#multiple')
const wrap = page.locator('#multiple')
const select = wrap.locator('.tiny-base-select').nth(3)
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const tag = select.locator('.tiny-tag')
await select.click()
await option.nth(0).click()
await option.nth(1).click()
await expect(tag).toHaveCount(2)
await expect(option.filter({ hasText: '全部' })).toHaveCount(0)
const list = await option.all()
list.forEach(async (item, index) => {
if (index <= 1) {
await expect(item).toHaveClass(/selected/)
} else {
await expect(item).toHaveClass(/is-disabled/)
}
})
})

View File

@ -0,0 +1,98 @@
<template>
<div>
<br />
<div>场景1多选</div>
<br />
<tiny-base-select v-model="value1" multiple searchable>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景2必选</div>
<br />
<tiny-base-select v-model="value2" multiple>
<tiny-option
v-for="item in options2"
:key="item.value"
:label="item.label"
:value="item.value"
:required="item.required"
>
</tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景3配置式必选</div>
<br />
<tiny-base-select v-model="value3" multiple :options="options2"> </tiny-base-select>
<br />
<br />
<br />
<div>场景4多选个数限制</div>
<br />
<tiny-base-select v-model="value4" multiple :multiple-limit="2" show-limit-text>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<br />
<div>场景5自定义图标 + 自定义样式</div>
<br />
<tiny-base-select
v-model="value4"
multiple
:dropdown-icon="iconPopup"
:drop-style="{ width: '200px', 'min-width': '200px' }"
>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
import { iconPopup } from '@opentiny/vue-icon'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options1: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' },
{ value: '选项6', label: '南京' },
{ value: '选项7', label: '成都' }
],
options2: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳', required: true },
{ value: '选项6', label: '南京' },
{ value: '选项7', label: '成都' }
],
value1: ['选项1', '选项2'],
value2: ['选项1', '选项2'],
value3: ['选项1', '选项2'],
value4: [],
iconPopup: iconPopup()
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<tiny-base-select v-model="value" placeholder="自定义 placeholder" name="inputName" autocomplete>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,16 @@
import { test, expect } from '@playwright/test'
test('原生属性', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#native-properties')
const wrap = page.locator('#native-properties')
const select = wrap.locator('.tiny-base-select')
const input = select.locator('.tiny-input__inner')
await expect(input).toHaveAttribute('name', 'inputName')
await expect(input).toHaveAttribute('placeholder', '自定义 placeholder')
const isHasAutocomplete = await input.evaluate((input) => input.hasAttribute('autocomplete'))
await expect(isHasAutocomplete).toBe(true)
})

View File

@ -0,0 +1,34 @@
<template>
<tiny-base-select v-model="value" placeholder="自定义 placeholder" name="inputName" autocomplete>
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,34 @@
<template>
<div>
<p>场景1默认</p>
<tiny-base-select v-model="value">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2自定义空数据文本</p>
<tiny-base-select v-model="value" no-data-text="暂无数据">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景3显示空数据图片</p>
<tiny-base-select v-model="value" no-data-text="None" :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([])
const value = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,39 @@
import { test, expect } from '@playwright/test'
test('默认空数据文本', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#no-data-text')
const wrap = page.locator('#no-data-text')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
await input.click()
await expect(dropdown.locator('.tiny-select-dropdown__empty')).toHaveText('暂无相关数据')
})
test('自定义空数据文本', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#no-data-text')
const wrap = page.locator('#no-data-text')
const select = wrap.locator('.tiny-base-select').nth(1)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
await input.click()
await expect(dropdown.locator('.tiny-select-dropdown__empty')).toHaveText('暂无数据')
})
test('显示空数据图片', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#no-data-text')
const wrap = page.locator('#no-data-text')
const select = wrap.locator('.tiny-base-select').nth(2)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
await input.click()
await expect(dropdown.locator('.tiny-select-dropdown__empty-images')).toBeVisible()
})

View File

@ -0,0 +1,43 @@
<template>
<div>
<p>场景1默认</p>
<tiny-base-select v-model="value">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景2自定义空数据文本</p>
<tiny-base-select v-model="value" no-data-text="暂无数据">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<p>场景3显示空数据图片</p>
<tiny-base-select v-model="value" no-data-text="None" :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [],
value: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,45 @@
<template>
<div>
<p>单选</p>
<tiny-base-select v-model="value1" :options="options" optimization @change="onChange"></tiny-base-select>
<p>多选</p>
<tiny-base-select
v-model="value2"
optimization
multiple
collapse-tags
:multiple-limit="5"
:options="options"
@change="onChange"
></tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Modal } from '@opentiny/vue'
const buildOptions = () =>
Array.from({ length: 100000 }).map((item, i) => JSON.parse(`{"value":"选项${i}","label":"北京${i}"}`))
const value1 = ref('')
const value2 = ref([])
const options = ref(buildOptions())
const onChange = (value) => {
Modal.message({
message: JSON.stringify(value),
status: 'info'
})
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,46 @@
import { test, expect } from '@playwright/test'
test('单选虚拟滚动', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#optimization')
const wrap = page.locator('#optimization')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await select.click()
await expect((await option.all()).length).toBeLessThan(20) // 新虚拟滚动,预加载行数不一样了
await expect(option.filter({ hasText: '北京17' })).toBeHidden()
await option.nth(9).scrollIntoViewIfNeeded()
await page.waitForTimeout(1000)
await expect(option.filter({ hasText: '北京17' })).toBeVisible() // 现在预加载的行比较多所以17行已经可以看到了
})
test('多选虚拟滚动', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#optimization')
const wrap = page.locator('#optimization')
const select = wrap.locator('.tiny-base-select').nth(1)
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const tag = select.locator('.tiny-tag')
await select.click()
await expect((await option.all()).length).toBeLessThan(20) // 新虚拟滚动,预加载行数不一样了
await expect(option.filter({ hasText: '北京17' })).toBeHidden()
await expect(option.filter({ hasText: '北京16' })).toBeHidden()
await page.waitForTimeout(500)
await option.nth(9).scrollIntoViewIfNeeded()
await page.waitForTimeout(1000)
await option.nth(9).scrollIntoViewIfNeeded()
await expect(option.filter({ hasText: '北京16' })).toBeVisible()
await expect(option.filter({ hasText: '北京17' })).toBeVisible()
await option.filter({ hasText: '北京17' }).click()
await expect(tag.first()).toHaveText('北京17')
await expect((await tag.all()).length).toEqual(1)
await option.filter({ hasText: '北京16' }).click()
await expect((await tag.all()).length).toEqual(2)
await expect(tag.nth(1)).toHaveText('+ 1')
})

View File

@ -0,0 +1,54 @@
<template>
<div>
<p>单选</p>
<tiny-base-select v-model="value1" :options="options" optimization @change="onChange"></tiny-base-select>
<p>多选</p>
<tiny-base-select
v-model="value2"
optimization
multiple
collapse-tags
:multiple-limit="5"
:options="options"
@change="onChange"
></tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Modal } from '@opentiny/vue'
const buildOptions = () =>
Array.from({ length: 100000 }).map((item, i) => JSON.parse(`{"value":"选项${i}","label":"北京${i}"}`))
export default {
components: {
TinyBaseSelect: BaseSelect
},
data() {
return {
value1: '',
value2: [],
options: buildOptions()
}
},
methods: {
onChange(value) {
Modal.message({
message: JSON.stringify(value),
status: 'info'
})
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,96 @@
<template>
<div>
<div>场景1分组 + 多选 + 面板可搜索</div>
<br />
<tiny-base-select v-model="value2" multiple filterable :searchable="true">
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
<br />
<br />
<div>场景2单选分组</div>
<br />
<tiny-base-select v-model="value1" filterable>
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
<br />
<br />
<div>场景3分组 + 多选</div>
<br />
<tiny-base-select v-model="value2" multiple>
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
<br />
<br />
<div>场景4分组 + 多选 + 可搜索</div>
<br />
<tiny-base-select v-model="value2" filterable multiple>
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption, OptionGroup as TinyOptionGroup } from '@opentiny/vue'
const options3 = ref([
{
label: '热门城市',
disabled: true,
options: [
{ value: 'Shanghai', label: '上海' },
{ value: 'Beijing', label: '北京' }
]
},
{
label: '城市名',
options: [
{ value: 'Chengdu', label: '成都' },
{ value: 'Shenzhen', label: '深圳' },
{ value: 'Guangzhou', label: '广州' },
{ value: 'Dalian', label: '大连' },
{ value: 'Chongqing', label: '重庆' },
{ value: 'Xianggang', label: '香港' },
{ value: 'Aomen', label: '澳门' },
{ value: 'Wulumuqi', label: '乌鲁木齐' }
]
}
])
const value1 = ref([])
const value2 = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,22 @@
import { test, expect } from '@playwright/test'
test('option-group', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#option-group')
const wrap = page.locator('#option-group')
const select = wrap.locator('.tiny-base-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const title = dropdown.locator('.tiny-option-group__title')
const group = dropdown.locator('.tiny-option-group ')
await select.click()
await expect(title.nth(0)).toHaveText('热门城市')
await expect(title.nth(1)).toHaveText('城市名')
await expect(option.filter({ hasText: '上海' })).toHaveClass(/is-disabled/)
await expect(option.filter({ hasText: '北京' })).toHaveClass(/is-disabled/)
await expect(group.nth(0).locator('.tiny-option')).toHaveCount(2)
await expect(group.nth(1).locator('.tiny-option')).toHaveCount(8)
await expect((await group.all()).length).toEqual(2)
})

View File

@ -0,0 +1,105 @@
<template>
<div>
<div>场景1分组 + 多选 + 面板可搜索</div>
<br />
<tiny-base-select v-model="value2" multiple filterable :searchable="true">
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
<br />
<br />
<div>场景2单选分组</div>
<br />
<tiny-base-select v-model="value1" filterable>
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
<br />
<br />
<div>场景3分组 + 多选</div>
<br />
<tiny-base-select v-model="value2" multiple>
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
<br />
<br />
<div>场景4分组 + 多选 + 可搜索</div>
<br />
<tiny-base-select v-model="value2" filterable multiple>
<tiny-option-group v-for="group in options3" :key="group.label" :label="group.label" :disabled="!!group.disabled">
<tiny-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
></tiny-option>
</tiny-option-group>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option, OptionGroup } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyOptionGroup: OptionGroup
},
data() {
return {
value1: [],
value2: '',
options3: [
{
label: '热门城市',
disabled: true,
options: [
{ value: 'Shanghai', label: '上海' },
{ value: 'Beijing', label: '北京' }
]
},
{
label: '城市名',
options: [
{ value: 'Chengdu', label: '成都' },
{ value: 'Shenzhen', label: '深圳' },
{ value: 'Guangzhou', label: '广州' },
{ value: 'Dalian', label: '大连' },
{ value: 'Chongqing', label: '重庆' },
{ value: 'Xianggang', label: '香港' },
{ value: 'Aomen', label: '澳门' },
{ value: 'Wulumuqi', label: '乌鲁木齐' }
]
}
]
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,32 @@
<template>
<tiny-base-select v-model="value" popper-class="drop" :popper-append-to-body="false" placement="top">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = reactive([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
margin-top: 30px;
}
</style>
<style>
.drop {
background-color: #d5e8ff;
}
</style>

View File

@ -0,0 +1,16 @@
import { test, expect } from '@playwright/test'
test('popup-style-position', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#popup-style-position')
const wrap = page.locator('#popup-style-position')
const select = wrap.locator('.tiny-base-select')
const dropdown = select.locator('.tiny-base-select__tags-group > .tiny-select-dropdown')
await select.click()
await expect(dropdown).toHaveCount(1)
await expect(dropdown).toHaveClass(/drop/)
await expect(dropdown).toHaveCSS('background-color', 'rgb(213, 232, 255)')
await expect(dropdown).toHaveAttribute('x-placement', 'top')
})

View File

@ -0,0 +1,41 @@
<template>
<tiny-base-select v-model="value" popper-class="drop" :popper-append-to-body="false" placement="top">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
margin-top: 30px;
}
</style>
<style>
.drop {
background-color: #d5e8ff;
}
</style>

View File

@ -0,0 +1,283 @@
<template>
<div>
<div>场景1远程搜索单选</div>
<br />
<tiny-base-select
v-model="value1"
filterable
remote
:remote-method="remoteMethod1"
:loading="loading1"
loading-text="Loading..."
>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2远程搜索多选 + 保留搜索关键字</div>
<br />
<tiny-base-select
v-model="value2"
filterable
multiple
remote
reserve-keyword
placeholder="请输入关键词"
:remote-method="remoteMethod2"
:loading="loading2"
loading-text="Loading..."
>
<tiny-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3获焦时触发远程搜索</div>
<br />
<tiny-base-select
v-model="value3"
filterable
remote
:remote-config="{ autoSearch: true }"
:remote-method="remoteMethod3"
:loading="loading3"
loading-text="Loading..."
>
<tiny-option v-for="item in options3" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景4失焦清空查询数据显示下拉图标</div>
<br />
<tiny-base-select
v-model="value4"
multiple
filterable
remote
:remote-config="{ clearData: true, showIcon: true }"
reserve-keyword
:remote-method="remoteMethod4"
>
<tiny-option v-for="item in options4" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景5聚焦时触发搜索失焦清空查询数据显示下拉图标</div>
<br />
<tiny-base-select
v-model="value5"
multiple
filterable
remote
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
reserve-keyword
:remote-method="remoteMethod5"
:loading="loading5"
loading-text="loading..."
>
<tiny-option v-for="item in options5" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景6远程搜索配置 trim 属性去除绑定值空格</div>
<br />
<tiny-button @click="trim = !trim">trim {{ trim }}</tiny-button>
<tiny-button @click="changeVal">改变成有空格的值</tiny-button>
<tiny-button @click="resetVal">重置绑定值</tiny-button>
<br />
<br />
<tiny-base-select
v-model="value6"
filterable
remote
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
reserve-keyword
:remote-method="remoteMethod6"
:loading="loading5"
loading-text="Loading..."
:trim="trim"
>
<tiny-option v-for="item in options6" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption, Button as TinyButton } from '@opentiny/vue'
const loading1 = ref(false)
const loading2 = ref(false)
const loading3 = ref(false)
const loading4 = ref(false)
const loading5 = ref(false)
const value1 = ref('')
const value2 = ref([])
const value3 = ref('')
const value4 = ref([])
const value5 = ref([])
const value6 = ref('')
const options1 = ref([])
const options2 = ref([])
const options3 = ref([])
const options4 = ref([])
const options5 = ref([])
const options6 = ref([])
const list = ref([])
const trim = ref(false)
const remoteMethod1 = (query) => {
if (query !== undefined) {
loading1.value = true
setTimeout(() => {
loading1.value = false
options1.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
options1.value = []
}
}
const remoteMethod2 = (query) => {
if (query !== undefined) {
loading2.value = true
setTimeout(() => {
loading2.value = false
options2.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
options2.value = []
}
}
const remoteMethod3 = (query) => {
if (query !== undefined) {
loading3.value = true
setTimeout(() => {
loading3.value = false
options3.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 300)
} else {
options3.value = []
}
}
const remoteMethod4 = (query) => {
if (query !== undefined) {
loading4.value = true
setTimeout(() => {
loading4.value = false
options4.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 300)
} else {
options4.value = []
}
}
const remoteMethod5 = (query) => {
if (query !== undefined) {
loading5.value = true
setTimeout(() => {
loading5.value = false
options5.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 300)
} else {
options5.value = []
}
}
const remoteMethod6 = (query) => {
if (query !== undefined) {
setTimeout(() => {
options6.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 300)
} else {
options6.value = []
}
}
const changeVal = () => {
value6.value = ' Wyoming'
}
const resetVal = () => {
value6.value = ''
}
const states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
]
onMounted(() => {
list.value = states.map((item) => {
return { value: item, label: item }
})
})
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,68 @@
import { expect, test } from '@playwright/test'
test('远程搜索单选', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#remote-method')
const wrap = page.locator('#remote-method')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await input.focus()
await expect(option).toHaveCount(0)
await expect(dropdown).toBeHidden()
await input.fill('al')
await input.press('Enter')
await page.waitForTimeout(800)
await expect((await option.all()).length).toEqual(3)
await option.filter({ hasText: 'Alaska' }).click()
await expect(input).toHaveValue('Alaska')
})
test('远程搜索多选 + 保留搜索关键字', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#remote-method')
const wrap = page.locator('#remote-method')
const select = wrap.locator('.tiny-base-select').nth(1)
const input = select.locator('.tiny-base-select__input')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
const tag = select.locator('.tiny-tag')
await input.focus()
await expect(option).toHaveCount(0)
await input.press('a')
await input.press('Enter')
await page.waitForTimeout(300)
await option.filter({ hasText: 'Alabama' }).click()
await expect(input).toHaveValue('a')
await expect((await tag.all()).length).toEqual(1)
await input.press('l')
await page.waitForTimeout(500)
await expect((await option.all()).length).toEqual(3)
await option.filter({ hasText: 'Alaska' }).click()
await page.waitForTimeout(300)
await expect((await tag.all()).length).toEqual(2)
await expect(input).toHaveValue('al')
})
test('获焦时触发远程搜索', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#remote-method')
const wrap = page.locator('#remote-method')
const select = wrap.locator('.tiny-base-select').nth(2)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await input.click()
await expect(option).toHaveCount(0)
await page.waitForTimeout(300)
await expect(option).toHaveCount(50)
})

View File

@ -0,0 +1,285 @@
<template>
<div>
<div>场景1远程搜索(单选)</div>
<br />
<tiny-base-select
v-model="value1"
filterable
remote
:remote-method="remoteMethod1"
:loading="loading1"
loading-text="Loading..."
>
<tiny-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2远程搜索多选 + 保留搜索关键字</div>
<br />
<tiny-base-select
v-model="value2"
filterable
multiple
remote
reserve-keyword
placeholder="请输入关键词"
:remote-method="remoteMethod2"
:loading="loading2"
loading-text="Loading..."
>
<tiny-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3获焦时触发远程搜索</div>
<br />
<tiny-base-select
v-model="value3"
filterable
remote
:remote-config="{ autoSearch: true }"
:remote-method="remoteMethod3"
:loading="loading3"
loading-text="Loading..."
>
<tiny-option v-for="item in options3" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景4失焦清空查询数据显示下拉图标</div>
<br />
<tiny-base-select
v-model="value4"
multiple
filterable
remote
:remote-config="{ clearData: true, showIcon: true }"
reserve-keyword
:remote-method="remoteMethod4"
>
<tiny-option v-for="item in options4" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景5聚焦时触发搜索失焦清空查询数据显示下拉图标</div>
<br />
<tiny-base-select
v-model="value5"
multiple
filterable
remote
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
reserve-keyword
:remote-method="remoteMethod5"
:loading="loading5"
loading-text="Loading..."
>
<tiny-option v-for="item in options5" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景6远程搜索配置 trim 属性去除绑定值空格</div>
<br />
<tiny-button @click="trim = !trim">trim {{ trim }}</tiny-button>
<tiny-button @click="changeVal">改变成有空格的值</tiny-button>
<tiny-button @click="resetVal">重置绑定值</tiny-button>
<br />
<br />
<tiny-base-select
v-model="value6"
filterable
remote
:remote-config="{ autoSearch: true, clearData: true, showIcon: true }"
reserve-keyword
:remote-method="remoteMethod6"
:loading="loading5"
loading-text="Loading..."
:trim="trim"
>
<tiny-option v-for="item in options6" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option, Button } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyButton: Button
},
data() {
return {
loading1: false,
loading2: false,
loading3: false,
loading4: false,
loading5: false,
value1: '',
value2: [],
value3: '',
value4: [],
value5: [],
value6: '',
options1: [],
options2: [],
options3: [],
options4: [],
options5: [],
options6: [],
list: [],
trim: false
}
},
mounted() {
const states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming'
]
this.list = states.map((item) => {
return { value: item, label: item }
})
},
methods: {
remoteMethod1(query) {
if (query !== undefined) {
this.loading1 = true
setTimeout(() => {
this.loading1 = false
this.options1 = this.list.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
this.options1 = []
}
},
remoteMethod2(query) {
if (query !== undefined) {
this.loading2 = true
setTimeout(() => {
this.loading2 = false
this.options2 = this.list.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
this.options2 = []
}
},
remoteMethod3(query) {
if (query !== undefined) {
this.loading3 = true
setTimeout(() => {
this.loading3 = false
this.options3 = this.list.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 300)
} else {
this.options3 = []
}
},
remoteMethod4(query) {
if (query !== undefined) {
this.loading4 = true
setTimeout(() => {
this.loading4 = false
this.options4 = this.list.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
this.options4 = []
}
},
remoteMethod5(query) {
if (query !== undefined) {
this.loading5 = true
setTimeout(() => {
this.loading5 = false
this.options5 = this.list.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
this.options5 = []
}
},
remoteMethod6(query) {
if (query !== undefined) {
setTimeout(() => {
this.options6 = this.list.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
this.options6 = []
}
},
changeVal() {
this.value6 = ' Wyoming'
},
resetVal() {
this.value6 = ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,46 @@
<template>
<div>
<div>场景1单选</div>
<br />
<tiny-base-select v-model="value" :searchable="true" :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2多选</div>
<br />
<tiny-base-select v-model="multivalue" :searchable="true" multiple :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3多选 + 保留搜索关键字</div>
<br />
<tiny-base-select v-model="multivalue" :searchable="true" reserve-keyword multiple :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' },
{ value: '选项6', label: '南京' },
{ value: '选项7', label: '成都' }
])
const value = ref('')
const multivalue = ref([])
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,66 @@
import { expect, test } from '@playwright/test'
test('searchable-single', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#searchable')
const wrap = page.locator('#searchable')
const select = wrap.locator('.tiny-base-select').nth(0)
const dropdown = page.locator('body > .tiny-select-dropdown')
const input = dropdown.locator('.tiny-input__inner')
const option = dropdown.locator('.tiny-option')
await expect(input).toBeHidden()
await select.click()
await page.waitForTimeout(500)
await expect(input).toBeVisible()
await input.fill('上海')
await input.press('Enter')
await page.waitForTimeout(500)
const list = await option.all()
list.forEach(async (item) => {
const text = await item.innerText()
const isVisibleItem = text === '上海' || text === '全部'
if (isVisibleItem) {
await expect(item).toHaveCSS('display', 'flex')
} else {
await expect(item).toHaveCSS('display', 'none')
}
})
await option.filter({ hasText: '上海' }).click()
await page.waitForTimeout(500)
await expect(input).toHaveValue('')
})
test('searchable-multiple', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#searchable')
const wrap = page.locator('#searchable')
const select = wrap.locator('.tiny-base-select').nth(1)
const dropdown = page.locator('body > .tiny-select-dropdown')
const input = dropdown.locator('.tiny-input__inner')
const option = dropdown.locator('.tiny-option')
const tags = select.locator('.tiny-tag')
await expect(input).toBeHidden()
await select.click()
await page.waitForTimeout(500)
await expect(input).toBeVisible()
await input.fill('上海')
await input.press('Enter')
await page.waitForTimeout(500)
const list = await option.all()
list.forEach(async (item) => {
const text = await item.innerText()
const isVisibleItem = text === '上海' || text === '全部'
if (isVisibleItem) {
await expect(item).toHaveCSS('display', 'flex')
} else {
await expect(item).toHaveCSS('display', 'none')
}
})
await option.filter({ hasText: '上海' }).click()
await page.waitForTimeout(500)
await expect((await tags.all()).length).toEqual(1)
})

View File

@ -0,0 +1,55 @@
<template>
<div>
<div>场景1单选</div>
<br />
<tiny-base-select v-model="value" :searchable="true" :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景2多选</div>
<br />
<tiny-base-select v-model="multivalue" :searchable="true" multiple :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
<br />
<br />
<div>场景3多选 + 保留搜索关键字</div>
<br />
<tiny-base-select v-model="multivalue" :searchable="true" reserve-keyword multiple :show-empty-image="true">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' },
{ value: '选项6', label: '南京' },
{ value: '选项7', label: '成都' }
],
value: '',
multivalue: []
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<tiny-base-select v-model="value" multiple :show-alloption="false">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,15 @@
import { test, expect } from '@playwright/test'
test('show-alloption', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#show-alloption')
const wrap = page.locator('#show-alloption')
const select = wrap.locator('.tiny-base-select')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await select.click()
await page.waitForTimeout(500)
await expect(option.filter({ hasText: '全部' })).toBeHidden()
})

View File

@ -0,0 +1,34 @@
<template>
<tiny-base-select v-model="value" multiple :show-alloption="false">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
</style>

View File

@ -0,0 +1,19 @@
<template>
<tiny-base-select v-model="value" multiple show-overflow-tooltip style="width: 200px">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect, Option as TinyOption } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value = ref('')
</script>

View File

@ -0,0 +1,28 @@
<template>
<tiny-base-select v-model="value" multiple show-overflow-tooltip style="width: 200px">
<tiny-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </tiny-option>
</tiny-base-select>
</template>
<script>
import { BaseSelect, Option } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value: ''
}
}
}
</script>

View File

@ -0,0 +1,39 @@
<template>
<div>
<p>默认</p>
<tiny-base-select v-model="value1" multiple :options="options"></tiny-base-select>
<p>medium</p>
<tiny-base-select v-model="value2" size="medium" multiple :options="options"></tiny-base-select>
<p>small</p>
<tiny-base-select v-model="value3" size="small" multiple :options="options"> </tiny-base-select>
<p>mini</p>
<tiny-base-select v-model="value4" size="mini" multiple :options="options"> </tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { BaseSelect as TinyBaseSelect } from '@opentiny/vue'
const options = ref([
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const value1 = ref(['选项1', '选项2'])
const value2 = ref(['选项1', '选项2'])
const value3 = ref(['选项1', '选项2'])
const value4 = ref(['选项1', '选项2'])
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,58 @@
import { test, expect } from '@playwright/test'
test('默认尺寸', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#size')
const wrap = page.locator('#size')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input')
const tag = select.locator('.tiny-tag')
await expect(input.locator('.tiny-input__inner')).toHaveCSS('height', '30px')
await expect(tag.nth(0)).toHaveClass(/tiny-tag--light/)
})
test('medium 尺寸', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#size')
const wrap = page.locator('#size')
const select = wrap.locator('.tiny-base-select').nth(1)
const input = select.locator('.tiny-input')
const tag = select.locator('.tiny-tag')
await expect(input).toHaveClass(/tiny-input-medium/)
await expect(input.locator('.tiny-input__inner')).toHaveCSS('height', '40px')
await expect(tag.nth(0)).toHaveClass(/tiny-tag--medium tiny-tag--light/)
})
test('small 尺寸', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#size')
const wrap = page.locator('#size')
const select = wrap.locator('.tiny-base-select').nth(2)
const input = select.locator('.tiny-input')
const tag = select.locator('.tiny-tag')
const { height } = await input.locator('.tiny-input__inner').boundingBox()
await expect(input).toHaveClass(/tiny-input-small/)
await expect(tag.nth(0)).toHaveClass(/tiny-tag--small tiny-tag--light/)
expect(height).toBeCloseTo(32, 1)
})
test('mini 尺寸', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#size')
const wrap = page.locator('#size')
const select = wrap.locator('.tiny-base-select').nth(3)
const input = select.locator('.tiny-input')
const tag = select.locator('.tiny-tag')
const { height } = await input.locator('.tiny-input__inner').boundingBox()
await expect(input).toHaveClass(/tiny-input-mini/)
await expect(tag.nth(0)).toHaveClass(/tiny-tag--mini tiny-tag--light/)
expect(height).toBeCloseTo(24, 1)
})

View File

@ -0,0 +1,47 @@
<template>
<div>
<p>默认</p>
<tiny-base-select v-model="value1" multiple :options="options"></tiny-base-select>
<p>medium</p>
<tiny-base-select v-model="value2" size="medium" multiple :options="options"></tiny-base-select>
<p>small</p>
<tiny-base-select v-model="value3" size="small" multiple :options="options"> </tiny-base-select>
<p>mini</p>
<tiny-base-select v-model="value4" size="mini" multiple :options="options"> </tiny-base-select>
</div>
</template>
<script>
import { BaseSelect } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect
},
data() {
return {
options: [
{ value: '选项1', label: '北京' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
value1: ['选项1', '选项2'],
value2: ['选项1', '选项2'],
value3: ['选项1', '选项2'],
value4: ['选项1', '选项2']
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
p {
font-size: 14px;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,88 @@
<template>
<div>
<p>场景1带标签和提示信息</p>
<tiny-base-select v-model="value1" popper-class="slot-default">
<template v-for="item in options1">
<tiny-tooltip v-if="item.tip" :content="item.tip" placement="right" effect="light" :key="item.value">
<tiny-option :label="item.label" :value="item.value">
<span class="left">{{ item.label }}</span>
<tiny-tag v-if="item.tag" type="danger" effect="light" size="small">{{ item.tag }}</tiny-tag>
</tiny-option>
</tiny-tooltip>
<tiny-option v-if="!item.tip" :key="item.value" :label="item.label" :value="item.value">
<span class="left">{{ item.label }}</span>
<tiny-tag v-if="item.tag" type="danger" effect="light" size="small">{{ item.tag }}</tiny-tag>
</tiny-option>
</template>
</tiny-base-select>
<p>场景2选项双行</p>
<tiny-base-select v-model="value2" popper-class="double-row">
<tiny-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value">
<div>
<p>
{{ item.label }}
</p>
<p>{{ item.desc }}</p>
</div>
</tiny-option>
</tiny-base-select>
</div>
</template>
<script setup>
import { ref } from 'vue'
import {
BaseSelect as TinyBaseSelect,
Option as TinyOption,
Tag as TinyTag,
Tooltip as TinyTooltip
} from '@opentiny/vue'
const options1 = ref([
{ value: '选项1', label: '北京', tag: 'New', tip: '自定义提示' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
])
const options2 = ref([
{ value: '选项1', label: '子网1 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项2', label: '子网2 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项3', label: '子网3 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项4', label: '子网4 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项5', label: '子网5 192.168.0.1/24', desc: '可用 IP 数 250' }
])
const value1 = ref('选项1')
const value2 = ref('选项1')
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
.tiny-option {
.left {
margin-right: 8px;
}
p {
height: 18px;
line-height: 18px;
margin: 6px;
}
}
</style>
<style lang="less">
.double-row {
&.tiny-select-dropdown .tiny-select-dropdown__wrap {
max-height: 224px;
.tiny-option {
height: 54px;
}
}
}
</style>

View File

@ -0,0 +1,18 @@
import { expect, test } from '@playwright/test'
test('选项插槽', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('base-select#slot-default')
const wrap = page.locator('#slot-default')
const select = wrap.locator('.tiny-base-select').nth(0)
const input = select.locator('.tiny-input__inner')
const dropdown = page.locator('body > .tiny-select-dropdown')
const option = dropdown.locator('.tiny-option')
await input.click()
await expect(option.filter({ hasText: '北京' })).toBeVisible()
await expect(option.filter({ hasText: '北京' }).locator('.tiny-tag')).toHaveText('New')
await option.filter({ hasText: '北京' }).hover()
await expect(page.locator('body > .tiny-tooltip').filter({ hasText: '自定义提示' })).toBeVisible()
})

View File

@ -0,0 +1,92 @@
<template>
<div>
<p>场景1带标签和提示信息</p>
<tiny-base-select v-model="value1" popper-class="slot-default">
<template v-for="item in options1">
<tiny-tooltip v-if="item.tip" :content="item.tip" placement="right" effect="light" :key="item.value">
<tiny-option :label="item.label" :value="item.value">
<span class="left">{{ item.label }}</span>
<tiny-tag v-if="item.tag" type="danger" effect="light" size="small">{{ item.tag }}</tiny-tag>
</tiny-option>
</tiny-tooltip>
<tiny-option v-if="!item.tip" :key="item.value" :label="item.label" :value="item.value">
<span class="left">{{ item.label }}</span>
<tiny-tag v-if="item.tag" type="danger" effect="light" size="small">{{ item.tag }}</tiny-tag>
</tiny-option>
</template>
</tiny-base-select>
<p>场景2选项双行</p>
<tiny-base-select v-model="value2" popper-class="double-row">
<tiny-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value">
<div>
<p>
{{ item.label }}
</p>
<p>{{ item.desc }}</p>
</div>
</tiny-option>
</tiny-base-select>
</div>
</template>
<script>
import { BaseSelect, Option, Tag, Tooltip } from '@opentiny/vue'
export default {
components: {
TinyBaseSelect: BaseSelect,
TinyOption: Option,
TinyTag: Tag,
TinyTooltip: Tooltip
},
data() {
return {
options1: [
{ value: '选项1', label: '北京', tag: 'New', tip: '自定义提示' },
{ value: '选项2', label: '上海' },
{ value: '选项3', label: '天津' },
{ value: '选项4', label: '重庆' },
{ value: '选项5', label: '深圳' }
],
options2: [
{ value: '选项1', label: '子网1 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项2', label: '子网2 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项3', label: '子网3 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项4', label: '子网4 192.168.0.1/24', desc: '可用 IP 数 250' },
{ value: '选项5', label: '子网5 192.168.0.1/24', desc: '可用 IP 数 250' }
],
value1: '选项1',
value2: '选项1'
}
}
}
</script>
<style lang="less" scoped>
.tiny-base-select {
width: 280px;
}
.tiny-option {
.left {
margin-right: 8px;
}
p {
height: 18px;
line-height: 18px;
margin: 6px;
}
}
</style>
<style lang="less">
.double-row {
&.tiny-select-dropdown .tiny-select-dropdown__wrap {
max-height: 224px;
.tiny-option {
height: 54px;
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More