diff --git a/CHANGE.md b/CHANGE.md index 6f5d756..a36640b 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -38,4 +38,6 @@ DropdownItem key 改为 name, Dropdown 的 visible 要使用 @on-visible-change DropdownItem 里,this.$parent.$parent 与1.0 有区别 ### Menu MenuItem 和 Submenu 的 key 改为了 name -Menu 的 activeKey 改为 activeName,openKeys 改为 openNames \ No newline at end of file +Menu 的 activeKey 改为 activeName,openKeys 改为 openNames +### Cascader +Caspanel 的 sublist 从 prop -> data diff --git a/README.md b/README.md index 94328f2..0e707fa 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ - [x] Slider - [ ] DatePicker - [ ] TimePicker -- [ ] Cascader +- [x] Cascader - [ ] Transfer - [x] InputNumber - [x] Rate diff --git a/examples/app.vue b/examples/app.vue index a966f99..0f3fa75 100644 --- a/examples/app.vue +++ b/examples/app.vue @@ -42,6 +42,7 @@ li + li { border-left: solid 1px #bbb; padding-left: 10px; margin-left: 10px; } <li><router-link to="/breadcrumb">Breadcrumb</router-link></li> <li><router-link to="/menu">Menu</router-link></li> <li><router-link to="/spin">Spin</router-link></li> + <li><router-link to="/cascader">Cascader</router-link></li> </ul> </nav> <router-view></router-view> diff --git a/examples/main.js b/examples/main.js index cc2ebdb..c3b505e 100644 --- a/examples/main.js +++ b/examples/main.js @@ -132,6 +132,10 @@ const router = new VueRouter({ { path: '/spin', component: require('./routers/spin.vue') + }, + { + path: '/cascader', + component: require('./routers/cascader.vue') } ] }); diff --git a/examples/routers/cascader.vue b/examples/routers/cascader.vue index d8ebc3f..595d367 100644 --- a/examples/routers/cascader.vue +++ b/examples/routers/cascader.vue @@ -1,24 +1,85 @@ -<template> - {{ text }} - <Cascader :data="data" @on-change="handleChange"> - <a href="javascript:void(0)">选择</a> - </Cascader> +<!--<template>--> + <!--<div>--> + <!--<Cascader :data="data" v-model="value1"></Cascader>--> + <!--{{ value1 }}--> + <!--<div @click="c">change</div>--> + <!--</div>--> +<!--</template>--> +<!--<script>--> + <!--export default {--> + <!--data () {--> + <!--return {--> + <!--value1: [],--> + <!--data: [{--> + <!--value: 'beijing',--> + <!--label: '北京',--> + <!--children: [--> + <!--{--> + <!--value: 'gugong',--> + <!--label: '故宫'--> + <!--},--> + <!--{--> + <!--value: 'tiantan',--> + <!--label: '天坛'--> + <!--},--> + <!--{--> + <!--value: 'wangfujing',--> + <!--label: '王府井'--> + <!--}--> + <!--]--> + <!--}, {--> + <!--value: 'jiangsu',--> + <!--label: '江苏',--> + <!--children: [--> + <!--{--> + <!--value: 'nanjing',--> + <!--label: '南京',--> + <!--children: [--> + <!--{--> + <!--value: 'fuzimiao',--> + <!--label: '夫子庙',--> + <!--}--> + <!--]--> + <!--},--> + <!--{--> + <!--value: 'suzhou',--> + <!--label: '苏州',--> + <!--children: [--> + <!--{--> + <!--value: 'zhuozhengyuan',--> + <!--label: '拙政园',--> + <!--},--> + <!--{--> + <!--value: 'shizilin',--> + <!--label: '狮子林',--> + <!--}--> + <!--]--> + <!--}--> + <!--],--> + <!--}]--> + <!--}--> + <!--},--> + <!--methods: {--> + <!--c () {--> + <!--this.value1 = ['jiangsu', 'suzhou', 'zhuozhengyuan']--> + <!--}--> + <!--}--> + <!--}--> +<!--</script>--> + - <Row> - <i-col span="4"> - Disabled <Switch :checked.sync="disabled"></Switch> - </i-col> - <i-col span="4"> - <Cascader :data="data" :value.sync="value1" :disabled="disabled"></Cascader> - </i-col> - </Row> +<template> + <div> + <Cascader :data="data" v-model="value2" change-on-select></Cascader> + {{ value2 }} + <div @click="c">change</div> + </div> </template> <script> export default { data () { return { - disabled: false, - text: '未选择', + value2: [], data: [{ value: 'beijing', label: '北京', @@ -69,8 +130,8 @@ } }, methods: { - handleChange (value, selectedData) { - this.text = selectedData.map(o => o.label).join(', '); + c () { + this.value2 = ['jiangsu', 'suzhou', 'zhuozhengyuan'] } } } diff --git a/src/components/cascader/cascader.vue b/src/components/cascader/cascader.vue index 254c30d..9ff66e2 100644 --- a/src/components/cascader/cascader.vue +++ b/src/components/cascader/cascader.vue @@ -5,39 +5,43 @@ <i-input readonly :disabled="disabled" - :value.sync="displayRender" + v-model="displayRender" :size="size" :placeholder="placeholder"></i-input> - <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.stop="clearSelect"></Icon> + <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSelect"></Icon> <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']"></Icon> </slot> </div> - <Dropdown v-show="visible" transition="slide-up"> - <div> - <Caspanel - v-ref:caspanel - :prefix-cls="prefixCls" - :data.sync="data" - :disabled="disabled" - :change-on-select="changeOnSelect" - :trigger="trigger"></Caspanel> - </div> - </Dropdown> + <transition name="slide-up"> + <Drop v-show="visible"> + <div> + <Caspanel + ref="caspanel" + :prefix-cls="prefixCls" + :data="data" + :disabled="disabled" + :change-on-select="changeOnSelect" + :trigger="trigger"></Caspanel> + </div> + </Drop> + </transition> </div> </template> <script> import iInput from '../input/input.vue'; - import Dropdown from '../select/dropdown.vue'; + import Drop from '../select/dropdown.vue'; import Icon from '../icon/icon.vue'; import Caspanel from './caspanel.vue'; import clickoutside from '../../directives/clickoutside'; import { oneOf } from '../../utils/assist'; + import Emitter from '../../mixins/emitter'; const prefixCls = 'ivu-cascader'; export default { name: 'Cascader', - components: { iInput, Dropdown, Icon, Caspanel }, + mixins: [ Emitter ], + components: { iInput, Drop, Icon, Caspanel }, directives: { clickoutside }, props: { data: { @@ -92,7 +96,8 @@ visible: false, selected: [], tmpSelected: [], - updatingValue: false // to fix set value in changeOnSelect type + updatingValue: false, // to fix set value in changeOnSelect type + currentValue: this.value }; }, computed: { @@ -107,7 +112,7 @@ ]; }, showCloseIcon () { - return this.value && this.value.length && this.clearable; + return this.currentValue && this.currentValue.length && this.clearable; }, displayRender () { let label = []; @@ -120,11 +125,12 @@ }, methods: { clearSelect () { - const oldVal = JSON.stringify(this.value); - this.value = this.selected = this.tmpSelected = []; + const oldVal = JSON.stringify(this.currentValue); + this.currentValue = this.selected = this.tmpSelected = []; this.handleClose(); - this.emitValue(this.value, oldVal); - this.$broadcast('on-clear'); + this.emitValue(this.currentValue, oldVal); +// this.$broadcast('on-clear'); + this.broadcast('Caspanel', 'on-clear'); }, handleClose () { this.visible = false; @@ -139,8 +145,8 @@ }, onFocus () { this.visible = true; - if (!this.value.length) { - this.$broadcast('on-clear'); + if (!this.currentValue.length) { + this.broadcast('Caspanel', 'on-clear'); } }, updateResult (result) { @@ -148,25 +154,30 @@ }, updateSelected (init = false) { if (!this.changeOnSelect || init) { - this.$broadcast('on-find-selected', this.value); + this.broadcast('Caspanel', 'on-find-selected', { + value: this.currentValue + }); } }, emitValue (val, oldVal) { if (JSON.stringify(val) !== oldVal) { - this.$emit('on-change', this.value, JSON.parse(JSON.stringify(this.selected))); - this.$dispatch('on-form-change', this.value, JSON.parse(JSON.stringify(this.selected))); + this.$emit('on-change', this.currentValue, JSON.parse(JSON.stringify(this.selected))); + // todo 事件 +// this.$dispatch('on-form-change', this.currentValue, JSON.parse(JSON.stringify(this.selected))); } } }, - ready () { + mounted () { this.updateSelected(true); - }, - events: { - // lastValue: is click the final val - // fromInit: is this emit from update value - 'on-result-change' (lastValue, changeOnSelect, fromInit) { + this.$on('on-result-change', (params) => { + // lastValue: is click the final val + // fromInit: is this emit from update value + const lastValue = params.lastValue; + const changeOnSelect = params.changeOnSelect; + const fromInit = params.fromInit; + if (lastValue || changeOnSelect) { - const oldVal = JSON.stringify(this.value); + const oldVal = JSON.stringify(this.currentValue); this.selected = this.tmpSelected; let newVal = []; @@ -176,30 +187,37 @@ if (!fromInit) { this.updatingValue = true; - this.value = newVal; - this.emitValue(this.value, oldVal); + this.currentValue = newVal; + this.emitValue(this.currentValue, oldVal); } } if (lastValue && !fromInit) { this.handleClose(); } - }, - 'on-form-blur' () { - return false; - }, - 'on-form-change' () { - return false; - } + }); }, + // todo 事件 这是因为内部的input会触发,应该组织 +// events: { +// 'on-form-blur' () { +// return false; +// }, +// 'on-form-change' () { +// return false; +// } +// }, watch: { visible (val) { if (val) { - if (this.value.length) { + if (this.currentValue.length) { this.updateSelected(); } } }, - value () { + value (val) { + this.currentValue = val; + }, + currentValue () { + this.$emit('input', this.currentValue); if (this.updatingValue) { this.updatingValue = false; return; diff --git a/src/components/cascader/casitem.vue b/src/components/cascader/casitem.vue index a0171b2..2f68ce9 100644 --- a/src/components/cascader/casitem.vue +++ b/src/components/cascader/casitem.vue @@ -3,6 +3,7 @@ </template> <script> export default { + name: 'Casitem', props: { data: Object, prefixCls: String, diff --git a/src/components/cascader/caspanel.vue b/src/components/cascader/caspanel.vue index bae638c..804092f 100644 --- a/src/components/cascader/caspanel.vue +++ b/src/components/cascader/caspanel.vue @@ -1,19 +1,23 @@ <template> - <ul v-if="data && data.length" :class="[prefixCls + '-menu']"> - <Casitem - v-for="item in data" - :prefix-cls="prefixCls" - :data.sync="item" - :tmp-item="tmpItem" - @click.stop="handleClickItem(item)" - @mouseenter.stop="handleHoverItem(item)"></Casitem> - </ul><Caspanel v-if="sublist && sublist.length" :prefix-cls="prefixCls" :data.sync="sublist" :disabled="disabled" :trigger="trigger" :change-on-select="changeOnSelect"></Caspanel> + <span> + <ul v-if="data && data.length" :class="[prefixCls + '-menu']"> + <Casitem + v-for="item in data" + :prefix-cls="prefixCls" + :data="item" + :tmp-item="tmpItem" + @click.native.stop="handleClickItem(item)" + @mouseenter.native.stop="handleHoverItem(item)"></Casitem> + </ul><Caspanel v-if="sublist && sublist.length" :prefix-cls="prefixCls" :data="sublist" :disabled="disabled" :trigger="trigger" :change-on-select="changeOnSelect"></Caspanel> + </span> </template> <script> import Casitem from './casitem.vue'; + import Emitter from '../../mixins/emitter'; export default { name: 'Caspanel', + mixins: [ Emitter ], components: { Casitem }, props: { data: { @@ -22,12 +26,6 @@ return []; } }, - sublist: { - type: Array, - default () { - return []; - } - }, disabled: Boolean, changeOnSelect: Boolean, trigger: String, @@ -36,9 +34,15 @@ data () { return { tmpItem: {}, - result: [] + result: [], + sublist: [] }; }, + watch: { + data () { + this.sublist = []; + } + }, methods: { handleClickItem (item) { if (this.trigger !== 'click' && item.children) return; @@ -58,10 +62,20 @@ if (item.children && item.children.length){ this.sublist = item.children; - this.$dispatch('on-result-change', false, this.changeOnSelect, fromInit); +// this.$dispatch('on-result-change', false, this.changeOnSelect, fromInit); + this.dispatch('Cascader', 'on-result-change', { + lastValue: false, + changeOnSelect: this.changeOnSelect, + fromInit: fromInit + }); } else { this.sublist = []; - this.$dispatch('on-result-change', true, this.changeOnSelect, fromInit); +// this.$dispatch('on-result-change', true, this.changeOnSelect, fromInit); + this.dispatch('Cascader', 'on-result-change', { + lastValue: true, + changeOnSelect: this.changeOnSelect, + fromInit: fromInit + }); } }, updateResult (item) { @@ -84,13 +98,9 @@ } } }, - watch: { - data () { - this.sublist = []; - } - }, - events: { - 'on-find-selected' (val) { + mounted () { + this.$on('on-find-selected', (params) => { + const val = params.value; let value = [...val]; for (let i = 0; i < value.length; i++) { for (let j = 0; j < this.data.length; j++) { @@ -98,17 +108,19 @@ this.handleTriggerItem(this.data[j], true); value.splice(0, 1); this.$nextTick(() => { - this.$broadcast('on-find-selected', value); + this.broadcast('Caspanel', 'on-find-selected', { + value: value + }); }); return false; } } } - }, - 'on-clear' () { + }); + this.$on('on-clear', () => { this.sublist = []; this.tmpItem = {}; - } + }); } }; </script> diff --git a/src/components/spin/spin.vue b/src/components/spin/spin.vue index b4e8cc3..25a619d 100644 --- a/src/components/spin/spin.vue +++ b/src/components/spin/spin.vue @@ -14,6 +14,7 @@ const prefixCls = 'ivu-spin'; export default { + name: 'Spin', props: { size: { validator (value) { diff --git a/src/index.js b/src/index.js index 8b9373f..eec82f8 100644 --- a/src/index.js +++ b/src/index.js @@ -9,7 +9,7 @@ import Breadcrumb from './components/breadcrumb'; import Button from './components/button'; import Card from './components/card'; import Carousel from './components/carousel'; -// import Cascader from './components/cascader'; +import Cascader from './components/cascader'; import Checkbox from './components/checkbox'; import Circle from './components/circle'; import Collapse from './components/collapse'; @@ -59,7 +59,7 @@ const iview = { Card, Carousel, CarouselItem: Carousel.Item, - // Cascader, + Cascader, Checkbox, CheckboxGroup: Checkbox.Group, iCircle: Circle, diff --git a/src/mixins/emitter.js b/src/mixins/emitter.js index 422f5ac..e4bac47 100755 --- a/src/mixins/emitter.js +++ b/src/mixins/emitter.js @@ -5,6 +5,7 @@ function broadcast(componentName, eventName, params) { if (name === componentName) { child.$emit.apply(child, [eventName].concat(params)); } else { + // todo 如果 params 是空数组,接收到的会是 undefined broadcast.apply(child, [componentName, eventName].concat([params])); } }); -- libgit2 0.21.4