Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
D
dataease
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
njgzx
dataease
Commits
2cf026ed
提交
2cf026ed
authored
5月 26, 2022
作者:
fit2cloud-chenyw
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 过滤条件增加下拉树
上级
adff0992
隐藏空白字符变更
内嵌
并排
正在显示
18 个修改的文件
包含
1540 行增加
和
18 行删除
+1540
-18
ShiroServiceImpl.java
.../java/io/dataease/auth/service/impl/ShiroServiceImpl.java
+1
-1
BaseTreeNode.java
...src/main/java/io/dataease/commons/model/BaseTreeNode.java
+28
-0
TreeUtils.java
...nd/src/main/java/io/dataease/commons/utils/TreeUtils.java
+41
-0
DataSetTableFieldController.java
...aease/controller/dataset/DataSetTableFieldController.java
+17
-0
DataSetFieldService.java
...java/io/dataease/service/dataset/DataSetFieldService.java
+2
-0
DirectFieldService.java
...aease/service/dataset/impl/direct/DirectFieldService.java
+56
-12
dataset.js
frontend/src/api/dataset/dataset.js
+13
-3
dom.js
frontend/src/components/ElTreeSelect/dom.js
+37
-0
index.vue
frontend/src/components/ElTreeSelect/index.vue
+594
-0
utils.js
frontend/src/components/ElTreeSelect/utils.js
+78
-0
DeSelectTree.vue
frontend/src/components/widget/DeWidget/DeSelectTree.vue
+331
-0
TextSelectTreeServiceImpl.js
...omponents/widget/serviceImpl/TextSelectTreeServiceImpl.js
+108
-0
en.js
frontend/src/lang/en.js
+4
-0
tw.js
frontend/src/lang/tw.js
+4
-0
zh.js
frontend/src/lang/zh.js
+5
-1
index.vue
frontend/src/views/panel/filter/index.vue
+2
-1
MyTree.vue
frontend/src/views/system/test/MyTree.vue
+150
-0
index.vue
frontend/src/views/system/test/index.vue
+69
-0
没有找到文件。
backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java
浏览文件 @
2cf026ed
...
...
@@ -54,7 +54,6 @@ public class ShiroServiceImpl implements ShiroService {
// 验证链接
filterChainDefinitionMap
.
put
(
"/api/link/validate**"
,
ANON
);
filterChainDefinitionMap
.
put
(
"/api/map/areaEntitys/**"
,
ANON
);
filterChainDefinitionMap
.
put
(
"/dataset/field/fieldValues/**"
,
ANON
);
filterChainDefinitionMap
.
put
(
"/linkJump/queryPanelJumpInfo/**"
,
ANON
);
filterChainDefinitionMap
.
put
(
"/linkJump/queryTargetPanelJumpInfo"
,
ANON
);
...
...
@@ -98,6 +97,7 @@ public class ShiroServiceImpl implements ShiroService {
filterChainDefinitionMap
.
put
(
"/api/link/viewDetail/**"
,
"link"
);
filterChainDefinitionMap
.
put
(
"/panel/group/exportDetails"
,
ANON
);
filterChainDefinitionMap
.
put
(
"/dataset/field/linkMultFieldValues"
,
"link"
);
filterChainDefinitionMap
.
put
(
"/dataset/field/linkMappingFieldValues"
,
"link"
);
filterChainDefinitionMap
.
put
(
"/**"
,
"authc"
);
...
...
backend/src/main/java/io/dataease/commons/model/BaseTreeNode.java
0 → 100644
浏览文件 @
2cf026ed
package
io
.
dataease
.
commons
.
model
;
import
io.dataease.plugins.common.model.ITreeBase
;
import
lombok.Data
;
import
java.util.List
;
@Data
public
class
BaseTreeNode
implements
ITreeBase
<
BaseTreeNode
>
{
private
String
id
;
private
String
pid
;
private
String
text
;
private
String
nodeType
;
private
List
<
BaseTreeNode
>
children
;
public
BaseTreeNode
(
String
id
,
String
pid
,
String
text
,
String
nodeType
)
{
this
.
id
=
id
;
this
.
pid
=
pid
;
this
.
text
=
text
;
this
.
nodeType
=
nodeType
;
}
}
backend/src/main/java/io/dataease/commons/utils/TreeUtils.java
浏览文件 @
2cf026ed
package
io
.
dataease
.
commons
.
utils
;
import
io.dataease.plugins.common.model.ITreeBase
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.util.Assert
;
import
org.springframework.util.CollectionUtils
;
...
...
@@ -14,6 +15,9 @@ import java.util.stream.Collectors;
*/
public
class
TreeUtils
{
public
final
static
String
DEFAULT_ROOT
=
"root"
;
public
final
static
String
SEPARATOR
=
"-de-"
;
/**
* Description: rootPid 是根节点PID
*/
...
...
@@ -53,4 +57,41 @@ public class TreeUtils{
return
mergeTree
(
tree
,
"0"
);
}
public
static
<
T
extends
ITreeBase
>
List
<
T
>
mergeDuplicateTree
(
List
<
T
>
tree
,
String
...
rootPid
)
{
Assert
.
notNull
(
rootPid
,
"Root Pid cannot be null"
);
if
(
CollectionUtils
.
isEmpty
(
tree
)){
return
null
;
}
List
<
T
>
result
=
new
ArrayList
<>();
// 构建id-节点map映射
Map
<
String
,
T
>
treePidMap
=
tree
.
stream
().
collect
(
Collectors
.
toMap
(
node
->
node
.
getNodeType
(),
t
->
t
));
tree
.
stream
().
filter
(
item
->
StringUtils
.
isNotBlank
(
item
.
getId
())).
forEach
(
node
->
{
String
nodeType
=
node
.
getNodeType
();
String
[]
links
=
nodeType
.
split
(
SEPARATOR
);
int
length
=
links
.
length
;
int
level
=
Integer
.
parseInt
(
links
[
length
-
1
]);
// 判断根节点
if
(
Arrays
.
asList
(
rootPid
).
contains
(
node
.
getPid
())
&&
0
==
level
)
{
result
.
add
(
node
);
}
else
{
//找到父元素
String
[]
pLinks
=
new
String
[
level
];
System
.
arraycopy
(
links
,
0
,
pLinks
,
0
,
level
);
String
parentType
=
Arrays
.
stream
(
pLinks
).
collect
(
Collectors
.
joining
(
SEPARATOR
))
+
TreeUtils
.
SEPARATOR
+
(
level
-
1
);
T
parentNode
=
treePidMap
.
get
(
parentType
);
if
(
parentNode
==
null
){
// 可能出现 rootPid 更高的节点 这个操作相当于截断
return
;
}
if
(
parentNode
.
getChildren
()
==
null
)
{
parentNode
.
setChildren
(
new
ArrayList
());
}
parentNode
.
getChildren
().
add
(
node
);
}
});
return
result
;
}
}
backend/src/main/java/io/dataease/controller/dataset/DataSetTableFieldController.java
浏览文件 @
2cf026ed
...
...
@@ -177,6 +177,23 @@ public class DataSetTableFieldController {
return
list
;
}
@ApiIgnore
@PostMapping
(
"linkMappingFieldValues"
)
public
List
<
Object
>
linkMappingFieldValues
(
@RequestBody
MultFieldValuesRequest
multFieldValuesRequest
)
throws
Exception
{
HttpServletRequest
request
=
((
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
()).
getRequest
();
String
linkToken
=
request
.
getHeader
(
F2CLinkFilter
.
LINK_TOKEN_KEY
);
DecodedJWT
jwt
=
JWT
.
decode
(
linkToken
);
Long
userId
=
jwt
.
getClaim
(
"userId"
).
asLong
();
multFieldValuesRequest
.
setUserId
(
userId
);
return
dataSetFieldService
.
fieldValues
(
multFieldValuesRequest
.
getFieldIds
(),
multFieldValuesRequest
.
getUserId
(),
true
,
true
);
}
@ApiIgnore
@PostMapping
(
"mappingFieldValues"
)
public
List
<
Object
>
mappingFieldValues
(
@RequestBody
MultFieldValuesRequest
multFieldValuesRequest
)
throws
Exception
{
return
dataSetFieldService
.
fieldValues
(
multFieldValuesRequest
.
getFieldIds
(),
multFieldValuesRequest
.
getUserId
(),
true
,
true
);
}
@ApiIgnore
@PostMapping
(
"multFieldValuesForPermissions"
)
public
List
<
Object
>
multFieldValuesForPermissions
(
@RequestBody
MultFieldValuesRequest
multFieldValuesRequest
)
throws
Exception
{
...
...
backend/src/main/java/io/dataease/service/dataset/DataSetFieldService.java
浏览文件 @
2cf026ed
...
...
@@ -6,4 +6,6 @@ import java.util.List;
public
interface
DataSetFieldService
{
List
<
Object
>
fieldValues
(
String
fieldId
,
Long
userId
,
Boolean
userPermissions
)
throws
Exception
;
List
<
Object
>
fieldValues
(
List
<
String
>
fieldIds
,
Long
userId
,
Boolean
userPermissions
,
Boolean
needMapping
)
throws
Exception
;
}
backend/src/main/java/io/dataease/service/dataset/impl/direct/DirectFieldService.java
浏览文件 @
2cf026ed
package
io
.
dataease
.
service
.
dataset
.
impl
.
direct
;
import
com.google.gson.Gson
;
import
io.dataease.commons.model.BaseTreeNode
;
import
io.dataease.commons.utils.TreeUtils
;
import
io.dataease.plugins.common.base.domain.DatasetTable
;
import
io.dataease.plugins.common.base.domain.DatasetTableField
;
import
io.dataease.plugins.common.base.domain.Datasource
;
...
...
@@ -22,9 +24,7 @@ import org.apache.commons.lang3.StringUtils;
import
org.springframework.stereotype.Service
;
import
javax.annotation.Resource
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.*
;
import
java.util.stream.Collectors
;
...
...
@@ -45,6 +45,14 @@ public class DirectFieldService implements DataSetFieldService {
@Override
public
List
<
Object
>
fieldValues
(
String
fieldId
,
Long
userId
,
Boolean
userPermissions
)
throws
Exception
{
List
<
String
>
filedIds
=
new
ArrayList
<>();
filedIds
.
add
(
fieldId
);
return
fieldValues
(
filedIds
,
userId
,
userPermissions
,
false
);
}
@Override
public
List
<
Object
>
fieldValues
(
List
<
String
>
fieldIds
,
Long
userId
,
Boolean
userPermissions
,
Boolean
needMapping
)
throws
Exception
{
String
fieldId
=
fieldIds
.
get
(
0
);
DatasetTableField
field
=
dataSetTableFieldsService
.
selectByPrimaryKey
(
fieldId
);
if
(
field
==
null
||
StringUtils
.
isEmpty
(
field
.
getTableId
()))
return
null
;
...
...
@@ -54,15 +62,23 @@ public class DirectFieldService implements DataSetFieldService {
DatasetTableField
datasetTableField
=
DatasetTableField
.
builder
().
tableId
(
field
.
getTableId
()).
checked
(
Boolean
.
TRUE
).
build
();
List
<
DatasetTableField
>
fields
=
dataSetTableFieldsService
.
list
(
datasetTableField
);
List
<
DatasetTableField
>
permissionFields
=
fields
;
List
<
ChartFieldCustomFilterDTO
>
customFilter
=
new
ArrayList
<>();
if
(
userPermissions
){
//列权限
List
<
String
>
desensitizationList
=
new
ArrayList
<>();
fields
=
permissionService
.
filterColumnPermissons
(
fields
,
desensitizationList
,
datasetTable
.
getId
(),
userId
);
//禁用的
if
(!
fields
.
stream
().
map
(
DatasetTableField:
:
getId
).
collect
(
Collectors
.
toList
()).
contains
(
fieldId
)){
permissionFields
=
fields
.
stream
().
filter
(
node
->
fieldIds
.
stream
().
anyMatch
(
item
->
StringUtils
.
equals
(
node
.
getId
(),
item
))).
collect
(
Collectors
.
toList
());
if
(
CollectionUtils
.
isEmpty
(
permissionFields
))
{
return
new
ArrayList
<>();
}
//禁用的
/*if(!fields.stream().map(DatasetTableField::getId).collect(Collectors.toList()).contains(fieldId)){
return new ArrayList<>();
}*/
if
(
CollectionUtils
.
isNotEmpty
(
desensitizationList
)
&&
desensitizationList
.
contains
(
field
.
getDataeaseName
()))
{
List
<
Object
>
results
=
new
ArrayList
<>();
results
.
add
(
ColumnPermissionConstants
.
Desensitization_desc
);
...
...
@@ -87,18 +103,18 @@ public class DirectFieldService implements DataSetFieldService {
QueryProvider
qp
=
ProviderFactory
.
getQueryProvider
(
ds
.
getType
());
if
(
StringUtils
.
equalsIgnoreCase
(
datasetTable
.
getType
(),
"db"
))
{
datasourceRequest
.
setTable
(
dataTableInfoDTO
.
getTable
());
datasourceRequest
.
setQuery
(
qp
.
createQuerySQL
(
dataTableInfoDTO
.
getTable
(),
Collections
.
singletonList
(
field
)
,
true
,
ds
,
customFilter
));
datasourceRequest
.
setQuery
(
qp
.
createQuerySQL
(
dataTableInfoDTO
.
getTable
(),
permissionFields
,
true
,
ds
,
customFilter
));
}
else
if
(
StringUtils
.
equalsIgnoreCase
(
datasetTable
.
getType
(),
"sql"
))
{
datasourceRequest
.
setQuery
(
qp
.
createQuerySQLAsTmp
(
dataTableInfoDTO
.
getSql
(),
Collections
.
singletonList
(
field
)
,
true
,
customFilter
));
datasourceRequest
.
setQuery
(
qp
.
createQuerySQLAsTmp
(
dataTableInfoDTO
.
getSql
(),
permissionFields
,
true
,
customFilter
));
}
else
if
(
StringUtils
.
equalsIgnoreCase
(
datasetTable
.
getType
(),
"custom"
))
{
DataTableInfoDTO
dt
=
new
Gson
().
fromJson
(
datasetTable
.
getInfo
(),
DataTableInfoDTO
.
class
);
List
<
DataSetTableUnionDTO
>
listUnion
=
dataSetTableUnionService
.
listByTableId
(
dt
.
getList
().
get
(
0
).
getTableId
());
String
sql
=
dataSetTableService
.
getCustomSQLDatasource
(
dt
,
listUnion
,
ds
);
datasourceRequest
.
setQuery
(
qp
.
createQuerySQLAsTmp
(
sql
,
Collections
.
singletonList
(
field
)
,
true
,
customFilter
));
datasourceRequest
.
setQuery
(
qp
.
createQuerySQLAsTmp
(
sql
,
permissionFields
,
true
,
customFilter
));
}
else
if
(
StringUtils
.
equalsIgnoreCase
(
datasetTable
.
getType
(),
"union"
))
{
DataTableInfoDTO
dt
=
new
Gson
().
fromJson
(
datasetTable
.
getInfo
(),
DataTableInfoDTO
.
class
);
String
sql
=
(
String
)
dataSetTableService
.
getUnionSQLDatasource
(
dt
,
ds
).
get
(
"sql"
);
datasourceRequest
.
setQuery
(
qp
.
createQuerySQLAsTmp
(
sql
,
Collections
.
singletonList
(
field
)
,
true
,
customFilter
));
datasourceRequest
.
setQuery
(
qp
.
createQuerySQLAsTmp
(
sql
,
permissionFields
,
true
,
customFilter
));
}
}
else
if
(
datasetTable
.
getMode
()
==
1
)
{
// 抽取
// 连接doris,构建doris数据源查询
...
...
@@ -109,11 +125,39 @@ public class DirectFieldService implements DataSetFieldService {
String
tableName
=
"ds_"
+
datasetTable
.
getId
().
replaceAll
(
"-"
,
"_"
);
datasourceRequest
.
setTable
(
tableName
);
QueryProvider
qp
=
ProviderFactory
.
getQueryProvider
(
ds
.
getType
());
datasourceRequest
.
setQuery
(
qp
.
createQuerySQL
(
tableName
,
Collections
.
singletonList
(
field
)
,
true
,
null
,
customFilter
));
datasourceRequest
.
setQuery
(
qp
.
createQuerySQL
(
tableName
,
permissionFields
,
true
,
null
,
customFilter
));
}
List
<
String
[]>
rows
=
datasourceProvider
.
getData
(
datasourceRequest
);
List
<
Object
>
results
=
rows
.
stream
().
map
(
row
->
row
[
0
]).
distinct
().
collect
(
Collectors
.
toList
());
return
results
;
if
(!
needMapping
)
{
List
<
Object
>
results
=
rows
.
stream
().
map
(
row
->
row
[
0
]).
distinct
().
collect
(
Collectors
.
toList
());
return
results
;
}
Set
<
String
>
pkSet
=
new
HashSet
<>();
List
<
BaseTreeNode
>
treeNodes
=
rows
.
stream
().
map
(
row
->
buildTreeNode
(
row
,
pkSet
)).
flatMap
(
Collection:
:
stream
).
collect
(
Collectors
.
toList
());
List
tree
=
TreeUtils
.
mergeDuplicateTree
(
treeNodes
,
TreeUtils
.
DEFAULT_ROOT
);
return
tree
;
}
private
List
<
BaseTreeNode
>
buildTreeNode
(
String
[]
row
,
Set
<
String
>
pkSet
)
{
List
<
BaseTreeNode
>
nodes
=
new
ArrayList
<>();
List
<
String
>
parentPkList
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
row
.
length
;
i
++)
{
String
text
=
row
[
i
];
parentPkList
.
add
(
text
);
String
val
=
parentPkList
.
stream
().
collect
(
Collectors
.
joining
(
TreeUtils
.
SEPARATOR
));
String
parentVal
=
i
==
0
?
TreeUtils
.
DEFAULT_ROOT
:
row
[
i
-
1
];
String
pk
=
parentPkList
.
stream
().
collect
(
Collectors
.
joining
(
TreeUtils
.
SEPARATOR
));
if
(
pkSet
.
contains
(
pk
))
continue
;
pkSet
.
add
(
pk
);
BaseTreeNode
node
=
new
BaseTreeNode
(
val
,
parentVal
,
text
,
pk
+
TreeUtils
.
SEPARATOR
+
i
);
nodes
.
add
(
node
);
}
return
nodes
;
}
}
frontend/src/api/dataset/dataset.js
浏览文件 @
2cf026ed
...
...
@@ -146,11 +146,21 @@ export function post(url, data, showLoading = true, timeout = 60000) {
})
}
export
function
fieldValues
(
fieldId
)
{
export
function
mappingFieldValues
(
data
)
{
return
request
({
url
:
'/dataset/field/
fieldValues/'
+
fieldId
,
url
:
'/dataset/field/
mappingFieldValues'
,
method
:
'post'
,
loading
:
true
loading
:
true
,
data
})
}
export
function
linkMappingFieldValues
(
data
)
{
return
request
({
url
:
'/dataset/field/linkMappingFieldValues'
,
method
:
'post'
,
loading
:
true
,
data
})
}
...
...
frontend/src/components/ElTreeSelect/dom.js
0 → 100644
浏览文件 @
2cf026ed
/*
* @moduleName:
* @Author: dawdler
* @LastModifiedBy: dawdler
* @Date: 2019-03-22 14:47:35
* @LastEditTime: 2019-03-22 16:31:38
*/
export
const
on
=
(
function
()
{
if
(
document
.
addEventListener
)
{
return
function
(
element
,
event
,
handler
)
{
if
(
element
&&
event
&&
handler
)
{
element
.
addEventListener
(
event
,
handler
,
false
)
}
}
}
else
{
return
function
(
element
,
event
,
handler
)
{
if
(
element
&&
event
&&
handler
)
{
element
.
attachEvent
(
'on'
+
event
,
handler
)
}
}
}
})()
export
const
off
=
(
function
()
{
if
(
document
.
removeEventListener
)
{
return
function
(
element
,
event
,
handler
)
{
if
(
element
&&
event
)
{
element
.
removeEventListener
(
event
,
handler
,
false
)
}
}
}
else
{
return
function
(
element
,
event
,
handler
)
{
if
(
element
&&
event
)
{
element
.
detachEvent
(
'on'
+
event
,
handler
)
}
}
}
})()
frontend/src/components/ElTreeSelect/index.vue
0 → 100644
浏览文件 @
2cf026ed
<!--
* @moduleName: 下拉树组件
* @Author: dawdler
* @Date: 2018-12-19 14:03:03
* @LastModifiedBy: dawdler
* @LastEditTime: 2020-12-26 14:51:20
-->
<
template
>
<div
class=
"el-tree-select"
:class=
"selectClass"
>
<!-- 下拉文本 -->
<el-select
:id=
"'el-tree-select-' + guid"
ref=
"select"
v-model=
"labels"
v-popover:popover
:style=
"styles"
class=
"el-tree-select-input"
:disabled=
"disabled"
popper-class=
"select-option"
v-bind=
"selectParams"
:popper-append-to-body=
"false"
:filterable=
"false"
:multiple=
"selectParams.multiple"
:title=
"labels"
@
remove-tag=
"_selectRemoveTag"
@
clear=
"_selectClearFun"
@
focus=
"_popoverShowFun"
/>
<!-- 弹出框 -->
<el-popover
ref=
"popover"
v-model=
"visible"
:placement=
"placement"
:transition=
"transition"
:popper-class=
"popperClass"
:width=
"width"
trigger=
"click"
>
<!-- 是否显示搜索框 -->
<el-input
v-if=
"treeParams.filterable"
v-model=
"keywords"
size=
"mini"
class=
"input-with-select mb10"
@
change=
"_searchFun"
>
<el-button
slot=
"append"
icon=
"el-icon-search"
/>
</el-input>
<el-scrollbar
tag=
"div"
wrap-class=
"el-select-dropdown__wrap"
view-class=
"el-select-dropdown__list"
class=
"is-empty"
>
<!-- 树列表 -->
<el-tree
v-show=
"data.length > 0"
ref=
"tree"
v-bind=
"treeParams"
:data=
"data"
:node-key=
"propsValue"
:draggable=
"false"
:current-node-key=
"ids.length > 0 ? ids[0] : ''"
:show-checkbox=
"selectParams.multiple"
:filter-node-method=
"filterNodeMethod ? filterNodeMethod : _filterFun"
:render-content=
"treeRenderFun"
@
node-click=
"_treeNodeClickFun"
@
check=
"_treeCheckFun"
/>
<!-- 暂无数据 -->
<div
v-if=
"data.length === 0"
class=
"no-data"
>
暂无数据
</div>
</el-scrollbar>
</el-popover>
</div>
</
template
>
<
script
>
import
{
on
,
off
}
from
'./dom'
import
{
each
,
guid
}
from
'./utils'
// @group api
export
default
{
name
:
'ElTreeSelect'
,
components
:
{},
props
:
{
// v-model,存储的是treeParams.data里面的id
value
:
{
// `String` / `Array` / `Number`
type
:
[
String
,
Array
,
Number
],
// `''`
default
()
{
return
''
}
},
// el-select样式
styles
:
{
type
:
Object
,
// {}
default
()
{
return
{}
}
},
// 下拉框 挂类
selectClass
:
{
type
:
String
,
default
()
{
return
''
}
},
// popover 挂类
popoverClass
:
{
type
:
String
,
default
()
{
return
''
}
},
// 是否禁用文本框
disabled
:
{
type
:
Boolean
,
// false
default
()
{
return
false
}
},
// 弹出框位置
placement
:
{
type
:
String
,
// bottom
default
()
{
return
'bottom'
}
},
// 弹出框过渡动画
transition
:
{
type
:
String
,
// el-zoom-in-top
default
()
{
return
'el-zoom-in-top'
}
},
// 树渲染方法,具体参考el-tree Function(h, { node, data, store }) {}
treeRenderFun
:
Function
,
// 搜索过滤方法,具体参考el-tree Function(h, { value, data, node }) {}
filterNodeMethod
:
Function
,
/*
文本框参数,几乎支持el-select所有的API<br>
取消参数:<br>
设定下拉框的弹出框隐藏:<br>
`:popper-append-to-body="false"` <br>
搜索从弹出框里面执行: <br>
`filterable="false"`
*/
selectParams
:
{
type
:
Object
,
/*
Object默认参数:<br><br>
是否可以清空选项:<br>
`clearable: true,`<br><br>
是否禁用:<br>
`disabled: false,`<br><br>
搜索框placeholder文字:<br>
`placeholder: '请选择',`<br><br>
*/
default
()
{
return
{
clearable
:
true
,
disabled
:
false
,
placeholder
:
'请选择'
}
}
},
/*
下拉树参数,几乎支持el-tree所有的API<br>
取消参数:<br>
`:show-checkbox="selectParams.multiple"`<br>
使用下拉框参数multiple判断是否对树进行多选<br>
取消对el-tree的人为传参show-checkbox<br>
`:node-key="propsValue"` 自动获取treeParams.props.value<br>
`:draggable="false"` 屏蔽拖动
*/
treeParams
:
{
type
:
Object
,
/*
Object默认参数:<br><br>
在有子级的情况下是否点击父级关闭弹出框,false 只能点击子级关闭弹出框:<br><br>
`clickParent: false`<br><br>
是否显示搜索框:<br><br>
`filterable: false`<br><br>
是否只是叶子节点:<br><br>
`leafOnly: false`<br><br>
是否包含半选节点:<br><br>
`includeHalfChecked: false`<br><br>
下拉树的数据:<br><br>
`data:[]`<br><br>
下拉树的props:<br><br>
`props: {`<br>
`children: 'children',`<br>
`label: 'name',`<br>
`value: 'flowId',`<br>
`disabled: 'disabled'`<br>
`}`
*/
default
()
{
return
{
clickParent
:
false
,
filterable
:
false
,
leafOnly
:
false
,
includeHalfChecked
:
false
,
data
:
[],
showParent
:
false
,
props
:
{
children
:
'children'
,
label
:
'name'
,
code
:
'code'
,
value
:
'flowId'
,
disabled
:
'disabled'
}
}
}
}
},
data
()
{
return
{
guid
:
guid
(),
propsValue
:
'flowId'
,
propsLabel
:
'name'
,
propsCode
:
null
,
// 可能有空的情况
propsDisabled
:
'disabled'
,
propsChildren
:
'children'
,
leafOnly
:
false
,
includeHalfChecked
:
false
,
data
:
[],
keywords
:
''
,
labels
:
''
,
// 存储名称,用于下拉框显示内容
ids
:
[],
// 存储id
visible
:
false
,
// popover v-model
width
:
150
,
showParent
:
false
}
},
computed
:
{
popperClass
()
{
const
_c
=
'el-tree-select-popper '
+
this
.
popoverClass
return
this
.
disabled
?
_c
+
' disabled '
:
_c
}
},
watch
:
{
ids
:
function
(
val
)
{
if
(
val
!==
undefined
)
{
this
.
$nextTick
(()
=>
{
this
.
_setSelectNodeFun
(
val
)
})
}
},
value
:
function
(
val
)
{
if
(
this
.
ids
!==
val
)
{
this
.
_setMultipleFun
()
if
(
this
.
selectParams
.
multiple
)
{
this
.
ids
=
[...
val
]
}
else
{
this
.
ids
=
val
===
''
?
[]
:
[
val
]
}
}
}
},
created
()
{
const
{
props
,
data
,
leafOnly
,
includeHalfChecked
,
showParent
}
=
this
.
treeParams
this
.
_setMultipleFun
()
this
.
propsValue
=
props
.
value
this
.
propsLabel
=
props
.
label
this
.
propsCode
=
props
.
code
||
null
// 可能为空
this
.
propsDisabled
=
props
.
disabled
this
.
propsChildren
=
props
.
children
this
.
leafOnly
=
leafOnly
this
.
includeHalfChecked
=
includeHalfChecked
this
.
data
=
data
.
length
>
0
?
[...
data
]
:
[]
if
(
this
.
selectParams
.
multiple
)
{
this
.
labels
=
[]
this
.
ids
=
this
.
value
}
else
{
this
.
labels
=
''
this
.
ids
=
this
.
value
instanceof
Array
?
this
.
value
:
[
this
.
value
]
}
this
.
showParent
=
showParent
},
mounted
()
{
this
.
_updateH
()
this
.
$nextTick
(()
=>
{
on
(
document
,
'mouseup'
,
this
.
_popoverHideFun
)
})
},
beforeDestroy
()
{
off
(
document
,
'mouseup'
,
this
.
_popoverHideFun
)
},
methods
:
{
// 根据类型判断单选,多选
_setMultipleFun
()
{
let
multiple
=
false
if
(
this
.
value
instanceof
Array
)
{
multiple
=
true
}
this
.
$set
(
this
.
selectParams
,
'multiple'
,
multiple
)
},
// 输入文本框输入内容抛出
_searchFun
()
{
/*
对外抛出搜索方法,自行判断是走后台查询,还是前端过滤<br>
前端过滤:this.$refs.treeSelect.$refs.tree.filter(value);<br>
后台查询:this.$refs.treeSelect.treeDataUpdateFun(data);
*/
this
.
$emit
(
'searchFun'
,
this
.
keywords
)
},
// 根据id筛选当前树名称,以及选中树列表
_setSelectNodeFun
(
ids
)
{
const
el
=
this
.
$refs
.
tree
if
(
!
el
)
{
throw
new
Error
(
'找不到tree dom'
)
}
const
{
multiple
}
=
this
.
selectParams
// 长度为0,清空选择
if
(
ids
.
length
===
0
||
this
.
data
.
length
===
0
)
{
this
.
labels
=
multiple
?
[]
:
''
if
(
multiple
)
{
el
.
setCheckedKeys
([])
}
else
{
el
.
setCurrentKey
(
null
)
}
return
}
if
(
multiple
)
{
// element-ui bug. 如果是父子节点全选 el.setCheckedKeys([非全量id]);之后el.getCheckedNodes()还是全量
el
.
getCheckedNodes
(
this
.
leafOnly
,
this
.
includeHalfChecked
).
forEach
(
item
=>
{
el
.
setChecked
(
item
,
false
)
})
ids
.
forEach
(
id
=>
{
el
.
setChecked
(
id
,
true
)
})
const
nodes
=
el
.
getCheckedNodes
(
this
.
leafOnly
,
this
.
includeHalfChecked
)
if
(
!
this
.
showParent
)
{
if
(
this
.
propsCode
)
{
// 如果有code labels=code(name)
this
.
labels
=
nodes
.
map
(
item
=>
(
item
[
this
.
propsCode
]
?
item
[
this
.
propsLabel
]
+
'('
+
item
[
this
.
propsCode
]
+
')'
:
item
[
this
.
propsLabel
]))
||
[]
}
else
{
this
.
labels
=
nodes
.
map
(
item
=>
item
[
this
.
propsLabel
])
||
[]
}
}
else
{
this
.
labels
=
nodes
.
map
(
item
=>
this
.
cascadeLabels
(
item
))
||
[]
}
}
else
{
el
.
setCurrentKey
(
ids
[
0
])
const
node
=
el
.
getCurrentNode
()
if
(
node
)
{
if
(
!
this
.
showParent
)
{
if
(
this
.
propsCode
)
{
// 如果有code labels=code(name)
this
.
labels
=
node
[
this
.
propsCode
]
?
node
[
this
.
propsLabel
]
+
'('
+
node
[
this
.
propsCode
]
+
')'
:
node
[
this
.
propsLabel
]
}
else
{
this
.
labels
=
node
[
this
.
propsLabel
]
}
}
else
{
this
.
labels
=
this
.
cascadeLabels
(
node
)
}
}
else
{
this
.
labels
=
''
}
}
this
.
_updatePopoverLocationFun
()
},
parentNodes
(
node
)
{
const
results
=
[]
let
currentNode
=
node
while
(
currentNode
&&
currentNode
.
data
&&
!
(
currentNode
.
data
instanceof
Array
))
{
results
.
push
(
currentNode
)
currentNode
=
currentNode
.
parent
}
return
results
},
cascadeLabels
(
data
)
{
const
cNode
=
this
.
$refs
.
tree
.
getNode
(
data
)
const
linkedNodes
=
this
.
parentNodes
(
cNode
)
const
labels
=
linkedNodes
.
map
(
item
=>
item
.
data
[
this
.
propsLabel
]).
reverse
().
join
(
':'
)
return
labels
},
// 更新popover位置
_updatePopoverLocationFun
()
{
// dom高度还没有更新,做一个延迟
setTimeout
(()
=>
{
this
.
$refs
.
popover
.
updatePopper
()
},
50
)
},
// 获取MouseEvent.path 针对浏览器兼容性兼容ie11,edge,chrome,firefox,safari
_getEventPath
(
evt
)
{
const
path
=
(
evt
.
composedPath
&&
evt
.
composedPath
())
||
evt
.
path
const
target
=
evt
.
target
if
(
path
!=
null
)
{
return
path
.
indexOf
(
window
)
<
0
?
path
.
concat
(
window
)
:
path
}
if
(
target
===
window
)
{
return
[
window
]
}
function
getParents
(
node
,
memo
)
{
memo
=
memo
||
[]
const
parentNode
=
node
.
parentNode
if
(
!
parentNode
)
{
return
memo
}
else
{
return
getParents
(
parentNode
,
memo
.
concat
(
parentNode
))
}
}
return
[
target
].
concat
(
getParents
(
target
),
window
)
},
// 树过滤
_filterFun
(
value
,
data
,
node
)
{
if
(
!
value
)
return
true
return
data
[
this
.
propsLabel
].
indexOf
(
value
)
!==
-
1
},
// 树点击
_treeNodeClickFun
(
data
,
node
,
vm
)
{
const
{
multiple
}
=
this
.
selectParams
const
{
clickParent
}
=
this
.
treeParams
const
checkStrictly
=
this
.
treeParams
[
'check-strictly'
]
const
{
propsValue
,
propsChildren
,
propsDisabled
}
=
this
const
children
=
data
[
propsChildren
]
||
[]
if
(
data
[
propsDisabled
])
{
// 禁用
return
}
if
(
node
.
checked
)
{
const
value
=
data
[
propsValue
]
this
.
ids
=
this
.
ids
.
filter
(
id
=>
id
!==
value
)
if
(
!
checkStrictly
&&
children
.
length
)
{
children
.
forEach
(
item
=>
{
this
.
ids
=
this
.
ids
.
filter
(
id
=>
id
!==
item
[
propsValue
])
})
}
}
else
{
if
(
!
multiple
)
{
// 多选,不关闭,单选,判断是否允许点击父级关闭弹出框
if
(
!
clickParent
)
{
// 如果不允许点击父级,自身为末级,允许点击之后关闭
if
(
children
.
length
===
0
)
{
this
.
ids
=
[
data
[
propsValue
]]
this
.
visible
=
false
}
else
{
// 不允许父级,阻止继续派发
return
false
}
}
else
{
this
.
ids
=
[
data
[
propsValue
]]
this
.
visible
=
false
}
}
else
{
if
(
!
clickParent
&&
children
.
length
===
0
)
{
// 如果不能点击父级
this
.
ids
.
push
(
data
[
propsValue
])
}
else
if
(
clickParent
)
{
// 允许点击父级
this
.
ids
.
push
(
data
[
propsValue
])
// 如果父子关联,将子节点push进勾选项
if
(
!
checkStrictly
&&
children
.
length
)
{
children
.
forEach
(
item
=>
{
this
.
ids
.
push
(
item
[
propsValue
])
})
}
}
}
}
this
.
_emitFun
()
/*
点击节点,对外抛出 `data, node, vm`<br>
`data:` 当前点击的节点数据<br>
`node:` 当前点击的node<br>
`vm:` 当前组件的vm
*/
this
.
$emit
(
'node-click'
,
data
,
node
,
vm
)
},
// 树勾选
_treeCheckFun
(
data
,
node
,
vm
)
{
this
.
ids
=
[]
const
{
propsValue
}
=
this
node
.
checkedNodes
.
forEach
(
item
=>
{
this
.
ids
.
push
(
item
[
propsValue
])
})
/*
点击复选框,对外抛出 `data, node, vm`<br>
`data:` 当前点击的节点数据<br>
`node:` 当前点击的node<br>
`vm:` 当前组件的vm
*/
this
.
$emit
(
'check'
,
data
,
node
,
vm
)
this
.
_emitFun
()
},
// 下拉框移除tag时触发
_selectRemoveTag
(
tag
)
{
const
{
data
,
propsValue
,
propsLabel
,
propsChildren
}
=
this
each
(
data
,
item
=>
{
const
labels
=
this
.
showParent
?
this
.
cascadeLabels
(
item
)
:
item
[
propsLabel
]
if
(
labels
===
tag
)
{
const
value
=
item
[
propsValue
]
this
.
ids
=
this
.
ids
.
filter
(
id
=>
id
!==
value
)
}
},
propsChildren
)
this
.
$refs
.
tree
.
setCheckedKeys
(
this
.
ids
)
this
.
$emit
(
'removeTag'
,
this
.
ids
,
tag
)
this
.
_emitFun
()
},
// 下拉框清空数据
_selectClearFun
()
{
this
.
ids
=
[]
const
{
multiple
}
=
this
.
selectParams
// 下拉框清空,对外抛出``this.$emit('input', multiple ? [] : '');`
this
.
$emit
(
'input'
,
multiple
?
[]
:
''
)
// 下拉框清空,对外抛出``this.$emit('select-clear');`
this
.
$emit
(
'select-clear'
)
this
.
_updatePopoverLocationFun
()
},
// 判断类型,抛出当前选中id
_emitFun
()
{
const
{
multiple
}
=
this
.
selectParams
this
.
$emit
(
'input'
,
multiple
?
this
.
ids
:
this
.
ids
.
length
>
0
?
this
.
ids
[
0
]
:
''
)
this
.
_updatePopoverLocationFun
()
},
// 更新宽度
_updateH
()
{
this
.
$nextTick
(()
=>
{
this
.
width
=
this
.
$refs
.
select
.
$el
.
getBoundingClientRect
().
width
})
},
// 显示弹出框的时候容错,查看是否和el宽度一致
_popoverShowFun
(
val
)
{
this
.
_updateH
()
},
// 判断是否隐藏弹出框
_popoverHideFun
(
e
)
{
const
path
=
this
.
_getEventPath
(
e
)
const
isInside
=
path
.
some
(
list
=>
{
// 鼠标在弹出框内部,阻止隐藏弹出框
return
list
.
className
&&
typeof
list
.
className
===
'string'
&&
list
.
className
.
indexOf
(
'el-tree-select'
)
!==
-
1
})
if
(
!
isInside
)
{
this
.
visible
=
false
}
},
/**
* @vuese
* 树列表更新数据
* @arg Array
*/
treeDataUpdateFun
(
data
)
{
this
.
data
=
data
// 数据更新完成之后,判断是否回显内容
if
(
data
.
length
>
0
)
{
setTimeout
(()
=>
{
this
.
_setSelectNodeFun
(
this
.
ids
)
},
300
)
}
},
/**
* @vuese
* 本地过滤方法
* @arg String
*/
filterFun
(
val
)
{
this
.
$refs
.
tree
.
filter
(
val
)
}
}
}
</
script
>
<
style
>
.el-tree-select
.select-option
{
display
:
none
!important
;
}
[
aria-disabled
=
'true'
]
>
.el-tree-node__content
{
color
:
inherit
!important
;
background
:
transparent
!important
;
cursor
:
no-drop
!important
;
}
.el-tree-select-popper
{
max-height
:
400px
;
overflow
:
auto
;
}
.el-tree-select-popper.disabled
{
display
:
none
!important
;
}
.el-tree-select-popper
.el-button--small
{
width
:
25px
!important
;
min-width
:
25px
!important
;
}
.el-tree-select-popper
[
x-placement
^=
'bottom'
]
{
margin-top
:
5px
;
}
.mb10
{
margin-bottom
:
10px
;
}
.no-data
{
height
:
32px
;
line-height
:
32px
;
font-size
:
14px
;
color
:
#cccccc
;
text-align
:
center
;
}
</
style
>
frontend/src/components/ElTreeSelect/utils.js
0 → 100644
浏览文件 @
2cf026ed
/*
* @moduleName:通用工具类
* @Author: dawdler
* @Date: 2019-01-09 15:30:18
* @LastModifiedBy: dawdler
* @LastEditTime: 2020-12-26 14:06:09
*/
export
default
{
getTreeData
,
each
}
/*
each(arr, (item, children) => {
item.value = xx;
// 该item 包含children,因此直接赋值,不需要单独处理children里面的值
});
* [_each description] 倒查、展平、数据回调返回回当前一条数据和子集
* @param {[Array]} data [description]
* @param {Function} callback [description]
* @param {String} childName[description]
* @return {[Array]} [description]
* 默认使用副本,在callback处理数据,如果不使用副本,那么需要重新对treeData赋值
treeData = each(treeData, (item, children) => {
item.value = xx;
});
*/
export
function
each
(
data
,
callback
,
childName
=
'children'
)
{
let
current
let
children
for
(
let
i
=
0
,
len
=
data
.
length
;
i
<
len
;
i
++
)
{
current
=
data
[
i
]
children
=
[]
if
(
current
[
childName
]
&&
current
[
childName
].
length
>
0
)
{
children
=
current
[
childName
]
}
callback
&&
callback
(
current
,
children
)
if
(
children
.
length
>
0
)
{
each
(
children
,
callback
,
childName
)
}
}
}
/**
* @Author yihuang",
* @param data 数据
* @param id 要比对的名称
* @param val 要比对的值
* @param name 要返回的名称
* @param children 子集名称
* @param isRow 是否返回这一行的数据
* @注 迭代判断多层
* //=======================
* 返回这一条数据的中文名
* let name=utils.getTreeData(arr, 'flowId', item.decategoryId, 'name');
* //=======================
* 返回所有匹配的数据
* let arr=utils.getTreeData(arr, 'flowId', item.decategoryId, 'name','children',true);
*/
export
function
getTreeData
(
data
,
id
=
'id'
,
val
=
''
,
name
=
'name'
,
children
=
'children'
,
isRow
=
false
)
{
const
arr
=
[]
each
(
data
,
item
=>
{
if
(
item
[
id
]
===
val
)
{
arr
.
push
(
item
)
}
},
children
)
return
arr
.
length
>
0
?
(
isRow
?
arr
:
arr
[
0
][
name
])
:
null
}
export
function
guid
()
{
return
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
.
replace
(
/
[
xy
]
/g
,
function
(
c
)
{
const
r
=
(
Math
.
random
()
*
16
)
|
0
const
v
=
c
===
'x'
?
r
:
(
r
&
0x3
)
|
0x8
return
v
.
toString
(
16
)
})
}
frontend/src/components/widget/DeWidget/DeSelectTree.vue
0 → 100644
浏览文件 @
2cf026ed
<
template
>
<el-tree-select
v-if=
"element.options!== null && element.options.attrs!==null && show"
ref=
"deSelectTree"
v-model=
"value"
popover-class=
"test-class-wrap"
:is-single=
"isSingle"
:data=
"datas"
:select-params=
"selectParams"
:tree-params=
"treeParams"
:filter-node-method=
"_filterFun"
:tree-render-fun=
"_renderFun"
@
searchFun=
"_searchFun"
@
node-click=
"changeNode"
@
removeTag=
"changeNodeIds"
@
check=
"changeCheckNode"
@
select-clear=
"selectClear"
/>
</
template
>
<
script
>
import
{
mappingFieldValues
,
linkMappingFieldValues
}
from
'@/api/dataset/dataset'
import
bus
from
'@/utils/bus'
import
{
getLinkToken
,
getToken
}
from
'@/utils/auth'
import
ElTreeSelect
from
'@/components/ElTreeSelect'
export
default
{
components
:
{
ElTreeSelect
},
props
:
{
element
:
{
type
:
Object
,
default
:
()
=>
{}
},
inDraw
:
{
type
:
Boolean
,
default
:
true
},
inScreen
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
},
size
:
String
},
data
()
{
return
{
showNumber
:
false
,
selectOptionWidth
:
0
,
show
:
true
,
datas
:
[],
value
:
this
.
isSingle
?
''
:
[],
selectParams
:
{
clearable
:
true
,
placeholder
:
this
.
$t
(
this
.
element
.
options
.
attrs
.
placeholder
)
},
treeParams
:
{
showParent
:
true
,
clickParent
:
true
,
filterable
:
true
,
// 只想要子节点,不需要父节点
leafOnly
:
false
,
includeHalfChecked
:
false
,
'check-strictly'
:
false
,
'default-expand-all'
:
false
,
'expand-on-click-node'
:
false
,
'render-content'
:
this
.
_renderFun
,
data
:
[],
props
:
{
children
:
'children'
,
label
:
'text'
,
rootId
:
'root'
,
disabled
:
'disabled'
,
parentId
:
'pid'
,
value
:
'id'
}
}
}
},
computed
:
{
operator
()
{
return
this
.
element
.
options
.
attrs
.
multiple
?
'in'
:
'eq'
},
defaultValueStr
()
{
if
(
!
this
.
element
||
!
this
.
element
.
options
||
!
this
.
element
.
options
.
value
)
return
''
return
this
.
element
.
options
.
value
.
toString
()
},
viewIds
()
{
if
(
!
this
.
element
||
!
this
.
element
.
options
||
!
this
.
element
.
options
.
attrs
.
viewIds
)
return
''
return
this
.
element
.
options
.
attrs
.
viewIds
.
toString
()
},
manualModify
()
{
return
!!
this
.
element
.
options
.
manualModify
},
panelInfo
()
{
return
this
.
$store
.
state
.
panel
.
panelInfo
},
isSingle
()
{
return
this
.
element
.
options
.
attrs
.
multiple
}
},
watch
:
{
'viewIds'
:
function
(
value
,
old
)
{
if
(
typeof
value
===
'undefined'
||
value
===
old
)
return
this
.
setCondition
()
},
'defaultValueStr'
:
function
(
value
,
old
)
{
if
(
value
===
old
)
return
this
.
value
=
this
.
fillValueDerfault
()
this
.
changeValue
(
value
)
},
'element.options.attrs.fieldId'
:
function
(
value
,
old
)
{
if
(
value
===
null
||
typeof
value
===
'undefined'
||
value
===
old
)
return
this
.
datas
=
[]
let
method
=
mappingFieldValues
const
token
=
this
.
$store
.
getters
.
token
||
getToken
()
const
linkToken
=
this
.
$store
.
getters
.
linkToken
||
getLinkToken
()
if
(
!
token
&&
linkToken
)
{
method
=
linkMappingFieldValues
}
const
param
=
{
fieldIds
:
this
.
element
.
options
.
attrs
.
fieldId
.
split
(
','
)
}
if
(
this
.
panelInfo
.
proxy
)
{
param
.
userId
=
this
.
panelInfo
.
proxy
}
this
.
element
.
options
.
attrs
.
fieldId
&&
this
.
element
.
options
.
attrs
.
fieldId
.
length
>
0
&&
method
(
param
).
then
(
res
=>
{
this
.
datas
=
this
.
optionDatas
(
res
.
data
)
this
.
$nextTick
(()
=>
{
this
.
$refs
.
deSelectTree
.
treeDataUpdateFun
(
this
.
datas
)
})
})
||
(
this
.
element
.
options
.
value
=
''
)
},
'element.options.attrs.multiple'
:
function
(
value
,
old
)
{
if
(
typeof
old
===
'undefined'
||
value
===
old
)
return
if
(
!
this
.
inDraw
)
{
this
.
value
=
value
?
[]
:
null
this
.
element
.
options
.
value
=
''
}
this
.
show
=
false
this
.
$nextTick
(()
=>
{
this
.
show
=
true
})
}
},
created
()
{
this
.
initLoad
()
},
mounted
()
{
bus
.
$on
(
'onScroll'
,
()
=>
{
})
bus
.
$on
(
'reset-default-value'
,
id
=>
{
if
(
this
.
inDraw
&&
this
.
manualModify
&&
this
.
element
.
id
===
id
)
{
this
.
value
=
this
.
fillValueDerfault
()
this
.
changeValue
(
this
.
value
)
}
})
},
methods
:
{
selectClear
()
{
this
.
changeValue
(
this
.
value
)
},
changeNode
(
data
,
node
)
{
this
.
changeValue
(
this
.
value
)
},
changeCheckNode
(
data
,
obj
)
{
const
{
checkedKeys
}
=
obj
if
(
checkedKeys
)
this
.
value
=
checkedKeys
this
.
changeValue
(
this
.
value
)
},
changeNodeIds
(
ids
)
{
this
.
value
=
ids
this
.
changeValue
(
this
.
value
)
},
initLoad
()
{
this
.
value
=
this
.
fillValueDerfault
()
this
.
datas
=
[]
if
(
this
.
element
.
options
.
attrs
.
fieldId
)
{
let
method
=
mappingFieldValues
const
token
=
this
.
$store
.
getters
.
token
||
getToken
()
const
linkToken
=
this
.
$store
.
getters
.
linkToken
||
getLinkToken
()
if
(
!
token
&&
linkToken
)
{
method
=
linkMappingFieldValues
}
method
({
fieldIds
:
this
.
element
.
options
.
attrs
.
fieldId
.
split
(
','
)
}).
then
(
res
=>
{
this
.
datas
=
this
.
optionDatas
(
res
.
data
)
this
.
$nextTick
(()
=>
{
this
.
$refs
.
deSelectTree
.
treeDataUpdateFun
(
this
.
datas
)
})
})
}
if
(
this
.
element
.
options
.
value
)
{
this
.
value
=
this
.
fillValueDerfault
()
this
.
changeValue
(
this
.
value
)
}
},
changeValue
(
value
)
{
if
(
!
this
.
inDraw
)
{
if
(
value
===
null
)
{
this
.
element
.
options
.
value
=
''
}
else
{
this
.
element
.
options
.
value
=
Array
.
isArray
(
value
)
?
value
.
join
()
:
value
}
this
.
element
.
options
.
manualModify
=
false
}
else
{
this
.
element
.
options
.
manualModify
=
true
}
this
.
setCondition
()
this
.
showNumber
=
false
this
.
$nextTick
(()
=>
{
if
(
!
this
.
element
.
options
.
attrs
.
multiple
||
!
this
.
$refs
.
deSelect
||
!
this
.
$refs
.
deSelect
.
$refs
.
tags
)
{
return
}
const
kids
=
this
.
$refs
.
deSelect
.
$refs
.
tags
.
children
[
0
].
children
let
contentWidth
=
0
kids
.
forEach
(
kid
=>
{
contentWidth
+=
kid
.
offsetWidth
})
this
.
showNumber
=
contentWidth
>
((
this
.
$refs
.
deSelectTree
.
$refs
.
tags
.
clientWidth
-
30
)
*
0.9
)
})
},
setCondition
()
{
const
param
=
{
component
:
this
.
element
,
value
:
this
.
formatFilterValue
(),
operator
:
this
.
operator
}
this
.
inDraw
&&
this
.
$store
.
commit
(
'addViewFilter'
,
param
)
},
formatFilterValue
()
{
const
SEPARATOR
=
'-de-'
if
(
this
.
value
===
null
)
return
[]
if
(
Array
.
isArray
(
this
.
value
))
{
const
results
=
[]
const
duplicateMap
=
{}
this
.
value
.
forEach
(
item
=>
{
const
links
=
item
.
split
(
SEPARATOR
)
let
temp
=
''
for
(
let
index
=
0
;
index
<
links
.
length
;
index
++
)
{
const
isLast
=
index
===
(
links
.
length
-
1
)
const
isFirst
=
index
===
0
const
node
=
links
[
index
]
temp
+=
((
isFirst
?
''
:
SEPARATOR
)
+
node
)
if
(
duplicateMap
[
temp
]
&&
!
isLast
)
{
delete
duplicateMap
[
temp
]
}
}
duplicateMap
[
item
]
=
true
})
for
(
const
key
in
duplicateMap
)
{
if
(
Object
.
hasOwnProperty
.
call
(
duplicateMap
,
key
)
&&
duplicateMap
[
key
])
{
const
node
=
key
.
replaceAll
(
SEPARATOR
,
','
)
results
.
push
(
node
)
}
}
return
results
// return this.value
}
return
this
.
value
.
split
(
','
)
},
fillValueDerfault
()
{
const
defaultV
=
this
.
element
.
options
.
value
===
null
?
''
:
this
.
element
.
options
.
value
.
toString
()
if
(
this
.
element
.
options
.
attrs
.
multiple
)
{
if
(
defaultV
===
null
||
typeof
defaultV
===
'undefined'
||
defaultV
===
''
||
defaultV
===
'[object Object]'
)
return
[]
return
defaultV
.
split
(
','
)
}
else
{
if
(
defaultV
===
null
||
typeof
defaultV
===
'undefined'
||
defaultV
===
''
||
defaultV
===
'[object Object]'
)
return
null
return
defaultV
.
split
(
','
)[
0
]
}
},
optionDatas
(
datas
)
{
if
(
!
datas
)
return
null
return
datas
.
filter
(
item
=>
!!
item
)
},
setOptionWidth
(
event
)
{
// 下拉框弹出时,设置弹框的宽度
this
.
$nextTick
(()
=>
{
// this.selectOptionWidth = event.srcElement.offsetWidth + 'px'
this
.
selectOptionWidth
=
event
.
srcElement
.
parentElement
.
parentElement
.
offsetWidth
+
'px'
})
},
/* 下面是树的渲染方法 */
_filterFun
(
value
,
data
,
node
)
{
if
(
!
value
)
return
true
return
data
.
id
.
toString
().
indexOf
(
value
.
toString
())
!==
-
1
},
// 树点击
_nodeClickFun
(
data
,
node
,
vm
)
{
console
.
log
(
'this _nodeClickFun'
,
this
.
value
,
data
,
node
)
},
// 树过滤
_searchFun
(
value
)
{
console
.
log
(
value
,
'<--_searchFun'
)
// 自行判断 是走后台查询,还是前端过滤
this
.
$refs
.
treeSelect
.
filterFun
(
value
)
// 后台查询
// this.$refs.treeSelect.treeDataUpdateFun(treeData);
},
// 自定义render
_renderFun
(
h
,
{
node
,
data
,
store
})
{
const
{
props
,
clickParent
}
=
this
.
treeParams
return
(
<
span
class
=
{[
'custom-tree-node'
,
!
clickParent
&&
data
[
props
.
children
]
&&
data
[
props
.
children
].
length
?
'disabled'
:
null
]}
>
<
span
>
{
node
.
label
}
<
/span
>
<
/span
>
)
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
</
style
>
frontend/src/components/widget/serviceImpl/TextSelectTreeServiceImpl.js
0 → 100644
浏览文件 @
2cf026ed
import
{
WidgetService
}
from
'../service/WidgetService'
const
leftPanel
=
{
icon
:
'iconfont icon-xialashu'
,
label
:
'detextselectTree.label'
,
defaultClass
:
'text-filter'
}
const
dialogPanel
=
{
options
:
{
attrs
:
{
multiple
:
false
,
placeholder
:
'detextselectTree.placeholder'
,
viewIds
:
[],
datas
:
[],
key
:
'id'
,
label
:
'text'
,
value
:
'id'
,
fieldId
:
''
,
dragItems
:
[]
},
value
:
''
,
manualModify
:
false
},
defaultClass
:
'text-filter'
,
component
:
'de-select-tree'
,
miniSizex
:
1
,
miniSizey
:
1
}
const
drawPanel
=
{
type
:
'custom'
,
style
:
{
width
:
300
,
height
:
90
,
fontSize
:
14
,
fontWeight
:
500
,
lineHeight
:
''
,
letterSpacing
:
0
,
textAlign
:
''
,
color
:
''
,
hPosition
:
'left'
,
vPosition
:
'center'
},
component
:
'de-select-tree'
}
class
TextSelectTreeServiceImpl
extends
WidgetService
{
constructor
(
options
=
{})
{
Object
.
assign
(
options
,
{
name
:
'textSelectTreeWidget'
})
super
(
options
)
this
.
filterDialog
=
true
this
.
showSwitch
=
true
}
initLeftPanel
()
{
const
value
=
JSON
.
parse
(
JSON
.
stringify
(
leftPanel
))
return
value
}
initFilterDialog
()
{
const
value
=
JSON
.
parse
(
JSON
.
stringify
(
dialogPanel
))
return
value
}
initDrawPanel
()
{
const
value
=
JSON
.
parse
(
JSON
.
stringify
(
drawPanel
))
return
value
}
filterFieldMethod
(
fields
)
{
return
fields
.
filter
(
field
=>
{
return
field
[
'deType'
]
===
0
})
}
optionDatas
(
datas
)
{
if
(
!
datas
)
return
null
return
datas
.
filter
(
item
=>
!!
item
).
map
(
item
=>
{
return
{
id
:
item
,
text
:
item
}
})
}
getParam
(
element
)
{
const
value
=
this
.
fillValueDerfault
(
element
)
const
param
=
{
component
:
element
,
value
:
!
value
?
[]
:
Array
.
isArray
(
value
)
?
value
:
value
.
toString
().
split
(
','
),
operator
:
element
.
options
.
attrs
.
multiple
?
'in'
:
'eq'
}
return
param
}
fillValueDerfault
(
element
)
{
const
defaultV
=
element
.
options
.
value
===
null
?
''
:
element
.
options
.
value
.
toString
()
if
(
element
.
options
.
attrs
.
multiple
)
{
if
(
defaultV
===
null
||
typeof
defaultV
===
'undefined'
||
defaultV
===
''
||
defaultV
===
'[object Object]'
)
return
[]
return
defaultV
.
split
(
','
)
}
else
{
if
(
defaultV
===
null
||
typeof
defaultV
===
'undefined'
||
defaultV
===
''
||
defaultV
===
'[object Object]'
)
return
null
return
defaultV
.
split
(
','
)[
0
]
}
}
}
const
textSelectTreeServiceImpl
=
new
TextSelectTreeServiceImpl
()
export
default
textSelectTreeServiceImpl
frontend/src/lang/en.js
浏览文件 @
2cf026ed
...
...
@@ -1885,6 +1885,10 @@ export default {
label
:
'Text selector'
,
placeholder
:
'Please select'
},
detextselectTree
:
{
label
:
'Tree selector'
,
placeholder
:
'Please select'
},
detextgridselect
:
{
label
:
'Text list'
,
placeholder
:
'Please select'
...
...
frontend/src/lang/tw.js
浏览文件 @
2cf026ed
...
...
@@ -1897,6 +1897,10 @@ export default {
label
:
'文本下拉'
,
placeholder
:
'請選擇'
},
detextselectTree
:
{
label
:
'下拉树'
,
placeholder
:
'請選擇'
},
detextgridselect
:
{
label
:
'文本列錶'
,
placeholder
:
'請選擇'
...
...
frontend/src/lang/zh.js
浏览文件 @
2cf026ed
...
...
@@ -1331,7 +1331,7 @@ export default {
sql_ds_union_error
:
'直连模式下SQL数据集,不支持关联'
,
api_data
:
'API 数据集'
},
driver
:{
driver
:
{
driver
:
'驱动'
,
please_choose_driver
:
'请选择驱动'
,
mgm
:
'驱动管理'
,
...
...
@@ -1905,6 +1905,10 @@ export default {
label
:
'文本下拉'
,
placeholder
:
'请选择'
},
detextselectTree
:
{
label
:
'下拉树'
,
placeholder
:
'请选择'
},
detextgridselect
:
{
label
:
'文本列表'
,
placeholder
:
'请选择'
...
...
frontend/src/views/panel/filter/index.vue
浏览文件 @
2cf026ed
...
...
@@ -55,7 +55,8 @@ export default {
'文本过滤组件'
:
[
'textSelectWidget'
,
'textSelectGridWidget'
,
'textInputWidget'
'textInputWidget'
,
'textSelectTreeWidget'
],
'数字过滤组件'
:
[
'numberSelectWidget'
,
...
...
frontend/src/views/system/test/MyTree.vue
0 → 100644
浏览文件 @
2cf026ed
<!--
* @Author: dawdler
* @Date: 2020-12-26 11:52:05
* @Description: demo
* @LastModifiedBy: dawdler
-->
<
template
>
<el-tree-select
ref=
"treeSelect"
v-model=
"values"
popover-class=
"test-class-wrap"
:styles=
"styles"
:select-params=
"selectParams"
:tree-params=
"treeParams"
:filter-node-method=
"_filterFun"
:tree-render-fun=
"_renderFun"
@
searchFun=
"_searchFun"
/>
</
template
>
<
script
>
import
ElTreeSelect
from
'@/components/ElTreeSelect'
export
default
{
name
:
'MyTreeSelect'
,
components
:
{
ElTreeSelect
},
props
:
{
params
:
Object
,
isSingle
:
{
type
:
Boolean
,
default
()
{
return
false
}
}
},
data
()
{
return
{
styles
:
{
width
:
'300px'
},
// 单选value为字符串,多选为数组
values
:
this
.
isSingle
?
''
:
[],
selectParams
:
{
clearable
:
true
,
placeholder
:
'请输入内容'
},
treeParams
:
{
clickParent
:
false
,
filterable
:
true
,
// 只想要子节点,不需要父节点
leafOnly
:
false
,
includeHalfChecked
:
false
,
'check-strictly'
:
false
,
'default-expand-all'
:
true
,
'expand-on-click-node'
:
false
,
'render-content'
:
this
.
_renderFun
,
data
:
[],
props
:
{
children
:
'children'
,
label
:
'name'
,
rootId
:
'0'
,
disabled
:
'disabled'
,
parentId
:
'parentId'
,
value
:
'id'
},
...
this
.
params
}
}
},
watch
:
{},
created
()
{},
mounted
()
{
// 手动更新树数据
const
data
=
[]
const
{
label
,
children
,
parentId
,
value
,
rootId
}
=
this
.
treeParams
.
props
for
(
let
i
=
0
;
i
<
5
;
i
++
)
{
const
rootNode
=
{
[
label
]:
`节点
${
i
}
`
,
[
parentId
]:
rootId
,
[
value
]:
i
,
[
children
]:
[]
}
for
(
let
a
=
0
;
a
<
5
;
a
++
)
{
const
subId
=
`
${
rootNode
[
value
]}
_
${
a
}
`
const
subNode
=
{
[
label
]:
`子节点
${
subId
}
`
,
[
parentId
]:
rootNode
[
value
],
[
value
]:
subId
,
[
children
]:
[]
}
for
(
let
b
=
0
;
b
<
5
;
b
++
)
{
const
endId
=
`
${
subId
}
_
${
b
}
`
const
endNode
=
{
[
label
]:
`末级节点
${
endId
}
`
,
[
parentId
]:
subNode
[
value
],
[
value
]:
endId
,
[
children
]:
[]
}
subNode
[
children
].
push
(
endNode
)
}
rootNode
[
children
].
push
(
subNode
)
}
data
.
push
(
rootNode
)
}
const
myNode
=
{
[
label
]:
'测试超长节点啊啊啊测试超长节点啊啊啊测试超长节点啊啊啊测试超长节点啊啊啊测试超长节点啊啊啊测试超长节点啊啊啊'
,
[
parentId
]:
rootId
,
[
value
]:
1000
,
[
children
]:
[]
}
data
.
push
(
myNode
)
this
.
$nextTick
(()
=>
{
this
.
$refs
.
treeSelect
.
treeDataUpdateFun
(
data
)
})
},
methods
:
{
_filterFun
(
value
,
data
,
node
)
{
if
(
!
value
)
return
true
return
data
.
id
.
toString
().
indexOf
(
value
.
toString
())
!==
-
1
},
// 树点击
_nodeClickFun
(
data
,
node
,
vm
)
{
console
.
log
(
'this _nodeClickFun'
,
this
.
values
,
data
,
node
)
},
// 树过滤
_searchFun
(
value
)
{
console
.
log
(
value
,
'<--_searchFun'
)
// 自行判断 是走后台查询,还是前端过滤
this
.
$refs
.
treeSelect
.
filterFun
(
value
)
// 后台查询
// this.$refs.treeSelect.treeDataUpdateFun(treeData);
},
// 自定义render
_renderFun
(
h
,
{
node
,
data
,
store
})
{
const
{
props
,
clickParent
}
=
this
.
treeParams
return
(
<
span
class
=
{[
'custom-tree-node'
,
!
clickParent
&&
data
[
props
.
children
]
&&
data
[
props
.
children
].
length
?
'disabled'
:
null
]}
>
<
span
>
{
node
.
label
}
<
/span
>
<
/span
>
)
}
}
}
</
script
>
<
style
lang=
"less"
>
.disabled {
cursor: no-drop;
}
.custom-tree-node {
width: calc(100% - 40px);
}
</
style
>
frontend/src/views/system/test/index.vue
0 → 100644
浏览文件 @
2cf026ed
<!--
* @moduleName: 测试el-tree-select
* @Author: dawdler
* @Date: 2018-12-19 14:03:03
* @LastModifiedBy: dawdler
-->
<
template
>
<div
id=
"app111"
>
<el-form
label-width=
"120px"
>
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<el-form-item
label=
"单选"
>
<MyTree
:is-single=
"true"
:params=
"
{ clickParent: true, showParent: true }" />
</el-form-item>
</el-col>
<el-col
:span=
"12"
>
<el-form-item
label=
"多选"
>
<MyTree
:params=
"
{ clickParent: true, showParent: true }" />
</el-form-item>
</el-col>
<el-col
:span=
"12"
>
<el-form-item
label=
"弹框关闭调试"
>
<el-select
v-model=
"test"
multiple
placeholder=
"请选择"
@
change=
"_selectChange"
>
<el-option
v-for=
"item in testData"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
<div>
测试焦点触发
<svg>
<circle
cx=
"100"
cy=
"50"
r=
"40"
stroke=
"black"
stroke-width=
"2"
fill=
"red"
/>
</svg>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</
template
>
<
script
>
import
MyTree
from
'./MyTree'
export
default
{
name
:
'App111'
,
components
:
{
MyTree
},
data
()
{
return
{
test
:
''
,
testData
:
[
'test1'
,
'test2'
]
}
},
created
()
{},
mounted
()
{},
methods
:
{
// 下拉框修改
_selectChange
(
val
)
{
console
.
log
(
val
,
'<-select change'
)
}
}
}
</
script
>
<
style
lang=
"less"
>
#app111 {
display: flex;
justify-content: space-between;
width: 100%;
}
.el-select {
width: 300px;
}
</
style
>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论