2021-08-17 14:03:49 +00:00
|
|
|
|
import { VNode, PropType, DefineComponent, ExtractPropTypes, ref } from 'vue';
|
|
|
|
|
import { defineComponent } from 'vue';
|
2020-08-31 08:53:19 +00:00
|
|
|
|
import classNames from '../_util/classNames';
|
2021-08-17 14:03:49 +00:00
|
|
|
|
import VcTree, { TreeNode } from '../vc-tree';
|
2019-01-12 03:33:27 +00:00
|
|
|
|
import animation from '../_util/openAnimation';
|
|
|
|
|
import PropTypes from '../_util/vue-types';
|
2021-08-17 14:03:49 +00:00
|
|
|
|
import { filterEmpty } from '../_util/props-util';
|
2020-10-22 06:25:02 +00:00
|
|
|
|
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
2021-08-17 14:03:49 +00:00
|
|
|
|
import { DataNode, FieldNames, Key } from '../vc-tree/interface';
|
|
|
|
|
import { treeProps as vcTreeProps } from '../vc-tree/props';
|
|
|
|
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
|
|
|
|
import renderSwitcherIcon from './utils/iconUtil';
|
|
|
|
|
import dropIndicatorRender from './utils/dropIndicator';
|
2018-09-26 14:57:01 +00:00
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntdTreeNodeAttribute {
|
|
|
|
|
eventKey: string;
|
|
|
|
|
prefixCls: string;
|
|
|
|
|
className: string;
|
|
|
|
|
expanded: boolean;
|
|
|
|
|
selected: boolean;
|
|
|
|
|
checked: boolean;
|
|
|
|
|
halfChecked: boolean;
|
|
|
|
|
children: any;
|
|
|
|
|
title: any;
|
|
|
|
|
pos: string;
|
|
|
|
|
dragOver: boolean;
|
|
|
|
|
dragOverGapTop: boolean;
|
|
|
|
|
dragOverGapBottom: boolean;
|
|
|
|
|
isLeaf: boolean;
|
|
|
|
|
selectable: boolean;
|
|
|
|
|
disabled: boolean;
|
|
|
|
|
disableCheckbox: boolean;
|
|
|
|
|
}
|
2020-11-18 09:37:15 +00:00
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntTreeNodeProps {
|
|
|
|
|
className?: string;
|
|
|
|
|
checkable?: boolean;
|
|
|
|
|
disabled?: boolean;
|
|
|
|
|
disableCheckbox?: boolean;
|
|
|
|
|
title?: string | any;
|
|
|
|
|
key?: Key;
|
|
|
|
|
eventKey?: string;
|
2020-11-18 09:37:15 +00:00
|
|
|
|
isLeaf?: boolean;
|
2021-08-17 14:03:49 +00:00
|
|
|
|
checked?: boolean;
|
|
|
|
|
expanded?: boolean;
|
|
|
|
|
loading?: boolean;
|
|
|
|
|
selected?: boolean;
|
2020-11-18 09:37:15 +00:00
|
|
|
|
selectable?: boolean;
|
2021-08-17 14:03:49 +00:00
|
|
|
|
icon?: ((treeNode: AntdTreeNodeAttribute) => any) | VNode;
|
|
|
|
|
children?: any;
|
|
|
|
|
[customProp: string]: any;
|
2020-11-18 09:37:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntTreeNode extends DefineComponent<AntTreeNodeProps, {}> {}
|
|
|
|
|
|
|
|
|
|
export interface AntTreeNodeBaseEvent {
|
|
|
|
|
node: AntTreeNode;
|
2020-11-18 09:37:15 +00:00
|
|
|
|
nativeEvent: MouseEvent;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntTreeNodeCheckedEvent extends AntTreeNodeBaseEvent {
|
|
|
|
|
event: 'check';
|
|
|
|
|
checked?: boolean;
|
|
|
|
|
checkedNodes?: AntTreeNode[];
|
2020-11-18 09:37:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntTreeNodeSelectedEvent extends AntTreeNodeBaseEvent {
|
|
|
|
|
event: 'select';
|
|
|
|
|
selected?: boolean;
|
|
|
|
|
selectedNodes?: DataNode[];
|
2020-11-18 09:37:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntTreeNodeExpandedEvent extends AntTreeNodeBaseEvent {
|
|
|
|
|
expanded?: boolean;
|
2021-01-26 15:48:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntTreeNodeMouseEvent {
|
|
|
|
|
node: AntTreeNode;
|
2021-01-26 15:48:48 +00:00
|
|
|
|
event: DragEvent;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export interface AntTreeNodeDragEnterEvent extends AntTreeNodeMouseEvent {
|
|
|
|
|
expandedKeys: Key[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AntTreeNodeDropEvent {
|
|
|
|
|
node: AntTreeNode;
|
|
|
|
|
dragNode: AntTreeNode;
|
|
|
|
|
dragNodesKeys: Key[];
|
2021-01-26 15:48:48 +00:00
|
|
|
|
dropPosition: number;
|
2021-08-17 14:03:49 +00:00
|
|
|
|
dropToGap?: boolean;
|
|
|
|
|
event: MouseEvent;
|
2020-11-18 09:37:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
// [Legacy] Compatible for v2
|
|
|
|
|
export type TreeDataItem = DataNode;
|
|
|
|
|
|
|
|
|
|
export const treeProps = () => {
|
2018-09-26 14:57:01 +00:00
|
|
|
|
return {
|
2021-08-17 14:03:49 +00:00
|
|
|
|
...vcTreeProps(),
|
|
|
|
|
showLine: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 是否支持多选 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
multiple: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 是否自动展开父节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
autoExpandParent: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
2021-08-17 14:03:49 +00:00
|
|
|
|
checkStrictly: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 是否支持选中 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
checkable: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 是否禁用树 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
disabled: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 默认展开所有树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
defaultExpandAll: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 默认展开对应树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
defaultExpandParent: { type: Boolean, default: undefined },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 默认展开指定的树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
defaultExpandedKeys: { type: Array as PropType<Key[]> },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** (受控)展开指定的树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
expandedKeys: { type: Array as PropType<Key[]> },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** (受控)选中复选框的树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
checkedKeys: {
|
|
|
|
|
type: [Array, Object] as PropType<Key[] | { checked: Key[]; halfChecked: Key[] }>,
|
|
|
|
|
},
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 默认选中复选框的树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
defaultCheckedKeys: { type: Array as PropType<Key[]> },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** (受控)设置选中的树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
selectedKeys: { type: Array as PropType<Key[]> },
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** 默认选中的树节点 */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
defaultSelectedKeys: { type: Array as PropType<Key[]> },
|
|
|
|
|
selectable: { type: Boolean, default: undefined },
|
2020-08-12 10:07:41 +00:00
|
|
|
|
|
2018-09-26 14:57:01 +00:00
|
|
|
|
/** filter some AntTreeNodes as you need. it should return true */
|
2021-08-17 14:03:49 +00:00
|
|
|
|
filterAntTreeNode: { type: Function as PropType<(node: AntTreeNode) => boolean> },
|
|
|
|
|
loadedKeys: { type: Array as PropType<Key[]> },
|
|
|
|
|
draggable: { type: Boolean, default: undefined },
|
|
|
|
|
showIcon: { type: Boolean, default: undefined },
|
|
|
|
|
icon: { type: Function as PropType<(nodeProps: AntdTreeNodeAttribute) => any> },
|
2019-04-10 05:28:07 +00:00
|
|
|
|
switcherIcon: PropTypes.any,
|
2018-09-26 14:57:01 +00:00
|
|
|
|
prefixCls: PropTypes.string,
|
2019-11-15 07:05:38 +00:00
|
|
|
|
/**
|
|
|
|
|
* @default{title,key,children}
|
2021-08-17 14:03:49 +00:00
|
|
|
|
* deprecated, please use `fieldNames` instead
|
2019-11-15 07:05:38 +00:00
|
|
|
|
* 替换treeNode中 title,key,children字段为treeData中对应的字段
|
|
|
|
|
*/
|
2021-08-17 14:03:49 +00:00
|
|
|
|
replaceFields: { type: Object as PropType<FieldNames> },
|
|
|
|
|
blockNode: { type: Boolean, default: undefined },
|
2019-01-12 03:33:27 +00:00
|
|
|
|
};
|
2021-08-17 14:03:49 +00:00
|
|
|
|
};
|
2018-09-26 14:57:01 +00:00
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
export type TreeProps = Partial<ExtractPropTypes<ReturnType<typeof treeProps>>>;
|
2018-09-26 14:57:01 +00:00
|
|
|
|
|
2020-10-22 06:25:02 +00:00
|
|
|
|
export default defineComponent({
|
2018-09-26 14:57:01 +00:00
|
|
|
|
name: 'ATree',
|
2020-07-16 10:31:20 +00:00
|
|
|
|
inheritAttrs: false,
|
2021-08-17 14:03:49 +00:00
|
|
|
|
props: initDefaultProps(treeProps(), {
|
2018-09-26 14:57:01 +00:00
|
|
|
|
checkable: false,
|
2021-08-17 14:03:49 +00:00
|
|
|
|
selectable: true,
|
2018-09-26 14:57:01 +00:00
|
|
|
|
showIcon: false,
|
|
|
|
|
openAnimation: {
|
2020-07-16 10:31:20 +00:00
|
|
|
|
...animation,
|
2021-08-17 14:03:49 +00:00
|
|
|
|
appear: false,
|
2018-09-26 14:57:01 +00:00
|
|
|
|
},
|
2020-03-07 11:45:13 +00:00
|
|
|
|
blockNode: false,
|
2018-09-26 14:57:01 +00:00
|
|
|
|
}),
|
2021-08-17 14:03:49 +00:00
|
|
|
|
slots: ['icon', 'title', 'switcherIcon'],
|
|
|
|
|
emits: [
|
|
|
|
|
'update:selectedKeys',
|
|
|
|
|
'update:checkedKeys',
|
|
|
|
|
'update:expandedKeys',
|
|
|
|
|
'expand',
|
|
|
|
|
'select',
|
|
|
|
|
'check',
|
|
|
|
|
],
|
2018-09-26 14:57:01 +00:00
|
|
|
|
TreeNode,
|
2021-08-17 14:03:49 +00:00
|
|
|
|
setup(props, { attrs, expose, emit, slots }) {
|
|
|
|
|
const { prefixCls, direction, virtual } = useConfigInject('tree', props);
|
|
|
|
|
const tree = ref();
|
|
|
|
|
expose({
|
|
|
|
|
tree,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleCheck: TreeProps['onCheck'] = (checkedObjOrKeys, eventObj) => {
|
|
|
|
|
emit('update:checkedKeys', checkedObjOrKeys);
|
|
|
|
|
emit('check', checkedObjOrKeys, eventObj);
|
|
|
|
|
};
|
|
|
|
|
const handleExpand: TreeProps['onExpand'] = (expandedKeys, eventObj) => {
|
|
|
|
|
emit('update:expandedKeys', expandedKeys);
|
|
|
|
|
emit('expand', expandedKeys, eventObj);
|
|
|
|
|
};
|
|
|
|
|
const handleSelect: TreeProps['onSelect'] = (selectedKeys, eventObj) => {
|
|
|
|
|
emit('update:selectedKeys', selectedKeys);
|
|
|
|
|
emit('select', selectedKeys, eventObj);
|
|
|
|
|
};
|
|
|
|
|
return () => {
|
|
|
|
|
const {
|
|
|
|
|
showIcon,
|
|
|
|
|
showLine,
|
|
|
|
|
switcherIcon = slots.switcherIcon?.(),
|
|
|
|
|
icon = slots.icon,
|
|
|
|
|
blockNode,
|
|
|
|
|
checkable,
|
|
|
|
|
selectable,
|
|
|
|
|
fieldNames,
|
|
|
|
|
replaceFields,
|
|
|
|
|
} = props;
|
|
|
|
|
const newProps = {
|
|
|
|
|
...attrs,
|
|
|
|
|
...props,
|
|
|
|
|
showLine: Boolean(showLine),
|
|
|
|
|
dropIndicatorRender,
|
|
|
|
|
fieldNames: fieldNames || (replaceFields as FieldNames),
|
|
|
|
|
icon,
|
|
|
|
|
};
|
2020-03-07 11:45:13 +00:00
|
|
|
|
|
2021-08-17 14:03:49 +00:00
|
|
|
|
return (
|
|
|
|
|
<VcTree
|
|
|
|
|
itemHeight={20}
|
|
|
|
|
virtual={virtual.value}
|
|
|
|
|
{...newProps}
|
|
|
|
|
ref={tree}
|
|
|
|
|
prefixCls={prefixCls.value}
|
|
|
|
|
class={classNames(
|
|
|
|
|
{
|
|
|
|
|
[`${prefixCls.value}-icon-hide`]: !showIcon,
|
|
|
|
|
[`${prefixCls.value}-block-node`]: blockNode,
|
|
|
|
|
[`${prefixCls.value}-unselectable`]: !selectable,
|
|
|
|
|
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
|
|
|
|
},
|
|
|
|
|
attrs.class,
|
|
|
|
|
)}
|
|
|
|
|
direction={direction.value}
|
|
|
|
|
checkable={checkable}
|
|
|
|
|
selectable={selectable}
|
|
|
|
|
switcherIcon={(nodeProps: AntTreeNodeProps) =>
|
|
|
|
|
renderSwitcherIcon(prefixCls.value, switcherIcon, showLine, nodeProps)
|
|
|
|
|
}
|
|
|
|
|
onCheck={handleCheck}
|
|
|
|
|
onExpand={handleExpand}
|
|
|
|
|
onSelect={handleSelect}
|
|
|
|
|
v-slots={{
|
|
|
|
|
checkable: () => <span class={`${prefixCls.value}-checkbox-inner`} />,
|
|
|
|
|
}}
|
|
|
|
|
children={filterEmpty(slots.default?.())}
|
|
|
|
|
></VcTree>
|
2020-03-07 11:45:13 +00:00
|
|
|
|
);
|
2021-08-17 14:03:49 +00:00
|
|
|
|
};
|
2018-09-26 14:57:01 +00:00
|
|
|
|
},
|
2021-08-17 14:03:49 +00:00
|
|
|
|
// methods: {
|
|
|
|
|
// renderSwitcherIcon(prefixCls: string, switcherIcon: VNode, { isLeaf, loading, expanded }) {
|
|
|
|
|
// const { showLine } = this.$props;
|
|
|
|
|
// if (loading) {
|
|
|
|
|
// return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// if (isLeaf) {
|
|
|
|
|
// return showLine ? <FileOutlined class={`${prefixCls}-switcher-line-icon`} /> : null;
|
|
|
|
|
// }
|
|
|
|
|
// const switcherCls = `${prefixCls}-switcher-icon`;
|
|
|
|
|
// if (switcherIcon) {
|
|
|
|
|
// return cloneElement(switcherIcon, {
|
|
|
|
|
// class: switcherCls,
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
// return showLine ? (
|
|
|
|
|
// expanded ? (
|
|
|
|
|
// <MinusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
|
|
|
|
// ) : (
|
|
|
|
|
// <PlusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
|
|
|
|
// )
|
|
|
|
|
// ) : (
|
|
|
|
|
// <CaretDownFilled class={switcherCls} />
|
|
|
|
|
// );
|
|
|
|
|
// },
|
|
|
|
|
// updateTreeData(treeData: TreeDataItem[]) {
|
|
|
|
|
// const { $slots } = this;
|
|
|
|
|
// const defaultFields = { children: 'children', title: 'title', key: 'key' };
|
|
|
|
|
// const replaceFields = { ...defaultFields, ...this.$props.replaceFields };
|
|
|
|
|
// return treeData.map(item => {
|
|
|
|
|
// const key = item[replaceFields.key];
|
|
|
|
|
// const children = item[replaceFields.children];
|
|
|
|
|
// const { slots = {}, class: cls, style, ...restProps } = item;
|
|
|
|
|
// const treeNodeProps = {
|
|
|
|
|
// ...restProps,
|
|
|
|
|
// icon: $slots[slots.icon] || restProps.icon,
|
|
|
|
|
// switcherIcon: $slots[slots.switcherIcon] || restProps.switcherIcon,
|
|
|
|
|
// title: $slots[slots.title] || $slots.title || restProps[replaceFields.title],
|
|
|
|
|
// dataRef: item,
|
|
|
|
|
// key,
|
|
|
|
|
// class: cls,
|
|
|
|
|
// style,
|
|
|
|
|
// };
|
|
|
|
|
// if (children) {
|
|
|
|
|
// return { ...treeNodeProps, children: this.updateTreeData(children) };
|
|
|
|
|
// }
|
|
|
|
|
// return treeNodeProps;
|
|
|
|
|
// });
|
|
|
|
|
// },
|
|
|
|
|
// setTreeRef(node: VNode) {
|
|
|
|
|
// this.tree = node;
|
|
|
|
|
// },
|
|
|
|
|
// handleCheck(checkedObj: (number | string)[], eventObj: CheckEvent) {
|
|
|
|
|
// this.$emit('update:checkedKeys', checkedObj);
|
|
|
|
|
// this.$emit('check', checkedObj, eventObj);
|
|
|
|
|
// },
|
|
|
|
|
// handleExpand(expandedKeys: (number | string)[], eventObj: ExpendEvent) {
|
|
|
|
|
// this.$emit('update:expandedKeys', expandedKeys);
|
|
|
|
|
// this.$emit('expand', expandedKeys, eventObj);
|
|
|
|
|
// },
|
|
|
|
|
// handleSelect(selectedKeys: (number | string)[], eventObj: SelectEvent) {
|
|
|
|
|
// this.$emit('update:selectedKeys', selectedKeys);
|
|
|
|
|
// this.$emit('select', selectedKeys, eventObj);
|
|
|
|
|
// },
|
|
|
|
|
// },
|
|
|
|
|
// render() {
|
|
|
|
|
// const props = getOptionProps(this);
|
|
|
|
|
// const { prefixCls: customizePrefixCls, showIcon, treeNodes, blockNode } = props;
|
|
|
|
|
// const getPrefixCls = this.configProvider.getPrefixCls;
|
|
|
|
|
// const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
|
|
|
|
// const switcherIcon = getComponent(this, 'switcherIcon');
|
|
|
|
|
// const checkable = props.checkable;
|
|
|
|
|
// let treeData = props.treeData || treeNodes;
|
|
|
|
|
// if (treeData) {
|
|
|
|
|
// treeData = this.updateTreeData(treeData);
|
|
|
|
|
// }
|
|
|
|
|
// const { class: className, ...restAttrs } = this.$attrs;
|
|
|
|
|
// const vcTreeProps = {
|
|
|
|
|
// ...props,
|
|
|
|
|
// prefixCls,
|
|
|
|
|
// checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
|
|
|
|
// children: getSlot(this),
|
|
|
|
|
// switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, switcherIcon, nodeProps),
|
|
|
|
|
// ref: this.setTreeRef,
|
|
|
|
|
// ...restAttrs,
|
|
|
|
|
// class: classNames(className, {
|
|
|
|
|
// [`${prefixCls}-icon-hide`]: !showIcon,
|
|
|
|
|
// [`${prefixCls}-block-node`]: blockNode,
|
|
|
|
|
// }),
|
|
|
|
|
// onCheck: this.handleCheck,
|
|
|
|
|
// onExpand: this.handleExpand,
|
|
|
|
|
// onSelect: this.handleSelect,
|
|
|
|
|
// } as Record<string, any>;
|
|
|
|
|
// if (treeData) {
|
|
|
|
|
// vcTreeProps.treeData = treeData;
|
|
|
|
|
// }
|
|
|
|
|
// return <VcTree {...vcTreeProps} />;
|
|
|
|
|
// },
|
2020-10-22 06:25:02 +00:00
|
|
|
|
});
|