Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
淡淡忧伤的程序员
DolphinScheduler
提交
d45bf677
DolphinScheduler
项目概览
淡淡忧伤的程序员
/
DolphinScheduler
与 Fork 源项目一致
Fork自
apache / DolphinScheduler
通知
48
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
DolphinScheduler
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
d45bf677
编写于
5月 21, 2019
作者:
G
gongzijian
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'upstream/branch-1.0.2' into branch-1.0.2
上级
0cfa35da
3becf592
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
232 addition
and
22 deletion
+232
-22
escheduler-api/src/main/java/cn/escheduler/api/quartz/QuartzExecutors.java
...c/main/java/cn/escheduler/api/quartz/QuartzExecutors.java
+7
-3
escheduler-api/src/main/java/cn/escheduler/api/service/SchedulerService.java
...main/java/cn/escheduler/api/service/SchedulerService.java
+7
-0
escheduler-ui/.env
escheduler-ui/.env
+1
-1
escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/sql.vue
...rc/js/conf/home/pages/dag/_source/formModel/tasks/sql.vue
+17
-0
escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
...ges/projects/pages/definition/pages/list/_source/list.vue
+95
-3
escheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue
...pages/projects/pages/instance/pages/list/_source/list.vue
+45
-4
escheduler-ui/src/js/conf/home/pages/projects/pages/taskInstance/_source/list.vue
...f/home/pages/projects/pages/taskInstance/_source/list.vue
+7
-5
escheduler-ui/src/js/conf/home/pages/security/pages/tenement/_source/list.vue
.../conf/home/pages/security/pages/tenement/_source/list.vue
+13
-6
escheduler-ui/src/js/conf/home/store/dag/actions.js
escheduler-ui/src/js/conf/home/store/dag/actions.js
+36
-0
escheduler-ui/src/js/module/i18n/locale/en_US.js
escheduler-ui/src/js/module/i18n/locale/en_US.js
+2
-0
escheduler-ui/src/js/module/i18n/locale/zh_CN.js
escheduler-ui/src/js/module/i18n/locale/zh_CN.js
+2
-0
未找到文件。
escheduler-api/src/main/java/cn/escheduler/api/quartz/QuartzExecutors.java
浏览文件 @
d45bf677
...
...
@@ -26,8 +26,8 @@ import org.quartz.impl.matchers.GroupMatcher;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.util.Calendar
;
import
java.util.*
;
import
java.util.Calendar
;
import
java.util.concurrent.locks.ReadWriteLock
;
import
java.util.concurrent.locks.ReentrantReadWriteLock
;
...
...
@@ -226,8 +226,12 @@ public class QuartzExecutors {
public
boolean
deleteJob
(
String
jobName
,
String
jobGroupName
)
{
lock
.
writeLock
().
lock
();
try
{
logger
.
info
(
"try to delete job, job name: {}, job group name: {},"
,
jobName
,
jobGroupName
);
return
scheduler
.
deleteJob
(
new
JobKey
(
jobName
,
jobGroupName
));
JobKey
jobKey
=
new
JobKey
(
jobName
,
jobGroupName
);
if
(
scheduler
.
checkExists
(
jobKey
)){
logger
.
info
(
"try to delete job, job name: {}, job group name: {},"
,
jobName
,
jobGroupName
);
return
scheduler
.
deleteJob
(
jobKey
);
}
}
catch
(
SchedulerException
e
)
{
logger
.
error
(
String
.
format
(
"delete job : %s failed"
,
jobName
),
e
);
}
finally
{
...
...
escheduler-api/src/main/java/cn/escheduler/api/service/SchedulerService.java
浏览文件 @
d45bf677
...
...
@@ -514,6 +514,13 @@ public class SchedulerService extends BaseService {
putMsg
(
result
,
Status
.
SCHEDULE_CRON_NOT_EXISTS
,
scheduleId
);
return
result
;
}
// Determine if the login user is the owner of the schedule
if
(
loginUser
.
getId
()
!=
schedule
.
getUserId
())
{
putMsg
(
result
,
Status
.
USER_NO_OPERATION_PERM
);
return
result
;
}
// check schedule is already online
if
(
schedule
.
getReleaseState
()
==
ReleaseState
.
ONLINE
){
putMsg
(
result
,
Status
.
SCHEDULE_CRON_STATE_ONLINE
,
schedule
.
getId
());
...
...
escheduler-ui/.env
浏览文件 @
d45bf677
# 后端接口地址
11
# 后端接口地址
API_BASE = http://192.168.xx.xx:12345
# 本地开发如需ip访问项目把"#"号去掉
...
...
escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/sql.vue
浏览文件 @
d45bf677
...
...
@@ -28,6 +28,17 @@
</div>
</m-list-box>
<template
v-if=
"!sqlType && showType.length"
>
<m-list-box>
<div
slot=
"text"
>
{{
$t
(
'
Title
'
)
}}
</div>
<div
slot=
"content"
>
<x-input
type=
"input"
v-model=
"title"
:placeholder=
"$t('Please enter the title of email')"
autocomplete=
"off"
>
</x-input>
</div>
</m-list-box>
<m-list-box>
<div
slot=
"text"
>
{{
$t
(
'
Recipient
'
)
}}
</div>
<div
slot=
"content"
>
...
...
@@ -141,6 +152,8 @@
udfs
:
''
,
// Sql type
sqlType
:
0
,
// Email title
title
:
''
,
// Form/attachment
showType
:
[
'
TABLE
'
],
// Sql parameter
...
...
@@ -241,6 +254,7 @@
sql
:
editor
.
getValue
(),
udfs
:
this
.
udfs
,
sqlType
:
this
.
sqlType
,
title
:
this
.
title
,
receivers
:
this
.
receivers
.
join
(
'
,
'
),
receiversCc
:
this
.
receiversCc
.
join
(
'
,
'
),
showType
:
(()
=>
{
...
...
@@ -308,6 +322,7 @@
this
.
showType
=
[]
}
if
(
val
!==
0
)
{
this
.
title
=
''
this
.
receivers
=
[]
this
.
receiversCc
=
[]
}
...
...
@@ -321,6 +336,7 @@
//
showType
(
val
)
{
if
(
!
val
.
length
)
{
this
.
title
=
''
this
.
receivers
=
[]
this
.
receiversCc
=
[]
}
...
...
@@ -342,6 +358,7 @@
this
.
showType
=
o
.
params
.
showType
.
split
(
'
,
'
)
||
[]
this
.
preStatements
=
o
.
params
.
preStatements
||
[]
this
.
postStatements
=
o
.
params
.
postStatements
||
[]
this
.
title
=
o
.
params
.
title
||
''
this
.
receivers
=
o
.
params
.
receivers
&&
o
.
params
.
receivers
.
split
(
'
,
'
)
||
[]
this
.
receiversCc
=
o
.
params
.
receiversCc
&&
o
.
params
.
receiversCc
.
split
(
'
,
'
)
||
[]
}
...
...
escheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
浏览文件 @
d45bf677
<
template
>
<div
class=
"list-model"
>
<div
class=
"list-model"
style=
"position: relative;"
>
<div
class=
"table-box"
>
<table
class=
"fixed"
>
<tr>
<th
width=
"50"
>
<x-checkbox
@
on-change=
"_topCheckBoxClick"
v-model=
"checkAll"
></x-checkbox>
</th>
<th>
<span>
{{
$t
(
'
#
'
)
}}
</span>
</th>
...
...
@@ -29,6 +32,7 @@
</th>
</tr>
<tr
v-for=
"(item, $index) in list"
:key=
"item.id"
>
<td
width=
"50"
><x-checkbox
v-model=
"item.isCheck"
@
on-change=
"_arrDelChange"
></x-checkbox></td>
<td>
<span>
{{
parseInt
(
pageNo
===
1
?
(
$index
+
1
)
:
((
$index
+
1
)
+
(
pageSize
*
(
pageNo
-
1
))))
}}
</span>
</td>
...
...
@@ -60,11 +64,33 @@
<x-button
type=
"error"
shape=
"circle"
size=
"xsmall"
data-toggle=
"tooltip"
:title=
"$t('offline')"
@
click=
"_downline(item)"
v-if=
"item.releaseState === 'ONLINE'"
v-ps=
"['GENERAL_USER']"
icon=
"iconfont icon-erji-xiaxianjilu"
>
<!--
{{
$t
(
'
下线
'
)
}}
-->
</x-button>
<x-button
type=
"warning"
shape=
"circle"
size=
"xsmall"
data-toggle=
"tooltip"
:title=
"$t('online')"
@
click=
"_poponline(item)"
v-if=
"item.releaseState === 'OFFLINE'"
v-ps=
"['GENERAL_USER']"
icon=
"iconfont icon-erji-xiaxianjilu-copy"
>
<!--
{{
$t
(
'
上线
'
)
}}
-->
</x-button>
<x-button
type=
"info"
shape=
"circle"
size=
"xsmall"
data-toggle=
"tooltip"
:title=
"$t('Cron Manage')"
@
click=
"_timingManage(item)"
:disabled=
"item.releaseState !== 'ONLINE'"
v-ps=
"['GENERAL_USER']"
icon=
"iconfont icon-paibanguanli"
>
<!--
{{
$t
(
'
定时管理
'
)
}}
-->
</x-button>
<x-poptip
:ref=
"'poptip-delete-' + $index"
placement=
"bottom-end"
width=
"90"
>
<p>
{{
$t
(
'
Delete?
'
)
}}
</p>
<div
style=
"text-align: right; margin: 0;padding-top: 4px;"
>
<x-button
type=
"text"
size=
"xsmall"
shape=
"circle"
@
click=
"_closeDelete($index)"
>
{{
$t
(
'
Cancel
'
)
}}
</x-button>
<x-button
type=
"primary"
size=
"xsmall"
shape=
"circle"
@
click=
"_delete(item,$index)"
>
{{
$t
(
'
Confirm
'
)
}}
</x-button>
</div>
<template
slot=
"reference"
>
<x-button
icon=
"iconfont icon-shanchu"
type=
"error"
shape=
"circle"
size=
"xsmall"
data-toggle=
"tooltip"
:title=
"$t('delete')"
v-ps=
"['GENERAL_USER']"
>
</x-button>
</
template
>
</x-poptip>
<x-button
type=
"info"
shape=
"circle"
size=
"xsmall"
data-toggle=
"tooltip"
:title=
"$t('TreeView')"
@
click=
"_treeView(item)"
icon=
"iconfont icon-juxingkaobei"
>
<!--{{$t('树形图')}}-->
</x-button>
</td>
</tr>
</table>
</div>
<x-button
style=
"position: absolute; bottom: -52px; left: 22px;"
v-if=
"strDelete !== ''"
@
click=
"_batchDelete"
>
批量删除
</x-button>
</div>
</template>
<
script
>
...
...
@@ -78,7 +104,9 @@
name
:
'
definition-list
'
,
data
()
{
return
{
list
:
[]
list
:
[],
strDelete
:
''
,
checkAll
:
false
}
},
props
:
{
...
...
@@ -87,7 +115,7 @@
pageSize
:
Number
},
methods
:
{
...
mapActions
(
'
dag
'
,
[
'
editProcessState
'
,
'
getStartCheck
'
,
'
getReceiver
'
]),
...
mapActions
(
'
dag
'
,
[
'
editProcessState
'
,
'
getStartCheck
'
,
'
getReceiver
'
,
'
deleteDefinition
'
,
'
batchDeleteDefinition
'
]),
_rtPublishStatus
(
code
)
{
return
_
.
filter
(
publishStatus
,
v
=>
v
.
code
===
code
)[
0
].
desc
},
...
...
@@ -179,6 +207,27 @@
_timingManage
(
item
)
{
this
.
$router
.
push
({
path
:
`/projects/definition/list/timing/
${
item
.
id
}
`
})
},
/**
* Close the delete layer
*/
_closeDelete
(
i
)
{
this
.
$refs
[
`poptip-delete-
${
i
}
`
][
0
].
doClose
()
},
/**
* delete
*/
_delete
(
item
,
i
)
{
this
.
deleteDefinition
({
processDefinitionId
:
item
.
id
}).
then
(
res
=>
{
this
.
$refs
[
`poptip-delete-
${
i
}
`
][
0
].
doClose
()
this
.
_onUpdate
()
this
.
$message
.
success
(
res
.
msg
)
}).
catch
(
e
=>
{
this
.
$refs
[
`poptip-delete-
${
i
}
`
][
0
].
doClose
()
this
.
$message
.
error
(
e
.
msg
||
''
)
})
},
/**
* edit
*/
...
...
@@ -217,6 +266,45 @@
},
_onUpdate
()
{
this
.
$emit
(
'
on-update
'
)
},
/**
* click the select-all checkbox
*/
_topCheckBoxClick
(
v
)
{
this
.
list
.
forEach
((
item
,
i
)
=>
{
this
.
$set
(
this
.
list
[
i
],
'
isCheck
'
,
v
)
})
this
.
_arrDelChange
()
},
/**
* the array that to be delete
*/
_arrDelChange
(
v
)
{
let
arr
=
[]
this
.
list
.
forEach
((
item
)
=>
{
if
(
item
.
isCheck
)
{
arr
.
push
(
item
.
id
)
}
})
this
.
strDelete
=
_
.
join
(
arr
,
'
,
'
)
if
(
v
===
false
)
{
this
.
checkAll
=
false
}
},
/**
* batch delete
*/
_batchDelete
()
{
this
.
batchDeleteDefinition
({
processDefinitionIds
:
this
.
strDelete
}).
then
(
res
=>
{
this
.
_onUpdate
()
this
.
checkAll
=
false
this
.
$message
.
success
(
res
.
msg
)
}).
catch
(
e
=>
{
this
.
checkAll
=
false
this
.
$message
.
error
(
e
.
msg
||
''
)
})
}
},
watch
:
{
...
...
@@ -225,6 +313,10 @@
setTimeout
(()
=>
{
this
.
list
=
a
})
},
pageNo
()
{
this
.
checkAll
=
false
this
.
strDelete
=
''
}
},
created
()
{
...
...
escheduler-ui/src/js/conf/home/pages/projects/pages/instance/pages/list/_source/list.vue
浏览文件 @
d45bf677
<
template
>
<div
class=
"list-model"
>
<div
class=
"list-model"
style=
"position: relative;"
>
<div
class=
"table-box"
>
<table
class=
"fixed"
>
<tr>
<th
width=
"50"
>
<x-checkbox
@
on-change=
"_topCheckBoxClick"
v-model=
"checkAll"
></x-checkbox>
</th>
<th>
<span>
{{
$t
(
'
#
'
)
}}
</span>
</th>
...
...
@@ -38,6 +41,7 @@
</th>
</tr>
<tr
v-for=
"(item, $index) in list"
:key=
"item.id"
>
<td
width=
"50"
><x-checkbox
v-model=
"item.isCheck"
@
on-change=
"_arrDelChange"
></x-checkbox></td>
<td>
<span>
{{
parseInt
(
pageNo
===
1
?
(
$index
+
1
)
:
((
$index
+
1
)
+
(
pageSize
*
(
pageNo
-
1
))))
}}
</span>
</td>
...
...
@@ -245,6 +249,7 @@
</tr>
</table>
</div>
<x-button
style=
"position: absolute; bottom: -52px; left: 22px;"
v-if=
"strDelete !== ''"
@
click=
"_batchDelete"
>
批量删除
</x-button>
</div>
</template>
<
script
>
...
...
@@ -259,7 +264,9 @@
// 数据
list
:
[],
// 按钮类型
buttonType
:
''
buttonType
:
''
,
strDelete
:
''
,
checkAll
:
false
}
},
props
:
{
...
...
@@ -268,7 +275,7 @@
pageSize
:
Number
},
methods
:
{
...
mapActions
(
'
dag
'
,
[
'
editExecutorsState
'
,
'
deleteInstance
'
]),
...
mapActions
(
'
dag
'
,
[
'
editExecutorsState
'
,
'
deleteInstance
'
,
'
batchDeleteInstance
'
]),
/**
* Return run type
*/
...
...
@@ -440,6 +447,36 @@
},
_gantt
(
item
)
{
this
.
$router
.
push
({
path
:
`/projects/instance/gantt/
${
item
.
id
}
`
})
},
_topCheckBoxClick
(
v
)
{
this
.
list
.
forEach
((
item
,
i
)
=>
{
this
.
$set
(
this
.
list
[
i
],
'
isCheck
'
,
v
)
})
this
.
_arrDelChange
()
},
_arrDelChange
(
v
)
{
let
arr
=
[]
this
.
list
.
forEach
((
item
)
=>
{
if
(
item
.
isCheck
)
{
arr
.
push
(
item
.
id
)
}
})
this
.
strDelete
=
_
.
join
(
arr
,
'
,
'
)
if
(
v
===
false
)
{
this
.
checkAll
=
false
}
},
_batchDelete
()
{
this
.
batchDeleteInstance
({
processInstanceIds
:
this
.
strDelete
}).
then
(
res
=>
{
this
.
_onUpdate
()
this
.
checkAll
=
false
this
.
$message
.
success
(
res
.
msg
)
}).
catch
(
e
=>
{
this
.
checkAll
=
false
this
.
$message
.
error
(
e
.
msg
||
''
)
})
}
},
watch
:
{
...
...
@@ -448,6 +485,10 @@
setTimeout
(()
=>
{
this
.
list
=
this
.
_listDataHandle
(
a
)
})
},
pageNo
()
{
this
.
checkAll
=
false
this
.
strDelete
=
''
}
},
created
()
{
...
...
@@ -457,4 +498,4 @@
},
components
:
{
}
}
</
script
>
\ No newline at end of file
</
script
>
escheduler-ui/src/js/conf/home/pages/projects/pages/taskInstance/_source/list.vue
浏览文件 @
d45bf677
...
...
@@ -45,10 +45,9 @@
<span>
{{
parseInt
(
pageNo
===
1
?
(
$index
+
1
)
:
((
$index
+
1
)
+
(
pageSize
*
(
pageNo
-
1
))))
}}
</span>
</td>
<td>
<span
v-if=
"isAuth"
class=
"ellipsis"
><a
href=
"javascript:"
class=
"links"
>
{{
item
.
name
}}
</a></span>
<span
v-if=
"!isAuth"
class=
"ellipsis"
><a
href=
"javascript:"
class=
"links"
>
{{
item
.
name
}}
</a></span>
<span
class=
"ellipsis"
><a
href=
"javascript:"
class=
"links"
>
{{
item
.
name
}}
</a></span>
</td>
<td><
span
class=
"ellipsis"
>
{{
item
.
processInstanceName
}}
</span
></td>
<td><
a
href=
"javascript:"
class=
"links"
@
click=
"_go(item)"
><span
class=
"ellipsis"
>
{{
item
.
processInstanceName
}}
</span></a
></td>
<td><span>
{{
item
.
taskType
}}
</span></td>
<td><span
v-html=
"_rtState(item.state)"
style=
"cursor: pointer;"
></span></td>
<td><span>
{{
item
.
submitTime
|
formatDate
}}
</span></td>
...
...
@@ -125,7 +124,10 @@
})
}
})
}
},
_go
(
item
)
{
this
.
$router
.
push
({
path
:
`/projects/instance/list/
${
item
.
processInstanceId
}
`
})
},
},
watch
:
{
taskInstanceList
(
a
)
{
...
...
@@ -142,4 +144,4 @@
},
components
:
{
}
}
</
script
>
\ No newline at end of file
</
script
>
escheduler-ui/src/js/conf/home/pages/security/pages/tenement/_source/list.vue
浏览文件 @
d45bf677
...
...
@@ -64,19 +64,26 @@
@
click=
"_edit(item)"
icon=
"iconfont icon-bianjixiugai"
>
</x-button>
<
!--
<
x-poptip
:ref=
"'poptip-' + $index"
placement=
"bottom-end"
width=
"90"
>
<x-poptip
:ref=
"'poptip-' + $index"
placement=
"bottom-end"
width=
"90"
>
<p>
{{
$t
(
'
Delete?
'
)
}}
</p>
<div
style=
"text-align: right; margin: 0;padding-top: 4px;"
>
<x-button
type=
"text"
size=
"xsmall"
shape=
"circle"
@
click=
"_closeDelete($index)"
>
{{
$t
(
'
Cancel
'
)
}}
</x-button>
<x-button
type=
"primary"
size=
"xsmall"
shape=
"circle"
@
click=
"_delete(item,$index)"
>
{{
$t
(
'
Confirm
'
)
}}
</x-button>
</div>
<template
slot=
"reference"
>
<x-button
type=
"error"
shape=
"circle"
size=
"xsmall"
data-toggle=
"tooltip"
:title=
"$t('delete')"
>
{{
$t
(
'
delete
'
)
}}
</x-button>
<x-button
icon=
"iconfont icon-shanchu"
type=
"error"
shape=
"circle"
size=
"xsmall"
data-toggle=
"tooltip"
:title=
"$t('delete')"
>
</x-button>
</
template
>
</x-poptip>
-->
</x-poptip>
</td>
</tr>
</table>
...
...
escheduler-ui/src/js/conf/home/store/dag/actions.js
浏览文件 @
d45bf677
...
...
@@ -437,6 +437,42 @@ export default {
})
})
},
/**
* Batch delete process instance
*/
batchDeleteInstance
({
state
},
payload
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
io
.
get
(
`projects/
${
state
.
projectName
}
/instance/batch-delete`
,
payload
,
res
=>
{
resolve
(
res
)
}).
catch
(
e
=>
{
reject
(
e
)
})
})
},
/**
* Delete definition
*/
deleteDefinition
({
state
},
payload
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
io
.
get
(
`projects/
${
state
.
projectName
}
/process/delete`
,
payload
,
res
=>
{
resolve
(
res
)
}).
catch
(
e
=>
{
reject
(
e
)
})
})
},
/**
* Batch delete definition
*/
batchDeleteDefinition
({
state
},
payload
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
io
.
get
(
`projects/
${
state
.
projectName
}
/process/batch-delete`
,
payload
,
res
=>
{
resolve
(
res
)
}).
catch
(
e
=>
{
reject
(
e
)
})
})
},
/**
* Process instance get variable
*/
...
...
escheduler-ui/src/js/module/i18n/locale/en_US.js
浏览文件 @
d45bf677
...
...
@@ -111,6 +111,8 @@ export default {
'
Please enter ExecutorPlease enter Executor core number
'
:
'
Please enter ExecutorPlease enter Executor core number
'
,
'
Core number should be positive integer
'
:
'
Core number should be positive integer
'
,
'
SQL Type
'
:
'
SQL Type
'
,
'
Title
'
:
'
Title
'
,
'
Please enter the title of email
'
:
'
Please enter the title of email
'
,
'
Table
'
:
'
Table
'
,
'
Attachment
'
:
'
Attachment
'
,
'
SQL Parameter
'
:
'
SQL Parameter
'
,
...
...
escheduler-ui/src/js/module/i18n/locale/zh_CN.js
浏览文件 @
d45bf677
...
...
@@ -111,6 +111,8 @@ export default {
'
Please enter ExecutorPlease enter Executor core number
'
:
'
请填写Executor内核数
'
,
'
Core number should be positive integer
'
:
'
内核数为正整数
'
,
'
SQL Type
'
:
'
sql类型
'
,
'
Title
'
:
'
主题
'
,
'
Please enter the title of email
'
:
'
请输入邮件主题
'
,
'
Table
'
:
'
表格
'
,
'
Attachment
'
:
'
附件
'
,
'
SQL Parameter
'
:
'
sql参数
'
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录