mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
Add deep-select component and fix a popup bug. (#103)
* feat: new deep select * fix: add deep-select test * fix: import * fix: icon import
This commit is contained in:
parent
c594091955
commit
1418117681
@ -29,4 +29,3 @@ gulp.task('copy', function(callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
runSequence('copy');
|
runSequence('copy');
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ module.exports = {
|
|||||||
chunkFilename: 'async.[name].js'
|
chunkFilename: 'async.[name].js'
|
||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
|
host: '0.0.0.0',
|
||||||
historyApiFallback: {
|
historyApiFallback: {
|
||||||
rewrites: [
|
rewrites: [
|
||||||
{ from: /^\/zanui\/vue\/examples/, to: '/examples.html' },
|
{ from: /^\/zanui\/vue\/examples/, to: '/examples.html' },
|
||||||
|
169
docs/examples-docs/deep-select.md
Normal file
169
docs/examples-docs/deep-select.md
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
items: [{
|
||||||
|
text: '所有城市',
|
||||||
|
children: [{
|
||||||
|
text: '杭州',
|
||||||
|
id: 1001
|
||||||
|
}, {
|
||||||
|
text: '温州',
|
||||||
|
id: 1002
|
||||||
|
}, {
|
||||||
|
text: '海南',
|
||||||
|
id: 1100
|
||||||
|
}, {
|
||||||
|
text: '宁波',
|
||||||
|
id: 1003
|
||||||
|
}, {
|
||||||
|
text: '义乌',
|
||||||
|
id: 1004
|
||||||
|
}, {
|
||||||
|
text: '无锡',
|
||||||
|
id: 1011
|
||||||
|
}, {
|
||||||
|
text: '常州',
|
||||||
|
id: 1012
|
||||||
|
}, {
|
||||||
|
text: '大连',
|
||||||
|
id: 1031
|
||||||
|
}, {
|
||||||
|
text: '诸暨',
|
||||||
|
id: 1005
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
text: '浙江',
|
||||||
|
children: [{
|
||||||
|
text: '杭州',
|
||||||
|
id: 1001
|
||||||
|
}, {
|
||||||
|
text: '温州',
|
||||||
|
id: 1002
|
||||||
|
}, {
|
||||||
|
text: '宁波',
|
||||||
|
id: 1003
|
||||||
|
}, {
|
||||||
|
text: '义乌',
|
||||||
|
id: 1004
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
text: '江苏',
|
||||||
|
children: [{
|
||||||
|
text: '无锡',
|
||||||
|
id: 1011
|
||||||
|
}, {
|
||||||
|
text: '常州',
|
||||||
|
id: 1012
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
mainActiveIndex: 0,
|
||||||
|
activeId: 1001
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onNavClick(index) {
|
||||||
|
this.mainActiveIndex = index;
|
||||||
|
},
|
||||||
|
onItemClick(data) {
|
||||||
|
console.log(data);
|
||||||
|
this.activeId = data.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
## DeepSelect 分类选择组件
|
||||||
|
|
||||||
|
### 使用指南
|
||||||
|
``` javascript
|
||||||
|
import { DeepSelect } from 'vant';
|
||||||
|
|
||||||
|
Vue.component(DeepSelect.name, DeepSelect);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 代码演示
|
||||||
|
|
||||||
|
#### 基础用法
|
||||||
|
|
||||||
|
:::demo 基础用法
|
||||||
|
```html
|
||||||
|
<van-deep-select
|
||||||
|
:items="items"
|
||||||
|
:main-active-index="mainActiveIndex"
|
||||||
|
:active-id="activeId"
|
||||||
|
@navclick="onNavClick"
|
||||||
|
@itemclick="onItemClick"
|
||||||
|
></van-deep-select>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
items: items,
|
||||||
|
// 左侧高亮元素的index
|
||||||
|
mainActiveIndex: 0,
|
||||||
|
// 被选中元素的id
|
||||||
|
activeId: 1001
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onNavClick(index) {
|
||||||
|
this.mainActiveIndex = index;
|
||||||
|
},
|
||||||
|
onItemClick(data) {
|
||||||
|
console.log(data);
|
||||||
|
this.activeId = data.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
#### 传入参数
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 | 必须 |
|
||||||
|
|-----------|-----------|-----------|-------------|-------------|
|
||||||
|
| items | 分类显示所需的数据,具体数据结构可看 数据结构 | Array | [] | |
|
||||||
|
| mainActiveIndex | 左侧导航高亮的索引 | Number | 0 | |
|
||||||
|
| activeId | 右侧选择项,高亮的数据id | Number | 0 | |
|
||||||
|
|
||||||
|
#### 事件
|
||||||
|
| 事件名 | 说明 | 参数 |
|
||||||
|
|-----------|-----------|-----------|
|
||||||
|
| navclick | 左侧导航点击时,触发的事件 | index:被点击的导航的索引 |
|
||||||
|
| itemclick | 右侧选择项被点击时,会触发的事件 | data: 该点击项的数据 |
|
||||||
|
|
||||||
|
### 数据格式
|
||||||
|
#### items 分类显示所需数据的数据结构
|
||||||
|
`items` 整体为一个数组,数组内包含一系列描述分类的 object。
|
||||||
|
|
||||||
|
每个分类里,text表示当前分类的名称。children 表示分类里的可选项,为数组结构,id被用来唯一标识每个选项
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{
|
||||||
|
// 导航名称
|
||||||
|
text: '所有城市',
|
||||||
|
// 该导航下所有的可选项
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
// 可选项的名称
|
||||||
|
text: '温州',
|
||||||
|
// 可选项的id,高亮的时候是根据id是否和选中的id是否相同进行判断的
|
||||||
|
id: 1002
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 可选项的名称
|
||||||
|
text: '杭州',
|
||||||
|
// 可选项的id,高亮的时候是根据id是否和选中的id是否相同进行判断的
|
||||||
|
id: 1001
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
@ -185,6 +185,10 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
"groupName": "业务组件",
|
"groupName": "业务组件",
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"path": "/deep-select",
|
||||||
|
"title": "DeepSelect 分类选择组件"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/express-way",
|
"path": "/express-way",
|
||||||
"title": "ExpressWay 配送方式"
|
"title": "ExpressWay 配送方式"
|
||||||
|
85
packages/deep-select/index.vue
Normal file
85
packages/deep-select/index.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div class="van-deep-select" v-bind:style="{ height: mainHeight + 'px' }">
|
||||||
|
<div class="van-deep-select__nav">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in items"
|
||||||
|
class="van-deep-select__nitem"
|
||||||
|
v-bind:class="{ 'van-deep-select__nitem--active': mainActiveIndex === index }"
|
||||||
|
@click="onNavClick(index)">
|
||||||
|
{{ item.text }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="van-deep-select__content" v-bind:style="{ height: itemHeight + 'px' }">
|
||||||
|
<div
|
||||||
|
v-for="item in subItems"
|
||||||
|
:key="item.id"
|
||||||
|
class="van-deep-select__item"
|
||||||
|
v-bind:class="{ 'van-deep-select__item--active': activeId === item.id }"
|
||||||
|
@click="onItemSelect(item)">
|
||||||
|
{{ item.text }}
|
||||||
|
<van-icon
|
||||||
|
v-if="activeId === item.id"
|
||||||
|
name="success"
|
||||||
|
class="van-deep-select__selected"
|
||||||
|
></van-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Icon from 'packages/icon';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'van-deep-select',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
'van-icon': Icon
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default () {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mainActiveIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
activeId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
maxHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
subItems() {
|
||||||
|
const selectedItem = this.items[this.mainActiveIndex] || {};
|
||||||
|
return selectedItem.children || [];
|
||||||
|
},
|
||||||
|
mainHeight() {
|
||||||
|
const maxHeight = Math.max(this.items.length * 44, this.subItems.length * 44);
|
||||||
|
return Math.min(maxHeight, this.maxHeight);
|
||||||
|
},
|
||||||
|
itemHeight() {
|
||||||
|
return Math.min(this.subItems.length * 44, this.maxHeight);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onNavClick(index) {
|
||||||
|
this.$emit('navclick', index);
|
||||||
|
},
|
||||||
|
onItemSelect(data) {
|
||||||
|
const exportData = Object.assign({}, data);
|
||||||
|
this.$emit('itemclick', exportData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -10,6 +10,7 @@ import Checkbox from './checkbox';
|
|||||||
import CheckboxGroup from './checkbox-group';
|
import CheckboxGroup from './checkbox-group';
|
||||||
import Col from './col';
|
import Col from './col';
|
||||||
import DatetimePicker from './datetime-picker';
|
import DatetimePicker from './datetime-picker';
|
||||||
|
import DeepSelect from './deep-select';
|
||||||
import Dialog from './dialog';
|
import Dialog from './dialog';
|
||||||
import ExpressWay from './express-way';
|
import ExpressWay from './express-way';
|
||||||
import Field from './field';
|
import Field from './field';
|
||||||
@ -57,6 +58,7 @@ const components = [
|
|||||||
CheckboxGroup,
|
CheckboxGroup,
|
||||||
Col,
|
Col,
|
||||||
DatetimePicker,
|
DatetimePicker,
|
||||||
|
DeepSelect,
|
||||||
ExpressWay,
|
ExpressWay,
|
||||||
Field,
|
Field,
|
||||||
Icon,
|
Icon,
|
||||||
@ -114,6 +116,7 @@ export {
|
|||||||
CheckboxGroup,
|
CheckboxGroup,
|
||||||
Col,
|
Col,
|
||||||
DatetimePicker,
|
DatetimePicker,
|
||||||
|
DeepSelect,
|
||||||
Dialog,
|
Dialog,
|
||||||
ExpressWay,
|
ExpressWay,
|
||||||
Field,
|
Field,
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
let context;
|
|
||||||
const _global = Vue.prototype.$isServer ? global : window;
|
const _global = Vue.prototype.$isServer ? global : window;
|
||||||
|
|
||||||
if (_global && _global.popupContext) {
|
|
||||||
context = _global.popupContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_CONTEXT = {
|
const DEFAULT_CONTEXT = {
|
||||||
idSeed: 1,
|
idSeed: 1,
|
||||||
zIndex: 2000,
|
zIndex: 2000,
|
||||||
@ -15,23 +10,24 @@ const DEFAULT_CONTEXT = {
|
|||||||
modalStack: []
|
modalStack: []
|
||||||
};
|
};
|
||||||
|
|
||||||
context = _global.popupContext = {
|
if (!_global.popupContext) {
|
||||||
...DEFAULT_CONTEXT,
|
_global.popupContext = {
|
||||||
...context
|
...DEFAULT_CONTEXT
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const PopupContext = {
|
const PopupContext = {
|
||||||
getContext(key) {
|
getContext(key) {
|
||||||
return context[key];
|
return _global.popupContext[key];
|
||||||
},
|
},
|
||||||
|
|
||||||
setContext(key, value) {
|
setContext(key, value) {
|
||||||
context[key] = value;
|
_global.popupContext[key] = value;
|
||||||
},
|
},
|
||||||
|
|
||||||
plusKeyByOne(key) {
|
plusKeyByOne(key) {
|
||||||
const oldVal = +context[key];
|
const oldVal = +_global.popupContext[key];
|
||||||
context[key] = oldVal + 1;
|
_global.popupContext[key] = oldVal + 1;
|
||||||
|
|
||||||
return oldVal;
|
return oldVal;
|
||||||
}
|
}
|
||||||
|
51
packages/vant-css/src/deep-select.css
Normal file
51
packages/vant-css/src/deep-select.css
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
@import "./mixins/ellipsis";
|
||||||
|
@import "./mixins/clearfix";
|
||||||
|
@import './common/var.css';
|
||||||
|
.van-deep-select {
|
||||||
|
user-select: none;
|
||||||
|
position: relative;
|
||||||
|
@mixin clearfix;
|
||||||
|
&__nav {
|
||||||
|
width: 143px;
|
||||||
|
/*float: left;*/
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow: scroll;
|
||||||
|
background-color: $c-white;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
&__nitem {
|
||||||
|
line-height: 44px;
|
||||||
|
padding: 0 15px;
|
||||||
|
background-color: $c-white;
|
||||||
|
@mixin multi-ellipsis 1;
|
||||||
|
&--active {
|
||||||
|
background-color: $c-background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__content {
|
||||||
|
padding: 0 15px;
|
||||||
|
margin-left: 143px;
|
||||||
|
overflow: scroll;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
&__item {
|
||||||
|
position: relative;
|
||||||
|
line-height: 44px;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 18px;
|
||||||
|
@mixin multi-ellipsis 1;
|
||||||
|
&--active {
|
||||||
|
color: $button-danger-background-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__selected {
|
||||||
|
float: right;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
@import './cell.css';
|
@import './cell.css';
|
||||||
@import './cell-swipe.css';
|
@import './cell-swipe.css';
|
||||||
@import './card.css';
|
@import './card.css';
|
||||||
|
@import './deep-select.css';
|
||||||
@import './dialog.css';
|
@import './dialog.css';
|
||||||
@import './field.css';
|
@import './field.css';
|
||||||
@import './icon.css';
|
@import './icon.css';
|
||||||
|
68
test/unit/specs/deep-select.spec.js
Normal file
68
test/unit/specs/deep-select.spec.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import DeepSelect from 'packages/deep-select';
|
||||||
|
import { mount } from 'avoriaz';
|
||||||
|
|
||||||
|
describe('DeepSelect', () => {
|
||||||
|
let wrapper;
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper && wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create an empty deep-select', () => {
|
||||||
|
wrapper = mount(DeepSelect);
|
||||||
|
expect(wrapper.hasStyle('height', '0px')).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create a deep-select correctly', () => {
|
||||||
|
wrapper = mount(DeepSelect, {
|
||||||
|
propsData: {
|
||||||
|
items: [{
|
||||||
|
text: 'A',
|
||||||
|
children: [{
|
||||||
|
text: 'Cc',
|
||||||
|
id: 123
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
maxHeight: 200
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(wrapper.hasClass('van-deep-select')).to.be.true;
|
||||||
|
expect(wrapper.hasStyle('height', '44px')).to.be.true;
|
||||||
|
expect(wrapper.propsData().maxHeight).to.equal(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('interact with this component', () => {
|
||||||
|
wrapper = mount(DeepSelect, {
|
||||||
|
propsData: {
|
||||||
|
items: [{
|
||||||
|
text: 'A',
|
||||||
|
children: [{
|
||||||
|
text: 'Cc',
|
||||||
|
id: 123
|
||||||
|
}, {
|
||||||
|
text: 'Bb',
|
||||||
|
id: 234
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
text: 'B',
|
||||||
|
children: [{
|
||||||
|
text: 'Nmi',
|
||||||
|
id: 345
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
maxHeight: 220
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wrapper.vm.$on('navclick', index => {
|
||||||
|
wrapper.vm.mainActiveIndex = index;
|
||||||
|
});
|
||||||
|
wrapper.vm.$on('itemclick', item => {
|
||||||
|
wrapper.vm.activeId = item.id;
|
||||||
|
});
|
||||||
|
const secondNav = wrapper.find('.van-deep-select__nitem')[1];
|
||||||
|
secondNav.trigger('click');
|
||||||
|
expect(wrapper.vm.mainActiveIndex).to.equal(1);
|
||||||
|
const target = wrapper.find('.van-deep-select__item')[0];
|
||||||
|
target.trigger('click');
|
||||||
|
expect(wrapper.vm.activeId).to.equal(345);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user