From 34ee7b4a14b4eb6db62def19528fe2c9c0758c13 Mon Sep 17 00:00:00 2001 From: 梁灏 Date: Thu, 2 Mar 2017 17:40:19 +0800 Subject: [PATCH] support Tree & add dispatch and broadcast methods --- CHANGE.md | 4 +++- README.md | 2 +- src/components/alert/alert.vue | 1 + src/components/badge/badge.vue | 1 + src/components/button/button-group.vue | 2 +- src/components/cascader/cascader.vue | 1 + src/components/checkbox/checkbox-group.vue | 2 +- src/components/circle/circle.vue | 1 + src/components/collapse/collapse.vue | 1 + src/components/collapse/panel.vue | 1 + src/components/grid/col.vue | 1 + src/components/grid/row.vue | 1 + src/components/input-number/input-number.vue | 1 + src/components/radio/radio-group.vue | 2 +- src/components/steps/step.vue | 1 + src/components/steps/steps.vue | 1 + src/components/switch/switch.vue | 1 + src/components/tag/tag.vue | 1 + src/components/timeline/timeline-item.vue | 1 + src/components/timeline/timeline.vue | 1 + src/components/tree/tree.vue | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------- src/components/upload/upload-list.vue | 1 + src/components/upload/upload.vue | 1 + src/index.js | 4 ++-- src/mixins/emitter.js | 33 +++++++++++++++++++++++++++++++++ test/app.vue | 1 + test/main.js | 4 ++++ test/routers/tree.vue | 14 ++++++++------ 28 files changed, 190 insertions(+), 69 deletions(-) create mode 100755 src/mixins/emitter.js diff --git a/CHANGE.md b/CHANGE.md index 70016e2..503c287 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -21,4 +21,6 @@ class 改为了 className ### Collapse 废弃 activeKey,使用 v-model,key 是保留的,更名为 name ### Carousel -废弃 activeIndex,使用 v-model,v-for="n in slides.length",Vue2的数字循环,是从1开始的 \ No newline at end of file +废弃 activeIndex,使用 v-model,v-for="n in slides.length",Vue2的数字循环,是从1开始的 +### Tree +废弃 data,改为 value,使用 v-model,key 更名为 name \ No newline at end of file diff --git a/README.md b/README.md index ead257b..8d47dad 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ - [ ] Tooltip - [ ] Poptip - [x] Carousel -- [ ] Tree +- [x] Tree - [ ] Menu - [ ] Tabs - [ ] Dropdown diff --git a/src/components/alert/alert.vue b/src/components/alert/alert.vue index a8b9e4a..981a59f 100644 --- a/src/components/alert/alert.vue +++ b/src/components/alert/alert.vue @@ -23,6 +23,7 @@ const prefixCls = 'ivu-alert'; export default { + name: 'Alert', components: { Icon }, props: { type: { diff --git a/src/components/badge/badge.vue b/src/components/badge/badge.vue index 9581302..84e1b12 100644 --- a/src/components/badge/badge.vue +++ b/src/components/badge/badge.vue @@ -12,6 +12,7 @@ const prefixCls = 'ivu-badge'; export default { + name: 'Badge', props: { count: [Number, String], dot: { diff --git a/src/components/button/button-group.vue b/src/components/button/button-group.vue index edd83fb..a363426 100644 --- a/src/components/button/button-group.vue +++ b/src/components/button/button-group.vue @@ -9,7 +9,7 @@ const prefixCls = 'ivu-btn-group'; export default { - name: 'buttonGroup', + name: 'ButtonGroup', props: { size: { validator (value) { diff --git a/src/components/cascader/cascader.vue b/src/components/cascader/cascader.vue index 4ebfdb9..254c30d 100644 --- a/src/components/cascader/cascader.vue +++ b/src/components/cascader/cascader.vue @@ -36,6 +36,7 @@ const prefixCls = 'ivu-cascader'; export default { + name: 'Cascader', components: { iInput, Dropdown, Icon, Caspanel }, directives: { clickoutside }, props: { diff --git a/src/components/checkbox/checkbox-group.vue b/src/components/checkbox/checkbox-group.vue index c6e9942..e73b25f 100644 --- a/src/components/checkbox/checkbox-group.vue +++ b/src/components/checkbox/checkbox-group.vue @@ -7,7 +7,7 @@ const prefixCls = 'ivu-checkbox-group'; export default { - name: 'checkboxGroup', + name: 'CheckboxGroup', props: { value: { type: Array, diff --git a/src/components/circle/circle.vue b/src/components/circle/circle.vue index a1cc95e..852d4c0 100644 --- a/src/components/circle/circle.vue +++ b/src/components/circle/circle.vue @@ -15,6 +15,7 @@ const prefixCls = 'ivu-chart-circle'; export default { + name: 'Circle', props: { percent: { type: Number, diff --git a/src/components/collapse/collapse.vue b/src/components/collapse/collapse.vue index 0b6125d..4046319 100644 --- a/src/components/collapse/collapse.vue +++ b/src/components/collapse/collapse.vue @@ -7,6 +7,7 @@ const prefixCls = 'ivu-collapse'; export default { + name: 'Collapse', props: { accordion: { type: Boolean, diff --git a/src/components/collapse/panel.vue b/src/components/collapse/panel.vue index fee3ca0..e217bb1 100644 --- a/src/components/collapse/panel.vue +++ b/src/components/collapse/panel.vue @@ -14,6 +14,7 @@ const prefixCls = 'ivu-collapse'; export default { + name: 'Panel', components: { Icon }, props: { name: { diff --git a/src/components/grid/col.vue b/src/components/grid/col.vue index 7e05b81..2bb00d3 100644 --- a/src/components/grid/col.vue +++ b/src/components/grid/col.vue @@ -7,6 +7,7 @@ const prefixCls = 'ivu-col'; export default { + name: 'iCol', props: { span: [Number, String], order: [Number, String], diff --git a/src/components/grid/row.vue b/src/components/grid/row.vue index 8554d7b..7de24ec 100644 --- a/src/components/grid/row.vue +++ b/src/components/grid/row.vue @@ -9,6 +9,7 @@ const prefixCls = 'ivu-row'; export default { + name: 'Row', props: { type: { validator (value) { diff --git a/src/components/input-number/input-number.vue b/src/components/input-number/input-number.vue index 46f014b..199dd87 100644 --- a/src/components/input-number/input-number.vue +++ b/src/components/input-number/input-number.vue @@ -61,6 +61,7 @@ } export default { + name: 'InputNumber', props: { max: { type: Number, diff --git a/src/components/radio/radio-group.vue b/src/components/radio/radio-group.vue index e472b60..c4d598a 100644 --- a/src/components/radio/radio-group.vue +++ b/src/components/radio/radio-group.vue @@ -9,7 +9,7 @@ const prefixCls = 'ivu-radio-group'; export default { - name: 'radioGroup', + name: 'RadioGroup', props: { value: { type: [String, Number], diff --git a/src/components/steps/step.vue b/src/components/steps/step.vue index e8c89fd..3ee1263 100644 --- a/src/components/steps/step.vue +++ b/src/components/steps/step.vue @@ -20,6 +20,7 @@ const iconPrefixCls = 'ivu-icon'; export default { + name: 'Step', props: { status: { validator (value) { diff --git a/src/components/steps/steps.vue b/src/components/steps/steps.vue index cc68ddc..4679d82 100644 --- a/src/components/steps/steps.vue +++ b/src/components/steps/steps.vue @@ -9,6 +9,7 @@ const prefixCls = 'ivu-steps'; export default { + name: 'Steps', props: { current: { type: Number, diff --git a/src/components/switch/switch.vue b/src/components/switch/switch.vue index ca5609e..8e54682 100644 --- a/src/components/switch/switch.vue +++ b/src/components/switch/switch.vue @@ -12,6 +12,7 @@ const prefixCls = 'ivu-switch'; export default { + name: 'Switch', props: { value: { type: Boolean, diff --git a/src/components/tag/tag.vue b/src/components/tag/tag.vue index add0b61..80e157e 100644 --- a/src/components/tag/tag.vue +++ b/src/components/tag/tag.vue @@ -12,6 +12,7 @@ const prefixCls = 'ivu-tag'; export default { + name: 'Tag', components: { Icon }, props: { closable: { diff --git a/src/components/timeline/timeline-item.vue b/src/components/timeline/timeline-item.vue index a66aaa1..15410d7 100644 --- a/src/components/timeline/timeline-item.vue +++ b/src/components/timeline/timeline-item.vue @@ -11,6 +11,7 @@ const prefixCls = 'ivu-timeline'; export default { + name: 'TimelineItem', props: { color: { type: String, diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index be31f0b..681023c 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -7,6 +7,7 @@ const prefixCls = 'ivu-timeline'; export default { + name: 'Timeline', props: { pending: { type: Boolean, diff --git a/src/components/tree/tree.vue b/src/components/tree/tree.vue index 411c2c7..50f6a49 100644 --- a/src/components/tree/tree.vue +++ b/src/components/tree/tree.vue @@ -1,27 +1,28 @@ @@ -29,20 +30,22 @@ import Icon from '../icon/icon.vue'; import Checkbox from '../checkbox/checkbox.vue'; import { t } from '../../locale'; + import emitter from '../../mixins/emitter'; const prefixCls = 'ivu-tree'; export default { - name: 'tree', + name: 'Tree', components: { Icon, Checkbox }, + mixins: [ emitter ], props: { - data: { + value: { type: Array, default () { return []; } }, - key: { + name: { type: String, default: '0' }, @@ -63,12 +66,13 @@ }, data () { return { - prefixCls: prefixCls + prefixCls: prefixCls, + data: this.value }; }, computed: { classes () { - if (this.key === '0') { + if (this.name === '0') { return this.prefixCls; } else { return `${this.prefixCls}-child-tree`; @@ -76,8 +80,11 @@ } }, watch: { - data(){ - if (this.key === '0') { + value (val) { + this.data = val; + }, + data () { + if (this.name === '0') { this.setKey(); this.preHandle(); } @@ -118,55 +125,82 @@ }, setKey () { for (let i = 0; i < this.data.length; i++) { - this.data[i].key = `${this.key}.${i}`; + this.data[i].name = `${this.name}.${i}`; } }, preHandle () { - for (let [i,item] of this.data.entries()) { + for (let [i, item] of this.data.entries()) { if (!item.children || !item.children.length) { - this.$set(`data[${i}].isLeaf`, true); - this.$set(`data[${i}].childrenCheckedStatus`, 2); +// this.$set(`data[${i}].isLeaf`, true); +// this.$set(`data[${i}].childrenCheckedStatus`, 2); + this.$set(this.data[i], 'isLeaf', true); + this.$set(this.data[i], 'childrenCheckedStatus', 2); continue; } if (item.checked && !item.childrenCheckedStatus) { - this.$set(`data[${i}].childrenCheckedStatus`, 2); - this.$broadcast('parentChecked', true, `${this.key}.${i}`); +// this.$set(`data[${i}].childrenCheckedStatus`, 2); + this.$set(this.data[i], 'childrenCheckedStatus', 2); +// this.$broadcast('parentChecked', true, `${this.name}.${i}`); + this.broadcast('Tree', 'parentChecked', { + status: true, + name: `${this.name}.${i}` + }); } else { let status = this.getChildrenCheckedStatus(item.children); - this.$set(`data[${i}].childrenCheckedStatus`, status); - if (status !== 0) this.$set(`data[${i}].checked`, true); +// this.$set(`data[${i}].childrenCheckedStatus`, status); + this.$set(this.data[i], 'childrenCheckedStatus', status); +// if (status !== 0) this.$set(`data[${i}].checked`, true); + if (status !== 0) this.$set(this.data[i], 'checked', true); } } }, setExpand (disabled, index) { if (!disabled) { - this.$set(`data[${index}].expand`, !this.data[index].expand); +// this.$set(`data[${index}].expand`, !this.data[index].expand); + this.$set(this.data[index], 'expand', !this.data[index].expand); } }, setSelect (disabled, index) { if (!disabled) { const selected = !this.data[index].selected; if (this.multiple || !selected) { - this.$set(`data[${index}].selected`, selected); +// this.$set(`data[${index}].selected`, selected); + this.$set(this.data[index], 'selected', selected); } else { for (let i = 0; i < this.data.length; i++) { if (i == index) { - this.$set(`data[${i}].selected`, true); +// this.$set(`data[${i}].selected`, true); + this.$set(this.data[i], 'selected', true); } else { - this.$set(`data[${i}].selected`, false); +// this.$set(`data[${i}].selected`, false); + this.$set(this.data[i], 'selected', false); } } } - this.$dispatch('nodeSelected', this, selected); +// this.$dispatch('nodeSelected', this, selected); + this.dispatch('Tree', 'nodeSelected', { + ori: this, + selected: selected + }) } }, setCheck (disabled, index) { if (disabled) return; const checked = !this.data[index].checked; - this.$set(`data[${index}].checked`, checked); - this.$set(`data[${index}].childrenCheckedStatus`, checked ? 2 : 0); - this.$dispatch('childChecked', this, this.key); - this.$broadcast('parentChecked', checked, `${this.key}.${index}`); +// this.$set(`data[${index}].checked`, checked); + this.$set(this.data[index], 'checked', checked); +// this.$set(`data[${index}].childrenCheckedStatus`, checked ? 2 : 0); + this.$set(this.data[index], 'childrenCheckedStatus', checked ? 2 : 0); +// this.$dispatch('childChecked', this, this.name); + this.dispatch('Tree', 'childChecked', { + ori: this, + name: this.name + }); +// this.$broadcast('parentChecked', checked, `${this.name}.${index}`); + this.broadcast('Tree', 'parentChecked', { + status: checked, + name: `${this.name}.${index}` + }); }, getNodes (data, opt) { data = data || this.data; @@ -215,55 +249,83 @@ } } }, - ready(){ + mounted () { this.setKey(); this.preHandle(); - this.$on('nodeSelected', (ori, selected) => { - if (this.key !== '0') return true; +// this.$on('nodeSelected', (ori, selected) => { + this.$on('nodeSelected', (params) => { + const ori = params.ori; + const selected = params.selected; + + if (this.name !== '0') return true; if (!this.multiple && selected) { if (this !== ori) { for (let i = 0; i < this.data.length; i++) { - this.$set(`data[${i}].selected`, false); +// this.$set(`data[${i}].selected`, false); + this.$set(this.data[i], 'selected', false); } } - this.$broadcast('cancelSelected', ori); +// this.$broadcast('cancelSelected', ori); + this.broadcast('Tree', 'cancelSelected', ori); } this.$nextTick(() => { this.$emit('on-select-change', this.getSelectedNodes()); }); }); this.$on('cancelSelected', ori => { - this.$broadcast('cancelSelected', ori); +// this.$broadcast('cancelSelected', ori); + this.broadcast('Tree', 'cancelSelected', ori); if (this !== ori) { for (let i = 0; i < this.data.length; i++) { - this.$set(`data[${i}].selected`, false); +// this.$set(`data[${i}].selected`, false); + this.$set(this.data[i], 'selected', false); } } }); - this.$on('parentChecked', (status, key) => { - if (this.key == key || this.key.startsWith(key + '.')) { +// this.$on('parentChecked', (status, name) => { + this.$on('parentChecked', (params) => { + const status = params.status; + const name = params.name; + + if (this.name == name || this.name.startsWith(name + '.')) { for (let i = 0; i < this.data.length; i++) { - this.$set(`data[${i}].checked`, status); - this.$set(`data[${i}].childrenCheckedStatus`, status ? 2 : 0); +// this.$set(`data[${i}].checked`, status); + this.$set(this.data[i], 'checked', status); +// this.$set(`data[${i}].childrenCheckedStatus`, status ? 2 : 0); + this.$set(this.data[i], 'childrenCheckedStatus', status ? 2 : 0); } - this.$broadcast('parentChecked', status, key); +// this.$broadcast('parentChecked', status, name); + this.broadcast('Tree', 'parentChecked', { + status: status, + name: name + }); } }); - this.$on('childChecked', (ori, key) => { - if (this.key === '0') { +// this.$on('childChecked', (ori, name) => { + this.$on('childChecked', (params) => { + const ori = params.ori; + const name = params.name; + + if (this.name === '0') { this.$nextTick(() => { this.$emit('on-check-change', this.getCheckedNodes()); }); } if (this === ori) return; for (let [i,item] of this.data.entries()) { - if (this.key + '.' + i == key) { + if (this.name + '.' + i == name) { let temp = this.getChildrenCheckedStatus(item.children); if (temp != item.childrenCheckedStatus) { - this.$set(`data[${i}].checked`, !!temp); - this.$set(`data[${i}].childrenCheckedStatus`, temp); - if (this.key !== '0') this.$dispatch('childChecked', this, this.key); +// this.$set(`data[${i}].checked`, !!temp); + this.$set(this.data[i], 'checked', !!temp); +// this.$set(`data[${i}].childrenCheckedStatus`, temp); + this.$set(this.data[i], 'childrenCheckedStatus', temp); +// if (this.name !== '0') this.$dispatch('childChecked', this, this.name); + if (this.name !== '0') this.dispatch('Tree', 'childChecked', { + ori: this, + name: this.name + }); } } } diff --git a/src/components/upload/upload-list.vue b/src/components/upload/upload-list.vue index dce4ee8..2e86663 100644 --- a/src/components/upload/upload-list.vue +++ b/src/components/upload/upload-list.vue @@ -28,6 +28,7 @@ const prefixCls = 'ivu-upload'; export default { + name: 'UploadList', components: { Icon, Progress }, props: { files: { diff --git a/src/components/upload/upload.vue b/src/components/upload/upload.vue index e51dcb4..232889d 100644 --- a/src/components/upload/upload.vue +++ b/src/components/upload/upload.vue @@ -31,6 +31,7 @@ const prefixCls = 'ivu-upload'; export default { + name: 'Upload', components: { UploadList }, props: { action: { diff --git a/src/index.js b/src/index.js index de48edf..183da3e 100644 --- a/src/index.js +++ b/src/index.js @@ -40,7 +40,7 @@ import Timeline from './components/timeline'; // import TimePicker from './components/time-picker'; // import Tooltip from './components/tooltip'; // import Transfer from './components/transfer'; -// import Tree from './components/tree'; +import Tree from './components/tree'; import Upload from './components/upload'; import { Row, Col } from './components/grid'; // import { Select, Option, OptionGroup } from './components/select'; @@ -108,7 +108,7 @@ const iview = { // TimePicker, // Tooltip, // Transfer, - // Tree, + Tree, Upload }; diff --git a/src/mixins/emitter.js b/src/mixins/emitter.js new file mode 100755 index 0000000..422f5ac --- /dev/null +++ b/src/mixins/emitter.js @@ -0,0 +1,33 @@ +function broadcast(componentName, eventName, params) { + this.$children.forEach(child => { + const name = child.$options.name; + + if (name === componentName) { + child.$emit.apply(child, [eventName].concat(params)); + } else { + broadcast.apply(child, [componentName, eventName].concat([params])); + } + }); +} +export default { + methods: { + dispatch(componentName, eventName, params) { + let parent = this.$parent || this.$root; + let name = parent.$options.name; + + while (parent && (!name || name !== componentName)) { + parent = parent.$parent; + + if (parent) { + name = parent.$options.name; + } + } + if (parent) { + parent.$emit.apply(parent, [eventName].concat(params)); + } + }, + broadcast(componentName, eventName, params) { + broadcast.call(this, componentName, eventName, params); + } + } +}; \ No newline at end of file diff --git a/test/app.vue b/test/app.vue index 0c01427..0e41a13 100644 --- a/test/app.vue +++ b/test/app.vue @@ -30,6 +30,7 @@ li + li { border-left: solid 1px #bbb; padding-left: 10px; margin-left: 10px; }
  • Upload
  • Collapse
  • Carousel
  • +
  • Tree
  • diff --git a/test/main.js b/test/main.js index 36ab8d9..f5dc1bf 100644 --- a/test/main.js +++ b/test/main.js @@ -84,6 +84,10 @@ const router = new VueRouter({ { path: '/carousel', component: require('./routers/carousel.vue') + }, + { + path: '/tree', + component: require('./routers/tree.vue') } ] }); diff --git a/test/routers/tree.vue b/test/routers/tree.vue index 84bd633..8cc9a51 100644 --- a/test/routers/tree.vue +++ b/test/routers/tree.vue @@ -1,10 +1,12 @@