Commit e81207a2a24ef76d5298994ddd797a6a4c9192c1
1 parent
a861963b
update Tree
update Tree
Showing
7 changed files
with
459 additions
and
27 deletions
Show diff stats
src/components/tree/tree-node.vue deleted
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)"></span> | |
| 5 | + <span v-if="showCheckbox" :class="checkboxCls(item)" @click="setCheck(item.disabled||item.disableCheckbox,$index)"> | |
| 6 | + <span :class="[prefixCls + '-checkbox-inner']"></span> | |
| 7 | + </span> | |
| 8 | + <a :class="titleCls(item)" @click="setSelect(item.disabled, $index)"> | |
| 9 | + <span :class="[prefixCls + '-title']" v-html="item.title"></span> | |
| 10 | + </a> | |
| 11 | + <tree | |
| 12 | + v-if="!item.isLeaf" | |
| 13 | + v-show="item.expand" | |
| 14 | + :class="expandCls(item)" | |
| 15 | + :data.sync="item.node" | |
| 16 | + :key="this.key+'.'+$index" | |
| 17 | + :multiple="multiple" | |
| 18 | + :show-checkbox="showCheckbox" | |
| 19 | + transition="slide-up"></tree> | |
| 20 | + </li> | |
| 21 | + </ul> | |
| 3 | 22 | </template> |
| 4 | 23 | <script> |
| 24 | + import { t } from '../../locale'; | |
| 25 | + | |
| 26 | + const prefixCls = 'ivu-tree'; | |
| 27 | + | |
| 5 | 28 | export default { |
| 6 | - props: {}, | |
| 29 | + name: 'tree', | |
| 30 | + props: { | |
| 31 | + data: { | |
| 32 | + type: Array, | |
| 33 | + default () { | |
| 34 | + return []; | |
| 35 | + } | |
| 36 | + }, | |
| 37 | + key: { | |
| 38 | + type: String, | |
| 39 | + default: '0' | |
| 40 | + }, | |
| 41 | + multiple: { | |
| 42 | + type: Boolean, | |
| 43 | + default: false | |
| 44 | + }, | |
| 45 | + showCheckbox: { | |
| 46 | + type: Boolean, | |
| 47 | + default: false | |
| 48 | + }, | |
| 49 | + onSelect: { | |
| 50 | + type: Function, | |
| 51 | + default () { | |
| 52 | + return {}; | |
| 53 | + } | |
| 54 | + }, | |
| 55 | + onCheck: { | |
| 56 | + type: Function, | |
| 57 | + default () { | |
| 58 | + return {}; | |
| 59 | + } | |
| 60 | + }, | |
| 61 | + emptyText: { | |
| 62 | + type: String, | |
| 63 | + default () { | |
| 64 | + return t('i.tree.emptyText'); | |
| 65 | + } | |
| 66 | + } | |
| 67 | + }, | |
| 7 | 68 | data () { |
| 8 | - return {}; | |
| 69 | + return { | |
| 70 | + prefixCls: prefixCls | |
| 71 | + }; | |
| 9 | 72 | }, |
| 10 | - computed: {}, | |
| 11 | - methods: {} | |
| 73 | + computed: { | |
| 74 | + classes () { | |
| 75 | + if (this.key === '0') { | |
| 76 | + return this.prefixCls; | |
| 77 | + } else { | |
| 78 | + return `${this.prefixCls}-child-tree`; | |
| 79 | + } | |
| 80 | + } | |
| 81 | + }, | |
| 82 | + watch: { | |
| 83 | + data(){ | |
| 84 | + if (this.key === '0') { | |
| 85 | + this.setKey(); | |
| 86 | + this.preHandle(); | |
| 87 | + } | |
| 88 | + } | |
| 89 | + }, | |
| 90 | + methods: { | |
| 91 | + itemCls (item) { | |
| 92 | + return [ | |
| 93 | + { | |
| 94 | + [`${prefixCls}-item-disabled`]: item.disabled | |
| 95 | + } | |
| 96 | + ]; | |
| 97 | + }, | |
| 98 | + arrowCls (item) { | |
| 99 | + return [ | |
| 100 | + `${this.prefixCls}-switcher`, | |
| 101 | + { | |
| 102 | + [`${this.prefixCls}-switcher-disabled`]: item.disabled, | |
| 103 | + [`${this.prefixCls}-noline_close`]: !item.expand && !item.isLeaf, | |
| 104 | + [`${this.prefixCls}-noline_open`]: item.expand && !item.isLeaf, | |
| 105 | + [`${this.prefixCls}-switcher-noop`]: item.isLeaf | |
| 106 | + } | |
| 107 | + ]; | |
| 108 | + }, | |
| 109 | + checkboxCls (item) { | |
| 110 | + return [ | |
| 111 | + `${this.prefixCls}-checkbox`, | |
| 112 | + { | |
| 113 | + [`${this.prefixCls}-checkbox-disabled`]: item.disabled || item.disableCheckbox, | |
| 114 | + [`${this.prefixCls}-checkbox-checked`]: item.checked && item.childrenCheckedStatus == 2, | |
| 115 | + [`${this.prefixCls}-checkbox-indeterminate`]: item.checked && item.childrenCheckedStatus == 1 | |
| 116 | + } | |
| 117 | + ]; | |
| 118 | + }, | |
| 119 | + titleCls (item) { | |
| 120 | + return [ | |
| 121 | + { | |
| 122 | + [`${this.prefixCls}-node-selected`]: item.selected | |
| 123 | + } | |
| 124 | + ]; | |
| 125 | + }, | |
| 126 | + expandCls (item) { | |
| 127 | + return [ | |
| 128 | + { | |
| 129 | + [`${this.prefixCls}-child-tree-open`]: item.expand | |
| 130 | + } | |
| 131 | + ]; | |
| 132 | + }, | |
| 133 | + setKey () { | |
| 134 | + for (let i = 0; i < this.data.length; i++) { | |
| 135 | + this.data[i].key = `${this.key}.${i}`; | |
| 136 | + } | |
| 137 | + }, | |
| 138 | + preHandle(){ | |
| 139 | + for (let [i,item] of this.data.entries()) { | |
| 140 | + if (!item.node || !item.node.length) { | |
| 141 | + this.$set(`data[${i}].isLeaf`, true); | |
| 142 | + this.$set(`data[${i}].childrenCheckedStatus`, 2); | |
| 143 | + continue; | |
| 144 | + } | |
| 145 | + if (item.checked && !item.childrenCheckedStatus) { | |
| 146 | + this.$set(`data[${i}].childrenCheckedStatus`, 2); | |
| 147 | + this.$broadcast('parentChecked', true, `${this.key}.${i}`); | |
| 148 | + } else { | |
| 149 | + let status = this.getChildrenCheckedStatus(item.node); | |
| 150 | + this.$set(`data[${i}].childrenCheckedStatus`, status); | |
| 151 | + if (status !== 0) this.$set(`data[${i}].checked`, true); | |
| 152 | + } | |
| 153 | + } | |
| 154 | + }, | |
| 155 | + setExpand(disabled, index){ | |
| 156 | + if (!disabled) { | |
| 157 | + this.$set(`data[${index}].expand`, !this.data[index].expand); | |
| 158 | + } | |
| 159 | + }, | |
| 160 | + setSelect(disabled, index){ | |
| 161 | + if (!disabled) { | |
| 162 | + const selected = !this.data[index].selected; | |
| 163 | + if (this.multiple || !selected) { | |
| 164 | + this.$set(`data[${index}].selected`, selected); | |
| 165 | + } else { | |
| 166 | + for (let i = 0; i < this.data.length; i++) { | |
| 167 | + if (i == index) { | |
| 168 | + this.$set(`data[${i}].selected`, true); | |
| 169 | + } else { | |
| 170 | + this.$set(`data[${i}].selected`, false); | |
| 171 | + } | |
| 172 | + } | |
| 173 | + } | |
| 174 | + this.$dispatch('nodeSelected', this, selected); | |
| 175 | + } | |
| 176 | + }, | |
| 177 | + setCheck(disabled, index){ | |
| 178 | + if (disabled) return; | |
| 179 | + const checked = !this.data[index].checked; | |
| 180 | + this.$set(`data[${index}].checked`, checked); | |
| 181 | + this.$set(`data[${index}].childrenCheckedStatus`, checked ? 2 : 0); | |
| 182 | + this.$dispatch('childChecked', this, this.key); | |
| 183 | + this.$broadcast('parentChecked', checked, `${this.key}.${index}`); | |
| 184 | + }, | |
| 185 | + getNodes(data, opt){ | |
| 186 | + data = data || this.data; | |
| 187 | + let res = []; | |
| 188 | + for (let node of data) { | |
| 189 | + let tmp = true; | |
| 190 | + for (let [key, value] of Object.entries(opt)) { | |
| 191 | + if (node[key] != value) { | |
| 192 | + tmp = false; | |
| 193 | + break; | |
| 194 | + } | |
| 195 | + } | |
| 196 | + if (tmp) { | |
| 197 | + res.push(node); | |
| 198 | + } | |
| 199 | + if (node.node && node.node.length) { | |
| 200 | + res = res.concat(this.getNodes(node.node, opt)); | |
| 201 | + } | |
| 202 | + } | |
| 203 | + return res; | |
| 204 | + }, | |
| 205 | + getSelectedNodes(){ | |
| 206 | + return this.getNodes(this.data, {selected: true}); | |
| 207 | + }, | |
| 208 | + getCheckedNodes(){ | |
| 209 | + return this.getNodes(this.data, {checked: true, childrenCheckedStatus: 2}); | |
| 210 | + }, | |
| 211 | + getChildrenCheckedStatus(children){ | |
| 212 | + let checkNum = 0, child_childrenAllChecked = true; | |
| 213 | + for (let child of children) { | |
| 214 | + if (child.checked) { | |
| 215 | + checkNum++; | |
| 216 | + } | |
| 217 | + if (child.childrenCheckedStatus !== 2) { | |
| 218 | + child_childrenAllChecked = false; | |
| 219 | + } | |
| 220 | + } | |
| 221 | + // select all | |
| 222 | + if (checkNum == children.length) { | |
| 223 | + return child_childrenAllChecked ? 2 : 1; | |
| 224 | + // select some | |
| 225 | + } else if (checkNum > 0) { | |
| 226 | + return 1; | |
| 227 | + } else { | |
| 228 | + return 0; | |
| 229 | + } | |
| 230 | + } | |
| 231 | + }, | |
| 232 | + ready(){ | |
| 233 | + this.setKey(); | |
| 234 | + this.preHandle(); | |
| 235 | + | |
| 236 | + this.$on('nodeSelected', (ori, selected) => { | |
| 237 | + if (this.key !== '0') return true; | |
| 238 | + if (!this.multiple && selected) { | |
| 239 | + if (this !== ori) { | |
| 240 | + for (let i = 0; i < this.data.length; i++) { | |
| 241 | + this.$set(`data[${i}].selected`, false); | |
| 242 | + } | |
| 243 | + } | |
| 244 | + this.$broadcast('cancelSelected', ori); | |
| 245 | + } | |
| 246 | + if (this.onSelect) { | |
| 247 | + this.$nextTick(() => { | |
| 248 | + this.onSelect(this.getSelectedNodes()); | |
| 249 | + }); | |
| 250 | + } | |
| 251 | + }); | |
| 252 | + this.$on('cancelSelected', ori => { | |
| 253 | + this.$broadcast('cancelSelected', ori); | |
| 254 | + if (this !== ori) { | |
| 255 | + for (let i = 0; i < this.data.length; i++) { | |
| 256 | + this.$set(`data[${i}].selected`, false); | |
| 257 | + } | |
| 258 | + } | |
| 259 | + }); | |
| 260 | + this.$on('parentChecked', (status, key) => { | |
| 261 | + if (this.key == key || this.key.startsWith(key + '.')) { | |
| 262 | + for (let i = 0; i < this.data.length; i++) { | |
| 263 | + this.$set(`data[${i}].checked`, status); | |
| 264 | + this.$set(`data[${i}].childrenCheckedStatus`, status ? 2 : 0); | |
| 265 | + } | |
| 266 | + this.$broadcast('parentChecked', status, key); | |
| 267 | + } | |
| 268 | + }); | |
| 269 | + this.$on('childChecked', (ori, key) => { | |
| 270 | + if (this.key === '0' && this.onCheck) { | |
| 271 | + this.$nextTick(() => { | |
| 272 | + this.onCheck(this.getCheckedNodes()); | |
| 273 | + }); | |
| 274 | + } | |
| 275 | + if (this === ori) return; | |
| 276 | + for (let [i,item] of this.data.entries()) { | |
| 277 | + if (this.key + '.' + i == key) { | |
| 278 | + let temp = this.getChildrenCheckedStatus(item.node); | |
| 279 | + if (temp != item.childrenCheckedStatus) { | |
| 280 | + this.$set(`data[${i}].checked`, !!temp); | |
| 281 | + this.$set(`data[${i}].childrenCheckedStatus`, temp); | |
| 282 | + if (this.key !== '0') this.$dispatch('childChecked', this, this.key); | |
| 283 | + } | |
| 284 | + } | |
| 285 | + } | |
| 286 | + }); | |
| 287 | + } | |
| 12 | 288 | }; |
| 13 | 289 | </script> |
| 14 | 290 | \ No newline at end of file | ... | ... |
src/locale/lang/en-US.js
src/locale/lang/zh-CN.js
src/locale/lang/zh-TW.js
src/styles/components/tree.less
| 1 | 1 | @tree-prefix-cls: ~"@{css-prefix}tree"; |
| 2 | 2 | |
| 3 | 3 | .@{tree-prefix-cls} { |
| 4 | - | |
| 4 | + margin: 0; | |
| 5 | + padding: 5px; | |
| 6 | + font-size: 12px; | |
| 7 | + li { | |
| 8 | + padding: 0; | |
| 9 | + margin: 7px 0; | |
| 10 | + list-style: none; | |
| 11 | + white-space: nowrap; | |
| 12 | + outline: 0; | |
| 13 | + a[draggable], | |
| 14 | + a[draggable="true"] { | |
| 15 | + user-select: none; | |
| 16 | + /* Required to make elements draggable in old WebKit */ | |
| 17 | + -khtml-user-drag: element; | |
| 18 | + -webkit-user-drag: element; | |
| 19 | + } | |
| 20 | + &.drag-over { | |
| 21 | + > a[draggable] { | |
| 22 | + background-color: @primary-color; | |
| 23 | + color: white; | |
| 24 | + opacity: 0.8; | |
| 25 | + } | |
| 26 | + } | |
| 27 | + &.drag-over-gap-top { | |
| 28 | + > a[draggable] { | |
| 29 | + border-top: 2px @primary-color solid; | |
| 30 | + } | |
| 31 | + } | |
| 32 | + &.drag-over-gap-bottom { | |
| 33 | + > a[draggable] { | |
| 34 | + border-bottom: 2px @primary-color solid; | |
| 35 | + } | |
| 36 | + } | |
| 37 | + &.filter-node { | |
| 38 | + > a { | |
| 39 | + color: @error-color!important; | |
| 40 | + font-weight: bold!important; | |
| 41 | + } | |
| 42 | + } | |
| 43 | + ul { | |
| 44 | + margin: 0; | |
| 45 | + padding: 0 0 0 18px; | |
| 46 | + } | |
| 47 | + a { | |
| 48 | + display: inline-block; | |
| 49 | + padding: 1px 5px; | |
| 50 | + border-radius: 2px; | |
| 51 | + margin: 0; | |
| 52 | + cursor: pointer; | |
| 53 | + text-decoration: none; | |
| 54 | + vertical-align: top; | |
| 55 | + color: #666; | |
| 56 | + transition: all 0.3s ease; | |
| 57 | + &:hover { | |
| 58 | + background-color: tint(@primary-color, 90%); | |
| 59 | + } | |
| 60 | + &.@{tree-prefix-cls}-node-selected { | |
| 61 | + background-color: tint(@primary-color, 80%); | |
| 62 | + } | |
| 63 | + } | |
| 64 | + span { | |
| 65 | + &.@{tree-prefix-cls}-checkbox { | |
| 66 | + margin: 2px 4px 0 0; | |
| 67 | + } | |
| 68 | + &.@{tree-prefix-cls}-switcher, | |
| 69 | + &.@{tree-prefix-cls}-iconEle { | |
| 70 | + margin: 0; | |
| 71 | + width: 16px; | |
| 72 | + height: 16px; | |
| 73 | + line-height: 16px; | |
| 74 | + display: inline-block; | |
| 75 | + vertical-align: middle; | |
| 76 | + border: 0 none; | |
| 77 | + cursor: pointer; | |
| 78 | + outline: none; | |
| 79 | + } | |
| 80 | + &.@{tree-prefix-cls}-icon_loading { | |
| 81 | + &:after { | |
| 82 | + display: inline-block; | |
| 83 | + //.iconfont-font("\e6a1"); | |
| 84 | + animation: loadingCircle 1s infinite linear; | |
| 85 | + color: @primary-color; | |
| 86 | + } | |
| 87 | + } | |
| 88 | + &.@{tree-prefix-cls}-switcher { | |
| 89 | + &.@{tree-prefix-cls}-switcher-noop { | |
| 90 | + cursor: auto; | |
| 91 | + } | |
| 92 | + &.@{tree-prefix-cls}-roots_open, | |
| 93 | + &.@{tree-prefix-cls}-center_open, | |
| 94 | + &.@{tree-prefix-cls}-bottom_open, | |
| 95 | + &.@{tree-prefix-cls}-noline_open { | |
| 96 | + //.antTreeSwitcherIcon(); | |
| 97 | + } | |
| 98 | + &.@{tree-prefix-cls}-roots_close, | |
| 99 | + &.@{tree-prefix-cls}-center_close, | |
| 100 | + &.@{tree-prefix-cls}-bottom_close, | |
| 101 | + &.@{tree-prefix-cls}-noline_close { | |
| 102 | + //.antTreeSwitcherIcon(); | |
| 103 | + //.ie-rotate(3); | |
| 104 | + &:after { | |
| 105 | + transform: rotate(270deg) scale(0.5); | |
| 106 | + } | |
| 107 | + } | |
| 108 | + } | |
| 109 | + } | |
| 110 | + } | |
| 111 | + &-child-tree { | |
| 112 | + display: none; | |
| 113 | + &-open { | |
| 114 | + display: block; | |
| 115 | + } | |
| 116 | + } | |
| 117 | + &-treenode-disabled { | |
| 118 | + >span, | |
| 119 | + >a, | |
| 120 | + >a span { | |
| 121 | + color: #ccc; | |
| 122 | + cursor: not-allowed; | |
| 123 | + } | |
| 124 | + } | |
| 125 | + &-icon__open { | |
| 126 | + margin-right: 2px; | |
| 127 | + vertical-align: top; | |
| 128 | + } | |
| 129 | + &-icon__close { | |
| 130 | + margin-right: 2px; | |
| 131 | + vertical-align: top; | |
| 132 | + } | |
| 5 | 133 | } |
| 6 | 134 | \ No newline at end of file | ... | ... |
test/routers/tree.vue
| 1 | 1 | <template> |
| 2 | - | |
| 2 | + <Tree | |
| 3 | + :data.sync="treeData" | |
| 4 | + :checkable="true" | |
| 5 | + :multiple="true" | |
| 6 | + :on-select="selectFn" | |
| 7 | + :on-check="checkFn"></Tree> | |
| 3 | 8 | </template> |
| 4 | 9 | <script> |
| 5 | 10 | export default { |
| 6 | - props: {}, | |
| 7 | - data () { | |
| 8 | - return {}; | |
| 11 | + data: function() { | |
| 12 | + return { | |
| 13 | + treeData: [{ | |
| 14 | + title: 'parent 1', | |
| 15 | + selected: false, | |
| 16 | + node: [{ | |
| 17 | + title: 'parent 1-0', | |
| 18 | + expand: true, | |
| 19 | + disabled: true, | |
| 20 | + node: [{ | |
| 21 | + title: 'leaf', | |
| 22 | + disableCheckbox: true | |
| 23 | + }, { | |
| 24 | + title: 'leaf', | |
| 25 | + }] | |
| 26 | + }, { | |
| 27 | + title: 'parent 1-1', | |
| 28 | + checked: true, | |
| 29 | + node: [{ | |
| 30 | + title: '<span style="color: red">sss</span>', | |
| 31 | + }] | |
| 32 | + }] | |
| 33 | + }] | |
| 34 | + } | |
| 9 | 35 | }, |
| 10 | - computed: {}, | |
| 11 | - methods: {} | |
| 12 | - }; | |
| 13 | -</script> | |
| 14 | 36 | \ No newline at end of file |
| 37 | + methods: { | |
| 38 | + selectFn(data){ | |
| 39 | + console.log(data); | |
| 40 | + }, | |
| 41 | + checkFn(data){ | |
| 42 | + console.log(data); | |
| 43 | + } | |
| 44 | + } | |
| 45 | + } | |
| 46 | +</script> | ... | ... |