Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
易企天创
zentaoatf
提交
6a2c6981
Z
zentaoatf
项目概览
易企天创
/
zentaoatf
9 个月 前同步成功
通知
11
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
3
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Z
zentaoatf
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
3
Issue
3
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
6a2c6981
编写于
6月 15, 2022
作者:
aaronchen2k2k
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add context menu for tree component
上级
11583115
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
209 addition
and
7 deletion
+209
-7
internal/server/modules/v1/service/site.go
internal/server/modules/v1/service/site.go
+33
-0
ui/src/components/Tree.vue
ui/src/components/Tree.vue
+2
-0
ui/src/components/TreeNode.vue
ui/src/components/TreeNode.vue
+6
-0
ui/src/locales/en-US.ts
ui/src/locales/en-US.ts
+10
-2
ui/src/locales/zh-CN.ts
ui/src/locales/zh-CN.ts
+9
-1
ui/src/views/script/TreeContextMenu.vue
ui/src/views/script/TreeContextMenu.vue
+85
-0
ui/src/views/script/WorkDir.vue
ui/src/views/script/WorkDir.vue
+62
-4
xdoc/ztf-gui.cmd
xdoc/ztf-gui.cmd
+2
-0
未找到文件。
internal/server/modules/v1/service/site.go
浏览文件 @
6a2c6981
package
service
import
(
"errors"
configHelper
"github.com/easysoft/zentaoatf/internal/comm/helper/config"
zentaoHelper
"github.com/easysoft/zentaoatf/internal/comm/helper/zentao"
"github.com/easysoft/zentaoatf/internal/pkg/domain"
...
...
@@ -8,6 +9,7 @@ import (
serverDomain
"github.com/easysoft/zentaoatf/internal/server/modules/v1/domain"
"github.com/easysoft/zentaoatf/internal/server/modules/v1/model"
"github.com/easysoft/zentaoatf/internal/server/modules/v1/repo"
"regexp"
"strings"
)
...
...
@@ -42,6 +44,12 @@ func (s *SiteService) GetDomainObject(id uint) (site serverDomain.ZentaoSite, er
}
func
(
s
*
SiteService
)
Create
(
site
model
.
Site
)
(
id
uint
,
isDuplicate
bool
,
err
error
)
{
site
.
Url
=
fixSiteUlt
(
site
.
Url
)
if
site
.
Url
==
""
{
err
=
errors
.
New
(
"url not right"
)
return
}
site
.
Url
=
fileUtils
.
AddUrlPathSepIfNeeded
(
site
.
Url
)
config
:=
configHelper
.
LoadBySite
(
site
)
...
...
@@ -56,6 +64,12 @@ func (s *SiteService) Create(site model.Site) (id uint, isDuplicate bool, err er
}
func
(
s
*
SiteService
)
Update
(
site
model
.
Site
)
(
isDuplicate
bool
,
err
error
)
{
site
.
Url
=
fixSiteUlt
(
site
.
Url
)
if
site
.
Url
==
""
{
err
=
errors
.
New
(
"url not right"
)
return
}
site
.
Url
=
fileUtils
.
AddUrlPathSepIfNeeded
(
site
.
Url
)
config
:=
configHelper
.
LoadBySite
(
site
)
...
...
@@ -137,3 +151,22 @@ func (s *SiteService) CreateEmptySite(lang string) (err error) {
return
}
func
fixSiteUlt
(
url
string
)
(
ret
string
)
{
regx
:=
regexp
.
MustCompile
(
`(http|https):\/\/.+`
)
result
:=
regx
.
FindStringSubmatch
(
url
)
if
result
==
nil
{
return
}
regx
=
regexp
.
MustCompile
(
`[^:\/]\/`
)
result
=
regx
.
FindStringSubmatch
(
url
)
if
result
==
nil
{
// without /
ret
=
url
}
else
{
index
:=
strings
.
LastIndex
(
url
,
"/"
)
ret
=
url
[
:
index
+
1
]
}
return
}
ui/src/components/Tree.vue
浏览文件 @
6a2c6981
...
...
@@ -7,6 +7,7 @@
v-bind=
"_convertNodeData(data, undefined, 0)"
:childrenConverter=
"_convertNodeData"
@
click=
"_handleClick"
@
rightClick=
"emit('rightClick', $event)"
@
toggle=
"_handleToggle"
@
clickToolbar=
"emit('clickToolbar', $event)"
@
check=
"_handleCheck"
...
...
@@ -86,6 +87,7 @@ const emit = defineEmits<{
(
type
:
'
collapse
'
,
event
:
{
collapsed
:
Record
<
string
,
boolean
>
})
:
void
,
(
type
:
'
check
'
,
event
:
{
checked
:
Record
<
string
,
boolean
|
'
indeterminate
'
>
})
:
void
,
(
type
:
'
active
'
,
event
:
{
activeID
:
string
})
:
void
,
(
type
:
'
rightClick
'
,
event
:
{
node
:
TreeNodeData
,
parent
?:
TreeNodeData
,
event
:
any
})
:
void
,
(
type
:
'
clickToolbar
'
,
event
:
{
node
:
TreeNodeData
,
parent
?:
TreeNodeData
,
event
:
any
})
:
void
,
}
>
();
...
...
ui/src/components/TreeNode.vue
浏览文件 @
6a2c6981
...
...
@@ -6,6 +6,7 @@
@mouseenter="_handleMouseEnter"
@mouseleave="_handleMouseLeave"
@click.stop="emit('click', {node: props, event: $event})"
@contextmenu.prevent="emit('rightClick', {node: props, event: $event})"
>
<template
v-if=
"children"
>
<Button
v-if=
"isCollapsed"
size=
"sm"
:icon=
"collapsedIcon"
:class=
"collapsedIconClass"
:style=
"collapsedIconStyle"
class=
"tree-node-toggle"
@
click=
"emit('toggle',
{node: props, event: $event})" />
...
...
@@ -19,10 +20,12 @@
size=
"sm"
@
click=
"emit('check', {node: props, event: $event})"
/>
<div
class=
"tree-node-icon"
>
<Icon
v-if=
"icon"
:icon=
"icon"
:class=
"iconClass"
:style=
"iconStyle"
/>
</div>
<div
class=
"tree-node-title"
:class=
"titleClass"
:style=
"titleClass"
>
{{title}}
</div>
<Toolbar
v-if=
"toolbarItems && showToolbar"
class=
"tree-node-toolbar"
...
...
@@ -37,6 +40,7 @@
v-bind=
"childrenConverter ? childrenConverter(child, props, index) : child"
:childrenConverter=
"childrenConverter"
@
click=
"emit('click', {parent: props, ...$event})"
@
rightClick=
"emit('rightClick', {parent: props, ...$event})"
@
toggle=
"emit('toggle', {parent: props, ...$event})"
@
clickToolbar=
"emit('clickToolbar', {parent: props, ...$event})"
@
check=
"emit('check', {parent: props, ...$event})"
...
...
@@ -100,6 +104,7 @@ const showToolbar = ref(!props.toolbarShowOnHover);
const
emit
=
defineEmits
<
{
(
type
:
'
click
'
,
event
:
{
node
:
TreeNodeData
,
parent
?:
TreeNodeData
,
event
:
any
})
:
void
,
(
type
:
'
rightClick
'
,
event
:
{
node
:
TreeNodeData
,
parent
?:
TreeNodeData
,
event
:
any
})
:
void
,
(
type
:
'
check
'
,
event
:
{
node
:
TreeNodeData
,
parent
?:
TreeNodeData
,
event
:
any
})
:
void
,
(
type
:
'
toggle
'
,
event
:
{
node
:
TreeNodeData
,
parent
?:
TreeNodeData
,
event
:
any
})
:
void
,
(
type
:
'
clickToolbar
'
,
event
:
{
node
:
TreeNodeData
,
parent
?:
TreeNodeData
,
event
:
any
})
:
void
,
...
...
@@ -118,6 +123,7 @@ function _handleMouseEnter() {
function
_handleMouseLeave
()
{
showToolbar
.
value
=
false
;
}
</
script
>
<
style
scoped
>
...
...
ui/src/locales/en-US.ts
浏览文件 @
6a2c6981
...
...
@@ -216,8 +216,16 @@ export default {
'
test_type_other
'
:
'
Other Automated or Unit Test
'
,
'
input_keyword_to_search
'
:
'
Input Keyword To Search
'
,
'
tips_test_cmd
'
:
'
Please run "{cmd}" with command line.
'
,
'
pls_add_zentao_site
'
:
'
Please add zentao site.
'
,
'
sync-from-zentao
'
:
'
Sync From Zentao
'
,
'
sync-to-zentao
'
:
'
Sync To Zentao
'
,
'
copy
'
:
'
Copy
'
,
'
cut
'
:
'
Cut
'
,
'
paste
'
:
'
Paste
'
,
'
open-in-explore
'
:
'
Open In Explore
'
,
'
open-in-terminal
'
:
'
Open In Terminal
'
,
'
pls_add_zentao_site
'
:
'
Please add zentao site.
'
,
'
pls_create_workspace
'
:
'
Please create workspace to continue.
'
,
'
pls_product
'
:
'
Please select product.
'
,
'
pls_lang
'
:
'
Please select language.
'
,
...
...
@@ -235,7 +243,7 @@ export default {
'
pls_workspace_path
'
:
'
Please input workspace full path.
'
,
'
pls_workspace_type
'
:
'
Please input workspace type.
'
,
'
pls_zentao_url
'
:
'
Please input ZenTao URL
'
,
'
pls_zentao_url
'
:
'
Please input
right
ZenTao URL
'
,
'
pls_username
'
:
'
Please input user name.
'
,
'
pls_password
'
:
'
Please input password.
'
,
'
pls_input_lang
'
:
'
Please input language.
'
,
...
...
ui/src/locales/zh-CN.ts
浏览文件 @
6a2c6981
...
...
@@ -228,6 +228,14 @@ export default {
'
switch_display_bottom_side
'
:
'
切换显示底部边栏
'
,
'
switch_display_right_side
'
:
'
切换显示右侧边栏
'
,
'
sync-from-zentao
'
:
'
从禅道同步
'
,
'
sync-to-zentao
'
:
'
同步到禅道
'
,
'
copy
'
:
'
复制
'
,
'
cut
'
:
'
剪切
'
,
'
paste
'
:
'
粘贴
'
,
'
open-in-explore
'
:
'
在资源管理器中显示
'
,
'
open-in-terminal
'
:
'
从此位置打开命令行
'
,
'
pls_add_zentao_site
'
:
'
初次使用,请点击右上按钮新建禅道站点。
'
,
'
pls_create_workspace
'
:
'
请点击右上角链接新建工作目录
'
,
'
pls_product
'
:
'
请选择产品
'
,
...
...
@@ -246,7 +254,7 @@ export default {
'
pls_workspace_path
'
:
'
请输入工作目录完整路径
'
,
'
pls_workspace_type
'
:
'
请选择工作目录类型
'
,
'
pls_zentao_url
'
:
'
请输入禅道地址
'
,
'
pls_zentao_url
'
:
'
请输入
正确的
禅道地址
'
,
'
pls_username
'
:
'
请输入用户名
'
,
'
pls_password
'
:
'
请输入密码
'
,
'
pls_input_lang
'
:
'
请输入语言
'
,
...
...
ui/src/views/script/TreeContextMenu.vue
0 → 100644
浏览文件 @
6a2c6981
<
template
>
<div
class=
"tree-context-menu"
>
<div
class=
"menu"
>
<div
@
click=
"menuClick('sync-from-zentao')"
class=
"menu-item"
>
<span>
{{
t
(
'
sync-from-zentao
'
)
}}
{{
treeNode
.
type
}}
-
{{
treeNode
.
workspaceType
}}
</span>
</div>
<div
@
click=
"menuClick('sync-to-zentao')"
class=
"menu-item"
>
<span>
{{
t
(
'
sync-to-zentao
'
)
}}
{{
treeNode
.
type
}}
-
{{
treeNode
.
workspaceType
}}
</span>
</div>
<div
@
click=
"menuClick('exec')"
class=
"menu-item"
>
<span>
{{
t
(
'
exec
'
)
}}
</span>
</div>
<div
@
click=
"menuClick('copy')"
class=
"menu-item"
>
<span>
{{
t
(
'
copy
'
)
}}
</span>
</div>
<div
@
click=
"menuClick('cut')"
class=
"menu-item"
>
<span>
{{
t
(
'
cut
'
)
}}
</span>
</div>
<div
@
click=
"menuClick('paste')"
class=
"menu-item"
>
<span>
{{
t
(
'
paste
'
)
}}
</span>
</div>
<div
@
click=
"menuClick('open-in-explore')"
class=
"menu-item"
>
<span>
{{
t
(
'
open-in-explore
'
)
}}
</span>
</div>
<div
@
click=
"menuClick('open-in-terminal')"
class=
"menu-item"
>
<span>
{{
t
(
'
open-in-terminal
'
)
}}
</span>
</div>
</div>
</div>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
PropType
,
Ref
}
from
"
vue
"
;
import
{
useI18n
}
from
"
vue-i18n
"
;
export
default
defineComponent
({
name
:
'
TreeContextMenu
'
,
props
:
{
treeNode
:
{
type
:
Object
,
required
:
true
},
onMenuClick
:
{
type
:
Function
as
PropType
<
(
menuKey
:
string
,
targetId
:
number
)
=>
void
>
,
required
:
true
}
},
components
:
{
},
setup
(
props
)
{
const
{
t
}
=
useI18n
();
const
menuClick
=
(
menuKey
)
=>
{
props
.
onMenuClick
(
menuKey
,
props
.
treeNode
.
id
);
};
return
{
t
,
menuClick
}
}
})
</
script
>
<
style
lang=
"less"
>
.tree-context-menu {
.menu {
padding: 0;
border: 1px solid #dedfe1;
background-color: #fff;
.menu-item {
margin: 0;
padding: 5px 10px;
height: 22px;
line-height: 22px;
cursor: pointer;
&:hover {
background-color: #f5f5f5;
}
}
}
}
</
style
>
\ No newline at end of file
ui/src/views/script/WorkDir.vue
浏览文件 @
6a2c6981
...
...
@@ -4,7 +4,8 @@
:data=
"treeData"
:checkable=
"checkable"
ref=
"treeRef"
@
active=
"selectNode"
@
active=
"selectNode"
@
rightClick=
"onRightClick"
@
check=
"checkNode"
@
clickToolbar=
"onToolbarClicked"
@
collapse=
"expandNode"
...
...
@@ -18,6 +19,10 @@
:label=
"t('exec_selected')"
@
click=
"execSelected"
/>
<div
v-if=
"contextNode.id && rightVisible"
:style=
"menuStyle"
>
<TreeContextMenu
:treeNode=
"contextNode"
:onMenuClick=
"menuClick"
/>
</div>
</div>
</
template
>
...
...
@@ -33,6 +38,7 @@ import Tree from "@/components/Tree.vue";
import
notification
from
"
@/utils/notification
"
;
import
{
computed
,
defineExpose
,
onMounted
,
onUnmounted
,
ref
,
watch
}
from
"
vue
"
;
import
Button
from
'
@/components/Button.vue
'
;
import
TreeContextMenu
from
'
./TreeContextMenu.vue
'
;
import
bus
from
"
@/utils/eventBus
"
;
import
{
...
...
@@ -355,8 +361,6 @@ const execSelected = () => {
bus
.
emit
(
settings
.
eventExec
,
{
execType
:
'
ztf
'
,
scripts
:
arr
});
}
let
contextNode
=
ref
({}
as
any
)
let
menuStyle
=
ref
({}
as
any
)
const
editedData
=
ref
<
any
>
({})
const
nameFormVisible
=
ref
(
false
)
...
...
@@ -417,6 +421,52 @@ const expandNode = (expandedKeysMap) => {
setExpandedKeys
(
currSite
.
value
.
id
,
currProduct
.
value
.
id
,
expandedKeys
.
value
)
}
let
menuStyle
=
ref
({}
as
any
)
let
contextNode
=
ref
({}
as
any
)
let
targetModelId
=
0
let
rightVisible
=
ref
(
false
)
const
onRightClick
=
(
e
)
=>
{
console
.
log
(
'
onRightClick
'
,
e
)
const
{
event
,
node
}
=
e
const
y
=
event
.
currentTarget
.
getBoundingClientRect
().
top
const
x
=
event
.
currentTarget
.
getBoundingClientRect
().
right
const
contextNodeData
=
treeDataMap
.
value
[
node
.
id
]
contextNode
.
value
=
{
id
:
contextNodeData
.
id
,
title
:
contextNodeData
.
title
,
type
:
contextNodeData
.
type
,
isLeaf
:
contextNodeData
.
isLeaf
,
workspaceId
:
contextNodeData
.
workspaceId
,
workspaceType
:
contextNodeData
.
workspaceType
,
}
let
top
=
y
if
(
y
+
260
>
document
.
body
.
clientHeight
)
top
=
document
.
body
.
clientHeight
-
260
menuStyle
.
value
=
{
zIndex
:
9
,
position
:
'
fixed
'
,
left
:
`
${
x
+
10
}
px`
,
top
:
`
${
top
}
px`
,
}
rightVisible
.
value
=
true
}
const
menuClick
=
(
menuKey
:
string
,
targetId
:
number
)
=>
{
console
.
log
(
'
menuClick
'
,
menuKey
,
targetId
)
targetModelId
=
targetId
clearMenu
()
}
const
clearMenu
=
()
=>
{
console
.
log
(
'
clearMenu
'
)
contextNode
.
value
=
ref
(
null
)
}
defineExpose
({
get
isCheckable
()
{
return
checkable
.
value
;
...
...
@@ -431,12 +481,20 @@ defineExpose({
onToolbarClicked
,
loadScripts
});
onMounted
(()
=>
{
console
.
log
(
'
onMounted
'
)
document
.
addEventListener
(
"
click
"
,
clearMenu
)
})
onUnmounted
(()
=>
{
document
.
removeEventListener
(
"
click
"
,
clearMenu
)
})
</
script
>
<
style
lang=
"less"
scoped
>
.workdir {
height: calc(100vh - 80px);
position: relative;
.run-selected{
max-width: 100px;
...
...
xdoc/ztf-gui.cmd
0 → 100755
浏览文件 @
6a2c6981
@echo
off
start
gui
\ztf.exe
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录