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 | <template> | 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 | </template> | 22 | </template> |
| 4 | <script> | 23 | <script> |
| 24 | + import { t } from '../../locale'; | ||
| 25 | + | ||
| 26 | + const prefixCls = 'ivu-tree'; | ||
| 27 | + | ||
| 5 | export default { | 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 | data () { | 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 | </script> | 289 | </script> |
| 14 | \ No newline at end of file | 290 | \ No newline at end of file |
src/locale/lang/en-US.js
| @@ -87,6 +87,9 @@ export default { | @@ -87,6 +87,9 @@ export default { | ||
| 87 | rate: { | 87 | rate: { |
| 88 | star: 'Star', | 88 | star: 'Star', |
| 89 | stars: 'Stars' | 89 | stars: 'Stars' |
| 90 | + }, | ||
| 91 | + tree: { | ||
| 92 | + emptyText: 'No Data' | ||
| 90 | } | 93 | } |
| 91 | } | 94 | } |
| 92 | }; | 95 | }; |
| 93 | \ No newline at end of file | 96 | \ No newline at end of file |
src/locale/lang/zh-CN.js
src/locale/lang/zh-TW.js
src/styles/components/tree.less
| 1 | @tree-prefix-cls: ~"@{css-prefix}tree"; | 1 | @tree-prefix-cls: ~"@{css-prefix}tree"; |
| 2 | 2 | ||
| 3 | .@{tree-prefix-cls} { | 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 | \ No newline at end of file | 134 | \ No newline at end of file |
test/routers/tree.vue
| 1 | <template> | 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 | </template> | 8 | </template> |
| 4 | <script> | 9 | <script> |
| 5 | export default { | 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 | \ No newline at end of file | 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> |