update the master branch to the latest
This commit is contained in:
parent
67d534df27
commit
23a0ba9831
611 changed files with 122648 additions and 0 deletions
2
src/components/tree/index.js
Normal file
2
src/components/tree/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Tree from './tree.vue';
|
||||
export default Tree;
|
220
src/components/tree/node.vue
Normal file
220
src/components/tree/node.vue
Normal file
|
@ -0,0 +1,220 @@
|
|||
<template>
|
||||
<collapse-transition :appear="appear">
|
||||
<ul :class="classes">
|
||||
<li>
|
||||
<span :class="arrowClasses" @click="handleExpand">
|
||||
<Icon v-if="showArrow" :type="arrowType" :custom="customArrowType" :size="arrowSize" />
|
||||
<Icon v-if="showLoading" type="ios-loading" class="ivu-load-loop" />
|
||||
</span>
|
||||
<Checkbox
|
||||
v-if="showCheckbox"
|
||||
:value="data.checked"
|
||||
:indeterminate="data.indeterminate"
|
||||
:disabled="data.disabled || data.disableCheckbox"
|
||||
@click.native.prevent="handleCheck"></Checkbox>
|
||||
<Render v-if="data.render" :render="data.render" :data="data" :node="node"></Render>
|
||||
<Render v-else-if="isParentRender" :render="parentRender" :data="data" :node="node"></Render>
|
||||
<span v-else :class="titleClasses" @click="handleSelect">{{ data.title }}</span>
|
||||
<Tree-node
|
||||
v-if="data.expand"
|
||||
:appear="appearByClickArrow"
|
||||
v-for="(item, i) in children"
|
||||
:key="i"
|
||||
:data="item"
|
||||
:multiple="multiple"
|
||||
:show-checkbox="showCheckbox"
|
||||
:children-key="childrenKey">
|
||||
</Tree-node>
|
||||
</li>
|
||||
</ul>
|
||||
</collapse-transition>
|
||||
</template>
|
||||
<script>
|
||||
import Checkbox from '../checkbox/checkbox.vue';
|
||||
import Icon from '../icon/icon.vue';
|
||||
import Render from './render';
|
||||
import CollapseTransition from '../base/collapse-transition';
|
||||
import Emitter from '../../mixins/emitter';
|
||||
import { findComponentUpward } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-tree';
|
||||
|
||||
export default {
|
||||
name: 'TreeNode',
|
||||
mixins: [ Emitter ],
|
||||
inject: ['TreeInstance'],
|
||||
components: { Checkbox, Icon, CollapseTransition, Render },
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
childrenKey: {
|
||||
type: String,
|
||||
default: 'children'
|
||||
},
|
||||
showCheckbox: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
appear: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls,
|
||||
appearByClickArrow: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}-children`
|
||||
];
|
||||
},
|
||||
selectedCls () {
|
||||
return [
|
||||
{
|
||||
[`${prefixCls}-node-selected`]: this.data.selected
|
||||
}
|
||||
];
|
||||
},
|
||||
arrowClasses () {
|
||||
return [
|
||||
`${prefixCls}-arrow`,
|
||||
{
|
||||
[`${prefixCls}-arrow-disabled`]: this.data.disabled,
|
||||
[`${prefixCls}-arrow-open`]: this.data.expand
|
||||
}
|
||||
];
|
||||
},
|
||||
titleClasses () {
|
||||
return [
|
||||
`${prefixCls}-title`,
|
||||
{
|
||||
[`${prefixCls}-title-selected`]: this.data.selected
|
||||
}
|
||||
];
|
||||
},
|
||||
showArrow () {
|
||||
return (this.data[this.childrenKey] && this.data[this.childrenKey].length) || ('loading' in this.data && !this.data.loading);
|
||||
},
|
||||
showLoading () {
|
||||
return 'loading' in this.data && this.data.loading;
|
||||
},
|
||||
isParentRender () {
|
||||
const Tree = findComponentUpward(this, 'Tree');
|
||||
return Tree && Tree.render;
|
||||
},
|
||||
parentRender () {
|
||||
const Tree = findComponentUpward(this, 'Tree');
|
||||
if (Tree && Tree.render) {
|
||||
return Tree.render;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
node () {
|
||||
const Tree = findComponentUpward(this, 'Tree');
|
||||
if (Tree) {
|
||||
// 将所有的 node(即flatState)和当前 node 都传递
|
||||
return [Tree.flatState, Tree.flatState.find(item => item.nodeKey === this.data.nodeKey)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
children () {
|
||||
return this.data[this.childrenKey];
|
||||
},
|
||||
// 3.4.0, global setting customArrow 有值时,arrow 赋值空
|
||||
arrowType () {
|
||||
let type = 'ios-arrow-forward';
|
||||
|
||||
if (this.$IVIEW) {
|
||||
if (this.$IVIEW.tree.customArrow) {
|
||||
type = '';
|
||||
} else if (this.$IVIEW.tree.arrow) {
|
||||
type = this.$IVIEW.tree.arrow;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
},
|
||||
// 3.4.0, global setting
|
||||
customArrowType () {
|
||||
let type = '';
|
||||
|
||||
if (this.$IVIEW) {
|
||||
if (this.$IVIEW.tree.customArrow) {
|
||||
type = this.$IVIEW.tree.customArrow;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
},
|
||||
// 3.4.0, global setting
|
||||
arrowSize () {
|
||||
let size = '';
|
||||
|
||||
if (this.$IVIEW) {
|
||||
if (this.$IVIEW.tree.arrowSize) {
|
||||
size = this.$IVIEW.tree.arrowSize;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleExpand () {
|
||||
const item = this.data;
|
||||
if (item.disabled) return;
|
||||
|
||||
// Vue.js 2.6.9 对 transition 的 appear 进行了调整,导致 iView 初始化时无动画,加此方法来判断通过点击箭头展开时,加 appear,否则初始渲染时 appear 为 false
|
||||
this.appearByClickArrow = true;
|
||||
|
||||
// async loading
|
||||
if (item[this.childrenKey].length === 0) {
|
||||
const tree = findComponentUpward(this, 'Tree');
|
||||
if (tree && tree.loadData) {
|
||||
this.$set(this.data, 'loading', true);
|
||||
tree.loadData(item, children => {
|
||||
this.$set(this.data, 'loading', false);
|
||||
if (children.length) {
|
||||
this.$set(this.data, this.childrenKey, children);
|
||||
this.$nextTick(() => this.handleExpand());
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (item[this.childrenKey] && item[this.childrenKey].length) {
|
||||
this.$set(this.data, 'expand', !this.data.expand);
|
||||
this.dispatch('Tree', 'toggle-expand', this.data);
|
||||
}
|
||||
},
|
||||
handleSelect () {
|
||||
if (this.data.disabled) return;
|
||||
if (this.TreeInstance.showCheckbox && this.TreeInstance.checkDirectly) {
|
||||
this.handleCheck();
|
||||
} else {
|
||||
this.dispatch('Tree', 'on-selected', this.data.nodeKey);
|
||||
}
|
||||
},
|
||||
handleCheck () {
|
||||
if (this.data.disabled) return;
|
||||
const changes = {
|
||||
checked: !this.data.checked && !this.data.indeterminate,
|
||||
nodeKey: this.data.nodeKey
|
||||
};
|
||||
this.dispatch('Tree', 'on-check', changes);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
17
src/components/tree/render.js
Normal file
17
src/components/tree/render.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
export default {
|
||||
name: 'RenderCell',
|
||||
functional: true,
|
||||
props: {
|
||||
render: Function,
|
||||
data: Object,
|
||||
node: Array
|
||||
},
|
||||
render: (h, ctx) => {
|
||||
const params = {
|
||||
root: ctx.props.node[0],
|
||||
node: ctx.props.node[1],
|
||||
data: ctx.props.data
|
||||
};
|
||||
return ctx.props.render(h, params);
|
||||
}
|
||||
};
|
204
src/components/tree/tree.vue
Normal file
204
src/components/tree/tree.vue
Normal file
|
@ -0,0 +1,204 @@
|
|||
<template>
|
||||
<div :class="prefixCls">
|
||||
<Tree-node
|
||||
v-for="(item, i) in stateTree"
|
||||
:key="i"
|
||||
:data="item"
|
||||
visible
|
||||
:multiple="multiple"
|
||||
:show-checkbox="showCheckbox"
|
||||
:children-key="childrenKey">
|
||||
</Tree-node>
|
||||
<div :class="[prefixCls + '-empty']" v-if="!stateTree.length">{{ localeEmptyText }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import TreeNode from './node.vue';
|
||||
import Emitter from '../../mixins/emitter';
|
||||
import Locale from '../../mixins/locale';
|
||||
|
||||
const prefixCls = 'ivu-tree';
|
||||
|
||||
export default {
|
||||
name: 'Tree',
|
||||
mixins: [ Emitter, Locale ],
|
||||
components: { TreeNode },
|
||||
provide () {
|
||||
return { TreeInstance: this };
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showCheckbox: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
checkStrictly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 当开启 showCheckbox 时,如果开启 checkDirectly,select 将强制转为 check 事件
|
||||
checkDirectly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
emptyText: {
|
||||
type: String
|
||||
},
|
||||
childrenKey: {
|
||||
type: String,
|
||||
default: 'children'
|
||||
},
|
||||
loadData: {
|
||||
type: Function
|
||||
},
|
||||
render: {
|
||||
type: Function
|
||||
},
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls,
|
||||
stateTree: this.data,
|
||||
flatState: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
deep: true,
|
||||
handler () {
|
||||
this.stateTree = this.data;
|
||||
this.flatState = this.compileFlatState();
|
||||
this.rebuildTree();
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
localeEmptyText () {
|
||||
if (typeof this.emptyText === 'undefined') {
|
||||
return this.t('i.tree.emptyText');
|
||||
} else {
|
||||
return this.emptyText;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
compileFlatState () { // so we have always a relation parent/children of each node
|
||||
let keyCounter = 0;
|
||||
let childrenKey = this.childrenKey;
|
||||
const flatTree = [];
|
||||
function flattenChildren(node, parent) {
|
||||
node.nodeKey = keyCounter++;
|
||||
flatTree[node.nodeKey] = { node: node, nodeKey: node.nodeKey };
|
||||
if (typeof parent != 'undefined') {
|
||||
flatTree[node.nodeKey].parent = parent.nodeKey;
|
||||
flatTree[parent.nodeKey][childrenKey].push(node.nodeKey);
|
||||
}
|
||||
|
||||
if (node[childrenKey]) {
|
||||
flatTree[node.nodeKey][childrenKey] = [];
|
||||
node[childrenKey].forEach(child => flattenChildren(child, node));
|
||||
}
|
||||
}
|
||||
this.stateTree.forEach(rootNode => {
|
||||
flattenChildren(rootNode);
|
||||
});
|
||||
return flatTree;
|
||||
},
|
||||
updateTreeUp(nodeKey){
|
||||
const parentKey = this.flatState[nodeKey].parent;
|
||||
if (typeof parentKey == 'undefined' || this.checkStrictly) return;
|
||||
|
||||
const node = this.flatState[nodeKey].node;
|
||||
const parent = this.flatState[parentKey].node;
|
||||
if (node.checked == parent.checked && node.indeterminate == parent.indeterminate) return; // no need to update upwards
|
||||
|
||||
if (node.checked == true) {
|
||||
this.$set(parent, 'checked', parent[this.childrenKey].every(node => node.checked));
|
||||
this.$set(parent, 'indeterminate', !parent.checked);
|
||||
} else {
|
||||
this.$set(parent, 'checked', false);
|
||||
this.$set(parent, 'indeterminate', parent[this.childrenKey].some(node => node.checked || node.indeterminate));
|
||||
}
|
||||
this.updateTreeUp(parentKey);
|
||||
},
|
||||
rebuildTree () { // only called when `data` prop changes
|
||||
const checkedNodes = this.getCheckedNodes();
|
||||
checkedNodes.forEach(node => {
|
||||
this.updateTreeDown(node, {checked: true});
|
||||
// propagate upwards
|
||||
const parentKey = this.flatState[node.nodeKey].parent;
|
||||
if (!parentKey && parentKey !== 0) return;
|
||||
const parent = this.flatState[parentKey].node;
|
||||
const childHasCheckSetter = typeof node.checked != 'undefined' && node.checked;
|
||||
if (childHasCheckSetter && parent.checked != node.checked) {
|
||||
this.updateTreeUp(node.nodeKey); // update tree upwards
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getSelectedNodes () {
|
||||
/* public API */
|
||||
return this.flatState.filter(obj => obj.node.selected).map(obj => obj.node);
|
||||
},
|
||||
getCheckedNodes () {
|
||||
/* public API */
|
||||
return this.flatState.filter(obj => obj.node.checked).map(obj => obj.node);
|
||||
},
|
||||
getCheckedAndIndeterminateNodes () {
|
||||
/* public API */
|
||||
return this.flatState.filter(obj => (obj.node.checked || obj.node.indeterminate)).map(obj => obj.node);
|
||||
},
|
||||
updateTreeDown(node, changes = {}) {
|
||||
if (this.checkStrictly) return;
|
||||
|
||||
for (let key in changes) {
|
||||
this.$set(node, key, changes[key]);
|
||||
}
|
||||
if (node[this.childrenKey]) {
|
||||
node[this.childrenKey].forEach(child => {
|
||||
this.updateTreeDown(child, changes);
|
||||
});
|
||||
}
|
||||
},
|
||||
handleSelect (nodeKey) {
|
||||
const node = this.flatState[nodeKey].node;
|
||||
if (!this.multiple){ // reset previously selected node
|
||||
const currentSelectedKey = this.flatState.findIndex(obj => obj.node.selected);
|
||||
if (currentSelectedKey >= 0 && currentSelectedKey !== nodeKey) this.$set(this.flatState[currentSelectedKey].node, 'selected', false);
|
||||
}
|
||||
this.$set(node, 'selected', !node.selected);
|
||||
|
||||
this.$emit('on-select-change', this.getSelectedNodes(), node);
|
||||
},
|
||||
handleCheck({ checked, nodeKey }) {
|
||||
const node = this.flatState[nodeKey].node;
|
||||
this.$set(node, 'checked', checked);
|
||||
this.$set(node, 'indeterminate', false);
|
||||
|
||||
this.updateTreeUp(nodeKey); // propagate up
|
||||
this.updateTreeDown(node, {checked, indeterminate: false}); // reset `indeterminate` when going down
|
||||
|
||||
this.$emit('on-check-change', this.getCheckedNodes(), node);
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.flatState = this.compileFlatState();
|
||||
this.rebuildTree();
|
||||
},
|
||||
mounted () {
|
||||
this.$on('on-check', this.handleCheck);
|
||||
this.$on('on-selected', this.handleSelect);
|
||||
this.$on('toggle-expand', node => this.$emit('on-toggle-expand', node));
|
||||
}
|
||||
};
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue