diff --git a/build/bin/init.js b/build/bin/init.js index 799258e11..1cc94dc86 100644 --- a/build/bin/init.js +++ b/build/bin/init.js @@ -29,4 +29,3 @@ gulp.task('copy', function(callback) { }); runSequence('copy'); - diff --git a/build/webpack.config.dev.js b/build/webpack.config.dev.js index f4657d327..e38ba86e3 100644 --- a/build/webpack.config.dev.js +++ b/build/webpack.config.dev.js @@ -34,6 +34,7 @@ module.exports = { chunkFilename: 'async.[name].js' }, devServer: { + host: '0.0.0.0', historyApiFallback: { rewrites: [ { from: /^\/zanui\/vue\/examples/, to: '/examples.html' }, @@ -114,7 +115,7 @@ module.exports = { }, render: function(tokens, idx) { - return tokens[idx].nesting === 1 + return tokens[idx].nesting === 1 ? `
` :`
\n`; } diff --git a/docs/examples-docs/deep-select.md b/docs/examples-docs/deep-select.md new file mode 100644 index 000000000..846fdd9c5 --- /dev/null +++ b/docs/examples-docs/deep-select.md @@ -0,0 +1,169 @@ + + +## DeepSelect 分类选择组件 + +### 使用指南 +``` javascript +import { DeepSelect } from 'vant'; + +Vue.component(DeepSelect.name, DeepSelect); +``` + +### 代码演示 + +#### 基础用法 + +:::demo 基础用法 +```html + +``` + +```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; + } + } +} + +``` +::: + +### 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 + } + ] + } +] +``` diff --git a/docs/src/doc.config.js b/docs/src/doc.config.js index 53912fd7d..d4cba5b4a 100644 --- a/docs/src/doc.config.js +++ b/docs/src/doc.config.js @@ -185,6 +185,10 @@ module.exports = { { "groupName": "业务组件", "list": [ + { + "path": "/deep-select", + "title": "DeepSelect 分类选择组件" + }, { "path": "/express-way", "title": "ExpressWay 配送方式" diff --git a/packages/deep-select/index.vue b/packages/deep-select/index.vue new file mode 100644 index 000000000..410b6ae86 --- /dev/null +++ b/packages/deep-select/index.vue @@ -0,0 +1,85 @@ + + + diff --git a/packages/index.js b/packages/index.js index bd8f2a69a..ac61d310f 100644 --- a/packages/index.js +++ b/packages/index.js @@ -10,6 +10,7 @@ import Checkbox from './checkbox'; import CheckboxGroup from './checkbox-group'; import Col from './col'; import DatetimePicker from './datetime-picker'; +import DeepSelect from './deep-select'; import Dialog from './dialog'; import ExpressWay from './express-way'; import Field from './field'; @@ -57,6 +58,7 @@ const components = [ CheckboxGroup, Col, DatetimePicker, + DeepSelect, ExpressWay, Field, Icon, @@ -114,6 +116,7 @@ export { CheckboxGroup, Col, DatetimePicker, + DeepSelect, Dialog, ExpressWay, Field, diff --git a/packages/mixins/popup/popup-context.js b/packages/mixins/popup/popup-context.js index 45cbae621..0e0588b7b 100644 --- a/packages/mixins/popup/popup-context.js +++ b/packages/mixins/popup/popup-context.js @@ -1,12 +1,7 @@ import Vue from 'vue'; -let context; const _global = Vue.prototype.$isServer ? global : window; -if (_global && _global.popupContext) { - context = _global.popupContext; -} - const DEFAULT_CONTEXT = { idSeed: 1, zIndex: 2000, @@ -15,23 +10,24 @@ const DEFAULT_CONTEXT = { modalStack: [] }; -context = _global.popupContext = { - ...DEFAULT_CONTEXT, - ...context -}; +if (!_global.popupContext) { + _global.popupContext = { + ...DEFAULT_CONTEXT + }; +} const PopupContext = { getContext(key) { - return context[key]; + return _global.popupContext[key]; }, setContext(key, value) { - context[key] = value; + _global.popupContext[key] = value; }, plusKeyByOne(key) { - const oldVal = +context[key]; - context[key] = oldVal + 1; + const oldVal = +_global.popupContext[key]; + _global.popupContext[key] = oldVal + 1; return oldVal; } diff --git a/packages/vant-css/src/deep-select.css b/packages/vant-css/src/deep-select.css new file mode 100644 index 000000000..e36ce7c71 --- /dev/null +++ b/packages/vant-css/src/deep-select.css @@ -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; + } +} diff --git a/packages/vant-css/src/index.css b/packages/vant-css/src/index.css index 8731cbdb4..45c3ed50b 100644 --- a/packages/vant-css/src/index.css +++ b/packages/vant-css/src/index.css @@ -6,6 +6,7 @@ @import './cell.css'; @import './cell-swipe.css'; @import './card.css'; +@import './deep-select.css'; @import './dialog.css'; @import './field.css'; @import './icon.css'; diff --git a/test/unit/specs/deep-select.spec.js b/test/unit/specs/deep-select.spec.js new file mode 100644 index 000000000..2fa1a85b9 --- /dev/null +++ b/test/unit/specs/deep-select.spec.js @@ -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); + }); +});