style:首页
Showing
8 changed files
with
1790 additions
and
5 deletions
src/components/CheckBox/checkbox.vue
0 → 100644
| 1 | <template> | ||
| 2 | <label class="el-checkbox" :class="[ | ||
| 3 | border && checkboxSize ? 'el-checkbox--' + checkboxSize : '', | ||
| 4 | { 'is-disabled': isDisabled }, | ||
| 5 | { 'is-bordered': border }, | ||
| 6 | { 'is-checked': isChecked } | ||
| 7 | ]" :id="id"> | ||
| 8 | <span class="el-checkbox__input" :class="{ | ||
| 9 | 'is-disabled': isDisabled, | ||
| 10 | 'is-checked': isChecked, | ||
| 11 | 'is-indeterminate': indeterminate, | ||
| 12 | 'is-focus': focus | ||
| 13 | }" :tabindex="indeterminate ? 0 : false" :role="indeterminate ? 'checkbox' : false" | ||
| 14 | :aria-checked="indeterminate ? 'mixed' : false"> | ||
| 15 | <span class="el-checkbox__inner"></span> | ||
| 16 | <input v-if="trueLabel || falseLabel" class="el-checkbox__original" type="checkbox" | ||
| 17 | :aria-hidden="indeterminate ? 'true' : 'false'" :name="name" :disabled="isDisabled" :true-value="trueLabel" | ||
| 18 | :false-value="falseLabel" v-model="model" @change="handleChange" @focus="focus = true" @blur="focus = false"> | ||
| 19 | <input v-else class="el-checkbox__original" type="checkbox" :aria-hidden="indeterminate ? 'true' : 'false'" | ||
| 20 | :disabled="isDisabled" :value="label" :name="name" v-model="model" @change="handleChange" @focus="focus = true" | ||
| 21 | @blur="focus = false"> | ||
| 22 | </span> | ||
| 23 | <span class="el-checkbox__label"> | ||
| 24 | <slot></slot> | ||
| 25 | </span> | ||
| 26 | </label> | ||
| 27 | </template> | ||
| 28 | <script> | ||
| 29 | import Emitter from 'element-ui/src/mixins/emitter'; | ||
| 30 | |||
| 31 | export default { | ||
| 32 | name: 'ElCheckbox', | ||
| 33 | |||
| 34 | mixins: [Emitter], | ||
| 35 | |||
| 36 | inject: { | ||
| 37 | elForm: { | ||
| 38 | default: '' | ||
| 39 | }, | ||
| 40 | elFormItem: { | ||
| 41 | default: '' | ||
| 42 | } | ||
| 43 | }, | ||
| 44 | |||
| 45 | componentName: 'ElCheckbox', | ||
| 46 | |||
| 47 | data () { | ||
| 48 | return { | ||
| 49 | selfModel: false, | ||
| 50 | focus: false, | ||
| 51 | isLimitExceeded: false | ||
| 52 | }; | ||
| 53 | }, | ||
| 54 | |||
| 55 | computed: { | ||
| 56 | model: { | ||
| 57 | get () { | ||
| 58 | return this.isGroup | ||
| 59 | ? this.store : this.value !== undefined | ||
| 60 | ? this.value : this.selfModel; | ||
| 61 | }, | ||
| 62 | |||
| 63 | set (val) { | ||
| 64 | if (this.isGroup) { | ||
| 65 | this.isLimitExceeded = false; | ||
| 66 | (this._checkboxGroup.min !== undefined && | ||
| 67 | val.length < this._checkboxGroup.min && | ||
| 68 | (this.isLimitExceeded = true)); | ||
| 69 | |||
| 70 | (this._checkboxGroup.max !== undefined && | ||
| 71 | val.length > this._checkboxGroup.max && | ||
| 72 | (this.isLimitExceeded = true)); | ||
| 73 | |||
| 74 | this.isLimitExceeded === false && | ||
| 75 | this.dispatch('ElCheckboxGroup', 'input', [val]); | ||
| 76 | } else { | ||
| 77 | this.$emit('input', val); | ||
| 78 | this.selfModel = val; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | }, | ||
| 82 | |||
| 83 | isChecked () { | ||
| 84 | if ({}.toString.call(this.model) === '[object Boolean]') { | ||
| 85 | return this.model; | ||
| 86 | } else if (Array.isArray(this.model)) { | ||
| 87 | return this.model.indexOf(this.label) > -1; | ||
| 88 | } else if (this.model !== null && this.model !== undefined) { | ||
| 89 | return this.model === this.trueLabel; | ||
| 90 | } | ||
| 91 | }, | ||
| 92 | |||
| 93 | isGroup () { | ||
| 94 | let parent = this.$parent; | ||
| 95 | while (parent) { | ||
| 96 | if (parent.$options.componentName !== 'ElCheckboxGroup') { | ||
| 97 | parent = parent.$parent; | ||
| 98 | } else { | ||
| 99 | this._checkboxGroup = parent; | ||
| 100 | return true; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | return false; | ||
| 104 | }, | ||
| 105 | |||
| 106 | store () { | ||
| 107 | return this._checkboxGroup ? this._checkboxGroup.value : this.value; | ||
| 108 | }, | ||
| 109 | |||
| 110 | /* used to make the isDisabled judgment under max/min props */ | ||
| 111 | isLimitDisabled () { | ||
| 112 | const { max, min } = this._checkboxGroup; | ||
| 113 | return !!(max || min) && | ||
| 114 | (this.model.length >= max && !this.isChecked) || | ||
| 115 | (this.model.length <= min && this.isChecked); | ||
| 116 | }, | ||
| 117 | |||
| 118 | isDisabled () { | ||
| 119 | return this.isGroup | ||
| 120 | ? this._checkboxGroup.disabled || this.disabled || (this.elForm || {}).disabled || this.isLimitDisabled | ||
| 121 | : this.disabled || (this.elForm || {}).disabled; | ||
| 122 | }, | ||
| 123 | |||
| 124 | _elFormItemSize () { | ||
| 125 | return (this.elFormItem || {}).elFormItemSize; | ||
| 126 | }, | ||
| 127 | |||
| 128 | checkboxSize () { | ||
| 129 | const temCheckboxSize = this.size || this._elFormItemSize || (this.$ELEMENT || {}).size; | ||
| 130 | return this.isGroup | ||
| 131 | ? this._checkboxGroup.checkboxGroupSize || temCheckboxSize | ||
| 132 | : temCheckboxSize; | ||
| 133 | } | ||
| 134 | }, | ||
| 135 | |||
| 136 | props: { | ||
| 137 | value: {}, | ||
| 138 | label: {}, | ||
| 139 | indeterminate: Boolean, | ||
| 140 | disabled: Boolean, | ||
| 141 | checked: Boolean, | ||
| 142 | name: String, | ||
| 143 | trueLabel: [String, Number], | ||
| 144 | falseLabel: [String, Number], | ||
| 145 | id: String, /* 当indeterminate为真时,为controls提供相关连的checkbox的id,表明元素间的控制关系*/ | ||
| 146 | controls: String, /* 当indeterminate为真时,为controls提供相关连的checkbox的id,表明元素间的控制关系*/ | ||
| 147 | border: Boolean, | ||
| 148 | size: String | ||
| 149 | }, | ||
| 150 | |||
| 151 | methods: { | ||
| 152 | addToStore () { | ||
| 153 | if ( | ||
| 154 | Array.isArray(this.model) && | ||
| 155 | this.model.indexOf(this.label) === -1 | ||
| 156 | ) { | ||
| 157 | this.model.push(this.label); | ||
| 158 | } else { | ||
| 159 | this.model = this.trueLabel || true; | ||
| 160 | } | ||
| 161 | }, | ||
| 162 | handleChange (ev) { | ||
| 163 | if (this.isLimitExceeded) return; | ||
| 164 | let value; | ||
| 165 | if (ev.target.checked) { | ||
| 166 | value = this.trueLabel === undefined ? true : this.trueLabel; | ||
| 167 | } else { | ||
| 168 | value = this.falseLabel === undefined ? false : this.falseLabel; | ||
| 169 | } | ||
| 170 | this.$emit('change', value, ev); | ||
| 171 | this.$nextTick(() => { | ||
| 172 | if (this.isGroup) { | ||
| 173 | this.dispatch('ElCheckboxGroup', 'change', [this._checkboxGroup.value]); | ||
| 174 | } | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | }, | ||
| 178 | |||
| 179 | created () { | ||
| 180 | this.checked && this.addToStore(); | ||
| 181 | }, | ||
| 182 | mounted () { // 为indeterminate元素 添加aria-controls 属性 | ||
| 183 | if (this.indeterminate) { | ||
| 184 | this.$el.setAttribute('aria-controls', this.controls); | ||
| 185 | } | ||
| 186 | }, | ||
| 187 | |||
| 188 | watch: { | ||
| 189 | value (value) { | ||
| 190 | this.dispatch('ElFormItem', 'el.form.change', value); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | }; | ||
| 194 | </script> |
src/components/Tree/src/model/node.js
0 → 100644
| 1 | import objectAssign from 'element-ui/src/utils/merge'; | ||
| 2 | import { markNodeData, NODE_KEY } from './util'; | ||
| 3 | import { arrayFindIndex } from 'element-ui/src/utils/util'; | ||
| 4 | |||
| 5 | export const getChildState = node => { | ||
| 6 | let all = true; | ||
| 7 | let none = true; | ||
| 8 | let allWithoutDisable = true; | ||
| 9 | for (let i = 0, j = node.length; i < j; i++) { | ||
| 10 | const n = node[i]; | ||
| 11 | if (n.checked !== true || n.indeterminate) { | ||
| 12 | all = false; | ||
| 13 | if (!n.disabled) { | ||
| 14 | allWithoutDisable = false; | ||
| 15 | } | ||
| 16 | } | ||
| 17 | if (n.checked !== false || n.indeterminate) { | ||
| 18 | none = false; | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | return { all, none, allWithoutDisable, half: !all && !none }; | ||
| 23 | }; | ||
| 24 | |||
| 25 | const reInitChecked = function(node) { | ||
| 26 | if (node.childNodes.length === 0 || node.loading) return; | ||
| 27 | |||
| 28 | const {all, none, half} = getChildState(node.childNodes); | ||
| 29 | if (all) { | ||
| 30 | node.checked = true; | ||
| 31 | node.indeterminate = false; | ||
| 32 | } else if (half) { | ||
| 33 | node.checked = false; | ||
| 34 | node.indeterminate = true; | ||
| 35 | } else if (none) { | ||
| 36 | node.checked = false; | ||
| 37 | node.indeterminate = false; | ||
| 38 | } | ||
| 39 | |||
| 40 | const parent = node.parent; | ||
| 41 | if (!parent || parent.level === 0) return; | ||
| 42 | |||
| 43 | if (!node.store.checkStrictly) { | ||
| 44 | reInitChecked(parent); | ||
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | const getPropertyFromData = function(node, prop) { | ||
| 49 | const props = node.store.props; | ||
| 50 | const data = node.data || {}; | ||
| 51 | const config = props[prop]; | ||
| 52 | |||
| 53 | if (typeof config === 'function') { | ||
| 54 | return config(data, node); | ||
| 55 | } else if (typeof config === 'string') { | ||
| 56 | return data[config]; | ||
| 57 | } else if (typeof config === 'undefined') { | ||
| 58 | const dataProp = data[prop]; | ||
| 59 | return dataProp === undefined ? '' : dataProp; | ||
| 60 | } | ||
| 61 | }; | ||
| 62 | |||
| 63 | let nodeIdSeed = 0; | ||
| 64 | |||
| 65 | export default class Node { | ||
| 66 | constructor(options) { | ||
| 67 | this.id = nodeIdSeed++; | ||
| 68 | this.text = null; | ||
| 69 | this.checked = false; | ||
| 70 | this.indeterminate = false; | ||
| 71 | this.data = null; | ||
| 72 | this.expanded = false; | ||
| 73 | this.parent = null; | ||
| 74 | this.visible = true; | ||
| 75 | this.isCurrent = false; | ||
| 76 | |||
| 77 | for (let name in options) { | ||
| 78 | if (options.hasOwnProperty(name)) { | ||
| 79 | this[name] = options[name]; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | // internal | ||
| 84 | this.level = 0; | ||
| 85 | this.loaded = false; | ||
| 86 | this.childNodes = []; | ||
| 87 | this.loading = false; | ||
| 88 | |||
| 89 | if (this.parent) { | ||
| 90 | this.level = this.parent.level + 1; | ||
| 91 | } | ||
| 92 | |||
| 93 | const store = this.store; | ||
| 94 | if (!store) { | ||
| 95 | throw new Error('[Node]store is required!'); | ||
| 96 | } | ||
| 97 | store.registerNode(this); | ||
| 98 | |||
| 99 | const props = store.props; | ||
| 100 | if (props && typeof props.isLeaf !== 'undefined') { | ||
| 101 | const isLeaf = getPropertyFromData(this, 'isLeaf'); | ||
| 102 | if (typeof isLeaf === 'boolean') { | ||
| 103 | this.isLeafByUser = isLeaf; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | if (store.lazy !== true && this.data) { | ||
| 108 | this.setData(this.data); | ||
| 109 | |||
| 110 | if (store.defaultExpandAll) { | ||
| 111 | this.expanded = true; | ||
| 112 | } | ||
| 113 | } else if (this.level > 0 && store.lazy && store.defaultExpandAll) { | ||
| 114 | this.expand(); | ||
| 115 | } | ||
| 116 | if (!Array.isArray(this.data)) { | ||
| 117 | markNodeData(this, this.data); | ||
| 118 | } | ||
| 119 | if (!this.data) return; | ||
| 120 | const defaultExpandedKeys = store.defaultExpandedKeys; | ||
| 121 | const key = store.key; | ||
| 122 | if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) { | ||
| 123 | this.expand(null, store.autoExpandParent); | ||
| 124 | } | ||
| 125 | |||
| 126 | if (key && store.currentNodeKey !== undefined && this.key === store.currentNodeKey) { | ||
| 127 | store.currentNode = this; | ||
| 128 | store.currentNode.isCurrent = true; | ||
| 129 | } | ||
| 130 | |||
| 131 | if (store.lazy) { | ||
| 132 | store._initDefaultCheckedNode(this); | ||
| 133 | } | ||
| 134 | |||
| 135 | this.updateLeafState(); | ||
| 136 | } | ||
| 137 | |||
| 138 | setData(data) { | ||
| 139 | if (!Array.isArray(data)) { | ||
| 140 | markNodeData(this, data); | ||
| 141 | } | ||
| 142 | |||
| 143 | this.data = data; | ||
| 144 | this.childNodes = []; | ||
| 145 | |||
| 146 | let children; | ||
| 147 | if (this.level === 0 && this.data instanceof Array) { | ||
| 148 | children = this.data; | ||
| 149 | } else { | ||
| 150 | children = getPropertyFromData(this, 'children') || []; | ||
| 151 | } | ||
| 152 | |||
| 153 | for (let i = 0, j = children.length; i < j; i++) { | ||
| 154 | this.insertChild({ data: children[i] }); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | get label() { | ||
| 159 | return getPropertyFromData(this, 'label'); | ||
| 160 | } | ||
| 161 | |||
| 162 | get key() { | ||
| 163 | const nodeKey = this.store.key; | ||
| 164 | if (this.data) return this.data[nodeKey]; | ||
| 165 | return null; | ||
| 166 | } | ||
| 167 | |||
| 168 | get disabled() { | ||
| 169 | return getPropertyFromData(this, 'disabled'); | ||
| 170 | } | ||
| 171 | |||
| 172 | get nextSibling() { | ||
| 173 | const parent = this.parent; | ||
| 174 | if (parent) { | ||
| 175 | const index = parent.childNodes.indexOf(this); | ||
| 176 | if (index > -1) { | ||
| 177 | return parent.childNodes[index + 1]; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | return null; | ||
| 181 | } | ||
| 182 | |||
| 183 | get previousSibling() { | ||
| 184 | const parent = this.parent; | ||
| 185 | if (parent) { | ||
| 186 | const index = parent.childNodes.indexOf(this); | ||
| 187 | if (index > -1) { | ||
| 188 | return index > 0 ? parent.childNodes[index - 1] : null; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | return null; | ||
| 192 | } | ||
| 193 | |||
| 194 | contains(target, deep = true) { | ||
| 195 | const walk = function(parent) { | ||
| 196 | const children = parent.childNodes || []; | ||
| 197 | let result = false; | ||
| 198 | for (let i = 0, j = children.length; i < j; i++) { | ||
| 199 | const child = children[i]; | ||
| 200 | if (child === target || (deep && walk(child))) { | ||
| 201 | result = true; | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | return result; | ||
| 206 | }; | ||
| 207 | |||
| 208 | return walk(this); | ||
| 209 | } | ||
| 210 | |||
| 211 | remove() { | ||
| 212 | const parent = this.parent; | ||
| 213 | if (parent) { | ||
| 214 | parent.removeChild(this); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | insertChild(child, index, batch) { | ||
| 219 | if (!child) throw new Error('insertChild error: child is required.'); | ||
| 220 | |||
| 221 | if (!(child instanceof Node)) { | ||
| 222 | if (!batch) { | ||
| 223 | const children = this.getChildren(true) || []; | ||
| 224 | if (children.indexOf(child.data) === -1) { | ||
| 225 | if (typeof index === 'undefined' || index < 0) { | ||
| 226 | children.push(child.data); | ||
| 227 | } else { | ||
| 228 | children.splice(index, 0, child.data); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | objectAssign(child, { | ||
| 233 | parent: this, | ||
| 234 | store: this.store | ||
| 235 | }); | ||
| 236 | child = new Node(child); | ||
| 237 | } | ||
| 238 | |||
| 239 | child.level = this.level + 1; | ||
| 240 | |||
| 241 | if (typeof index === 'undefined' || index < 0) { | ||
| 242 | this.childNodes.push(child); | ||
| 243 | } else { | ||
| 244 | this.childNodes.splice(index, 0, child); | ||
| 245 | } | ||
| 246 | |||
| 247 | this.updateLeafState(); | ||
| 248 | } | ||
| 249 | |||
| 250 | insertBefore(child, ref) { | ||
| 251 | let index; | ||
| 252 | if (ref) { | ||
| 253 | index = this.childNodes.indexOf(ref); | ||
| 254 | } | ||
| 255 | this.insertChild(child, index); | ||
| 256 | } | ||
| 257 | |||
| 258 | insertAfter(child, ref) { | ||
| 259 | let index; | ||
| 260 | if (ref) { | ||
| 261 | index = this.childNodes.indexOf(ref); | ||
| 262 | if (index !== -1) index += 1; | ||
| 263 | } | ||
| 264 | this.insertChild(child, index); | ||
| 265 | } | ||
| 266 | |||
| 267 | removeChild(child) { | ||
| 268 | const children = this.getChildren() || []; | ||
| 269 | const dataIndex = children.indexOf(child.data); | ||
| 270 | if (dataIndex > -1) { | ||
| 271 | children.splice(dataIndex, 1); | ||
| 272 | } | ||
| 273 | |||
| 274 | const index = this.childNodes.indexOf(child); | ||
| 275 | |||
| 276 | if (index > -1) { | ||
| 277 | this.store && this.store.deregisterNode(child); | ||
| 278 | child.parent = null; | ||
| 279 | this.childNodes.splice(index, 1); | ||
| 280 | } | ||
| 281 | |||
| 282 | this.updateLeafState(); | ||
| 283 | } | ||
| 284 | |||
| 285 | removeChildByData(data) { | ||
| 286 | let targetNode = null; | ||
| 287 | |||
| 288 | for (let i = 0; i < this.childNodes.length; i++) { | ||
| 289 | if (this.childNodes[i].data === data) { | ||
| 290 | targetNode = this.childNodes[i]; | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | if (targetNode) { | ||
| 296 | this.removeChild(targetNode); | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | expand(callback, expandParent) { | ||
| 301 | const done = () => { | ||
| 302 | if (expandParent) { | ||
| 303 | let parent = this.parent; | ||
| 304 | while (parent.level > 0) { | ||
| 305 | parent.expanded = true; | ||
| 306 | parent = parent.parent; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | this.expanded = true; | ||
| 310 | if (callback) callback(); | ||
| 311 | }; | ||
| 312 | |||
| 313 | if (this.shouldLoadData()) { | ||
| 314 | this.loadData((data) => { | ||
| 315 | if (data instanceof Array) { | ||
| 316 | if (this.checked) { | ||
| 317 | this.setChecked(true, true); | ||
| 318 | } else if (!this.store.checkStrictly) { | ||
| 319 | reInitChecked(this); | ||
| 320 | } | ||
| 321 | done(); | ||
| 322 | } | ||
| 323 | }); | ||
| 324 | } else { | ||
| 325 | done(); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | doCreateChildren(array, defaultProps = {}) { | ||
| 330 | array.forEach((item) => { | ||
| 331 | this.insertChild(objectAssign({ data: item }, defaultProps), undefined, true); | ||
| 332 | }); | ||
| 333 | } | ||
| 334 | |||
| 335 | collapse() { | ||
| 336 | this.expanded = false; | ||
| 337 | } | ||
| 338 | |||
| 339 | shouldLoadData() { | ||
| 340 | return this.store.lazy === true && this.store.load && !this.loaded; | ||
| 341 | } | ||
| 342 | |||
| 343 | updateLeafState() { | ||
| 344 | if (this.store.lazy === true && this.loaded !== true && typeof this.isLeafByUser !== 'undefined') { | ||
| 345 | this.isLeaf = this.isLeafByUser; | ||
| 346 | return; | ||
| 347 | } | ||
| 348 | const childNodes = this.childNodes; | ||
| 349 | if (!this.store.lazy || (this.store.lazy === true && this.loaded === true)) { | ||
| 350 | this.isLeaf = !childNodes || childNodes.length === 0; | ||
| 351 | return; | ||
| 352 | } | ||
| 353 | this.isLeaf = false; | ||
| 354 | } | ||
| 355 | |||
| 356 | setChecked(value, deep, recursion, passValue) { | ||
| 357 | this.indeterminate = value === 'half'; | ||
| 358 | this.checked = value === true; | ||
| 359 | |||
| 360 | if (this.store.checkStrictly) return; | ||
| 361 | |||
| 362 | if (!(this.shouldLoadData() && !this.store.checkDescendants)) { | ||
| 363 | let { all, allWithoutDisable } = getChildState(this.childNodes); | ||
| 364 | |||
| 365 | if (!this.isLeaf && (!all && allWithoutDisable)) { | ||
| 366 | this.checked = false; | ||
| 367 | value = false; | ||
| 368 | } | ||
| 369 | |||
| 370 | const handleDescendants = () => { | ||
| 371 | if (deep) { | ||
| 372 | const childNodes = this.childNodes; | ||
| 373 | for (let i = 0, j = childNodes.length; i < j; i++) { | ||
| 374 | const child = childNodes[i]; | ||
| 375 | passValue = passValue || value !== false; | ||
| 376 | const isCheck = child.disabled ? child.checked : passValue; | ||
| 377 | child.setChecked(isCheck, deep, true, passValue); | ||
| 378 | } | ||
| 379 | const { half, all } = getChildState(childNodes); | ||
| 380 | if (!all) { | ||
| 381 | this.checked = all; | ||
| 382 | this.indeterminate = half; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | }; | ||
| 386 | |||
| 387 | if (this.shouldLoadData()) { | ||
| 388 | // Only work on lazy load data. | ||
| 389 | this.loadData(() => { | ||
| 390 | handleDescendants(); | ||
| 391 | reInitChecked(this); | ||
| 392 | }, { | ||
| 393 | checked: value !== false | ||
| 394 | }); | ||
| 395 | return; | ||
| 396 | } else { | ||
| 397 | handleDescendants(); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | const parent = this.parent; | ||
| 402 | if (!parent || parent.level === 0) return; | ||
| 403 | |||
| 404 | if (!recursion) { | ||
| 405 | reInitChecked(parent); | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | getChildren(forceInit = false) { // this is data | ||
| 410 | if (this.level === 0) return this.data; | ||
| 411 | const data = this.data; | ||
| 412 | if (!data) return null; | ||
| 413 | |||
| 414 | const props = this.store.props; | ||
| 415 | let children = 'children'; | ||
| 416 | if (props) { | ||
| 417 | children = props.children || 'children'; | ||
| 418 | } | ||
| 419 | |||
| 420 | if (data[children] === undefined) { | ||
| 421 | data[children] = null; | ||
| 422 | } | ||
| 423 | |||
| 424 | if (forceInit && !data[children]) { | ||
| 425 | data[children] = []; | ||
| 426 | } | ||
| 427 | |||
| 428 | return data[children]; | ||
| 429 | } | ||
| 430 | |||
| 431 | updateChildren() { | ||
| 432 | const newData = this.getChildren() || []; | ||
| 433 | const oldData = this.childNodes.map((node) => node.data); | ||
| 434 | |||
| 435 | const newDataMap = {}; | ||
| 436 | const newNodes = []; | ||
| 437 | |||
| 438 | newData.forEach((item, index) => { | ||
| 439 | const key = item[NODE_KEY]; | ||
| 440 | const isNodeExists = !!key && arrayFindIndex(oldData, data => data[NODE_KEY] === key) >= 0; | ||
| 441 | if (isNodeExists) { | ||
| 442 | newDataMap[key] = { index, data: item }; | ||
| 443 | } else { | ||
| 444 | newNodes.push({ index, data: item }); | ||
| 445 | } | ||
| 446 | }); | ||
| 447 | |||
| 448 | if (!this.store.lazy) { | ||
| 449 | oldData.forEach((item) => { | ||
| 450 | if (!newDataMap[item[NODE_KEY]]) this.removeChildByData(item); | ||
| 451 | }); | ||
| 452 | } | ||
| 453 | |||
| 454 | newNodes.forEach(({ index, data }) => { | ||
| 455 | this.insertChild({ data }, index); | ||
| 456 | }); | ||
| 457 | |||
| 458 | this.updateLeafState(); | ||
| 459 | } | ||
| 460 | |||
| 461 | loadData(callback, defaultProps = {}) { | ||
| 462 | if (this.store.lazy === true && this.store.load && !this.loaded && (!this.loading || Object.keys(defaultProps).length)) { | ||
| 463 | this.loading = true; | ||
| 464 | |||
| 465 | const resolve = (children) => { | ||
| 466 | this.childNodes = []; | ||
| 467 | |||
| 468 | this.doCreateChildren(children, defaultProps); | ||
| 469 | this.loaded = true; | ||
| 470 | this.loading = false; | ||
| 471 | this.updateLeafState(); | ||
| 472 | if (callback) { | ||
| 473 | callback.call(this, children); | ||
| 474 | } | ||
| 475 | }; | ||
| 476 | |||
| 477 | this.store.load(this, resolve); | ||
| 478 | } else { | ||
| 479 | if (callback) { | ||
| 480 | callback.call(this); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } |
src/components/Tree/src/model/tree-store.js
0 → 100644
| 1 | import Node from './node'; | ||
| 2 | import { getNodeKey } from './util'; | ||
| 3 | |||
| 4 | export default class TreeStore { | ||
| 5 | constructor(options) { | ||
| 6 | this.currentNode = null; | ||
| 7 | this.currentNodeKey = null; | ||
| 8 | |||
| 9 | for (let option in options) { | ||
| 10 | if (options.hasOwnProperty(option)) { | ||
| 11 | this[option] = options[option]; | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | this.nodesMap = {}; | ||
| 16 | |||
| 17 | this.root = new Node({ | ||
| 18 | data: this.data, | ||
| 19 | store: this | ||
| 20 | }); | ||
| 21 | |||
| 22 | if (this.lazy && this.load) { | ||
| 23 | const loadFn = this.load; | ||
| 24 | loadFn(this.root, (data) => { | ||
| 25 | this.root.doCreateChildren(data); | ||
| 26 | this._initDefaultCheckedNodes(); | ||
| 27 | }); | ||
| 28 | } else { | ||
| 29 | this._initDefaultCheckedNodes(); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | filter(value) { | ||
| 34 | const filterNodeMethod = this.filterNodeMethod; | ||
| 35 | const lazy = this.lazy; | ||
| 36 | const traverse = function(node) { | ||
| 37 | const childNodes = node.root ? node.root.childNodes : node.childNodes; | ||
| 38 | |||
| 39 | childNodes.forEach((child) => { | ||
| 40 | child.visible = filterNodeMethod.call(child, value, child.data, child); | ||
| 41 | |||
| 42 | traverse(child); | ||
| 43 | }); | ||
| 44 | |||
| 45 | if (!node.visible && childNodes.length) { | ||
| 46 | let allHidden = true; | ||
| 47 | allHidden = !childNodes.some(child => child.visible); | ||
| 48 | |||
| 49 | if (node.root) { | ||
| 50 | node.root.visible = allHidden === false; | ||
| 51 | } else { | ||
| 52 | node.visible = allHidden === false; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | if (!value) return; | ||
| 56 | |||
| 57 | if (node.visible && !node.isLeaf && !lazy) node.expand(); | ||
| 58 | }; | ||
| 59 | |||
| 60 | traverse(this); | ||
| 61 | } | ||
| 62 | |||
| 63 | setData(newVal) { | ||
| 64 | const instanceChanged = newVal !== this.root.data; | ||
| 65 | if (instanceChanged) { | ||
| 66 | this.root.setData(newVal); | ||
| 67 | this._initDefaultCheckedNodes(); | ||
| 68 | } else { | ||
| 69 | this.root.updateChildren(); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | getNode(data) { | ||
| 74 | if (data instanceof Node) return data; | ||
| 75 | const key = typeof data !== 'object' ? data : getNodeKey(this.key, data); | ||
| 76 | return this.nodesMap[key] || null; | ||
| 77 | } | ||
| 78 | |||
| 79 | insertBefore(data, refData) { | ||
| 80 | const refNode = this.getNode(refData); | ||
| 81 | refNode.parent.insertBefore({ data }, refNode); | ||
| 82 | } | ||
| 83 | |||
| 84 | insertAfter(data, refData) { | ||
| 85 | const refNode = this.getNode(refData); | ||
| 86 | refNode.parent.insertAfter({ data }, refNode); | ||
| 87 | } | ||
| 88 | |||
| 89 | remove(data) { | ||
| 90 | const node = this.getNode(data); | ||
| 91 | |||
| 92 | if (node && node.parent) { | ||
| 93 | if (node === this.currentNode) { | ||
| 94 | this.currentNode = null; | ||
| 95 | } | ||
| 96 | node.parent.removeChild(node); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | append(data, parentData) { | ||
| 101 | const parentNode = parentData ? this.getNode(parentData) : this.root; | ||
| 102 | |||
| 103 | if (parentNode) { | ||
| 104 | parentNode.insertChild({ data }); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | _initDefaultCheckedNodes() { | ||
| 109 | const defaultCheckedKeys = this.defaultCheckedKeys || []; | ||
| 110 | const nodesMap = this.nodesMap; | ||
| 111 | |||
| 112 | defaultCheckedKeys.forEach((checkedKey) => { | ||
| 113 | const node = nodesMap[checkedKey]; | ||
| 114 | |||
| 115 | if (node) { | ||
| 116 | node.setChecked(true, !this.checkStrictly); | ||
| 117 | } | ||
| 118 | }); | ||
| 119 | } | ||
| 120 | |||
| 121 | _initDefaultCheckedNode(node) { | ||
| 122 | const defaultCheckedKeys = this.defaultCheckedKeys || []; | ||
| 123 | |||
| 124 | if (defaultCheckedKeys.indexOf(node.key) !== -1) { | ||
| 125 | node.setChecked(true, !this.checkStrictly); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | setDefaultCheckedKey(newVal) { | ||
| 130 | if (newVal !== this.defaultCheckedKeys) { | ||
| 131 | this.defaultCheckedKeys = newVal; | ||
| 132 | this._initDefaultCheckedNodes(); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | registerNode(node) { | ||
| 137 | const key = this.key; | ||
| 138 | if (!key || !node || !node.data) return; | ||
| 139 | |||
| 140 | const nodeKey = node.key; | ||
| 141 | if (nodeKey !== undefined) this.nodesMap[node.key] = node; | ||
| 142 | } | ||
| 143 | |||
| 144 | deregisterNode(node) { | ||
| 145 | const key = this.key; | ||
| 146 | if (!key || !node || !node.data) return; | ||
| 147 | |||
| 148 | node.childNodes.forEach(child => { | ||
| 149 | this.deregisterNode(child); | ||
| 150 | }); | ||
| 151 | |||
| 152 | delete this.nodesMap[node.key]; | ||
| 153 | } | ||
| 154 | |||
| 155 | getCheckedNodes(leafOnly = false, includeHalfChecked = false) { | ||
| 156 | const checkedNodes = []; | ||
| 157 | const traverse = function(node) { | ||
| 158 | const childNodes = node.root ? node.root.childNodes : node.childNodes; | ||
| 159 | |||
| 160 | childNodes.forEach((child) => { | ||
| 161 | if ((child.checked || (includeHalfChecked && child.indeterminate)) && (!leafOnly || (leafOnly && child.isLeaf))) { | ||
| 162 | checkedNodes.push(child.data); | ||
| 163 | } | ||
| 164 | |||
| 165 | traverse(child); | ||
| 166 | }); | ||
| 167 | }; | ||
| 168 | |||
| 169 | traverse(this); | ||
| 170 | |||
| 171 | return checkedNodes; | ||
| 172 | } | ||
| 173 | |||
| 174 | getCheckedKeys(leafOnly = false) { | ||
| 175 | return this.getCheckedNodes(leafOnly).map((data) => (data || {})[this.key]); | ||
| 176 | } | ||
| 177 | |||
| 178 | getHalfCheckedNodes() { | ||
| 179 | const nodes = []; | ||
| 180 | const traverse = function(node) { | ||
| 181 | const childNodes = node.root ? node.root.childNodes : node.childNodes; | ||
| 182 | |||
| 183 | childNodes.forEach((child) => { | ||
| 184 | if (child.indeterminate) { | ||
| 185 | nodes.push(child.data); | ||
| 186 | } | ||
| 187 | |||
| 188 | traverse(child); | ||
| 189 | }); | ||
| 190 | }; | ||
| 191 | |||
| 192 | traverse(this); | ||
| 193 | |||
| 194 | return nodes; | ||
| 195 | } | ||
| 196 | |||
| 197 | getHalfCheckedKeys() { | ||
| 198 | return this.getHalfCheckedNodes().map((data) => (data || {})[this.key]); | ||
| 199 | } | ||
| 200 | |||
| 201 | _getAllNodes() { | ||
| 202 | const allNodes = []; | ||
| 203 | const nodesMap = this.nodesMap; | ||
| 204 | for (let nodeKey in nodesMap) { | ||
| 205 | if (nodesMap.hasOwnProperty(nodeKey)) { | ||
| 206 | allNodes.push(nodesMap[nodeKey]); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | return allNodes; | ||
| 211 | } | ||
| 212 | |||
| 213 | updateChildren(key, data) { | ||
| 214 | const node = this.nodesMap[key]; | ||
| 215 | if (!node) return; | ||
| 216 | const childNodes = node.childNodes; | ||
| 217 | for (let i = childNodes.length - 1; i >= 0; i--) { | ||
| 218 | const child = childNodes[i]; | ||
| 219 | this.remove(child.data); | ||
| 220 | } | ||
| 221 | for (let i = 0, j = data.length; i < j; i++) { | ||
| 222 | const child = data[i]; | ||
| 223 | this.append(child, node.data); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | _setCheckedKeys(key, leafOnly = false, checkedKeys) { | ||
| 228 | const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level); | ||
| 229 | const cache = Object.create(null); | ||
| 230 | const keys = Object.keys(checkedKeys); | ||
| 231 | allNodes.forEach(node => node.setChecked(false, false)); | ||
| 232 | for (let i = 0, j = allNodes.length; i < j; i++) { | ||
| 233 | const node = allNodes[i]; | ||
| 234 | const nodeKey = node.data[key].toString(); | ||
| 235 | let checked = keys.indexOf(nodeKey) > -1; | ||
| 236 | if (!checked) { | ||
| 237 | if (node.checked && !cache[nodeKey]) { | ||
| 238 | node.setChecked(false, false); | ||
| 239 | } | ||
| 240 | continue; | ||
| 241 | } | ||
| 242 | |||
| 243 | let parent = node.parent; | ||
| 244 | while (parent && parent.level > 0) { | ||
| 245 | cache[parent.data[key]] = true; | ||
| 246 | parent = parent.parent; | ||
| 247 | } | ||
| 248 | |||
| 249 | if (node.isLeaf || this.checkStrictly) { | ||
| 250 | node.setChecked(true, false); | ||
| 251 | continue; | ||
| 252 | } | ||
| 253 | node.setChecked(true, true); | ||
| 254 | |||
| 255 | if (leafOnly) { | ||
| 256 | node.setChecked(false, false); | ||
| 257 | const traverse = function(node) { | ||
| 258 | const childNodes = node.childNodes; | ||
| 259 | childNodes.forEach((child) => { | ||
| 260 | if (!child.isLeaf) { | ||
| 261 | child.setChecked(false, false); | ||
| 262 | } | ||
| 263 | traverse(child); | ||
| 264 | }); | ||
| 265 | }; | ||
| 266 | traverse(node); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | setCheckedNodes(array, leafOnly = false) { | ||
| 272 | const key = this.key; | ||
| 273 | const checkedKeys = {}; | ||
| 274 | array.forEach((item) => { | ||
| 275 | checkedKeys[(item || {})[key]] = true; | ||
| 276 | }); | ||
| 277 | |||
| 278 | this._setCheckedKeys(key, leafOnly, checkedKeys); | ||
| 279 | } | ||
| 280 | |||
| 281 | setCheckedKeys(keys, leafOnly = false) { | ||
| 282 | this.defaultCheckedKeys = keys; | ||
| 283 | const key = this.key; | ||
| 284 | const checkedKeys = {}; | ||
| 285 | keys.forEach((key) => { | ||
| 286 | checkedKeys[key] = true; | ||
| 287 | }); | ||
| 288 | |||
| 289 | this._setCheckedKeys(key, leafOnly, checkedKeys); | ||
| 290 | } | ||
| 291 | |||
| 292 | setDefaultExpandedKeys(keys) { | ||
| 293 | keys = keys || []; | ||
| 294 | this.defaultExpandedKeys = keys; | ||
| 295 | |||
| 296 | keys.forEach((key) => { | ||
| 297 | const node = this.getNode(key); | ||
| 298 | if (node) node.expand(null, this.autoExpandParent); | ||
| 299 | }); | ||
| 300 | } | ||
| 301 | |||
| 302 | setChecked(data, checked, deep) { | ||
| 303 | const node = this.getNode(data); | ||
| 304 | |||
| 305 | if (node) { | ||
| 306 | node.setChecked(!!checked, deep); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | getCurrentNode() { | ||
| 311 | return this.currentNode; | ||
| 312 | } | ||
| 313 | |||
| 314 | setCurrentNode(currentNode) { | ||
| 315 | const prevCurrentNode = this.currentNode; | ||
| 316 | if (prevCurrentNode) { | ||
| 317 | prevCurrentNode.isCurrent = false; | ||
| 318 | } | ||
| 319 | this.currentNode = currentNode; | ||
| 320 | this.currentNode.isCurrent = true; | ||
| 321 | } | ||
| 322 | |||
| 323 | setUserCurrentNode(node) { | ||
| 324 | const key = node[this.key]; | ||
| 325 | const currNode = this.nodesMap[key]; | ||
| 326 | this.setCurrentNode(currNode); | ||
| 327 | } | ||
| 328 | |||
| 329 | setCurrentNodeKey(key) { | ||
| 330 | if (key === null || key === undefined) { | ||
| 331 | this.currentNode && (this.currentNode.isCurrent = false); | ||
| 332 | this.currentNode = null; | ||
| 333 | return; | ||
| 334 | } | ||
| 335 | const node = this.getNode(key); | ||
| 336 | if (node) { | ||
| 337 | this.setCurrentNode(node); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | }; |
src/components/Tree/src/model/util.js
0 → 100644
| 1 | export const NODE_KEY = '$treeNodeId'; | ||
| 2 | |||
| 3 | export const markNodeData = function(node, data) { | ||
| 4 | if (!data || data[NODE_KEY]) return; | ||
| 5 | Object.defineProperty(data, NODE_KEY, { | ||
| 6 | value: node.id, | ||
| 7 | enumerable: false, | ||
| 8 | configurable: false, | ||
| 9 | writable: false | ||
| 10 | }); | ||
| 11 | }; | ||
| 12 | |||
| 13 | export const getNodeKey = function(key, data) { | ||
| 14 | if (!key) return data[NODE_KEY]; | ||
| 15 | return data[key]; | ||
| 16 | }; | ||
| 17 | |||
| 18 | export const findNearestComponent = (element, componentName) => { | ||
| 19 | let target = element; | ||
| 20 | while (target && target.tagName !== 'BODY') { | ||
| 21 | if (target.__vue__ && target.__vue__.$options.name === componentName) { | ||
| 22 | return target.__vue__; | ||
| 23 | } | ||
| 24 | target = target.parentNode; | ||
| 25 | } | ||
| 26 | return null; | ||
| 27 | }; |
src/components/Tree/src/tree-node.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div class="el-tree-node" @click.stop="handleClick" @contextmenu="($event) => this.handleContextMenu($event)" | ||
| 3 | v-show="node.visible" :class="{ | ||
| 4 | 'is-expanded': expanded, | ||
| 5 | 'is-current': node.isCurrent, | ||
| 6 | 'is-hidden': !node.visible, | ||
| 7 | 'is-focusable': !node.disabled, | ||
| 8 | 'is-checked': !node.disabled && node.checked | ||
| 9 | }" role="treeitem" tabindex="-1" :aria-expanded="expanded" :aria-disabled="node.disabled" | ||
| 10 | :aria-checked="node.checked" :draggable="tree.draggable" @dragstart.stop="handleDragStart" | ||
| 11 | @dragover.stop="handleDragOver" @dragend.stop="handleDragEnd" @drop.stop="handleDrop" ref="node"> | ||
| 12 | <div class="el-tree-node__content" :style="{ 'padding-left': (node.level - 1) * tree.indent + 'px' }"> | ||
| 13 | <span @click.stop="handleExpandIconClick" :class="[ | ||
| 14 | { 'is-leaf': node.isLeaf, expanded: !node.isLeaf && expanded }, | ||
| 15 | 'el-tree-node__expand-icon', | ||
| 16 | tree.iconClass ? tree.iconClass : 'el-icon-caret-right' | ||
| 17 | ]"> | ||
| 18 | </span> | ||
| 19 | <CheckBox v-if="showCheckbox" v-model="node.checked" :indeterminate="node.indeterminate" | ||
| 20 | :disabled="!!node.disabled" @click.native.stop @change="handleCheckChange"> | ||
| 21 | <node-content :node="node"></node-content> | ||
| 22 | </CheckBox> | ||
| 23 | |||
| 24 | </div> | ||
| 25 | <el-collapse-transition> | ||
| 26 | <div class="el-tree-node__children" v-if="!renderAfterExpand || childNodeRendered" v-show="expanded" role="group" | ||
| 27 | :aria-expanded="expanded"> | ||
| 28 | <el-tree-node :render-content="renderContent" v-for="child in node.childNodes" | ||
| 29 | :render-after-expand="renderAfterExpand" :show-checkbox="showCheckbox" :key="getNodeKey(child)" :node="child" | ||
| 30 | @node-expand="handleChildNodeExpand"> | ||
| 31 | </el-tree-node> | ||
| 32 | </div> | ||
| 33 | </el-collapse-transition> | ||
| 34 | </div> | ||
| 35 | </template> | ||
| 36 | |||
| 37 | <script type="text/jsx"> | ||
| 38 | import CheckBox from '@/components/CheckBox/checkbox' | ||
| 39 | import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition'; | ||
| 40 | import ElCheckbox from 'element-ui/packages/checkbox'; | ||
| 41 | import emitter from 'element-ui/src/mixins/emitter'; | ||
| 42 | import { getNodeKey } from './model/util'; | ||
| 43 | |||
| 44 | export default { | ||
| 45 | name: 'ElTreeNode', | ||
| 46 | |||
| 47 | componentName: 'ElTreeNode', | ||
| 48 | |||
| 49 | mixins: [emitter], | ||
| 50 | |||
| 51 | props: { | ||
| 52 | node: { | ||
| 53 | default () { | ||
| 54 | return {}; | ||
| 55 | } | ||
| 56 | }, | ||
| 57 | props: {}, | ||
| 58 | renderContent: Function, | ||
| 59 | renderAfterExpand: { | ||
| 60 | type: Boolean, | ||
| 61 | default: true | ||
| 62 | }, | ||
| 63 | showCheckbox: { | ||
| 64 | type: Boolean, | ||
| 65 | default: false | ||
| 66 | } | ||
| 67 | }, | ||
| 68 | |||
| 69 | components: { | ||
| 70 | ElCollapseTransition, | ||
| 71 | ElCheckbox, | ||
| 72 | CheckBox, | ||
| 73 | NodeContent: { | ||
| 74 | props: { | ||
| 75 | node: { | ||
| 76 | required: true | ||
| 77 | } | ||
| 78 | }, | ||
| 79 | render (h) { | ||
| 80 | const parent = this.$parent; | ||
| 81 | const tree = parent.tree; | ||
| 82 | const node = this.node; | ||
| 83 | const { data, store } = node; | ||
| 84 | return ( | ||
| 85 | <span class="el-tree-node__label">{node.label}</span> | ||
| 86 | ); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | }, | ||
| 90 | |||
| 91 | data () { | ||
| 92 | return { | ||
| 93 | tree: null, | ||
| 94 | expanded: false, | ||
| 95 | childNodeRendered: false, | ||
| 96 | oldChecked: null, | ||
| 97 | oldIndeterminate: null | ||
| 98 | }; | ||
| 99 | }, | ||
| 100 | |||
| 101 | watch: { | ||
| 102 | 'node.indeterminate' (val) { | ||
| 103 | this.handleSelectChange(this.node.checked, val); | ||
| 104 | }, | ||
| 105 | |||
| 106 | 'node.checked' (val) { | ||
| 107 | this.handleSelectChange(val, this.node.indeterminate); | ||
| 108 | }, | ||
| 109 | |||
| 110 | 'node.expanded' (val) { | ||
| 111 | this.$nextTick(() => this.expanded = val); | ||
| 112 | if (val) { | ||
| 113 | this.childNodeRendered = true; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | }, | ||
| 117 | |||
| 118 | methods: { | ||
| 119 | getNodeKey (node) { | ||
| 120 | return getNodeKey(this.tree.nodeKey, node.data); | ||
| 121 | }, | ||
| 122 | |||
| 123 | handleSelectChange (checked, indeterminate) { | ||
| 124 | if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) { | ||
| 125 | this.tree.$emit('check-change', this.node.data, checked, indeterminate); | ||
| 126 | } | ||
| 127 | this.oldChecked = checked; | ||
| 128 | this.indeterminate = indeterminate; | ||
| 129 | }, | ||
| 130 | |||
| 131 | handleClick () { | ||
| 132 | const store = this.tree.store; | ||
| 133 | store.setCurrentNode(this.node); | ||
| 134 | this.tree.$emit('current-change', store.currentNode ? store.currentNode.data : null, store.currentNode); | ||
| 135 | this.tree.currentNode = this; | ||
| 136 | if (this.tree.expandOnClickNode) { | ||
| 137 | this.handleExpandIconClick(); | ||
| 138 | } | ||
| 139 | if (this.tree.checkOnClickNode && !this.node.disabled) { | ||
| 140 | this.handleCheckChange(null, { | ||
| 141 | target: { checked: !this.node.checked } | ||
| 142 | }); | ||
| 143 | } | ||
| 144 | this.tree.$emit('node-click', this.node.data, this.node, this); | ||
| 145 | }, | ||
| 146 | |||
| 147 | handleContextMenu (event) { | ||
| 148 | if (this.tree._events['node-contextmenu'] && this.tree._events['node-contextmenu'].length > 0) { | ||
| 149 | event.stopPropagation(); | ||
| 150 | event.preventDefault(); | ||
| 151 | } | ||
| 152 | this.tree.$emit('node-contextmenu', event, this.node.data, this.node, this); | ||
| 153 | }, | ||
| 154 | |||
| 155 | handleExpandIconClick () { | ||
| 156 | if (this.node.isLeaf) return; | ||
| 157 | if (this.expanded) { | ||
| 158 | this.tree.$emit('node-collapse', this.node.data, this.node, this); | ||
| 159 | this.node.collapse(); | ||
| 160 | } else { | ||
| 161 | this.node.expand(); | ||
| 162 | this.$emit('node-expand', this.node.data, this.node, this); | ||
| 163 | } | ||
| 164 | }, | ||
| 165 | |||
| 166 | handleCheckChange (value, ev) { | ||
| 167 | this.node.setChecked(ev.target.checked, !this.tree.checkStrictly); | ||
| 168 | this.$nextTick(() => { | ||
| 169 | const store = this.tree.store; | ||
| 170 | this.tree.$emit('check', this.node.data, { | ||
| 171 | checkedNodes: store.getCheckedNodes(), | ||
| 172 | checkedKeys: store.getCheckedKeys(), | ||
| 173 | halfCheckedNodes: store.getHalfCheckedNodes(), | ||
| 174 | halfCheckedKeys: store.getHalfCheckedKeys(), | ||
| 175 | }); | ||
| 176 | }); | ||
| 177 | }, | ||
| 178 | |||
| 179 | handleChildNodeExpand (nodeData, node, instance) { | ||
| 180 | this.broadcast('ElTreeNode', 'tree-node-expand', node); | ||
| 181 | this.tree.$emit('node-expand', nodeData, node, instance); | ||
| 182 | }, | ||
| 183 | |||
| 184 | handleDragStart (event) { | ||
| 185 | if (!this.tree.draggable) return; | ||
| 186 | this.tree.$emit('tree-node-drag-start', event, this); | ||
| 187 | }, | ||
| 188 | |||
| 189 | handleDragOver (event) { | ||
| 190 | if (!this.tree.draggable) return; | ||
| 191 | this.tree.$emit('tree-node-drag-over', event, this); | ||
| 192 | event.preventDefault(); | ||
| 193 | }, | ||
| 194 | |||
| 195 | handleDrop (event) { | ||
| 196 | event.preventDefault(); | ||
| 197 | }, | ||
| 198 | |||
| 199 | handleDragEnd (event) { | ||
| 200 | if (!this.tree.draggable) return; | ||
| 201 | this.tree.$emit('tree-node-drag-end', event, this); | ||
| 202 | } | ||
| 203 | }, | ||
| 204 | |||
| 205 | created () { | ||
| 206 | const parent = this.$parent; | ||
| 207 | |||
| 208 | if (parent.isTree) { | ||
| 209 | this.tree = parent; | ||
| 210 | } else { | ||
| 211 | this.tree = parent.tree; | ||
| 212 | } | ||
| 213 | |||
| 214 | const tree = this.tree; | ||
| 215 | if (!tree) { | ||
| 216 | console.warn('Can not find node\'s tree.'); | ||
| 217 | } | ||
| 218 | |||
| 219 | const props = tree.props || {}; | ||
| 220 | const childrenKey = props['children'] || 'children'; | ||
| 221 | |||
| 222 | this.$watch(`node.data.${childrenKey}`, () => { | ||
| 223 | this.node.updateChildren(); | ||
| 224 | }); | ||
| 225 | |||
| 226 | if (this.node.expanded) { | ||
| 227 | this.expanded = true; | ||
| 228 | this.childNodeRendered = true; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (this.tree.accordion) { | ||
| 232 | this.$on('tree-node-expand', node => { | ||
| 233 | if (this.node !== node) { | ||
| 234 | this.node.collapse(); | ||
| 235 | } | ||
| 236 | }); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | }; | ||
| 240 | </script> |
src/components/Tree/src/tree.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div | ||
| 3 | class="el-tree" | ||
| 4 | :class="{ | ||
| 5 | 'el-tree--highlight-current': highlightCurrent, | ||
| 6 | 'is-dragging': !!dragState.draggingNode, | ||
| 7 | 'is-drop-not-allow': !dragState.allowDrop, | ||
| 8 | 'is-drop-inner': dragState.dropType === 'inner' | ||
| 9 | }" | ||
| 10 | role="tree" | ||
| 11 | > | ||
| 12 | <el-tree-node | ||
| 13 | v-for="child in root.childNodes" | ||
| 14 | :node="child" | ||
| 15 | :props="props" | ||
| 16 | :render-after-expand="renderAfterExpand" | ||
| 17 | :show-checkbox="showCheckbox" | ||
| 18 | :key="getNodeKey(child)" | ||
| 19 | :render-content="renderContent" | ||
| 20 | @node-expand="handleNodeExpand"> | ||
| 21 | </el-tree-node> | ||
| 22 | <div class="el-tree__empty-block" v-if="isEmpty"> | ||
| 23 | <span class="el-tree__empty-text">{{ emptyText }}</span> | ||
| 24 | </div> | ||
| 25 | <div | ||
| 26 | v-show="dragState.showDropIndicator" | ||
| 27 | class="el-tree__drop-indicator" | ||
| 28 | ref="dropIndicator"> | ||
| 29 | </div> | ||
| 30 | </div> | ||
| 31 | </template> | ||
| 32 | |||
| 33 | <script> | ||
| 34 | import TreeStore from './model/tree-store'; | ||
| 35 | import { getNodeKey, findNearestComponent } from './model/util'; | ||
| 36 | import ElTreeNode from './tree-node.vue'; | ||
| 37 | import {t} from 'element-ui/src/locale'; | ||
| 38 | import emitter from 'element-ui/src/mixins/emitter'; | ||
| 39 | import { addClass, removeClass } from 'element-ui/src/utils/dom'; | ||
| 40 | |||
| 41 | export default { | ||
| 42 | name: 'ElTree', | ||
| 43 | |||
| 44 | mixins: [emitter], | ||
| 45 | |||
| 46 | components: { | ||
| 47 | ElTreeNode | ||
| 48 | }, | ||
| 49 | |||
| 50 | data() { | ||
| 51 | return { | ||
| 52 | store: null, | ||
| 53 | root: null, | ||
| 54 | currentNode: null, | ||
| 55 | treeItems: null, | ||
| 56 | checkboxItems: [], | ||
| 57 | dragState: { | ||
| 58 | showDropIndicator: false, | ||
| 59 | draggingNode: null, | ||
| 60 | dropNode: null, | ||
| 61 | allowDrop: true | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | }, | ||
| 65 | |||
| 66 | props: { | ||
| 67 | data: { | ||
| 68 | type: Array | ||
| 69 | }, | ||
| 70 | emptyText: { | ||
| 71 | type: String, | ||
| 72 | default() { | ||
| 73 | return t('el.tree.emptyText'); | ||
| 74 | } | ||
| 75 | }, | ||
| 76 | renderAfterExpand: { | ||
| 77 | type: Boolean, | ||
| 78 | default: true | ||
| 79 | }, | ||
| 80 | nodeKey: String, | ||
| 81 | checkStrictly: Boolean, | ||
| 82 | defaultExpandAll: Boolean, | ||
| 83 | expandOnClickNode: { | ||
| 84 | type: Boolean, | ||
| 85 | default: true | ||
| 86 | }, | ||
| 87 | checkOnClickNode: Boolean, | ||
| 88 | checkDescendants: { | ||
| 89 | type: Boolean, | ||
| 90 | default: false | ||
| 91 | }, | ||
| 92 | autoExpandParent: { | ||
| 93 | type: Boolean, | ||
| 94 | default: true | ||
| 95 | }, | ||
| 96 | defaultCheckedKeys: Array, | ||
| 97 | defaultExpandedKeys: Array, | ||
| 98 | currentNodeKey: [String, Number], | ||
| 99 | renderContent: Function, | ||
| 100 | showCheckbox: { | ||
| 101 | type: Boolean, | ||
| 102 | default: false | ||
| 103 | }, | ||
| 104 | draggable: { | ||
| 105 | type: Boolean, | ||
| 106 | default: false | ||
| 107 | }, | ||
| 108 | allowDrag: Function, | ||
| 109 | allowDrop: Function, | ||
| 110 | props: { | ||
| 111 | default() { | ||
| 112 | return { | ||
| 113 | children: 'children', | ||
| 114 | label: 'label', | ||
| 115 | disabled: 'disabled' | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | }, | ||
| 119 | lazy: { | ||
| 120 | type: Boolean, | ||
| 121 | default: false | ||
| 122 | }, | ||
| 123 | highlightCurrent: Boolean, | ||
| 124 | load: Function, | ||
| 125 | filterNodeMethod: Function, | ||
| 126 | accordion: Boolean, | ||
| 127 | indent: { | ||
| 128 | type: Number, | ||
| 129 | default: 18 | ||
| 130 | }, | ||
| 131 | iconClass: String | ||
| 132 | }, | ||
| 133 | |||
| 134 | computed: { | ||
| 135 | children: { | ||
| 136 | set(value) { | ||
| 137 | this.data = value; | ||
| 138 | }, | ||
| 139 | get() { | ||
| 140 | return this.data; | ||
| 141 | } | ||
| 142 | }, | ||
| 143 | |||
| 144 | treeItemArray() { | ||
| 145 | return Array.prototype.slice.call(this.treeItems); | ||
| 146 | }, | ||
| 147 | |||
| 148 | isEmpty() { | ||
| 149 | const { childNodes } = this.root; | ||
| 150 | return !childNodes || childNodes.length === 0 || childNodes.every(({visible}) => !visible); | ||
| 151 | } | ||
| 152 | }, | ||
| 153 | |||
| 154 | watch: { | ||
| 155 | defaultCheckedKeys(newVal) { | ||
| 156 | this.store.setDefaultCheckedKey(newVal); | ||
| 157 | }, | ||
| 158 | |||
| 159 | defaultExpandedKeys(newVal) { | ||
| 160 | this.store.defaultExpandedKeys = newVal; | ||
| 161 | this.store.setDefaultExpandedKeys(newVal); | ||
| 162 | }, | ||
| 163 | |||
| 164 | data(newVal) { | ||
| 165 | this.store.setData(newVal); | ||
| 166 | }, | ||
| 167 | |||
| 168 | checkboxItems(val) { | ||
| 169 | Array.prototype.forEach.call(val, (checkbox) => { | ||
| 170 | checkbox.setAttribute('tabindex', -1); | ||
| 171 | }); | ||
| 172 | }, | ||
| 173 | |||
| 174 | checkStrictly(newVal) { | ||
| 175 | this.store.checkStrictly = newVal; | ||
| 176 | } | ||
| 177 | }, | ||
| 178 | |||
| 179 | methods: { | ||
| 180 | filter(value) { | ||
| 181 | if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter'); | ||
| 182 | this.store.filter(value); | ||
| 183 | }, | ||
| 184 | |||
| 185 | getNodeKey(node) { | ||
| 186 | return getNodeKey(this.nodeKey, node.data); | ||
| 187 | }, | ||
| 188 | |||
| 189 | getNodePath(data) { | ||
| 190 | if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in getNodePath'); | ||
| 191 | const node = this.store.getNode(data); | ||
| 192 | if (!node) return []; | ||
| 193 | const path = [node.data]; | ||
| 194 | let parent = node.parent; | ||
| 195 | while (parent && parent !== this.root) { | ||
| 196 | path.push(parent.data); | ||
| 197 | parent = parent.parent; | ||
| 198 | } | ||
| 199 | return path.reverse(); | ||
| 200 | }, | ||
| 201 | |||
| 202 | getCheckedNodes(leafOnly, includeHalfChecked) { | ||
| 203 | return this.store.getCheckedNodes(leafOnly, includeHalfChecked); | ||
| 204 | }, | ||
| 205 | |||
| 206 | getCheckedKeys(leafOnly) { | ||
| 207 | return this.store.getCheckedKeys(leafOnly); | ||
| 208 | }, | ||
| 209 | |||
| 210 | getCurrentNode() { | ||
| 211 | const currentNode = this.store.getCurrentNode(); | ||
| 212 | return currentNode ? currentNode.data : null; | ||
| 213 | }, | ||
| 214 | |||
| 215 | getCurrentKey() { | ||
| 216 | if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in getCurrentKey'); | ||
| 217 | const currentNode = this.getCurrentNode(); | ||
| 218 | return currentNode ? currentNode[this.nodeKey] : null; | ||
| 219 | }, | ||
| 220 | |||
| 221 | setCheckedNodes(nodes, leafOnly) { | ||
| 222 | if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedNodes'); | ||
| 223 | this.store.setCheckedNodes(nodes, leafOnly); | ||
| 224 | }, | ||
| 225 | |||
| 226 | setCheckedKeys(keys, leafOnly) { | ||
| 227 | if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys'); | ||
| 228 | this.store.setCheckedKeys(keys, leafOnly); | ||
| 229 | }, | ||
| 230 | |||
| 231 | setChecked(data, checked, deep) { | ||
| 232 | this.store.setChecked(data, checked, deep); | ||
| 233 | }, | ||
| 234 | |||
| 235 | getHalfCheckedNodes() { | ||
| 236 | return this.store.getHalfCheckedNodes(); | ||
| 237 | }, | ||
| 238 | |||
| 239 | getHalfCheckedKeys() { | ||
| 240 | return this.store.getHalfCheckedKeys(); | ||
| 241 | }, | ||
| 242 | |||
| 243 | setCurrentNode(node) { | ||
| 244 | if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode'); | ||
| 245 | this.store.setUserCurrentNode(node); | ||
| 246 | }, | ||
| 247 | |||
| 248 | setCurrentKey(key) { | ||
| 249 | if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey'); | ||
| 250 | this.store.setCurrentNodeKey(key); | ||
| 251 | }, | ||
| 252 | |||
| 253 | getNode(data) { | ||
| 254 | return this.store.getNode(data); | ||
| 255 | }, | ||
| 256 | |||
| 257 | remove(data) { | ||
| 258 | this.store.remove(data); | ||
| 259 | }, | ||
| 260 | |||
| 261 | append(data, parentNode) { | ||
| 262 | this.store.append(data, parentNode); | ||
| 263 | }, | ||
| 264 | |||
| 265 | insertBefore(data, refNode) { | ||
| 266 | this.store.insertBefore(data, refNode); | ||
| 267 | }, | ||
| 268 | |||
| 269 | insertAfter(data, refNode) { | ||
| 270 | this.store.insertAfter(data, refNode); | ||
| 271 | }, | ||
| 272 | |||
| 273 | handleNodeExpand(nodeData, node, instance) { | ||
| 274 | this.broadcast('ElTreeNode', 'tree-node-expand', node); | ||
| 275 | this.$emit('node-expand', nodeData, node, instance); | ||
| 276 | }, | ||
| 277 | |||
| 278 | updateKeyChildren(key, data) { | ||
| 279 | if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild'); | ||
| 280 | this.store.updateChildren(key, data); | ||
| 281 | }, | ||
| 282 | |||
| 283 | initTabIndex() { | ||
| 284 | this.treeItems = this.$el.querySelectorAll('.is-focusable[role=treeitem]'); | ||
| 285 | this.checkboxItems = this.$el.querySelectorAll('input[type=checkbox]'); | ||
| 286 | const checkedItem = this.$el.querySelectorAll('.is-checked[role=treeitem]'); | ||
| 287 | if (checkedItem.length) { | ||
| 288 | checkedItem[0].setAttribute('tabindex', 0); | ||
| 289 | return; | ||
| 290 | } | ||
| 291 | this.treeItems[0] && this.treeItems[0].setAttribute('tabindex', 0); | ||
| 292 | }, | ||
| 293 | |||
| 294 | handleKeydown(ev) { | ||
| 295 | const currentItem = ev.target; | ||
| 296 | if (currentItem.className.indexOf('el-tree-node') === -1) return; | ||
| 297 | const keyCode = ev.keyCode; | ||
| 298 | this.treeItems = this.$el.querySelectorAll('.is-focusable[role=treeitem]'); | ||
| 299 | const currentIndex = this.treeItemArray.indexOf(currentItem); | ||
| 300 | let nextIndex; | ||
| 301 | if ([38, 40].indexOf(keyCode) > -1) { // up、down | ||
| 302 | ev.preventDefault(); | ||
| 303 | if (keyCode === 38) { // up | ||
| 304 | nextIndex = currentIndex !== 0 ? currentIndex - 1 : 0; | ||
| 305 | } else { | ||
| 306 | nextIndex = (currentIndex < this.treeItemArray.length - 1) ? currentIndex + 1 : 0; | ||
| 307 | } | ||
| 308 | this.treeItemArray[nextIndex].focus(); // 选中 | ||
| 309 | } | ||
| 310 | if ([37, 39].indexOf(keyCode) > -1) { // left、right 展开 | ||
| 311 | ev.preventDefault(); | ||
| 312 | currentItem.click(); // 选中 | ||
| 313 | } | ||
| 314 | const hasInput = currentItem.querySelector('[type="checkbox"]'); | ||
| 315 | if ([13, 32].indexOf(keyCode) > -1 && hasInput) { // space enter选中checkbox | ||
| 316 | ev.preventDefault(); | ||
| 317 | hasInput.click(); | ||
| 318 | } | ||
| 319 | } | ||
| 320 | }, | ||
| 321 | |||
| 322 | created() { | ||
| 323 | this.isTree = true; | ||
| 324 | |||
| 325 | this.store = new TreeStore({ | ||
| 326 | key: this.nodeKey, | ||
| 327 | data: this.data, | ||
| 328 | lazy: this.lazy, | ||
| 329 | props: this.props, | ||
| 330 | load: this.load, | ||
| 331 | currentNodeKey: this.currentNodeKey, | ||
| 332 | checkStrictly: this.checkStrictly, | ||
| 333 | checkDescendants: this.checkDescendants, | ||
| 334 | defaultCheckedKeys: this.defaultCheckedKeys, | ||
| 335 | defaultExpandedKeys: this.defaultExpandedKeys, | ||
| 336 | autoExpandParent: this.autoExpandParent, | ||
| 337 | defaultExpandAll: this.defaultExpandAll, | ||
| 338 | filterNodeMethod: this.filterNodeMethod | ||
| 339 | }); | ||
| 340 | |||
| 341 | this.root = this.store.root; | ||
| 342 | |||
| 343 | let dragState = this.dragState; | ||
| 344 | this.$on('tree-node-drag-start', (event, treeNode) => { | ||
| 345 | if (typeof this.allowDrag === 'function' && !this.allowDrag(treeNode.node)) { | ||
| 346 | event.preventDefault(); | ||
| 347 | return false; | ||
| 348 | } | ||
| 349 | event.dataTransfer.effectAllowed = 'move'; | ||
| 350 | |||
| 351 | // wrap in try catch to address IE's error when first param is 'text/plain' | ||
| 352 | try { | ||
| 353 | // setData is required for draggable to work in FireFox | ||
| 354 | // the content has to be '' so dragging a node out of the tree won't open a new tab in FireFox | ||
| 355 | event.dataTransfer.setData('text/plain', ''); | ||
| 356 | } catch (e) {} | ||
| 357 | dragState.draggingNode = treeNode; | ||
| 358 | this.$emit('node-drag-start', treeNode.node, event); | ||
| 359 | }); | ||
| 360 | |||
| 361 | this.$on('tree-node-drag-over', (event, treeNode) => { | ||
| 362 | const dropNode = findNearestComponent(event.target, 'ElTreeNode'); | ||
| 363 | const oldDropNode = dragState.dropNode; | ||
| 364 | if (oldDropNode && oldDropNode !== dropNode) { | ||
| 365 | removeClass(oldDropNode.$el, 'is-drop-inner'); | ||
| 366 | } | ||
| 367 | const draggingNode = dragState.draggingNode; | ||
| 368 | if (!draggingNode || !dropNode) return; | ||
| 369 | |||
| 370 | let dropPrev = true; | ||
| 371 | let dropInner = true; | ||
| 372 | let dropNext = true; | ||
| 373 | let userAllowDropInner = true; | ||
| 374 | if (typeof this.allowDrop === 'function') { | ||
| 375 | dropPrev = this.allowDrop(draggingNode.node, dropNode.node, 'prev'); | ||
| 376 | userAllowDropInner = dropInner = this.allowDrop(draggingNode.node, dropNode.node, 'inner'); | ||
| 377 | dropNext = this.allowDrop(draggingNode.node, dropNode.node, 'next'); | ||
| 378 | } | ||
| 379 | event.dataTransfer.dropEffect = dropInner ? 'move' : 'none'; | ||
| 380 | if ((dropPrev || dropInner || dropNext) && oldDropNode !== dropNode) { | ||
| 381 | if (oldDropNode) { | ||
| 382 | this.$emit('node-drag-leave', draggingNode.node, oldDropNode.node, event); | ||
| 383 | } | ||
| 384 | this.$emit('node-drag-enter', draggingNode.node, dropNode.node, event); | ||
| 385 | } | ||
| 386 | |||
| 387 | if (dropPrev || dropInner || dropNext) { | ||
| 388 | dragState.dropNode = dropNode; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (dropNode.node.nextSibling === draggingNode.node) { | ||
| 392 | dropNext = false; | ||
| 393 | } | ||
| 394 | if (dropNode.node.previousSibling === draggingNode.node) { | ||
| 395 | dropPrev = false; | ||
| 396 | } | ||
| 397 | if (dropNode.node.contains(draggingNode.node, false)) { | ||
| 398 | dropInner = false; | ||
| 399 | } | ||
| 400 | if (draggingNode.node === dropNode.node || draggingNode.node.contains(dropNode.node)) { | ||
| 401 | dropPrev = false; | ||
| 402 | dropInner = false; | ||
| 403 | dropNext = false; | ||
| 404 | } | ||
| 405 | |||
| 406 | const targetPosition = dropNode.$el.getBoundingClientRect(); | ||
| 407 | const treePosition = this.$el.getBoundingClientRect(); | ||
| 408 | |||
| 409 | let dropType; | ||
| 410 | const prevPercent = dropPrev ? (dropInner ? 0.25 : (dropNext ? 0.45 : 1)) : -1; | ||
| 411 | const nextPercent = dropNext ? (dropInner ? 0.75 : (dropPrev ? 0.55 : 0)) : 1; | ||
| 412 | |||
| 413 | let indicatorTop = -9999; | ||
| 414 | const distance = event.clientY - targetPosition.top; | ||
| 415 | if (distance < targetPosition.height * prevPercent) { | ||
| 416 | dropType = 'before'; | ||
| 417 | } else if (distance > targetPosition.height * nextPercent) { | ||
| 418 | dropType = 'after'; | ||
| 419 | } else if (dropInner) { | ||
| 420 | dropType = 'inner'; | ||
| 421 | } else { | ||
| 422 | dropType = 'none'; | ||
| 423 | } | ||
| 424 | |||
| 425 | const iconPosition = dropNode.$el.querySelector('.el-tree-node__expand-icon').getBoundingClientRect(); | ||
| 426 | const dropIndicator = this.$refs.dropIndicator; | ||
| 427 | if (dropType === 'before') { | ||
| 428 | indicatorTop = iconPosition.top - treePosition.top; | ||
| 429 | } else if (dropType === 'after') { | ||
| 430 | indicatorTop = iconPosition.bottom - treePosition.top; | ||
| 431 | } | ||
| 432 | dropIndicator.style.top = indicatorTop + 'px'; | ||
| 433 | dropIndicator.style.left = (iconPosition.right - treePosition.left) + 'px'; | ||
| 434 | |||
| 435 | if (dropType === 'inner') { | ||
| 436 | addClass(dropNode.$el, 'is-drop-inner'); | ||
| 437 | } else { | ||
| 438 | removeClass(dropNode.$el, 'is-drop-inner'); | ||
| 439 | } | ||
| 440 | |||
| 441 | dragState.showDropIndicator = dropType === 'before' || dropType === 'after'; | ||
| 442 | dragState.allowDrop = dragState.showDropIndicator || userAllowDropInner; | ||
| 443 | dragState.dropType = dropType; | ||
| 444 | this.$emit('node-drag-over', draggingNode.node, dropNode.node, event); | ||
| 445 | }); | ||
| 446 | |||
| 447 | this.$on('tree-node-drag-end', (event) => { | ||
| 448 | const { draggingNode, dropType, dropNode } = dragState; | ||
| 449 | event.preventDefault(); | ||
| 450 | event.dataTransfer.dropEffect = 'move'; | ||
| 451 | |||
| 452 | if (draggingNode && dropNode) { | ||
| 453 | const draggingNodeCopy = { data: draggingNode.node.data }; | ||
| 454 | if (dropType !== 'none') { | ||
| 455 | draggingNode.node.remove(); | ||
| 456 | } | ||
| 457 | if (dropType === 'before') { | ||
| 458 | dropNode.node.parent.insertBefore(draggingNodeCopy, dropNode.node); | ||
| 459 | } else if (dropType === 'after') { | ||
| 460 | dropNode.node.parent.insertAfter(draggingNodeCopy, dropNode.node); | ||
| 461 | } else if (dropType === 'inner') { | ||
| 462 | dropNode.node.insertChild(draggingNodeCopy); | ||
| 463 | } | ||
| 464 | if (dropType !== 'none') { | ||
| 465 | this.store.registerNode(draggingNodeCopy); | ||
| 466 | } | ||
| 467 | |||
| 468 | removeClass(dropNode.$el, 'is-drop-inner'); | ||
| 469 | |||
| 470 | this.$emit('node-drag-end', draggingNode.node, dropNode.node, dropType, event); | ||
| 471 | if (dropType !== 'none') { | ||
| 472 | this.$emit('node-drop', draggingNode.node, dropNode.node, dropType, event); | ||
| 473 | } | ||
| 474 | } | ||
| 475 | if (draggingNode && !dropNode) { | ||
| 476 | this.$emit('node-drag-end', draggingNode.node, null, dropType, event); | ||
| 477 | } | ||
| 478 | |||
| 479 | dragState.showDropIndicator = false; | ||
| 480 | dragState.draggingNode = null; | ||
| 481 | dragState.dropNode = null; | ||
| 482 | dragState.allowDrop = true; | ||
| 483 | }); | ||
| 484 | }, | ||
| 485 | |||
| 486 | mounted() { | ||
| 487 | this.initTabIndex(); | ||
| 488 | this.$el.addEventListener('keydown', this.handleKeydown); | ||
| 489 | }, | ||
| 490 | |||
| 491 | updated() { | ||
| 492 | this.treeItems = this.$el.querySelectorAll('[role=treeitem]'); | ||
| 493 | this.checkboxItems = this.$el.querySelectorAll('input[type=checkbox]'); | ||
| 494 | } | ||
| 495 | }; | ||
| 496 | </script> |
| 1 | <template> | 1 | <template> |
| 2 | <dialogBox title="配置常办项目" @submitForm="submitForm" saveButton="保存" :isFullscreen="false" width="50%" | 2 | <dialogBox title="配置常办项目" @submitForm="submitForm" saveButton="保存" :isFullscreen="false" width="50%" |
| 3 | @closeDialog="closeDialog" v-model="myValue"> | 3 | @closeDialog="closeDialog" v-model="myValue"> |
| 4 | <el-tree :data="projectList" show-checkbox node-key="id" :default-checked-keys="defaultCheckeds" ref="tree" | 4 | <Tree :data="projectList" show-checkbox node-key="id" :default-checked-keys="defaultCheckeds" ref="tree" |
| 5 | default-expand-all :props="defaultProps" @check-change="handleClick"></el-tree> | 5 | default-expand-all :props="defaultProps" @check-change="handleClick" /> |
| 6 | </dialogBox> | 6 | </dialogBox> |
| 7 | </template> | 7 | </template> |
| 8 | <script> | 8 | <script> |
| 9 | import { getMenuInfo } from "@/api/user.js"; | 9 | import { getMenuInfo } from "@/api/user.js"; |
| 10 | import Tree from "@/components/Tree/src/tree.vue" | ||
| 10 | import { saveFrequentProjectsList, getHomeFrequentProjects } from "@/api/user.js"; | 11 | import { saveFrequentProjectsList, getHomeFrequentProjects } from "@/api/user.js"; |
| 11 | export default { | 12 | export default { |
| 13 | components: { | ||
| 14 | Tree | ||
| 15 | }, | ||
| 12 | props: { | 16 | props: { |
| 13 | value: { type: Boolean, default: false }, | 17 | value: { type: Boolean, default: false }, |
| 14 | bindItem: { type: Array, default: [] } | 18 | bindItem: { type: Array, default: [] } |
| ... | @@ -73,8 +77,8 @@ export default { | ... | @@ -73,8 +77,8 @@ export default { |
| 73 | } | 77 | } |
| 74 | return arr | 78 | return arr |
| 75 | } | 79 | } |
| 76 | that.defaultCheckeds = lookForAllId() | 80 | this.defaultCheckeds = lookForAllId() |
| 77 | console.log(that.defaultCheckeds); | 81 | console.log(this.defaultCheckeds, 'that.defaultCheckedsthat.defaultCheckedsthat.defaultCheckeds'); |
| 78 | }, | 82 | }, |
| 79 | dealCheckedItem () { | 83 | dealCheckedItem () { |
| 80 | }, | 84 | }, | ... | ... |
| ... | @@ -6,7 +6,7 @@ | ... | @@ -6,7 +6,7 @@ |
| 6 | <el-card shadow="hover" :body-style="{ padding: '0' }" style="height:260px"> | 6 | <el-card shadow="hover" :body-style="{ padding: '0' }" style="height:260px"> |
| 7 | <div slot="header" class="flexst"> | 7 | <div slot="header" class="flexst"> |
| 8 | <h5 class="title">常办项目</h5> | 8 | <h5 class="title">常办项目</h5> |
| 9 | <el-button type="primary" @click="setFrequencyProject()">配置常办</el-button> | 9 | <el-button type="primary" @click="setFrequencyProject">配置常办</el-button> |
| 10 | </div> | 10 | </div> |
| 11 | <ul class="workbench flexst"> | 11 | <ul class="workbench flexst"> |
| 12 | <li v-for="(item, index) in projectList" @click="handleProject(item)" class="pointer" :key="index" | 12 | <li v-for="(item, index) in projectList" @click="handleProject(item)" class="pointer" :key="index" | ... | ... |
-
Please register or sign in to post a comment