Commit 72b42d7b3539919a9baa4f1a7316842f85991c1e

Authored by Vben
1 parent d67bd496

feat(tree): add renderIcon props close #309

CHANGELOG.zh_CN.md
@@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
11 - 新增修改密码界面 11 - 新增修改密码界面
12 - 新增部门管理示例界面 12 - 新增部门管理示例界面
13 - 新增 WebSocket 示例和服务脚本 13 - 新增 WebSocket 示例和服务脚本
  14 +- BasicTree 组件新增 `renderIcon` 属性用于控制层级图标显示
14 15
15 ### ⚡ Performance Improvements 16 ### ⚡ Performance Improvements
16 17
src/components/Tree/index.ts
1 -import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';  
2 -  
3 -export const BasicTree = createAsyncComponent(() => import('./src/BasicTree')); 1 +import BasicTree from './src/index.vue';
4 2
  3 +export { BasicTree };
5 export type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; 4 export type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
6 export * from './src/types'; 5 export * from './src/types';
src/components/Tree/src/TreeIcon.ts 0 → 100644
  1 +import type { VNode, FunctionalComponent } from 'vue';
  2 +
  3 +import { h } from 'vue';
  4 +import { isString } from '/@/utils/is';
  5 +import { Icon } from '/@/components/Icon';
  6 +
  7 +export interface ComponentProps {
  8 + icon: VNode | string;
  9 +}
  10 +
  11 +export const TreeIcon: FunctionalComponent = ({ icon }: ComponentProps) => {
  12 + if (!icon) return null;
  13 + if (isString(icon)) {
  14 + return h(Icon, { icon, class: 'mr-1' });
  15 + }
  16 + return Icon;
  17 +};
