Skip to content

Commit

Permalink
feat: tree改变级联字段和增加级联显示模式
Browse files Browse the repository at this point in the history
  • Loading branch information
wanchun committed Dec 23, 2021
1 parent 62a3937 commit d6478b4
Show file tree
Hide file tree
Showing 13 changed files with 398 additions and 262 deletions.
82 changes: 54 additions & 28 deletions components/select-tree/selectTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
:selectable="treeSelectable"
:checkable="treeCheckable"
:checkStrictly="checkStrictly"
:cascade="cascade"
:multiple="multiple"
:childrenField="childrenField"
:valueField="valueField"
Expand All @@ -55,8 +56,8 @@
:inline="inline"
:remote="remote"
:loadData="loadData"
@update:selectedKeys="handleSelect"
@update:checkedKeys="handleSelect"
@select="handleSelect"
@check="handleCheck"
></Tree>
<div v-show="!data.length" :class="`${prefixCls}-null`">
{{ emptyText }}
Expand All @@ -79,7 +80,7 @@ import Tree from '../tree';
import Scrollbar from '../scrollbar';
import SELECT_PROPS from '../select/props';
import TREE_PROPS from '../tree/props';
import { flatNodes } from '../_util/utils';
import useData from '../tree/useData';
const prefixCls = getPrefixCls('select-tree');
Expand Down Expand Up @@ -123,7 +124,9 @@ export default defineComponent({
validate(CHANGE_EVENT);
});
const nodes = computed(() => flatNodes(props.data));
const { nodeList, getChildrenByValues, getParentByValues } =
useData(props);
const treeSelectable = computed(() => !props.multiple);
const treeCheckable = computed(() => props.multiple);
const selectedKeys = computed(() => {
Expand All @@ -132,57 +135,79 @@ export default defineComponent({
return [];
});
const checkedKeys = computed(() => {
if (props.multiple) return currentValue.value;
if (props.multiple) {
if (!props.cascade) {
return currentValue.value;
}
if (props.checkStrictly === 'all') {
return currentValue.value;
}
if (props.checkStrictly === 'parent') {
return getChildrenByValues(currentValue.value);
}
if (props.checkStrictly === 'child') {
return getParentByValues(currentValue.value);
}
}
return [];
});
watch(
() => props.checkStrictly,
() => {
if (props.multiple && props.cascade) {
updateCurrentValue([]);
}
},
);
const handleClear = () => {
const value = props.multiple ? [] : null;
updateCurrentValue(value);
emit('clear');
};
const handleSelect = (value) => {
const handleSelect = (data) => {
if (props.disabled) return;
filterText.value = '';
if (!props.multiple) {
updateCurrentValue(value[0]);
updateCurrentValue(data.selectedKeys[0]);
isOpened.value = false;
} else {
updateCurrentValue(value);
updateCurrentValue(data.selectedKeys);
}
};
const handleCheck = (data) => {
if (props.disabled) return;
filterText.value = '';
if (!props.multiple) {
updateCurrentValue(data.checkedKeys[0]);
isOpened.value = false;
} else {
updateCurrentValue(data.checkedKeys);
}
};
const handleRemove = (value) => {
if (!props.multiple) {
return;
}
const arr = currentValue.value;
const findIndex = arr.indexOf(value);
const findIndex = currentValue.value.indexOf(value);
if (findIndex !== -1) {
emit('removeTag', value);
arr.splice(findIndex, 1);
updateCurrentValue(arr);
// arrayModel会自动添加或者删除
updateCurrentValue(value);
}
};
const selectedOptions = computed(() =>
nodes.value
.map((option) => {
const value = option[props.valueField];
const label = option[props.labelField];
return {
...option,
value,
label,
};
})
.filter((option) => {
if (props.multiple) {
return currentValue.value.includes(option.value);
}
return [currentValue.value].includes(option.value);
}),
Object.values(nodeList).filter((option) => {
if (props.multiple) {
return currentValue.value.includes(option.value);
}
return [currentValue.value].includes(option.value);
}),
);
const focus = (e) => {
Expand Down Expand Up @@ -235,6 +260,7 @@ export default defineComponent({
selectedKeys,
treeCheckable,
handleSelect,
handleCheck,
checkedKeys,
refTree,
filterMethod,
Expand Down
6 changes: 6 additions & 0 deletions components/tree/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ export const PROVIDE_KEY = Symbol('FTree');
export const COMPONENT_NAME = {
TREE: 'FTree',
};

export const CHECK_STRATEGY = {
ALL: 'all',
PARENT: 'parent',
CHILD: 'child',
};
13 changes: 11 additions & 2 deletions components/tree/props.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CHECK_STRATEGY } from './const';

const PROPS = {
data: {
type: Array,
Expand Down Expand Up @@ -30,14 +32,21 @@ const PROPS = {
return [];
},
},
checkable: {
cascade: {
type: Boolean,
default: false,
},
checkStrictly: {
checkable: {
type: Boolean,
default: false,
},
checkStrictly: {
type: String,
default: 'all',
validator(value) {
return Object.values(CHECK_STRATEGY).includes(value);
},
},
checkedKeys: {
type: Array,
default() {
Expand Down
139 changes: 55 additions & 84 deletions components/tree/tree.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { defineComponent, computed, provide, onMounted, ref, watch } from 'vue';
import { isFunction, isString, isNil, cloneDeep } from 'lodash-es';
import { defineComponent, computed, provide, onMounted } from 'vue';
import { isFunction, isString, cloneDeep } from 'lodash-es';
import getPrefixCls from '../_util/getPrefixCls';
import { useTheme } from '../_theme/useTheme';
import { useNormalModel } from '../_util/use/useModel';
import TreeNode from './treeNode';
import { PROVIDE_KEY, COMPONENT_NAME } from './const';
import useFilter from './useFilter';
import { PROVIDE_KEY, COMPONENT_NAME, CHECK_STRATEGY } from './const';
import useData from './useData';
import useState from './useState';
import PROPS from './props';

const prefixCls = getPrefixCls('tree');
Expand All @@ -26,81 +26,24 @@ export default defineComponent({
],
setup(props, { emit, expose }) {
useTheme();
const nodeList = {};
const addNode = (value, item) => {
if (!nodeList[value]) {
nodeList[value] = item;
}
};
const currentData = ref([]);
const transformNode = (item, indexPath, level) => {
const copy = { ...item };
const value = copy[props.valueField];
const label = copy[props.labelField];
const children = copy[props.childrenField];
const hasChildren = Array.isArray(children) && children.length;
let isLeaf;
if (!isNil(copy.isLeaf)) {
isLeaf = copy.isLeaf;
} else if (hasChildren) {
isLeaf = false;
} else if (props.remote) {
isLeaf = false;
} else {
isLeaf = true;
}
copy.origin = item;
copy.value = value;
copy.label = label;
copy.isLeaf = isLeaf;
// 处理indexPath
copy.indexPath = [...indexPath, value];
copy.level = level;
copy.hasChildren = hasChildren;
// 扁平化
addNode(value, copy);
return copy;
};
const handleData = (arr, indexPath = [], level = 1) =>
arr.map((item) => {
const copy = transformNode(item, indexPath, level);
if (copy.hasChildren) {
copy.children = handleData(
copy.children,
copy.indexPath,
level + 1,
);
}
return copy;
});

watch(
() => props.data,
() => {
currentData.value = handleData(props.data);
},
{
immediate: true,
deep: true,
},
);
const { nodeList, currentData, handleData } = useData(props);

const {
currentExpandedKeys,
updateExpandedKeys,
currentCheckedKeys,
updateCheckedKeys,
currentSelectedKeys,
updateSelectedKeys,
filter,
hiddenKeys,
hasSelected,
hasChecked,
hasIndeterminate,
hasExpanded,
} = useState(props, { emit });

const [currentExpandedKeys, updateExpandedKeys] = useNormalModel(
props,
emit,
{ prop: 'expandedKeys', isEqual: true },
);
const [currentCheckedKeys, updateCheckedKeys] = useNormalModel(
props,
emit,
{ prop: 'checkedKeys', isEqual: true },
);
const [currentSelectedKeys, updateSelectedKeys] = useNormalModel(
props,
emit,
{ prop: 'selectedKeys', isEqual: true },
);
const { filter, hiddenKeys, filteredExpandedKeys } = useFilter(props);
onMounted(() => {
if (
props.defaultExpandAll &&
Expand All @@ -111,6 +54,7 @@ export default defineComponent({
);
}
});

const selectNode = (val, event) => {
const node = nodeList[val];
const values = cloneDeep(currentSelectedKeys.value);
Expand All @@ -137,6 +81,7 @@ export default defineComponent({
selected: values.includes(val),
});
};

const expandNode = (val, event) => {
const node = nodeList[val];
let values = cloneDeep(currentExpandedKeys.value);
Expand All @@ -160,6 +105,30 @@ export default defineComponent({
expanded: values.includes(val),
});
};

function getCheckedKeys(arr) {
return props.cascade
? arr.filter((key) => {
const node = nodeList[key];
if (props.checkStrictly === CHECK_STRATEGY.ALL) {
return true;
}
if (props.checkStrictly === CHECK_STRATEGY.PARENT) {
return (
!node.isLeaf &&
!hasIndeterminate(node) &&
node.indexPath.filter((_key) =>
arr.includes(_key),
).length === 1
);
}
if (props.checkStrictly === CHECK_STRATEGY.CHILD) {
return node.isLeaf;
}
return true;
})
: arr;
}
function handleChildren(arr, children, isAdd) {
children.forEach((child) => {
const index = arr.indexOf(child.value);
Expand Down Expand Up @@ -200,7 +169,7 @@ export default defineComponent({
const { isLeaf, children, indexPath } = node;
const values = cloneDeep(currentCheckedKeys.value);
const index = values.indexOf(val);
if (props.checkStrictly) {
if (!props.cascade) {
if (index !== -1) {
values.splice(index, 1);
} else {
Expand All @@ -219,9 +188,10 @@ export default defineComponent({
handleChildren(values, children, true);
}
}

updateCheckedKeys(values);
emit('check', {
checkedKeys: values,
checkedKeys: getCheckedKeys(values),
event,
node,
checked: values.includes(val),
Expand All @@ -240,12 +210,12 @@ export default defineComponent({
provide(PROVIDE_KEY, {
props,
selectNode,
currentSelectedKeys,
expandNode,
currentExpandedKeys,
checkNode,
currentCheckedKeys,
filteredExpandedKeys,
hasSelected,
hasChecked,
hasIndeterminate,
hasExpanded,
});

const classList = computed(() => [prefixCls].filter(Boolean).join(' '));
Expand Down Expand Up @@ -285,6 +255,7 @@ export default defineComponent({
);
});
const renderTreeNode = () => renderChildren(currentData.value);

return () => (
<div class={classList.value} role="tree">
{renderTreeNode()}
Expand Down
Loading

0 comments on commit d6478b4

Please sign in to comment.