🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

277 lines
8.6 KiB

import type { ExtractPropTypes } from 'vue';
import { watchEffect, ref, defineComponent, computed } from 'vue';
import classNames from '../_util/classNames';
import VcTree from '../vc-tree';
import PropTypes from '../_util/vue-types';
import { filterEmpty } from '../_util/props-util';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import type { DataNode, EventDataNode, FieldNames, Key, ScrollTo } from '../vc-tree/interface';
import type { TreeNodeProps } from '../vc-tree/props';
import { treeProps as vcTreeProps } from '../vc-tree/props';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import type { SwitcherIconProps } from './utils/iconUtil';
import renderSwitcherIcon from './utils/iconUtil';
import dropIndicatorRender from './utils/dropIndicator';
import devWarning from '../vc-util/devWarning';
import { warning } from '../vc-util/warning';
import omit from '../_util/omit';
import { booleanType, someType, arrayType, functionType, objectType } from '../_util/type';
// CSSINJS
import useStyle from './style';
import type { CustomSlotsType } from '../_util/type';
export interface AntdTreeNodeAttribute {
eventKey: string;
prefixCls: string;
class: 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;
}
export type AntTreeNodeProps = TreeNodeProps;
// [Legacy] Compatible for v2
export type TreeDataItem = DataNode;
export interface AntTreeNodeBaseEvent {
node: EventDataNode;
nativeEvent: MouseEvent;
}
export interface AntTreeNodeCheckedEvent extends AntTreeNodeBaseEvent {
event: 'check';
checked?: boolean;
checkedNodes?: DataNode[];
}
export interface AntTreeNodeSelectedEvent extends AntTreeNodeBaseEvent {
event: 'select';
selected?: boolean;
selectedNodes?: DataNode[];
}
export interface AntTreeNodeExpandedEvent extends AntTreeNodeBaseEvent {
expanded?: boolean;
}
export interface AntTreeNodeMouseEvent {
node: EventDataNode;
event: DragEvent;
}
export interface AntTreeNodeDragEnterEvent extends AntTreeNodeMouseEvent {
expandedKeys: Key[];
}
export interface AntTreeNodeDropEvent {
node: EventDataNode;
dragNode: EventDataNode;
dragNodesKeys: Key[];
dropPosition: number;
dropToGap?: boolean;
event: MouseEvent;
}
export const treeProps = () => {
const baseTreeProps = vcTreeProps();
return {
...baseTreeProps,
showLine: someType<boolean | { showLeafIcon: boolean }>([Boolean, Object]),
/** 是否支持多选 */
multiple: booleanType(),
/** 是否自动展开父节点 */
autoExpandParent: booleanType(),
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
checkStrictly: booleanType(),
/** 是否支持选中 */
checkable: booleanType(),
/** 是否禁用树 */
disabled: booleanType(),
/** 默认展开所有树节点 */
defaultExpandAll: booleanType(),
/** 默认展开对应树节点 */
defaultExpandParent: booleanType(),
/** 默认展开指定的树节点 */
defaultExpandedKeys: arrayType<Key[]>(),
/** (受控)展开指定的树节点 */
expandedKeys: arrayType<Key[]>(),
/** (受控)选中复选框的树节点 */
checkedKeys: someType<Key[] | { checked: Key[]; halfChecked: Key[] }>([Array, Object]),
/** 默认选中复选框的树节点 */
defaultCheckedKeys: arrayType<Key[]>(),
/** (受控)设置选中的树节点 */
selectedKeys: arrayType<Key[]>(),
/** 默认选中的树节点 */
defaultSelectedKeys: arrayType<Key[]>(),
selectable: booleanType(),
loadedKeys: arrayType<Key[]>(),
draggable: booleanType(),
showIcon: booleanType(),
icon: functionType<(nodeProps: AntdTreeNodeAttribute) => any>(),
switcherIcon: PropTypes.any,
prefixCls: String,
/**
* @default{title,key,children}
* deprecated, please use `fieldNames` instead
* 替换treeNode中 title,key,children字段为treeData中对应的字段
*/
replaceFields: objectType<FieldNames>(),
blockNode: booleanType(),
openAnimation: PropTypes.any,
onDoubleclick: baseTreeProps.onDblclick,
'onUpdate:selectedKeys': functionType<(keys: Key[]) => void>(),
'onUpdate:checkedKeys': functionType<(keys: Key[]) => void>(),
'onUpdate:expandedKeys': functionType<(keys: Key[]) => void>(),
};
};
export type TreeProps = Partial<ExtractPropTypes<ReturnType<typeof treeProps>>>;
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'ATree',
inheritAttrs: false,
props: initDefaultProps(treeProps(), {
checkable: false,
selectable: true,
showIcon: false,
blockNode: false,
}),
slots: Object as CustomSlotsType<{
icon?: any;
title?: any;
switcherIcon?: any;
titleRender?: any;
default?: any;
leafIcon?: any;
}>,
setup(props, { attrs, expose, emit, slots }) {
warning(
!(props.treeData === undefined && slots.default),
'`children` of Tree is deprecated. Please use `treeData` instead.',
);
const { prefixCls, direction, virtual } = useConfigInject('tree', props);
// style
const [wrapSSR, hashId] = useStyle(prefixCls);
const treeRef = ref();
const scrollTo: ScrollTo = scroll => {
treeRef.value?.scrollTo(scroll);
};
expose({
treeRef,
onNodeExpand: (...args) => {
treeRef.value?.onNodeExpand(...args);
},
scrollTo,
selectedKeys: computed(() => treeRef.value?.selectedKeys),
checkedKeys: computed(() => treeRef.value?.checkedKeys),
halfCheckedKeys: computed(() => treeRef.value?.halfCheckedKeys),
loadedKeys: computed(() => treeRef.value?.loadedKeys),
loadingKeys: computed(() => treeRef.value?.loadingKeys),
expandedKeys: computed(() => treeRef.value?.expandedKeys),
});
watchEffect(() => {
devWarning(
props.replaceFields === undefined,
'Tree',
'`replaceFields` is deprecated, please use fieldNames instead',
);
});
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 = props.replaceFields,
motion = props.openAnimation,
itemHeight = 28,
onDoubleclick,
onDblclick,
} = props as TreeProps;
const newProps = {
...attrs,
...omit(props, [
'onUpdate:checkedKeys',
'onUpdate:expandedKeys',
'onUpdate:selectedKeys',
'onDoubleclick',
]),
showLine: Boolean(showLine),
dropIndicatorRender,
fieldNames,
icon,
itemHeight,
};
const children = slots.default ? filterEmpty(slots.default()) : undefined;
return wrapSSR(
<VcTree
{...newProps}
virtual={virtual.value}
motion={motion}
ref={treeRef}
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,
hashId.value,
)}
direction={direction.value}
checkable={checkable}
selectable={selectable}
switcherIcon={(nodeProps: SwitcherIconProps) =>
renderSwitcherIcon(prefixCls.value, switcherIcon, nodeProps, slots.leafIcon, showLine)
}
onCheck={handleCheck}
onExpand={handleExpand}
onSelect={handleSelect}
onDblclick={onDblclick || onDoubleclick}
v-slots={{
...slots,
checkable: () => <span class={`${prefixCls.value}-checkbox-inner`} />,
}}
children={children}
></VcTree>,
);
};
},
});