src/components/Tree/src/index.less deleted 100644 → 0
1 -.basic-tree {  
2 - position: relative;  
3 -  
4 - &-title {  
5 - position: relative;  
6 - display: inline-block;  
7 - width: 100%;  
8 - padding-right: 10px;  
9 -  
10 - &:hover {  
11 - .basic-tree__action {  
12 - visibility: visible;  
13 - }  
14 - }  
15 - }  
16 -  
17 - &__content {  
18 - display: inline-block;  
19 - overflow: hidden;  
20 - }  
21 -  
22 - &__actions {  
23 - position: absolute;  
24 - top: 0;  
25 - right: 0;  
26 - display: flex;  
27 - }  
28 -  
29 - &__action {  
30 - margin-left: 4px;  
31 - // float: right;  
32 - // display: none;  
33 - visibility: hidden;  
34 - }  
35 -}  
src/components/Tree/src/BasicTree.tsx renamed to src/components/Tree/src/index.vue
1 -import './index.less';  
2 -  
3 -import type { ReplaceFields, TreeItem, Keys, CheckKeys, TreeActionType } from './types';  
4 -  
5 -import { defineComponent, reactive, computed, unref, ref, watchEffect, CSSProperties } from 'vue';  
6 -import { Tree } from 'ant-design-vue';  
7 -import { DownOutlined } from '@ant-design/icons-vue';  
8 -  
9 -import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu';  
10 -  
11 -import { isFunction } from '/@/utils/is';  
12 -import { omit } from 'lodash-es';  
13 -import { extendSlots } from '/@/utils/helper/tsxHelper';  
14 -  
15 -import { basicProps } from './props';  
16 -import { useTree } from './useTree';  
17 -import { useExpose } from '/@/hooks/core/useExpose';  
18 -import { onMounted } from 'vue';  
19 -  
20 -interface State {  
21 - expandedKeys: Keys;  
22 - selectedKeys: Keys;  
23 - checkedKeys: CheckKeys;  
24 -}  
25 -const prefixCls = 'basic-tree';  
26 -export default defineComponent({  
27 - name: 'BasicTree',  
28 - props: basicProps,  
29 - emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'get'],  
30 - setup(props, { attrs, slots, emit }) {  
31 - const state = reactive<State>({  
32 - expandedKeys: props.expandedKeys || [],  
33 - selectedKeys: props.selectedKeys || [],  
34 - checkedKeys: props.checkedKeys || [],  
35 - });  
36 -  
37 - const treeDataRef = ref<TreeItem[]>([]);  
38 -  
39 - const [createContextMenu] = useContextMenu();  
40 -  
41 - const getReplaceFields = computed(  
42 - (): Required<ReplaceFields> => {  
43 - const { replaceFields } = props;  
44 - return {  
45 - children: 'children',  
46 - title: 'title',  
47 - key: 'key',  
48 - ...replaceFields, 1 +<script lang="tsx">
  2 + import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types';
  3 +
  4 + import { defineComponent, reactive, computed, unref, ref, watchEffect, onMounted } from 'vue';
  5 + import { Tree } from 'ant-design-vue';
  6 + import { TreeIcon } from './TreeIcon';
  7 + // import { DownOutlined } from '@ant-design/icons-vue';
  8 +
  9 + import { omit, get } from 'lodash-es';
  10 + import { isFunction } from '/@/utils/is';
  11 + import { extendSlots } from '/@/utils/helper/tsxHelper';
  12 +
  13 + import { useTree } from './useTree';
  14 + import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu';
  15 + import { useExpose } from '/@/hooks/core/useExpose';
  16 + import { useDesign } from '/@/hooks/web/useDesign';
  17 +
  18 + import { basicProps } from './props';
  19 +
  20 + interface State {
  21 + expandedKeys: Keys;
  22 + selectedKeys: Keys;
  23 + checkedKeys: CheckKeys;
  24 + }
  25 + export default defineComponent({
  26 + name: 'BasicTree',
  27 + props: basicProps,
  28 + emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'get'],
  29 + setup(props, { attrs, slots, emit }) {
  30 + const state = reactive<State>({
  31 + expandedKeys: props.expandedKeys || [],
  32 + selectedKeys: props.selectedKeys || [],
  33 + checkedKeys: props.checkedKeys || [],
  34 + });
  35 +
  36 + const treeDataRef = ref<TreeItem[]>([]);
  37 +
  38 + const [createContextMenu] = useContextMenu();
  39 + const { prefixCls } = useDesign('basic-tree');
  40 +
  41 + const getReplaceFields = computed(
  42 + (): Required<ReplaceFields> => {
  43 + const { replaceFields } = props;
  44 + return {
  45 + children: 'children',
  46 + title: 'title',
  47 + key: 'key',
  48 + ...replaceFields,
  49 + };
  50 + }
  51 + );
  52 +
  53 + // const getContentStyle = computed(
  54 + // (): CSSProperties => {
  55 + // const { actionList } = props;
  56 + // const width = actionList.length * 18;
  57 + // return {
  58 + // width: `calc(100% - ${width}px)`,
  59 + // };
  60 + // }
  61 + // );
  62 +
  63 + const getBindValues = computed(() => {
  64 + let propsData = {
  65 + blockNode: true,
  66 + ...attrs,
  67 + ...props,
  68 + expandedKeys: state.expandedKeys,
  69 + selectedKeys: state.selectedKeys,
  70 + checkedKeys: state.checkedKeys,
  71 + replaceFields: unref(getReplaceFields),
  72 + 'onUpdate:expandedKeys': (v: Keys) => {
  73 + state.expandedKeys = v;
  74 + emit('update:expandedKeys', v);
  75 + },
  76 + 'onUpdate:selectedKeys': (v: Keys) => {
  77 + state.selectedKeys = v;
  78 + emit('update:selectedKeys', v);
  79 + },
  80 + onCheck: (v: CheckKeys, e) => {
  81 + state.checkedKeys = v;
  82 + console.log(e);
  83 + emit('update:value', v);
  84 + },
  85 + onRightClick: handleRightClick,
49 }; 86 };
  87 + propsData = omit(propsData, 'treeData');
  88 + return propsData;
  89 + });
  90 +
  91 + const getTreeData = computed((): TreeItem[] => unref(treeDataRef));
  92 +
  93 + const { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey } = useTree(
  94 + treeDataRef,
  95 + getReplaceFields
  96 + );
  97 +
  98 + function getIcon(params: Recordable, icon?: string) {
  99 + if (!icon) {
  100 + if (props.renderIcon && isFunction(props.renderIcon)) {
  101 + return props.renderIcon(params);
  102 + }
  103 + }
  104 + return icon;
50 } 105 }
51 - );  
52 106
53 - const getContentStyle = computed(  
54 - (): CSSProperties => { 107 + function renderAction(node: TreeItem) {
55 const { actionList } = props; 108 const { actionList } = props;
56 - const width = actionList.length * 18;  
57 - return {  
58 - width: `calc(100% - ${width}px)`,  
59 - }; 109 + if (!actionList || actionList.length === 0) return;
  110 + return actionList.map((item, index) => {
  111 + return (
  112 + <span key={index} class={`${prefixCls}__action`}>
  113 + {item.render(node)}
  114 + </span>
  115 + );
  116 + });
60 } 117 }
61 - );  
62 -  
63 - const getBindValues = computed(() => {  
64 - let propsData = {  
65 - blockNode: true,  
66 - ...attrs,  
67 - ...props,  
68 - expandedKeys: state.expandedKeys,  
69 - selectedKeys: state.selectedKeys,  
70 - checkedKeys: state.checkedKeys,  
71 - replaceFields: unref(getReplaceFields),  
72 - 'onUpdate:expandedKeys': (v: Keys) => {  
73 - state.expandedKeys = v;  
74 - emit('update:expandedKeys', v);  
75 - },  
76 - 'onUpdate:selectedKeys': (v: Keys) => {  
77 - state.selectedKeys = v;  
78 - emit('update:selectedKeys', v);  
79 - },  
80 - onCheck: (v: CheckKeys) => {  
81 - state.checkedKeys = v;  
82 - emit('update:value', v);  
83 - },  
84 - onRightClick: handleRightClick,  
85 - };  
86 - propsData = omit(propsData, 'treeData');  
87 - return propsData;  
88 - });  
89 118
90 - const getTreeData = computed((): TreeItem[] => unref(treeDataRef)); 119 + function renderTreeNode({ data, level }: { data: TreeItem[] | undefined; level: number }) {
  120 + if (!data) {
  121 + return null;
  122 + }
  123 + return data.map((item) => {
  124 + const { title: titleField, key: keyField, children: childrenField } = unref(
  125 + getReplaceFields
  126 + );
91 127
92 - const { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey } = useTree(  
93 - treeDataRef,  
94 - getReplaceFields  
95 - ); 128 + const propsData = omit(item, 'title');
  129 + const icon = getIcon({ ...item, level }, item.icon);
  130 + return (
  131 + <Tree.TreeNode {...propsData} key={get(item, keyField)}>
  132 + {{
  133 + title: () => (
  134 + <span class={`${prefixCls}-title`}>
  135 + {icon && <TreeIcon icon={icon} />}
  136 + <span
  137 + class={`${prefixCls}__content`}
  138 + // style={unref(getContentStyle)}
  139 + >
  140 + {get(item, titleField)}
  141 + </span>
  142 + <span class={`${prefixCls}__actions`}> {renderAction(item)}</span>
  143 + </span>
  144 + ),
  145 + default: () =>
  146 + renderTreeNode({ data: get(item, childrenField) || [], level: level + 1 }),
  147 + }}
  148 + </Tree.TreeNode>
  149 + );
  150 + });
  151 + }
96 152
97 - // 渲染操作按钮  
98 - function renderAction(node: TreeItem) {  
99 - const { actionList } = props; 153 + async function handleRightClick({ event, node }: any) {
  154 + const { rightMenuList: menuList = [], beforeRightClick } = props;
  155 + let rightMenuList: ContextMenuItem[] = [];
100 156
101 - if (!actionList || actionList.length === 0) return; 157 + if (beforeRightClick && isFunction(beforeRightClick)) {
  158 + rightMenuList = await beforeRightClick(node);
  159 + } else {
  160 + rightMenuList = menuList;
  161 + }
  162 + if (!rightMenuList.length) return;
  163 + createContextMenu({
  164 + event,
  165 + items: rightMenuList,
  166 + });
  167 + }
102 168
103 - return actionList.map((item, index) => {  
104 - return (  
105 - <span key={index} class={`${prefixCls}__action`}>  
106 - {item.render(node)}  
107 - </span>  
108 - );  
109 - });  
110 - }  
111 - // 渲染树节点  
112 - function renderTreeNode({ data }: { data: TreeItem[] | undefined }) {  
113 - if (!data) {  
114 - return null; 169 + function setExpandedKeys(keys: string[]) {
  170 + state.expandedKeys = keys;
115 } 171 }
116 - return data.map((item) => {  
117 - const { title: titleField, key: keyField, children: childrenField } = unref(  
118 - getReplaceFields  
119 - );  
120 - const propsData = omit(item, 'title');  
121 - const anyItem = item as any; 172 +
  173 + function getExpandedKeys() {
  174 + return state.expandedKeys;
  175 + }
  176 + function setSelectedKeys(keys: string[]) {
  177 + state.selectedKeys = keys;
  178 + }
  179 +
  180 + function getSelectedKeys() {
  181 + return state.selectedKeys;
  182 + }
  183 +
  184 + function setCheckedKeys(keys: CheckKeys) {
  185 + state.checkedKeys = keys;
  186 + }
  187 +
  188 + function getCheckedKeys() {
  189 + return state.checkedKeys;
  190 + }
  191 +
  192 + watchEffect(() => {
  193 + treeDataRef.value = props.treeData as TreeItem[];
  194 + state.expandedKeys = props.expandedKeys;
  195 + state.selectedKeys = props.selectedKeys;
  196 + state.checkedKeys = props.checkedKeys;
  197 + });
  198 +
  199 + const instance: TreeActionType = {
  200 + setExpandedKeys,
  201 + getExpandedKeys,
  202 + setSelectedKeys,
  203 + getSelectedKeys,
  204 + setCheckedKeys,
  205 + getCheckedKeys,
  206 + insertNodeByKey,
  207 + deleteNodeByKey,
  208 + updateNodeByKey,
  209 + filterByLevel: (level: number) => {
  210 + state.expandedKeys = filterByLevel(level);
  211 + },
  212 + };
  213 +
  214 + useExpose<TreeActionType>(instance);
  215 +
  216 + onMounted(() => {
  217 + emit('get', instance);
  218 + });
  219 +
  220 + return () => {
122 return ( 221 return (
123 - <Tree.TreeNode {...propsData} key={anyItem?.[keyField]}> 222 + <Tree {...unref(getBindValues)} showIcon={false} class={[prefixCls]}>
124 {{ 223 {{
125 - title: () => (  
126 - <span class={`${prefixCls}-title`}>  
127 - <span class={`${prefixCls}__content`} style={unref(getContentStyle)}>  
128 - {titleField && anyItem[titleField]}  
129 - </span>  
130 - <span class={`${prefixCls}__actions`}> {renderAction(item)}</span>  
131 - </span>  
132 - ),  
133 - default: () => renderTreeNode({ data: childrenField ? anyItem[childrenField] : [] }), 224 + // switcherIcon: () => <DownOutlined />,
  225 + default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }),
  226 + ...extendSlots(slots),
134 }} 227 }}
135 - </Tree.TreeNode> 228 + </Tree>
136 ); 229 );
137 - });  
138 - } 230 + };
  231 + },
  232 + });
  233 +</script>
  234 +<style lang="less">
  235 + @prefix-cls: ~'@{namespace}-basic-tree';
  236 +
  237 + .@{prefix-cls} {
  238 + position: relative;
139 239
140 - // 处理右键事件  
141 - async function handleRightClick({ event, node }: any) {  
142 - const { rightMenuList: menuList = [], beforeRightClick } = props;  
143 - let rightMenuList: ContextMenuItem[] = [];  
144 - if (beforeRightClick && isFunction(beforeRightClick)) {  
145 - rightMenuList = await beforeRightClick(node);  
146 - } else {  
147 - rightMenuList = menuList; 240 + .ant-tree-node-content-wrapper {
  241 + position: relative;
  242 +
  243 + .ant-tree-title {
  244 + position: absolute;
  245 + left: 0;
  246 + width: 100%;
148 } 247 }
149 - if (!rightMenuList.length) return;  
150 - createContextMenu({  
151 - event,  
152 - items: rightMenuList,  
153 - });  
154 } 248 }
155 249
156 - function setExpandedKeys(keys: string[]) {  
157 - state.expandedKeys = keys;  
158 - } 250 + &-title {
  251 + position: relative;
  252 + display: flex;
  253 + align-items: center;
  254 + width: 100%;
  255 + padding-right: 10px;
159 256
160 - function getExpandedKeys() {  
161 - return state.expandedKeys;  
162 - }  
163 - function setSelectedKeys(keys: string[]) {  
164 - state.selectedKeys = keys; 257 + &:hover {
  258 + .@{prefix-cls}__action {
  259 + visibility: visible;
  260 + }
  261 + }
165 } 262 }
166 263
167 - function getSelectedKeys() {  
168 - return state.selectedKeys; 264 + &__content {
  265 + display: inline-block;
  266 + overflow: hidden;
169 } 267 }
170 268
171 - function setCheckedKeys(keys: CheckKeys) {  
172 - state.checkedKeys = keys; 269 + &__actions {
  270 + position: absolute;
  271 + top: 2px;
  272 + right: 2px;
  273 + display: flex;
173 } 274 }
174 275
175 - function getCheckedKeys() {  
176 - return state.checkedKeys; 276 + &__action {
  277 + margin-left: 4px;
  278 + visibility: hidden;
177 } 279 }
178 -  
179 - watchEffect(() => {  
180 - treeDataRef.value = props.treeData as TreeItem[];  
181 - state.expandedKeys = props.expandedKeys;  
182 - state.selectedKeys = props.selectedKeys;  
183 - state.checkedKeys = props.checkedKeys;  
184 - });  
185 -  
186 - const instance: TreeActionType = {  
187 - setExpandedKeys,  
188 - getExpandedKeys,  
189 - setSelectedKeys,  
190 - getSelectedKeys,  
191 - setCheckedKeys,  
192 - getCheckedKeys,  
193 - insertNodeByKey,  
194 - deleteNodeByKey,  
195 - updateNodeByKey,  
196 - filterByLevel: (level: number) => {  
197 - state.expandedKeys = filterByLevel(level);  
198 - },  
199 - };  
200 -  
201 - useExpose<TreeActionType>(instance);  
202 -  
203 - onMounted(() => {  
204 - emit('get', instance);  
205 - });  
206 -  
207 - return () => {  
208 - return (  
209 - <Tree {...(unref(getBindValues) as any)} class={prefixCls}>  
210 - {{  
211 - switcherIcon: () => <DownOutlined />,  
212 - default: () => renderTreeNode({ data: unref(getTreeData) }),  
213 - ...extendSlots(slots),  
214 - }}  
215 - </Tree>  
216 - );  
217 - };  
218 - },  
219 -}); 280 + }
  281 +</style>
