vuecssuiant-designantdreactantantd-vueenterprisefrontendui-designvue-antdvue-antd-uivue3vuecomponent
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
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>, |
|
); |
|
}; |
|
}, |
|
});
|
|
|