Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
basic-vue-admin
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Basic
basic-vue-admin
Commits
72b42d7b
提交
72b42d7b
authored
3月 02, 2021
作者:
Vben
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat(tree): add renderIcon props close #309
上级
d67bd496
显示空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
164 行增加
和
183 行删除
+164
-183
CHANGELOG.zh_CN.md
CHANGELOG.zh_CN.md
+1
-0
index.ts
src/components/Tree/index.ts
+2
-3
TreeIcon.ts
src/components/Tree/src/TreeIcon.ts
+17
-0
index.less
src/components/Tree/src/index.less
+0
-35
index.vue
src/components/Tree/src/index.vue
+105
-43
props.ts
src/components/Tree/src/props.ts
+8
-4
types.ts
src/components/Tree/src/types.ts
+5
-83
useTree.ts
src/components/Tree/src/useTree.ts
+9
-8
ActionTree.vue
src/views/demo/tree/ActionTree.vue
+1
-0
EditTree.vue
src/views/demo/tree/EditTree.vue
+15
-3
data.ts
src/views/demo/tree/data.ts
+1
-4
没有找到文件。
CHANGELOG.zh_CN.md
浏览文件 @
72b42d7b
...
...
@@ -11,6 +11,7 @@
-
新增修改密码界面
-
新增部门管理示例界面
-
新增 WebSocket 示例和服务脚本
-
BasicTree 组件新增
`renderIcon`
属性用于控制层级图标显示
### ⚡ Performance Improvements
...
...
src/components/Tree/index.ts
浏览文件 @
72b42d7b
import
{
createAsyncComponent
}
from
'/@/utils/factory/createAsyncComponent'
;
export
const
BasicTree
=
createAsyncComponent
(()
=>
import
(
'./src/BasicTree'
));
import
BasicTree
from
'./src/index.vue'
;
export
{
BasicTree
};
export
type
{
ContextMenuItem
}
from
'/@/hooks/web/useContextMenu'
;
export
*
from
'./src/types'
;
src/components/Tree/src/TreeIcon.ts
0 → 100644
浏览文件 @
72b42d7b
import
type
{
VNode
,
FunctionalComponent
}
from
'vue'
;
import
{
h
}
from
'vue'
;
import
{
isString
}
from
'/@/utils/is'
;
import
{
Icon
}
from
'/@/components/Icon'
;
export
interface
ComponentProps
{
icon
:
VNode
|
string
;
}
export
const
TreeIcon
:
FunctionalComponent
=
({
icon
}:
ComponentProps
)
=>
{
if
(
!
icon
)
return
null
;
if
(
isString
(
icon
))
{
return
h
(
Icon
,
{
icon
,
class
:
'mr-1'
});
}
return
Icon
;
};
src/components/Tree/src/index.less
deleted
100644 → 0
浏览文件 @
d67bd496
.basic-tree {
position: relative;
&-title {
position: relative;
display: inline-block;
width: 100%;
padding-right: 10px;
&:hover {
.basic-tree__action {
visibility: visible;
}
}
}
&__content {
display: inline-block;
overflow: hidden;
}
&__actions {
position: absolute;
top: 0;
right: 0;
display: flex;
}
&__action {
margin-left: 4px;
// float: right;
// display: none;
visibility: hidden;
}
}
src/components/Tree/src/
BasicTree.tsx
→
src/components/Tree/src/
index.vue
浏览文件 @
72b42d7b
import
'./index.less'
;
<
script
lang=
"tsx"
>
import
type
{
ReplaceFields
,
Keys
,
CheckKeys
,
TreeActionType
,
TreeItem
}
from
'./types'
;
import
type
{
ReplaceFields
,
TreeItem
,
Keys
,
CheckKeys
,
TreeActionType
}
from
'./types'
;
import
{
defineComponent
,
reactive
,
computed
,
unref
,
ref
,
watchEffect
,
onMounted
}
from
'vue'
;
import
{
Tree
}
from
'ant-design-vue'
;
import
{
TreeIcon
}
from
'./TreeIcon'
;
// import { DownOutlined } from '@ant-design/icons-vue';
import
{
defineComponent
,
reactive
,
computed
,
unref
,
ref
,
watchEffect
,
CSSProperties
}
from
'vue
'
;
import
{
Tree
}
from
'ant-design-vue
'
;
import
{
DownOutlined
}
from
'@ant-design/icons-vue
'
;
import
{
omit
,
get
}
from
'lodash-es
'
;
import
{
isFunction
}
from
'/@/utils/is
'
;
import
{
extendSlots
}
from
'/@/utils/helper/tsxHelper
'
;
import
{
useContextMenu
,
ContextMenuItem
}
from
'/@/hooks/web/useContextMenu'
;
import
{
useTree
}
from
'./useTree'
;
import
{
useContextMenu
,
ContextMenuItem
}
from
'/@/hooks/web/useContextMenu'
;
import
{
useExpose
}
from
'/@/hooks/core/useExpose'
;
import
{
useDesign
}
from
'/@/hooks/web/useDesign'
;
import
{
isFunction
}
from
'/@/utils/is'
;
import
{
omit
}
from
'lodash-es'
;
import
{
extendSlots
}
from
'/@/utils/helper/tsxHelper'
;
import
{
basicProps
}
from
'./props'
;
import
{
basicProps
}
from
'./props'
;
import
{
useTree
}
from
'./useTree'
;
import
{
useExpose
}
from
'/@/hooks/core/useExpose'
;
import
{
onMounted
}
from
'vue'
;
interface
State
{
interface
State
{
expandedKeys
:
Keys
;
selectedKeys
:
Keys
;
checkedKeys
:
CheckKeys
;
}
const
prefixCls
=
'basic-tree'
;
export
default
defineComponent
({
}
export
default
defineComponent
({
name
:
'BasicTree'
,
props
:
basicProps
,
emits
:
[
'update:expandedKeys'
,
'update:selectedKeys'
,
'update:value'
,
'get'
],
...
...
@@ -37,6 +36,7 @@ export default defineComponent({
const
treeDataRef
=
ref
<
TreeItem
[]
>
([]);
const
[
createContextMenu
]
=
useContextMenu
();
const
{
prefixCls
}
=
useDesign
(
'basic-tree'
);
const
getReplaceFields
=
computed
(
():
Required
<
ReplaceFields
>
=>
{
...
...
@@ -50,15 +50,15 @@ export default defineComponent({
}
);
const
getContentStyle
=
computed
(
():
CSSProperties
=>
{
const
{
actionList
}
=
props
;
const
width
=
actionList
.
length
*
18
;
return
{
width
:
`calc(100% -
${
width
}
px)`
,
};
}
);
//
const getContentStyle = computed(
//
(): CSSProperties => {
//
const { actionList } = props;
//
const width = actionList.length * 18;
//
return {
//
width: `calc(100% - ${width}px)`,
//
};
//
}
//
);
const
getBindValues
=
computed
(()
=>
{
let
propsData
=
{
...
...
@@ -77,8 +77,9 @@ export default defineComponent({
state
.
selectedKeys
=
v
;
emit
(
'update:selectedKeys'
,
v
);
},
onCheck
:
(
v
:
CheckKeys
)
=>
{
onCheck
:
(
v
:
CheckKeys
,
e
)
=>
{
state
.
checkedKeys
=
v
;
console
.
log
(
e
);
emit
(
'update:value'
,
v
);
},
onRightClick
:
handleRightClick
,
...
...
@@ -94,12 +95,18 @@ export default defineComponent({
getReplaceFields
);
// 渲染操作按钮
function
getIcon
(
params
:
Recordable
,
icon
?:
string
)
{
if
(
!
icon
)
{
if
(
props
.
renderIcon
&&
isFunction
(
props
.
renderIcon
))
{
return
props
.
renderIcon
(
params
);
}
}
return
icon
;
}
function
renderAction
(
node
:
TreeItem
)
{
const
{
actionList
}
=
props
;
if
(
!
actionList
||
actionList
.
length
===
0
)
return
;
return
actionList
.
map
((
item
,
index
)
=>
{
return
(
<
span
key
=
{
index
}
class
=
{
`
${
prefixCls
}
__action`
}
>
...
...
@@ -108,8 +115,8 @@ export default defineComponent({
);
});
}
// 渲染树节点
function
renderTreeNode
({
data
}:
{
data
:
TreeItem
[]
|
undefined
})
{
function
renderTreeNode
({
data
,
level
}:
{
data
:
TreeItem
[]
|
undefined
;
level
:
number
})
{
if
(
!
data
)
{
return
null
;
}
...
...
@@ -117,30 +124,36 @@ export default defineComponent({
const
{
title
:
titleField
,
key
:
keyField
,
children
:
childrenField
}
=
unref
(
getReplaceFields
);
const
propsData
=
omit
(
item
,
'title'
);
const
anyItem
=
item
as
any
;
const
icon
=
getIcon
({
...
item
,
level
},
item
.
icon
)
;
return
(
<
Tree
.
TreeNode
{
...
propsData
}
key=
{
anyItem
?.[
keyField
]
}
>
<
Tree
.
TreeNode
{...
propsData
}
key
=
{
get
(
item
,
keyField
)
}
>
{{
title
:
()
=>
(
<
span
class
=
{
`
${
prefixCls
}
-title`
}
>
<
span
class=
{
`${prefixCls}__content`
}
style=
{
unref
(
getContentStyle
)
}
>
{
titleField
&&
anyItem
[
titleField
]
}
{
icon
&&
<
TreeIcon
icon
=
{
icon
}
/>
}
<
span
class
=
{
`
${
prefixCls
}
__content`
}
// style={unref(getContentStyle)}
>
{
get
(
item
,
titleField
)}
<
/span
>
<
span
class
=
{
`
${
prefixCls
}
__actions`
}
>
{
renderAction
(
item
)}
<
/span
>
<
/span
>
),
default
:
()
=>
renderTreeNode
({
data
:
childrenField
?
anyItem
[
childrenField
]
:
[]
}),
default
:
()
=>
renderTreeNode
({
data
:
get
(
item
,
childrenField
)
||
[],
level
:
level
+
1
}),
}}
<
/Tree.TreeNode
>
);
});
}
// 处理右键事件
async
function
handleRightClick
({
event
,
node
}:
any
)
{
const
{
rightMenuList
:
menuList
=
[],
beforeRightClick
}
=
props
;
let
rightMenuList
:
ContextMenuItem
[]
=
[];
if
(
beforeRightClick
&&
isFunction
(
beforeRightClick
))
{
rightMenuList
=
await
beforeRightClick
(
node
);
}
else
{
...
...
@@ -206,14 +219,63 @@ export default defineComponent({
return
()
=>
{
return
(
<
Tree
{
...
(
unref
(
getBindValues
)
as
any
)}
class=
{
prefixCls
}
>
<
Tree
{...
unref
(
getBindValues
)}
showIcon
=
{
false
}
class
=
{[
prefixCls
]
}
>
{{
switcherIcon
:
()
=>
<
DownOutlined
/>,
default
:
()
=>
renderTreeNode
({
data
:
unref
(
getTreeData
)
}),
//
switcherIcon: () =>
<
DownOutlined
/>
,
default
:
()
=>
renderTreeNode
({
data
:
unref
(
getTreeData
),
level
:
1
}),
...
extendSlots
(
slots
),
}}
<
/Tree
>
);
};
},
});
});
</
script
>
<
style
lang=
"less"
>
@prefix-cls
:
~
'@{namespace}-basic-tree'
;
.@
{
prefix-cls
}
{
position
:
relative
;
.ant-tree-node-content-wrapper
{
position
:
relative
;
.ant-tree-title
{
position
:
absolute
;
left
:
0
;
width
:
100%
;
}
}
&
-title
{
position
:
relative
;
display
:
flex
;
align-items
:
center
;
width
:
100%
;
padding-right
:
10px
;
&:hover
{
.@{prefix-cls
}
__action
{
visibility
:
visible
;
}
}
}
&
__content
{
display
:
inline-block
;
overflow
:
hidden
;
}
&
__actions
{
position
:
absolute
;
top
:
2px
;
right
:
2px
;
display
:
flex
;
}
&
__action
{
margin-left
:
4px
;
visibility
:
hidden
;
}
}
</
style
>
src/components/Tree/src/props.ts
浏览文件 @
72b42d7b
import
{
PropType
}
from
'vue'
;
import
type
{
ReplaceFields
,
TreeItem
,
ActionItem
,
Keys
,
CheckKeys
}
from
'./types'
;
import
type
{
PropType
}
from
'vue'
;
import
type
{
ReplaceFields
,
ActionItem
,
Keys
,
CheckKeys
}
from
'./types'
;
import
type
{
ContextMenuItem
}
from
'/@/hooks/web/useContextMenu'
;
import
type
{
TreeDataItem
}
from
'ant-design-vue/es/tree/Tree'
;
export
const
basicProps
=
{
renderIcon
:
{
type
:
Function
as
PropType
<
(
params
:
Recordable
)
=>
string
>
,
},
replaceFields
:
{
type
:
Object
as
PropType
<
ReplaceFields
>
,
},
treeData
:
{
type
:
Array
as
PropType
<
TreeItem
[]
>
,
type
:
Array
as
PropType
<
Tree
Data
Item
[]
>
,
},
actionList
:
{
...
...
@@ -50,7 +54,7 @@ export const treeNodeProps = {
type
:
Object
as
PropType
<
ReplaceFields
>
,
},
treeData
:
{
type
:
Array
as
PropType
<
TreeItem
[]
>
,
type
:
Array
as
PropType
<
Tree
Data
Item
[]
>
,
default
:
()
=>
[],
},
};
src/components/Tree/src/types.ts
浏览文件 @
72b42d7b
import
type
{
TreeDataItem
}
from
'ant-design-vue/es/tree/Tree'
;
export
interface
ActionItem
{
render
:
(
record
:
any
)
=>
any
;
}
export
interface
TreeItem
{
/**
* Class
* @description className
* @type string
*/
class
?:
string
;
/**
* Style
* @description style of tree node
* @type string | object
*/
style
?:
string
|
object
;
/**
* Disable Checkbox
* @description Disables the checkbox of the treeNode
* @default false
* @type boolean
*/
disableCheckbox
?:
boolean
;
/**
* Disabled
* @description Disabled or not
* @default false
* @type boolean
*/
disabled
?:
boolean
;
/**
* Icon
* @description customize icon. When you pass component, whose render will receive full TreeNode props as component props
* @type any (slot | slot-scope)
*/
export
interface
TreeItem
extends
TreeDataItem
{
icon
?:
any
;
/**
* Is Leaf?
* @description Leaf node or not
* @default false
* @type boolean
*/
isLeaf
?:
boolean
;
/**
* Key
* @description Required property, should be unique in the tree
* (In tree: Used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys)
* @default internal calculated position of treeNode or undefined
* @type string | number
*/
key
:
string
|
number
;
/**
* Selectable
* @description Set whether the treeNode can be selected
* @default true
* @type boolean
*/
selectable
?:
boolean
;
/**
* Title
* @description Content showed on the treeNodes
* @default '---'
* @type any (string | slot)
*/
title
:
any
;
/**
* Value
* @description Will be treated as treeNodeFilterProp by default, should be unique in the tree
* @default undefined
* @type string
*/
value
?:
string
;
children
?:
TreeItem
[];
slots
?:
any
;
scopedSlots
?:
any
;
}
export
interface
ReplaceFields
{
...
...
@@ -107,12 +29,12 @@ export interface TreeActionType {
filterByLevel
:
(
level
:
number
)
=>
void
;
insertNodeByKey
:
(
opt
:
InsertNodeParams
)
=>
void
;
deleteNodeByKey
:
(
key
:
string
)
=>
void
;
updateNodeByKey
:
(
key
:
string
,
node
:
Omit
<
TreeItem
,
'key'
>
)
=>
void
;
updateNodeByKey
:
(
key
:
string
,
node
:
Omit
<
Tree
Data
Item
,
'key'
>
)
=>
void
;
}
export
interface
InsertNodeParams
{
parentKey
:
string
|
null
;
node
:
TreeItem
;
list
?:
TreeItem
[];
node
:
Tree
Data
Item
;
list
?:
Tree
Data
Item
[];
push
?:
'push'
|
'unshift'
;
}
src/components/Tree/src/useTree.ts
浏览文件 @
72b42d7b
import
type
{
InsertNodeParams
,
ReplaceFields
,
TreeItem
}
from
'./types'
;
import
type
{
InsertNodeParams
,
ReplaceFields
}
from
'./types'
;
import
type
{
Ref
,
ComputedRef
}
from
'vue'
;
import
type
{
TreeDataItem
}
from
'ant-design-vue/es/tree/Tree'
;
import
{
cloneDeep
}
from
'lodash-es'
;
import
{
unref
}
from
'vue'
;
import
{
forEach
}
from
'/@/utils/helper/treeHelper'
;
export
function
useTree
(
treeDataRef
:
Ref
<
TreeItem
[]
>
,
treeDataRef
:
Ref
<
Tree
Data
Item
[]
>
,
getReplaceFields
:
ComputedRef
<
ReplaceFields
>
)
{
//
更新节点
function
updateNodeByKey
(
key
:
string
,
node
:
Tree
Item
,
list
?:
Tree
Item
[])
{
//
Update node
function
updateNodeByKey
(
key
:
string
,
node
:
Tree
DataItem
,
list
?:
TreeData
Item
[])
{
if
(
!
key
)
return
;
const
treeData
=
list
||
unref
(
treeDataRef
);
const
{
key
:
keyField
,
children
:
childrenField
}
=
unref
(
getReplaceFields
);
...
...
@@ -30,8 +31,8 @@ export function useTree(
}
}
//
展开指定级别
function
filterByLevel
(
level
=
1
,
list
?:
TreeItem
[],
currentLevel
=
1
)
{
//
Expand the specified level
function
filterByLevel
(
level
=
1
,
list
?:
Tree
Data
Item
[],
currentLevel
=
1
)
{
if
(
!
level
)
{
return
[];
}
...
...
@@ -74,8 +75,8 @@ export function useTree(
treeDataRef
.
value
=
treeData
;
}
//
删除节点
function
deleteNodeByKey
(
key
:
string
,
list
?:
TreeItem
[])
{
//
Delete node
function
deleteNodeByKey
(
key
:
string
,
list
?:
Tree
Data
Item
[])
{
if
(
!
key
)
return
;
const
treeData
=
list
||
unref
(
treeDataRef
);
const
{
key
:
keyField
,
children
:
childrenField
}
=
unref
(
getReplaceFields
);
...
...
src/views/demo/tree/ActionTree.vue
浏览文件 @
72b42d7b
...
...
@@ -35,6 +35,7 @@
setup
()
{
const
treeRef
=
ref
<
Nullable
<
TreeActionType
>>
(
null
);
const
{
createMessage
}
=
useMessage
();
function
getTree
()
{
const
tree
=
unref
(
treeRef
);
if
(
!
tree
)
{
...
...
src/views/demo/tree/EditTree.vue
浏览文件 @
72b42d7b
<
template
>
<PageWrapper
title=
"Tree函数操作示例"
>
<div
class=
"flex"
>
<CollapseContainer
title=
"右侧操作按钮"
class=
"mr-4"
:style=
"
{ width: '33%' }">
<BasicTree
:treeData=
"treeData"
:actionList=
"actionList"
/>
<CollapseContainer
title=
"右侧操作按钮
/自定义图标
"
class=
"mr-4"
:style=
"
{ width: '33%' }">
<BasicTree
:treeData=
"treeData"
:actionList=
"actionList"
:renderIcon=
"createIcon"
/>
</CollapseContainer>
<CollapseContainer
title=
"右键菜单"
class=
"mr-4"
:style=
"
{ width: '33%' }">
...
...
@@ -61,7 +61,19 @@
},
},
];
return
{
treeData
,
actionList
,
getRightMenuList
};
function
createIcon
({
level
})
{
if
(
level
===
1
)
{
return
'ion:git-compare-outline'
;
}
if
(
level
===
2
)
{
return
'ion:home'
;
}
if
(
level
===
3
)
{
return
'ion:airplane'
;
}
}
return
{
treeData
,
actionList
,
getRightMenuList
,
createIcon
};
},
});
</
script
>
src/views/demo/tree/data.ts
浏览文件 @
72b42d7b
...
...
@@ -2,9 +2,8 @@ import { TreeItem } from '/@/components/Tree/index';
export
const
treeData
:
TreeItem
[]
=
[
{
title
:
'parent
1parent
'
,
title
:
'parent '
,
key
:
'0-0'
,
icon
:
'home|svg'
,
children
:
[
{
title
:
'leaf'
,
key
:
'0-0-0'
},
{
...
...
@@ -20,7 +19,6 @@ export const treeData: TreeItem[] = [
{
title
:
'parent 2'
,
key
:
'1-1'
,
icon
:
'home|svg'
,
children
:
[
{
title
:
'leaf'
,
key
:
'1-1-0'
},
{
title
:
'leaf'
,
key
:
'1-1-1'
},
...
...
@@ -29,7 +27,6 @@ export const treeData: TreeItem[] = [
{
title
:
'parent 3'
,
key
:
'2-2'
,
icon
:
'home|svg'
,
children
:
[
{
title
:
'leaf'
,
key
:
'2-2-0'
},
{
title
:
'leaf'
,
key
:
'2-2-1'
},
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论