src/components/Tree/src/props.ts
1 -import { PropType } from 'vue';  
2 -import type { ReplaceFields, TreeItem, ActionItem, Keys, CheckKeys } from './types'; 1 +import type { PropType } from 'vue';
  2 +import type { ReplaceFields, ActionItem, Keys, CheckKeys } from './types';
3 import type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; 3 import type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
  4 +import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
4 5
5 export const basicProps = { 6 export const basicProps = {
  7 + renderIcon: {
  8 + type: Function as PropType<(params: Recordable) => string>,
  9 + },
6 replaceFields: { 10 replaceFields: {
7 type: Object as PropType<ReplaceFields>, 11 type: Object as PropType<ReplaceFields>,
8 }, 12 },
9 13
10 treeData: { 14 treeData: {
11 - type: Array as PropType<TreeItem[]>, 15 + type: Array as PropType<TreeDataItem[]>,
12 }, 16 },
13 17
14 actionList: { 18 actionList: {
@@ -50,7 +54,7 @@ export const treeNodeProps = { @@ -50,7 +54,7 @@ export const treeNodeProps = {
50 type: Object as PropType<ReplaceFields>, 54 type: Object as PropType<ReplaceFields>,
51 }, 55 },
52 treeData: { 56 treeData: {
53 - type: Array as PropType<TreeItem[]>, 57 + type: Array as PropType<TreeDataItem[]>,
54 default: () => [], 58 default: () => [],
55 }, 59 },
56 }; 60 };
src/components/Tree/src/types.ts
  1 +import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
1 export interface ActionItem { 2 export interface ActionItem {
2 render: (record: any) => any; 3 render: (record: any) => any;
3 } 4 }
4 5
5 -export interface TreeItem {  
6 - /**  
7 - * Class  
8 - * @description className  
9 - * @type string  
10 - */  
11 - class?: string;  
12 -  
13 - /**  
14 - * Style  
15 - * @description style of tree node  
16 - * @type string | object  
17 - */  
18 - style?: string | object;  
19 -  
20 - /**  
21 - * Disable Checkbox  
22 - * @description Disables the checkbox of the treeNode  
23 - * @default false  
24 - * @type boolean  
25 - */  
26 - disableCheckbox?: boolean;  
27 -  
28 - /**  
29 - * Disabled  
30 - * @description Disabled or not  
31 - * @default false  
32 - * @type boolean  
33 - */  
34 - disabled?: boolean;  
35 -  
36 - /**  
37 - * Icon  
38 - * @description customize icon. When you pass component, whose render will receive full TreeNode props as component props  
39 - * @type any (slot | slot-scope)  
40 - */ 6 +export interface TreeItem extends TreeDataItem {
41 icon?: any; 7 icon?: any;
42 -  
43 - /**  
44 - * Is Leaf?  
45 - * @description Leaf node or not  
46 - * @default false  
47 - * @type boolean  
48 - */  
49 - isLeaf?: boolean;  
50 -  
51 - /**  
52 - * Key  
53 - * @description Required property, should be unique in the tree  
54 - * (In tree: Used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys)  
55 - * @default internal calculated position of treeNode or undefined  
56 - * @type string | number  
57 - */  
58 - key: string | number;  
59 -  
60 - /**  
61 - * Selectable  
62 - * @description Set whether the treeNode can be selected  
63 - * @default true  
64 - * @type boolean  
65 - */  
66 - selectable?: boolean;  
67 -  
68 - /**  
69 - * Title  
70 - * @description Content showed on the treeNodes  
71 - * @default '---'  
72 - * @type any (string | slot)  
73 - */  
74 - title: any;  
75 -  
76 - /**  
77 - * Value  
78 - * @description Will be treated as treeNodeFilterProp by default, should be unique in the tree  
79 - * @default undefined  
80 - * @type string  
81 - */  
82 - value?: string;  
83 - children?: TreeItem[];  
84 - slots?: any;  
85 - scopedSlots?: any;  
86 } 8 }
87 9
88 export interface ReplaceFields { 10 export interface ReplaceFields {
@@ -107,12 +29,12 @@ export interface TreeActionType { @@ -107,12 +29,12 @@ export interface TreeActionType {
107 filterByLevel: (level: number) => void; 29 filterByLevel: (level: number) => void;
108 insertNodeByKey: (opt: InsertNodeParams) => void; 30 insertNodeByKey: (opt: InsertNodeParams) => void;
109 deleteNodeByKey: (key: string) => void; 31 deleteNodeByKey: (key: string) => void;
110 - updateNodeByKey: (key: string, node: Omit<TreeItem, 'key'>) => void; 32 + updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void;
111 } 33 }
112 34
113 export interface InsertNodeParams { 35 export interface InsertNodeParams {
114 parentKey: string | null; 36 parentKey: string | null;
115 - node: TreeItem;  
116 - list?: TreeItem[]; 37 + node: TreeDataItem;
  38 + list?: TreeDataItem[];
117 push?: 'push' | 'unshift'; 39 push?: 'push' | 'unshift';
118 } 40 }
src/components/Tree/src/useTree.ts
1 -import type { InsertNodeParams, ReplaceFields, TreeItem } from './types'; 1 +import type { InsertNodeParams, ReplaceFields } from './types';
2 import type { Ref, ComputedRef } from 'vue'; 2 import type { Ref, ComputedRef } from 'vue';
  3 +import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
3 4
4 import { cloneDeep } from 'lodash-es'; 5 import { cloneDeep } from 'lodash-es';
5 import { unref } from 'vue'; 6 import { unref } from 'vue';
6 import { forEach } from '/@/utils/helper/treeHelper'; 7 import { forEach } from '/@/utils/helper/treeHelper';
7 8
8 export function useTree( 9 export function useTree(
9 - treeDataRef: Ref<TreeItem[]>, 10 + treeDataRef: Ref<TreeDataItem[]>,
10 getReplaceFields: ComputedRef<ReplaceFields> 11 getReplaceFields: ComputedRef<ReplaceFields>
11 ) { 12 ) {
12 - // 更新节点  
13 - function updateNodeByKey(key: string, node: TreeItem, list?: TreeItem[]) { 13 + // Update node
  14 + function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) {
14 if (!key) return; 15 if (!key) return;
15 const treeData = list || unref(treeDataRef); 16 const treeData = list || unref(treeDataRef);
16 const { key: keyField, children: childrenField } = unref(getReplaceFields); 17 const { key: keyField, children: childrenField } = unref(getReplaceFields);
@@ -30,8 +31,8 @@ export function useTree( @@ -30,8 +31,8 @@ export function useTree(
30 } 31 }
31 } 32 }
32 33
33 - // 展开指定级别  
34 - function filterByLevel(level = 1, list?: TreeItem[], currentLevel = 1) { 34 + // Expand the specified level
  35 + function filterByLevel(level = 1, list?: TreeDataItem[], currentLevel = 1) {
35 if (!level) { 36 if (!level) {
36 return []; 37 return [];
37 } 38 }
@@ -74,8 +75,8 @@ export function useTree( @@ -74,8 +75,8 @@ export function useTree(
74 treeDataRef.value = treeData; 75 treeDataRef.value = treeData;
75 } 76 }
76 77
77 - // 删除节点  
78 - function deleteNodeByKey(key: string, list?: TreeItem[]) { 78 + // Delete node
  79 + function deleteNodeByKey(key: string, list?: TreeDataItem[]) {
79 if (!key) return; 80 if (!key) return;
80 const treeData = list || unref(treeDataRef); 81 const treeData = list || unref(treeDataRef);
81 const { key: keyField, children: childrenField } = unref(getReplaceFields); 82 const { key: keyField, children: childrenField } = unref(getReplaceFields);
src/views/demo/tree/ActionTree.vue
@@ -35,6 +35,7 @@ @@ -35,6 +35,7 @@
35 setup() { 35 setup() {
36 const treeRef = ref<Nullable<TreeActionType>>(null); 36 const treeRef = ref<Nullable<TreeActionType>>(null);
37 const { createMessage } = useMessage(); 37 const { createMessage } = useMessage();
  38 +
38 function getTree() { 39 function getTree() {
39 const tree = unref(treeRef); 40 const tree = unref(treeRef);
40 if (!tree) { 41 if (!tree) {
src/views/demo/tree/EditTree.vue
1 <template> 1 <template>
2 <PageWrapper title="Tree函数操作示例"> 2 <PageWrapper title="Tree函数操作示例">
3 <div class="flex"> 3 <div class="flex">
4 - <CollapseContainer title="右侧操作按钮" class="mr-4" :style="{ width: '33%' }">  
5 - <BasicTree :treeData="treeData" :actionList="actionList" /> 4 + <CollapseContainer title="右侧操作按钮/自定义图标" class="mr-4" :style="{ width: '33%' }">
  5 + <BasicTree :treeData="treeData" :actionList="actionList" :renderIcon="createIcon" />
6 </CollapseContainer> 6 </CollapseContainer>
7 7
8 <CollapseContainer title="右键菜单" class="mr-4" :style="{ width: '33%' }"> 8 <CollapseContainer title="右键菜单" class="mr-4" :style="{ width: '33%' }">
@@ -61,7 +61,19 @@ @@ -61,7 +61,19 @@
61 }, 61 },
62 }, 62 },
63 ]; 63 ];
64 - return { treeData, actionList, getRightMenuList }; 64 +
  65 + function createIcon({ level }) {
  66 + if (level === 1) {
  67 + return 'ion:git-compare-outline';
  68 + }
  69 + if (level === 2) {
  70 + return 'ion:home';
  71 + }
  72 + if (level === 3) {
  73 + return 'ion:airplane';
  74 + }
  75 + }
  76 + return { treeData, actionList, getRightMenuList, createIcon };
65 }, 77 },
66 }); 78 });
67 </script> 79 </script>
src/views/demo/tree/data.ts
@@ -2,9 +2,8 @@ import { TreeItem } from &#39;/@/components/Tree/index&#39;; @@ -2,9 +2,8 @@ import { TreeItem } from &#39;/@/components/Tree/index&#39;;
2 2
3 export const treeData: TreeItem[] = [ 3 export const treeData: TreeItem[] = [
4 { 4 {
5 - title: 'parent 1parent ', 5 + title: 'parent ',
6 key: '0-0', 6 key: '0-0',
7 - icon: 'home|svg',  
8 children: [ 7 children: [
9 { title: 'leaf', key: '0-0-0' }, 8 { title: 'leaf', key: '0-0-0' },
10 { 9 {
@@ -20,7 +19,6 @@ export const treeData: TreeItem[] = [ @@ -20,7 +19,6 @@ export const treeData: TreeItem[] = [
20 { 19 {
21 title: 'parent 2', 20 title: 'parent 2',
22 key: '1-1', 21 key: '1-1',
23 - icon: 'home|svg',  
24 children: [ 22 children: [
25 { title: 'leaf', key: '1-1-0' }, 23 { title: 'leaf', key: '1-1-0' },
26 { title: 'leaf', key: '1-1-1' }, 24 { title: 'leaf', key: '1-1-1' },
@@ -29,7 +27,6 @@ export const treeData: TreeItem[] = [ @@ -29,7 +27,6 @@ export const treeData: TreeItem[] = [
29 { 27 {
30 title: 'parent 3', 28 title: 'parent 3',
31 key: '2-2', 29 key: '2-2',
32 - icon: 'home|svg',  
33 children: [ 30 children: [
34 { title: 'leaf', key: '2-2-0' }, 31 { title: 'leaf', key: '2-2-0' },
35 { title: 'leaf', key: '2-2-1' }, 32 { title: 'leaf', key: '2-2-1' },