Commit 34ee7b4a14b4eb6db62def19528fe2c9c0758c13

Authored by 梁灏
1 parent bb71140e

support Tree & add dispatch and broadcast methods

support Tree,many component add name $option;add dispatch and broadcast
methods, mixins it
CHANGE.md
... ... @@ -21,4 +21,6 @@ class 改为了 className
21 21 ### Collapse
22 22 废弃 activeKey,使用 v-model,key 是保留的,更名为 name
23 23 ### Carousel
24   -废弃 activeIndex,使用 v-model,v-for="n in slides.length",Vue2的数字循环,是从1开始的
25 24 \ No newline at end of file
  25 +废弃 activeIndex,使用 v-model,v-for="n in slides.length",Vue2的数字循环,是从1开始的
  26 +### Tree
  27 +废弃 data,改为 value,使用 v-model,key 更名为 name
26 28 \ No newline at end of file
... ...
README.md
... ... @@ -48,7 +48,7 @@
48 48 - [ ] Tooltip
49 49 - [ ] Poptip
50 50 - [x] Carousel
51   -- [ ] Tree
  51 +- [x] Tree
52 52 - [ ] Menu
53 53 - [ ] Tabs
54 54 - [ ] Dropdown
... ...
src/components/alert/alert.vue
... ... @@ -23,6 +23,7 @@
23 23 const prefixCls = 'ivu-alert';
24 24  
25 25 export default {
  26 + name: 'Alert',
26 27 components: { Icon },
27 28 props: {
28 29 type: {
... ...
src/components/badge/badge.vue
... ... @@ -12,6 +12,7 @@
12 12 const prefixCls = 'ivu-badge';
13 13  
14 14 export default {
  15 + name: 'Badge',
15 16 props: {
16 17 count: [Number, String],
17 18 dot: {
... ...
src/components/button/button-group.vue
... ... @@ -9,7 +9,7 @@
9 9 const prefixCls = 'ivu-btn-group';
10 10  
11 11 export default {
12   - name: 'buttonGroup',
  12 + name: 'ButtonGroup',
13 13 props: {
14 14 size: {
15 15 validator (value) {
... ...
src/components/cascader/cascader.vue
... ... @@ -36,6 +36,7 @@
36 36 const prefixCls = 'ivu-cascader';
37 37  
38 38 export default {
  39 + name: 'Cascader',
39 40 components: { iInput, Dropdown, Icon, Caspanel },
40 41 directives: { clickoutside },
41 42 props: {
... ...
src/components/checkbox/checkbox-group.vue
... ... @@ -7,7 +7,7 @@
7 7 const prefixCls = 'ivu-checkbox-group';
8 8  
9 9 export default {
10   - name: 'checkboxGroup',
  10 + name: 'CheckboxGroup',
11 11 props: {
12 12 value: {
13 13 type: Array,
... ...
src/components/circle/circle.vue
... ... @@ -15,6 +15,7 @@
15 15 const prefixCls = 'ivu-chart-circle';
16 16  
17 17 export default {
  18 + name: 'Circle',
18 19 props: {
19 20 percent: {
20 21 type: Number,
... ...
src/components/collapse/collapse.vue
... ... @@ -7,6 +7,7 @@
7 7 const prefixCls = 'ivu-collapse';
8 8  
9 9 export default {
  10 + name: 'Collapse',
10 11 props: {
11 12 accordion: {
12 13 type: Boolean,
... ...
src/components/collapse/panel.vue
... ... @@ -14,6 +14,7 @@
14 14 const prefixCls = 'ivu-collapse';
15 15  
16 16 export default {
  17 + name: 'Panel',
17 18 components: { Icon },
18 19 props: {
19 20 name: {
... ...
src/components/grid/col.vue
... ... @@ -7,6 +7,7 @@
7 7 const prefixCls = 'ivu-col';
8 8  
9 9 export default {
  10 + name: 'iCol',
10 11 props: {
11 12 span: [Number, String],
12 13 order: [Number, String],
... ...
src/components/grid/row.vue
... ... @@ -9,6 +9,7 @@
9 9 const prefixCls = 'ivu-row';
10 10  
11 11 export default {
  12 + name: 'Row',
12 13 props: {
13 14 type: {
14 15 validator (value) {
... ...
src/components/input-number/input-number.vue
... ... @@ -61,6 +61,7 @@
61 61 }
62 62  
63 63 export default {
  64 + name: 'InputNumber',
64 65 props: {
65 66 max: {
66 67 type: Number,
... ...
src/components/radio/radio-group.vue
... ... @@ -9,7 +9,7 @@
9 9 const prefixCls = 'ivu-radio-group';
10 10  
11 11 export default {
12   - name: 'radioGroup',
  12 + name: 'RadioGroup',
13 13 props: {
14 14 value: {
15 15 type: [String, Number],
... ...
src/components/steps/step.vue
... ... @@ -20,6 +20,7 @@
20 20 const iconPrefixCls = 'ivu-icon';
21 21  
22 22 export default {
  23 + name: 'Step',
23 24 props: {
24 25 status: {
25 26 validator (value) {
... ...
src/components/steps/steps.vue
... ... @@ -9,6 +9,7 @@
9 9 const prefixCls = 'ivu-steps';
10 10  
11 11 export default {
  12 + name: 'Steps',
12 13 props: {
13 14 current: {
14 15 type: Number,
... ...
src/components/switch/switch.vue
... ... @@ -12,6 +12,7 @@
12 12 const prefixCls = 'ivu-switch';
13 13  
14 14 export default {
  15 + name: 'Switch',
15 16 props: {
16 17 value: {
17 18 type: Boolean,
... ...
src/components/tag/tag.vue
... ... @@ -12,6 +12,7 @@
12 12 const prefixCls = 'ivu-tag';
13 13  
14 14 export default {
  15 + name: 'Tag',
15 16 components: { Icon },
16 17 props: {
17 18 closable: {
... ...
src/components/timeline/timeline-item.vue
... ... @@ -11,6 +11,7 @@
11 11 const prefixCls = 'ivu-timeline';
12 12  
13 13 export default {
  14 + name: 'TimelineItem',
14 15 props: {
15 16 color: {
16 17 type: String,
... ...
src/components/timeline/timeline.vue
... ... @@ -7,6 +7,7 @@
7 7 const prefixCls = 'ivu-timeline';
8 8  
9 9 export default {
  10 + name: 'Timeline',
10 11 props: {
11 12 pending: {
12 13 type: Boolean,
... ...
src/components/tree/tree.vue
1 1 <template>
2 2 <ul :class="classes">
3   - <li v-for="item in data" :class="itemCls(item)">
4   - <span :class="arrowCls(item)" @click="setExpand(item.disabled, $index)">
  3 + <li v-for="(item, index) in data" :class="itemCls(item)">
  4 + <span :class="arrowCls(item)" @click="setExpand(item.disabled, index)">
5 5 <Icon type="arrow-right-b"></Icon>
6 6 </span>
7 7 <Checkbox
8 8 v-if="showCheckbox"
9   - :checked="item.checked && item.childrenCheckedStatus == 2"
  9 + :value="item.checked && item.childrenCheckedStatus == 2"
10 10 :disabled="item.disabled || item.disableCheckbox"
11 11 :indeterminate="item.checked && item.childrenCheckedStatus == 1"
12   - @click.prevent="setCheck(item.disabled||item.disableCheckbox,$index)"></Checkbox>
13   - <a :class="titleCls(item)" @click="setSelect(item.disabled, $index)">
  12 + @click.prevent="setCheck(item.disabled||item.disableCheckbox, index)"></Checkbox>
  13 + <a :class="titleCls(item)" @click="setSelect(item.disabled, index)">
14 14 <span :class="[prefixCls + '-title']" v-html="item.title"></span>
15 15 </a>
16   - <tree
17   - v-if="!item.isLeaf"
18   - v-show="item.expand"
19   - :class="expandCls(item)"
20   - :data.sync="item.children"
21   - :key="this.key+'.'+$index"
22   - :multiple="multiple"
23   - :show-checkbox="showCheckbox"
24   - transition="slide-up"></tree>
  16 + <transition name="slide-up">
  17 + <Tree
  18 + v-if="!item.isLeaf"
  19 + v-show="item.expand"
  20 + :class="expandCls(item)"
  21 + :value="item.children"
  22 + :name="item.name+'.'+index"
  23 + :multiple="multiple"
  24 + :show-checkbox="showCheckbox"></Tree>
  25 + </transition>
25 26 </li>
26 27 </ul>
27 28 </template>
... ... @@ -29,20 +30,22 @@
29 30 import Icon from '../icon/icon.vue';
30 31 import Checkbox from '../checkbox/checkbox.vue';
31 32 import { t } from '../../locale';
  33 + import emitter from '../../mixins/emitter';
32 34  
33 35 const prefixCls = 'ivu-tree';
34 36  
35 37 export default {
36   - name: 'tree',
  38 + name: 'Tree',
37 39 components: { Icon, Checkbox },
  40 + mixins: [ emitter ],
38 41 props: {
39   - data: {
  42 + value: {
40 43 type: Array,
41 44 default () {
42 45 return [];
43 46 }
44 47 },
45   - key: {
  48 + name: {
46 49 type: String,
47 50 default: '0'
48 51 },
... ... @@ -63,12 +66,13 @@
63 66 },
64 67 data () {
65 68 return {
66   - prefixCls: prefixCls
  69 + prefixCls: prefixCls,
  70 + data: this.value
67 71 };
68 72 },
69 73 computed: {
70 74 classes () {
71   - if (this.key === '0') {
  75 + if (this.name === '0') {
72 76 return this.prefixCls;
73 77 } else {
74 78 return `${this.prefixCls}-child-tree`;
... ... @@ -76,8 +80,11 @@
76 80 }
77 81 },
78 82 watch: {
79   - data(){
80   - if (this.key === '0') {
  83 + value (val) {
  84 + this.data = val;
  85 + },
  86 + data () {
  87 + if (this.name === '0') {
81 88 this.setKey();
82 89 this.preHandle();
83 90 }
... ... @@ -118,55 +125,82 @@
118 125 },
119 126 setKey () {
120 127 for (let i = 0; i < this.data.length; i++) {
121   - this.data[i].key = `${this.key}.${i}`;
  128 + this.data[i].name = `${this.name}.${i}`;
122 129 }
123 130 },
124 131 preHandle () {
125   - for (let [i,item] of this.data.entries()) {
  132 + for (let [i, item] of this.data.entries()) {
126 133 if (!item.children || !item.children.length) {
127   - this.$set(`data[${i}].isLeaf`, true);
128   - this.$set(`data[${i}].childrenCheckedStatus`, 2);
  134 +// this.$set(`data[${i}].isLeaf`, true);
  135 +// this.$set(`data[${i}].childrenCheckedStatus`, 2);
  136 + this.$set(this.data[i], 'isLeaf', true);
  137 + this.$set(this.data[i], 'childrenCheckedStatus', 2);
129 138 continue;
130 139 }
131 140 if (item.checked && !item.childrenCheckedStatus) {
132   - this.$set(`data[${i}].childrenCheckedStatus`, 2);
133   - this.$broadcast('parentChecked', true, `${this.key}.${i}`);
  141 +// this.$set(`data[${i}].childrenCheckedStatus`, 2);
  142 + this.$set(this.data[i], 'childrenCheckedStatus', 2);
  143 +// this.$broadcast('parentChecked', true, `${this.name}.${i}`);
  144 + this.broadcast('Tree', 'parentChecked', {
  145 + status: true,
  146 + name: `${this.name}.${i}`
  147 + });
134 148 } else {
135 149 let status = this.getChildrenCheckedStatus(item.children);
136   - this.$set(`data[${i}].childrenCheckedStatus`, status);
137   - if (status !== 0) this.$set(`data[${i}].checked`, true);
  150 +// this.$set(`data[${i}].childrenCheckedStatus`, status);
  151 + this.$set(this.data[i], 'childrenCheckedStatus', status);
  152 +// if (status !== 0) this.$set(`data[${i}].checked`, true);
  153 + if (status !== 0) this.$set(this.data[i], 'checked', true);
138 154 }
139 155 }
140 156 },
141 157 setExpand (disabled, index) {
142 158 if (!disabled) {
143   - this.$set(`data[${index}].expand`, !this.data[index].expand);
  159 +// this.$set(`data[${index}].expand`, !this.data[index].expand);
  160 + this.$set(this.data[index], 'expand', !this.data[index].expand);
144 161 }
145 162 },
146 163 setSelect (disabled, index) {
147 164 if (!disabled) {
148 165 const selected = !this.data[index].selected;
149 166 if (this.multiple || !selected) {
150   - this.$set(`data[${index}].selected`, selected);
  167 +// this.$set(`data[${index}].selected`, selected);
  168 + this.$set(this.data[index], 'selected', selected);
151 169 } else {
152 170 for (let i = 0; i < this.data.length; i++) {
153 171 if (i == index) {
154   - this.$set(`data[${i}].selected`, true);
  172 +// this.$set(`data[${i}].selected`, true);
  173 + this.$set(this.data[i], 'selected', true);
155 174 } else {
156   - this.$set(`data[${i}].selected`, false);
  175 +// this.$set(`data[${i}].selected`, false);
  176 + this.$set(this.data[i], 'selected', false);
157 177 }
158 178 }
159 179 }
160   - this.$dispatch('nodeSelected', this, selected);
  180 +// this.$dispatch('nodeSelected', this, selected);
  181 + this.dispatch('Tree', 'nodeSelected', {
  182 + ori: this,
  183 + selected: selected
  184 + })
161 185 }
162 186 },
163 187 setCheck (disabled, index) {
164 188 if (disabled) return;
165 189 const checked = !this.data[index].checked;
166   - this.$set(`data[${index}].checked`, checked);
167   - this.$set(`data[${index}].childrenCheckedStatus`, checked ? 2 : 0);
168   - this.$dispatch('childChecked', this, this.key);
169   - this.$broadcast('parentChecked', checked, `${this.key}.${index}`);
  190 +// this.$set(`data[${index}].checked`, checked);
  191 + this.$set(this.data[index], 'checked', checked);
  192 +// this.$set(`data[${index}].childrenCheckedStatus`, checked ? 2 : 0);
  193 + this.$set(this.data[index], 'childrenCheckedStatus', checked ? 2 : 0);
  194 +// this.$dispatch('childChecked', this, this.name);
  195 + this.dispatch('Tree', 'childChecked', {
  196 + ori: this,
  197 + name: this.name
  198 + });
  199 +// this.$broadcast('parentChecked', checked, `${this.name}.${index}`);
  200 + this.broadcast('Tree', 'parentChecked', {
  201 + status: checked,
  202 + name: `${this.name}.${index}`
  203 + });
170 204 },
171 205 getNodes (data, opt) {
172 206 data = data || this.data;
... ... @@ -215,55 +249,83 @@
215 249 }
216 250 }
217 251 },
218   - ready(){
  252 + mounted () {
219 253 this.setKey();
220 254 this.preHandle();
221 255  
222   - this.$on('nodeSelected', (ori, selected) => {
223   - if (this.key !== '0') return true;
  256 +// this.$on('nodeSelected', (ori, selected) => {
  257 + this.$on('nodeSelected', (params) => {
  258 + const ori = params.ori;
  259 + const selected = params.selected;
  260 +
  261 + if (this.name !== '0') return true;
224 262 if (!this.multiple && selected) {
225 263 if (this !== ori) {
226 264 for (let i = 0; i < this.data.length; i++) {
227   - this.$set(`data[${i}].selected`, false);
  265 +// this.$set(`data[${i}].selected`, false);
  266 + this.$set(this.data[i], 'selected', false);
228 267 }
229 268 }
230   - this.$broadcast('cancelSelected', ori);
  269 +// this.$broadcast('cancelSelected', ori);
  270 + this.broadcast('Tree', 'cancelSelected', ori);
231 271 }
232 272 this.$nextTick(() => {
233 273 this.$emit('on-select-change', this.getSelectedNodes());
234 274 });
235 275 });
236 276 this.$on('cancelSelected', ori => {
237   - this.$broadcast('cancelSelected', ori);
  277 +// this.$broadcast('cancelSelected', ori);
  278 + this.broadcast('Tree', 'cancelSelected', ori);
238 279 if (this !== ori) {
239 280 for (let i = 0; i < this.data.length; i++) {
240   - this.$set(`data[${i}].selected`, false);
  281 +// this.$set(`data[${i}].selected`, false);
  282 + this.$set(this.data[i], 'selected', false);
241 283 }
242 284 }
243 285 });
244   - this.$on('parentChecked', (status, key) => {
245   - if (this.key == key || this.key.startsWith(key + '.')) {
  286 +// this.$on('parentChecked', (status, name) => {
  287 + this.$on('parentChecked', (params) => {
  288 + const status = params.status;
  289 + const name = params.name;
  290 +
  291 + if (this.name == name || this.name.startsWith(name + '.')) {
246 292 for (let i = 0; i < this.data.length; i++) {
247   - this.$set(`data[${i}].checked`, status);
248   - this.$set(`data[${i}].childrenCheckedStatus`, status ? 2 : 0);
  293 +// this.$set(`data[${i}].checked`, status);
  294 + this.$set(this.data[i], 'checked', status);
  295 +// this.$set(`data[${i}].childrenCheckedStatus`, status ? 2 : 0);
  296 + this.$set(this.data[i], 'childrenCheckedStatus', status ? 2 : 0);
249 297 }
250   - this.$broadcast('parentChecked', status, key);
  298 +// this.$broadcast('parentChecked', status, name);
  299 + this.broadcast('Tree', 'parentChecked', {
  300 + status: status,
  301 + name: name
  302 + });
251 303 }
252 304 });
253   - this.$on('childChecked', (ori, key) => {
254   - if (this.key === '0') {
  305 +// this.$on('childChecked', (ori, name) => {
  306 + this.$on('childChecked', (params) => {
  307 + const ori = params.ori;
  308 + const name = params.name;
  309 +
  310 + if (this.name === '0') {
255 311 this.$nextTick(() => {
256 312 this.$emit('on-check-change', this.getCheckedNodes());
257 313 });
258 314 }
259 315 if (this === ori) return;
260 316 for (let [i,item] of this.data.entries()) {
261   - if (this.key + '.' + i == key) {
  317 + if (this.name + '.' + i == name) {
262 318 let temp = this.getChildrenCheckedStatus(item.children);
263 319 if (temp != item.childrenCheckedStatus) {
264   - this.$set(`data[${i}].checked`, !!temp);
265   - this.$set(`data[${i}].childrenCheckedStatus`, temp);
266   - if (this.key !== '0') this.$dispatch('childChecked', this, this.key);
  320 +// this.$set(`data[${i}].checked`, !!temp);
  321 + this.$set(this.data[i], 'checked', !!temp);
  322 +// this.$set(`data[${i}].childrenCheckedStatus`, temp);
  323 + this.$set(this.data[i], 'childrenCheckedStatus', temp);
  324 +// if (this.name !== '0') this.$dispatch('childChecked', this, this.name);
  325 + if (this.name !== '0') this.dispatch('Tree', 'childChecked', {
  326 + ori: this,
  327 + name: this.name
  328 + });
267 329 }
268 330 }
269 331 }
... ...
src/components/upload/upload-list.vue
... ... @@ -28,6 +28,7 @@
28 28 const prefixCls = 'ivu-upload';
29 29  
30 30 export default {
  31 + name: 'UploadList',
31 32 components: { Icon, Progress },
32 33 props: {
33 34 files: {
... ...
src/components/upload/upload.vue
... ... @@ -31,6 +31,7 @@
31 31 const prefixCls = 'ivu-upload';
32 32  
33 33 export default {
  34 + name: 'Upload',
34 35 components: { UploadList },
35 36 props: {
36 37 action: {
... ...
src/index.js
... ... @@ -40,7 +40,7 @@ import Timeline from &#39;./components/timeline&#39;;
40 40 // import TimePicker from './components/time-picker';
41 41 // import Tooltip from './components/tooltip';
42 42 // import Transfer from './components/transfer';
43   -// import Tree from './components/tree';
  43 +import Tree from './components/tree';
44 44 import Upload from './components/upload';
45 45 import { Row, Col } from './components/grid';
46 46 // import { Select, Option, OptionGroup } from './components/select';
... ... @@ -108,7 +108,7 @@ const iview = {
108 108 // TimePicker,
109 109 // Tooltip,
110 110 // Transfer,
111   - // Tree,
  111 + Tree,
112 112 Upload
113 113 };
114 114  
... ...
src/mixins/emitter.js 0 → 100755
  1 +function broadcast(componentName, eventName, params) {
  2 + this.$children.forEach(child => {
  3 + const name = child.$options.name;
  4 +
  5 + if (name === componentName) {
  6 + child.$emit.apply(child, [eventName].concat(params));
  7 + } else {
  8 + broadcast.apply(child, [componentName, eventName].concat([params]));
  9 + }
  10 + });
  11 +}
  12 +export default {
  13 + methods: {
  14 + dispatch(componentName, eventName, params) {
  15 + let parent = this.$parent || this.$root;
  16 + let name = parent.$options.name;
  17 +
  18 + while (parent && (!name || name !== componentName)) {
  19 + parent = parent.$parent;
  20 +
  21 + if (parent) {
  22 + name = parent.$options.name;
  23 + }
  24 + }
  25 + if (parent) {
  26 + parent.$emit.apply(parent, [eventName].concat(params));
  27 + }
  28 + },
  29 + broadcast(componentName, eventName, params) {
  30 + broadcast.call(this, componentName, eventName, params);
  31 + }
  32 + }
  33 +};
0 34 \ No newline at end of file
... ...
test/app.vue
... ... @@ -30,6 +30,7 @@ li + li { border-left: solid 1px #bbb; padding-left: 10px; margin-left: 10px; }
30 30 <li><router-link to="/upload">Upload</router-link></li>
31 31 <li><router-link to="/collapse">Collapse</router-link></li>
32 32 <li><router-link to="/carousel">Carousel</router-link></li>
  33 + <li><router-link to="/tree">Tree</router-link></li>
33 34 </ul>
34 35 </nav>
35 36 <router-view></router-view>
... ...
test/main.js
... ... @@ -84,6 +84,10 @@ const router = new VueRouter({
84 84 {
85 85 path: '/carousel',
86 86 component: require('./routers/carousel.vue')
  87 + },
  88 + {
  89 + path: '/tree',
  90 + component: require('./routers/tree.vue')
87 91 }
88 92 ]
89 93 });
... ...
test/routers/tree.vue
1 1 <template>
2   - <Tree
3   - :data.sync="treeData"
4   - :show-checkbox="true"
5   - :multiple="true"
6   - @on-select-change="selectFn"
7   - @on-check-change="checkFn"></Tree>
  2 + <div>
  3 + <Tree
  4 + v-model="treeData"
  5 + :show-checkbox="true"
  6 + :multiple="true"
  7 + @on-select-change="selectFn"
  8 + @on-check-change="checkFn"></Tree>
  9 + </div>
8 10 </template>
9 11 <script>
10 12 export default {
... ...