Browse Source

Merge remote-tracking branch 'remotes/upstream/dev-20190415' into dev-20190415

pull/2/head
ligang 5 years ago
parent
commit
8fc165e62f
  1. 6
      escheduler-api/src/main/java/cn/escheduler/api/service/DataAnalysisService.java
  2. 4
      escheduler-api/src/main/java/cn/escheduler/api/service/DataSourceService.java
  3. 2
      escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/_source/timeoutAlarm.vue
  4. 45
      escheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
  5. 10
      escheduler-ui/src/js/conf/home/pages/home/index.vue
  6. 8
      escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/master.vue
  7. 6
      escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/mysql.vue
  8. 2
      escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/servers.scss
  9. 8
      escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/worker.vue
  10. 77
      escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/commandStateCount.vue
  11. 70
      escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/defineUserCount.vue
  12. 122
      escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/processStateCount.vue
  13. 362
      escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/projectChart.vue
  14. 102
      escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/queueCount.vue
  15. 129
      escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/taskCtatusCount.vue
  16. 141
      escheduler-ui/src/js/conf/home/pages/projects/pages/index/index.vue
  17. 29
      escheduler-ui/src/js/conf/home/pages/security/pages/token/index.vue
  18. 6
      escheduler-ui/src/js/conf/home/pages/security/pages/workerGroups/_source/createWorker.vue
  19. 4
      escheduler-ui/src/js/conf/home/pages/security/pages/workerGroups/_source/list.vue
  20. 4
      escheduler-ui/src/js/conf/home/pages/security/pages/workerGroups/index.vue
  21. 12
      escheduler-ui/src/js/conf/home/pages/user/pages/token/_source/createToken.vue
  22. 12
      escheduler-ui/src/js/conf/home/pages/user/pages/token/_source/list.vue
  23. 4
      escheduler-ui/src/js/conf/home/pages/user/pages/token/index.vue
  24. 16
      escheduler-ui/src/js/conf/home/router/index.js
  25. 13
      escheduler-ui/src/js/conf/home/store/dag/actions.js
  26. 7
      escheduler-ui/src/js/conf/home/store/dag/mutations.js
  27. 2
      escheduler-ui/src/js/conf/home/store/dag/state.js
  28. 2
      escheduler-ui/src/js/module/components/nav/nav.vue
  29. 6
      escheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
  30. 19
      escheduler-ui/src/js/module/i18n/locale/en_US.js
  31. 19
      escheduler-ui/src/js/module/i18n/locale/zh_CN.js

6
escheduler-api/src/main/java/cn/escheduler/api/service/DataAnalysisService.java

@ -357,7 +357,7 @@ public class DataAnalysisService {
i = 0;
for (String taskKillStr : tasksKillList){
if (StringUtils.isNotEmpty(taskKillStr)){
String[] splits = taskKillStr.split("_");
String[] splits = taskKillStr.split("-");
if (splits.length == 2){
tasksKillIds[i++]=Integer.parseInt(splits[1]);
}
@ -370,8 +370,8 @@ public class DataAnalysisService {
taskQueueCount = taskInstanceMapper.countTask(loginUser.getId(),loginUser.getUserType(),projectId, tasksQueueIds);
}
if (tasksQueueIds.length != 0){
taskKillCount = taskInstanceMapper.countTask(loginUser.getId(),loginUser.getUserType(),projectId, tasksQueueIds);
if (tasksKillIds.length != 0){
taskKillCount = taskInstanceMapper.countTask(loginUser.getId(),loginUser.getUserType(),projectId, tasksKillIds);
}

4
escheduler-api/src/main/java/cn/escheduler/api/service/DataSourceService.java

@ -480,7 +480,9 @@ public class DataSourceService extends BaseService{
}
logger.info("parameters map-----" + JSONObject.toJSONString(parameterMap));
if(logger.isDebugEnabled()){
logger.info("parameters map-----" + JSONObject.toJSONString(parameterMap));
}
return JSONObject.toJSONString(parameterMap);

2
escheduler-ui/src/js/conf/home/pages/dag/_source/formModel/_source/timeoutAlarm.vue

@ -33,7 +33,7 @@
</div>
<div class="cont-box">
<label class="label-box">
<x-input v-model="interval" style="width: 128px;" :disabled="isDetails">
<x-input v-model="interval" style="width: 128px;" :disabled="isDetails" maxlength="9">
<span slot="append">{{$t('Minute')}}</span>
</x-input>
</label>

45
escheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue

@ -4,6 +4,7 @@
<div class="title">
<span>{{$t('Set the DAG diagram name')}}</span>
</div>
<div>
<x-input
type="text"
@ -12,6 +13,7 @@
:placeholder="$t('Please enter name(required)')">
</x-input>
</div>
<template v-if="router.history.current.name !== 'projects-instance-details'">
<div style="padding-top: 12px;">
<x-input
@ -23,6 +25,21 @@
</x-input>
</div>
</template>
<div class="title" style="padding-top: 6px;">
<span>超时告警</span>
<span style="padding-left: 6px;">
<x-switch v-model="checkedTimeout"></x-switch>
</span>
</div>
<div class="content" style="padding-bottom: 10px;" v-if="checkedTimeout">
<span>
<x-input v-model="timeout" style="width: 128px;" maxlength="9">
<span slot="append">{{$t('Minute')}}</span>
</x-input>
</span>
</div>
<div class="title" style="padding-top: 6px;">
<span>{{$t('Set global')}}</span>
</div>
@ -68,7 +85,11 @@
// Global custom parameters
udpList: [],
// Whether to update the process definition
syncDefine: true
syncDefine: true,
// Timeout alarm
timeout: 0,
// checked Timeout alarm
checkedTimeout: true
}
},
mixins: [disabledState],
@ -81,6 +102,14 @@
_onLocalParams (a) {
this.udpList = a
},
_verifTimeout () {
const reg = /^[1-9]\d*$/
if (!reg.test(this.timeout)) {
alert(1)
return false
}
return true
},
/**
* submit
*/
@ -95,9 +124,15 @@
if (!this.$refs.refLocalParams._verifProp()) {
return
}
// verification timeout
if (!this._verifTimeout()) {
return
}
// Storage global globalParams
this.store.commit('dag/setGlobalParams', _.cloneDeep(this.udpList))
this.store.commit('dag/setName', _.cloneDeep(this.name))
this.store.commit('dag/setTimeout', _.cloneDeep(this.timeout))
this.store.commit('dag/setDesc', _.cloneDeep(this.desc))
this.store.commit('dag/setSyncDefine', this.syncDefine)
Affirm.setIsPop(false)
@ -124,12 +159,20 @@
}
},
watch: {
checkedTimeout (val) {
if (!val) {
this.timeout = 0
this.store.commit('dag/setTimeout', _.cloneDeep(this.timeout))
}
}
},
created () {
this.udpList = this.store.state.dag.globalParams
this.name = this.store.state.dag.name
this.desc = this.store.state.dag.desc
this.syncDefine = this.store.state.dag.syncDefine
this.timeout = this.store.state.dag.timeout || 0
this.checkedTimeout = this.timeout !== 0
},
mounted () {},
components: { mLocalParams }

10
escheduler-ui/src/js/conf/home/pages/home/index.vue

@ -1,16 +1,12 @@
<template>
<m-list-construction :title="$t('Home')">
<template slot="content">
<m-project-chart :id="0"></m-project-chart>
</template>
</m-list-construction>
<m-project-home :id="0"></m-project-home>
</template>
<script>
import mProjectChart from '@/conf/home/pages/projects/pages/index/_source/projectChart'
import mProjectHome from '@/conf/home/pages/projects/pages/index'
import mListConstruction from '@/module/components/listConstruction/listConstruction'
export default {
name: 'home',
components: { mProjectChart, mListConstruction }
components: { mProjectHome, mListConstruction }
}
</script>

8
escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/master.vue

@ -6,12 +6,12 @@
<div class="row-title">
<div class="left">
<span class="sp">IP: {{item.host}}</span>
<span class="sp">端口: {{item.port}}</span>
<span class="sp">zk注册目录: {{item.zkDirectory}}</span>
<span class="sp">{{$t('Port')}}: {{item.port}}</span>
<span class="sp">{{$t('Zk registration directory')}}: {{item.zkDirectory}}</span>
</div>
<div class="right">
<span class="sp">创建时间: {{item.createTime | formatDate}}</span>
<span class="sp">最后心跳时间: {{item.lastHeartbeatTime | formatDate}}</span>
<span class="sp">{{$t('Create Time')}}: {{item.createTime | formatDate}}</span>
<span class="sp">{{$t('Last heartbeat time')}}: {{item.lastHeartbeatTime | formatDate}}</span>
</div>
</div>
<div class="row-cont">

6
escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/mysql.vue

@ -22,7 +22,7 @@
<div class="col-md-3">
<div class="text-num-model text">
<div class="title">
<span>最大连接数</span>
<span>最大连接数 - {{item.date | formatDate}}</span>
</div>
<div class="value-p">
<b :style="{color:color[0]}">{{item.maxConnections}}</b>
@ -48,13 +48,13 @@
<div class="col-md-2">
<div class="text-num-model text">
<div class="title">
<span>最大连接数</span>
<span>最大使用连接数</span>
</div>
<div class="value-p">
<b :style="{color:color[2]}">{{item.maxUsedConnections}}</b>
</div>
<div class="text-1">
最大连接数
最大使用连接数
</div>
</div>
</div>

2
escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/servers.scss

@ -59,7 +59,7 @@
height: 160px;
line-height: 160px;
>b {
font-size: 80px;
font-size: 72px;
}
>.state {
>i {

8
escheduler-ui/src/js/conf/home/pages/monitor/pages/servers/worker.vue

@ -6,12 +6,12 @@
<div class="row-title">
<div class="left">
<span class="sp">IP: {{item.host}}</span>
<span class="sp">端口: {{item.port}}</span>
<span class="sp">zk注册目录: {{item.zkDirectory}}</span>
<span class="sp">{{$t('Port')}}: {{item.port}}</span>
<span class="sp">{{$t('Zk registration directory')}}: {{item.zkDirectory}}</span>
</div>
<div class="right">
<span class="sp">创建时间: {{item.createTime | formatDate}}</span>
<span class="sp">最后心跳时间: {{item.lastHeartbeatTime | formatDate}}</span>
<span class="sp">{{$t('Create Time')}}: {{item.createTime | formatDate}}</span>
<span class="sp">{{$t('Last heartbeat time')}}: {{item.lastHeartbeatTime | formatDate}}</span>
</div>
</div>
<div class="row-cont">

77
escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/commandStateCount.vue

@ -0,0 +1,77 @@
<template>
<div class="command-state-count-model">
<template v-show="!msg">
<div class="data-area" v-spin="isSpin">
<div id="command-state-bar" style="height:500px"></div>
</div>
</template>
<template v-show="msg">
<m-no-data :msg="msg" v-if="msg"></m-no-data>
</template>
</div>
</template>
<script>
import _ from 'lodash'
import { mapActions } from 'vuex'
import { simple } from './chartConfig'
import Chart from '~/@analysys/ana-charts'
import mNoData from '@/module/components/noData/noData'
export default {
name: 'command-state-count',
data () {
return {
isSpin: true,
msg: ''
}
},
props: {
searchParams: Object
},
methods: {
...mapActions('projects', ['getCommandStateCount']),
_handleCommandState (res) {
let data = []
_.forEach(res.data, (v, i) => {
let key = _.keys(v)
if (key[0] === 'errorCount') {
data.push({ typeName: '错误指令数', key: v.commandState, value: v.errorCount })
}
})
_.forEach(res.data, (v, i) => {
let key = _.keys(v)
if (key[1] === 'normalCount') {
data.push({ typeName: '正常指令数', key: v.commandState, value: v.normalCount })
}
})
const myChart = Chart.bar('#command-state-bar', data, {
title: ''
})
myChart.echart.setOption(simple)
}
},
created () {
},
watch: {
'searchParams': {
deep: true,
immediate: true,
handler (o) {
this.isSpin = true
this.getCommandStateCount(o).then(res => {
this._handleCommandState(res)
this.isSpin = false
}).catch(e => {
this.msg = e.msg || 'error'
this.isSpin = false
})
}
}
},
mounted () {
},
computed: {},
components: { mNoData }
}
</script>

70
escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/defineUserCount.vue

@ -0,0 +1,70 @@
<template>
<div class="define-user-count-model">
<template v-if="!msg">
<div class="data-area" v-spin="isSpin">
<div id="process-definition-bar" style="height:500px"></div>
</div>
</template>
<template v-if="msg">
<m-no-data :msg="msg" v-if="msg"></m-no-data>
</template>
</div>
</template>
<script>
import _ from 'lodash'
import { mapActions } from 'vuex'
import { bar } from './chartConfig'
import Chart from '~/@analysys/ana-charts'
import mNoData from '@/module/components/noData/noData'
export default {
name: 'define-user-count',
data () {
return {
isSpin: true,
msg: ''
}
},
props: {
projectId: Number
},
methods: {
...mapActions('projects', ['getDefineUserCount']),
_handleDefineUser (res) {
let data = res.data.userList || []
this.defineUserList = _.map(data, v => {
return {
key: v.userName + ',' + v.userId + ',' + v.count,
value: v.count
}
})
const myChart = Chart.bar('#process-definition-bar', this.defineUserList, {})
myChart.echart.setOption(bar)
//
if (this.id) {
myChart.echart.on('click', e => {
this.$router.push({
name: 'projects-definition-list',
query: {
userId: e.name.split(',')[1]
}
})
})
}
}
},
created () {
this.isSpin = true
this.getDefineUserCount(this.projectId).then(res => {
this.defineUserList = []
this._handleDefineUser(res)
this.isSpin = false
}).catch(e => {
this.msg = e.msg || 'error'
this.isSpin = false
})
},
mounted () {
},
components: { mNoData }
}
</script>

122
escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/processStateCount.vue

@ -0,0 +1,122 @@
<template>
<div class="process-state-count-model">
<template v-show="!msg">
<div class="data-area" v-spin="isSpin" style="height: 430px;">
<div class="col-md-7">
<div id="process-state-pie" style="height:260px;margin-top: 100px;"></div>
</div>
<div class="col-md-5">
<div class="table-small-model">
<table>
<tr>
<th width="40">{{$t('#')}}</th>
<th>{{$t('Number')}}</th>
<th>{{$t('State')}}</th>
</tr>
<tr v-for="(item,$index) in processStateList">
<td><span>{{$index+1}}</span></td>
<td><span><a href="javascript:" @click="id && _goProcess(item.key)" :class="id ?'links':''">{{item.value}}</a></span></td>
<td><span class="ellipsis" style="width: 98%;" :title="item.key">{{item.key}}</span></td>
</tr>
</table>
</div>
</div>
</div>
</template>
<template v-show="msg">
<m-no-data :msg="msg" v-if="msg"></m-no-data>
</template>
</div>
</template>
<script>
import _ from 'lodash'
import { mapActions } from 'vuex'
import { pie } from './chartConfig'
import Chart from '~/@analysys/ana-charts'
import mNoData from '@/module/components/noData/noData'
import { stateType } from '@/conf/home/pages/projects/pages/_source/instanceConditions/common'
export default {
name: 'process-state-count',
data () {
return {
isSpin: true,
msg: '',
processStateList: []
}
},
props: {
searchParams: Object
},
methods: {
...mapActions('projects', ['getProcessStateCount']),
_goProcess (name) {
this.$router.push({
name: 'projects-instance-list',
query: {
stateType: _.find(stateType, ['label', name])['code'],
startDate: this.scheduleTime[0],
endDate: this.scheduleTime[1]
}
})
},
_handleProcessState (res) {
let data = res.data.taskCountDtos
this.processStateList = _.map(data, v => {
return {
key: _.find(stateType, ['code', v.taskStateType])['label'],
value: v.count
}
})
const myChart = Chart.pie('#process-state-pie', this.processStateList, { title: '' })
myChart.echart.setOption(pie)
//
if (this.id) {
myChart.echart.on('click', e => {
this._goProcess(e.data.name)
})
}
}
},
watch: {
'searchParams': {
deep: true,
immediate: true,
handler (o) {
this.isSpin = true
this.getProcessStateCount(o).then(res => {
this.processStateList = []
this._handleProcessState(res)
this.isSpin = false
}).catch(e => {
this.msg = e.msg || 'error'
this.isSpin = false
})
}
}
},
beforeCreate () {
},
created () {
},
beforeMount () {
},
mounted () {
},
beforeUpdate () {
},
updated () {
},
beforeDestroy () {
},
destroyed () {
},
computed: {},
components: { mNoData }
}
</script>
<style lang="scss" rel="stylesheet/scss">
.process-state-count-model {
}
</style>

362
escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/projectChart.vue

@ -1,362 +0,0 @@
<template>
<div>
<template v-show="!isLoading">
<div class="perject-home-content" v-show="!msg">
<div class="time-model">
<x-datepicker
:panel-num="2"
placement="bottom-end"
@on-change="_datepicker"
:value="[searchParams.startDate,searchParams.endDate]"
type="daterange"
:placeholder="$t('Select date range')"
format="YYYY-MM-DD HH:mm:ss">
</x-datepicker>
</div>
<div class="row" >
<div class="col-md-6">
<div class="chart-title">
<span>{{$t('Task status statistics')}}</span>
</div>
<div class="row">
<div class="col-md-7">
<div id="task-status-pie" style="height:260px;margin-top: 100px;"></div>
</div>
<div class="col-md-5">
<div class="table-small-model">
<table>
<tr>
<th width="40">{{$t('#')}}</th>
<th>{{$t('Number')}}</th>
<th>{{$t('State')}}</th>
</tr>
<tr v-for="(item,$index) in taskCtatusList">
<td><span>{{$index+1}}</span></td>
<td>
<span>
<a href="javascript:" @click="id && _goTask(item.key)" :class="id ?'links':''">{{item.value}}</a>
</span>
</td>
<td><span class="ellipsis" style="width: 98%;" :title="item.key">{{item.key}}</span></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="chart-title">
<span>{{$t('Process Status Statistics')}}</span>
</div>
<div class="row">
<div class="col-md-7">
<div id="process-state-pie" style="height:260px;margin-top: 100px;"></div>
</div>
<div class="col-md-5">
<div class="table-small-model">
<table>
<tr>
<th width="40">{{$t('#')}}</th>
<th>{{$t('Number')}}</th>
<th>{{$t('State')}}</th>
</tr>
<tr v-for="(item,$index) in processStateList">
<td><span>{{$index+1}}</span></td>
<td><span><a href="javascript:" @click="id && _goProcess(item.key)" :class="id ?'links':''">{{item.value}}</a></span></td>
<td><span class="ellipsis" style="width: 98%;" :title="item.key">{{item.key}}</span></td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row" style="padding-top: 20px;">
<div class="col-md-6">
</div>
<div class="col-md-6">
<div class="chart-title">
<span>队列统计</span>
</div>
<div class="row">
<div class="col-md-7">
<div id="queue-pie" style="height:260px;margin-top: 100px;"></div>
</div>
<div class="col-md-5">
<div class="table-small-model">
<table>
<tr>
<th width="40">{{$t('#')}}</th>
<th>等待执行任务</th>
<th>等待Kill任务</th>
</tr>
<tr v-for="(item,$index) in queueList">
<td><span>{{$index+1}}</span></td>
<td><span><a href="javascript:" >{{item.value}}</a></span></td>
<td><span class="ellipsis" style="width: 98%;" :title="item.key">{{item.key}}</span></td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="chart-title" style="margin-bottom: 20px;margin-top: 30px">
<span>命令状态统计</span>
</div>
<div>
<div id="command-state-bar" style="height:500px"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="chart-title" style="margin-bottom: -20px;margin-top: 30px">
<span>{{$t('Process Definition Statistics')}}</span>
</div>
<div>
<div id="process-definition-bar" style="height:500px"></div>
</div>
</div>
</div>
</div>
<m-no-data :msg="msg" v-if="msg"></m-no-data>
</template>
<m-spin :is-spin="isLoading" :is-left="id ? true : false">
</m-spin>
</div>
</template>
<script>
import _ from 'lodash'
import dayjs from 'dayjs'
import { mapActions } from 'vuex'
import { pie, bar, simple } from './chartConfig'
import Chart from '~/@analysys/ana-charts'
import mSpin from '@/module/components/spin/spin'
import mNoData from '@/module/components/noData/noData'
import { stateType } from '@/conf/home/pages/projects/pages/_source/instanceConditions/common'
export default {
name: 'perject-chart',
data () {
return {
isLoading: true,
taskCtatusList: [],
processStateList: [],
defineUserList: [],
commandStateList: [],
queueList: [],
searchParams: {
projectId: this.id,
startDate: '',
endDate: ''
},
msg: ''
}
},
props: {
id: Number
},
methods: {
...mapActions('projects', ['getTaskCtatusCount', 'getProcessStateCount', 'getDefineUserCount', 'getCommandStateCount', 'getQueueCount']),
_datepicker (val) {
this.searchParams.startDate = val[0]
this.searchParams.endDate = val[1]
this._getData(false)
},
_goTask (name) {
this.$router.push({
name: 'task-instance',
query: {
stateType: _.find(stateType, ['label', name])['code'],
startDate: this.scheduleTime[0],
endDate: this.scheduleTime[1]
}
})
},
_goProcess (name) {
this.$router.push({
name: 'projects-instance-list',
query: {
stateType: _.find(stateType, ['label', name])['code'],
startDate: this.scheduleTime[0],
endDate: this.scheduleTime[1]
}
})
},
_handleTaskCtatus (res) {
let data = res.data.taskCountDtos
this.taskCtatusList = _.map(data, v => {
return {
key: _.find(stateType, ['code', v.taskStateType])['label'],
value: v.count,
type: 'type'
}
})
const myChart = Chart.pie('#task-status-pie', this.taskCtatusList, { title: '' })
myChart.echart.setOption(pie)
//
if (this.id) {
myChart.echart.on('click', e => {
this._goTask(e.data.name)
})
}
},
_handleProcessState (res) {
let data = res.data.taskCountDtos
this.processStateList = _.map(data, v => {
return {
key: _.find(stateType, ['code', v.taskStateType])['label'],
value: v.count
}
})
const myChart = Chart.pie('#process-state-pie', this.processStateList, { title: '' })
myChart.echart.setOption(pie)
//
if (this.id) {
myChart.echart.on('click', e => {
this._goProcess(e.data.name)
})
}
},
_handleDefineUser (res) {
let data = res.data.userList
this.defineUserList = _.map(data, v => {
return {
key: v.userName + ',' + v.userId + ',' + v.count,
value: v.count
}
})
const myChart = Chart.bar('#process-definition-bar', this.defineUserList, {})
myChart.echart.setOption(bar)
//
if (this.id) {
myChart.echart.on('click', e => {
this.$router.push({
name: 'projects-definition-list',
query: {
userId: e.name.split(',')[1]
}
})
})
}
},
_handleCommandState (res) {
let data = []
_.forEach(res.data, (v, i) => {
let key = _.keys(v)
if (key[0] === 'errorCount') {
data.push({ typeName: '错误指令数', key: v.commandState, value: v.errorCount })
}
})
_.forEach(res.data, (v, i) => {
let key = _.keys(v)
if (key[1] === 'normalCount') {
data.push({ typeName: '正常指令数', key: v.commandState, value: v.normalCount })
}
})
const myChart = Chart.bar('#command-state-bar', data, {
title: ''
})
myChart.echart.setOption(simple)
},
_handleQueue (res) {
_.forEach(res.data, (v, k) => this.queueList.push({
key: k === 'taskQueue' ? '等待执行任务' : '等待kill任务',
value: v
}))
const myChart = Chart.pie('#queue-pie', this.queueList, { title: '' })
myChart.echart.setOption(pie)
},
_getData (is = true) {
this.isLoading = true
let ioList = [
this.getTaskCtatusCount(this.searchParams),
this.getProcessStateCount(this.searchParams),
this.getCommandStateCount(this.searchParams),
this.getQueueCount(this.searchParams)
]
if (is) {
ioList.push(this.getDefineUserCount(_.pick(this.searchParams, ['projectId'])))
}
Promise.all(ioList).then(res => {
this._handleTaskCtatus(res[0])
this._handleProcessState(res[1])
this._handleCommandState(res[2])
this._handleQueue(res[3])
if (is) {
this._handleDefineUser(res[4])
}
setTimeout(() => {
this.isLoading = false
}, 800)
}).catch(e => {
this.msg = e.msg || 'error'
this.isLoading = false
})
}
},
watch: {
},
created () {
this.searchParams.startDate = dayjs().format('YYYY-MM-DD 00:00:00')
this.searchParams.endDate = dayjs().format('YYYY-MM-DD HH:mm:ss')
// init get data
this._getData()
},
mounted () {
},
updated () {
},
beforeDestroy () {
},
destroyed () {
},
computed: {},
components: { mNoData, mSpin }
}
</script>
<style lang="scss" rel="stylesheet/scss">
.perject-home-content {
padding: 10px 20px;
position: relative;
.time-model {
position: absolute;
right: 8px;
top: -40px;
.ans-input {
>input {
width: 344px;
}
}
}
.chart-title {
text-align: center;
height: 60px;
line-height: 60px;
span {
font-size: 22px;
color: #333;
font-weight: bold;
}
}
}
.table-small-model {
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
}
</style>

102
escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/queueCount.vue

@ -0,0 +1,102 @@
<template>
<div class="queue-count-model">
<template v-show="!msg">
<div class="data-area" v-spin="isSpin" style="height: 430px;">
<div class="col-md-7">
<div id="queue-pie" style="height:260px;margin-top: 100px;"></div>
</div>
<div class="col-md-5">
<div class="table-small-model">
<table>
<tr>
<th width="40">{{$t('#')}}</th>
<th>等待执行任务</th>
<th>等待Kill任务</th>
</tr>
<tr v-for="(item,$index) in queueList">
<td><span>{{$index+1}}</span></td>
<td><span><a href="javascript:" >{{item.value}}</a></span></td>
<td><span class="ellipsis" style="width: 98%;" :title="item.key">{{item.key}}</span></td>
</tr>
</table>
</div>
</div>
</div>
</template>
<template v-show="msg">
<m-no-data :msg="msg" v-if="msg"></m-no-data>
</template>
</div>
</template>
<script>
import _ from 'lodash'
import { mapActions } from 'vuex'
import { pie } from './chartConfig'
import Chart from '~/@analysys/ana-charts'
import mNoData from '@/module/components/noData/noData'
export default {
name: 'queue-count',
data () {
return {
isSpin: true,
msg: '',
queueList: []
}
},
props: {
searchParams: Object
},
methods: {
...mapActions('projects', ['getQueueCount']),
_handleQueue (res) {
_.forEach(res.data, (v, k) => this.queueList.push({
key: k === 'taskQueue' ? '等待执行任务' : '等待kill任务',
value: v
}))
const myChart = Chart.pie('#queue-pie', this.queueList, { title: '' })
myChart.echart.setOption(pie)
}
},
watch: {
'searchParams': {
deep: true,
immediate: true,
handler (o) {
this.isSpin = true
this.getQueueCount(o).then(res => {
this.queueList = []
this._handleQueue(res)
this.isSpin = false
}).catch(e => {
this.msg = e.msg || 'error'
this.isSpin = false
})
}
}
},
beforeCreate () {
},
created () {
},
beforeMount () {
},
mounted () {
},
beforeUpdate () {
},
updated () {
},
beforeDestroy () {
},
destroyed () {
},
computed: {},
components: { mNoData }
}
</script>
<style lang="scss" rel="stylesheet/scss">
.queue-count-model {
}
</style>

129
escheduler-ui/src/js/conf/home/pages/projects/pages/index/_source/taskCtatusCount.vue

@ -0,0 +1,129 @@
<template>
<div class="task-ctatus-count-model">
<template v-show="!msg">
<div class="data-area" v-spin="isSpin" style="height: 430px;">
<div class="col-md-7">
<div id="task-status-pie" style="height:260px;margin-top: 100px;"></div>
</div>
<div class="col-md-5">
<div class="table-small-model">
<table>
<tr>
<th width="40">{{$t('#')}}</th>
<th>{{$t('Number')}}</th>
<th>{{$t('State')}}</th>
</tr>
<tr v-for="(item,$index) in taskCtatusList">
<td><span>{{$index+1}}</span></td>
<td>
<span>
<a href="javascript:" @click="id && _goTask(item.key)" :class="id ?'links':''">{{item.value}}</a>
</span>
</td>
<td><span class="ellipsis" style="width: 98%;" :title="item.key">{{item.key}}</span></td>
</tr>
</table>
</div>
</div>
</div>
</template>
<template v-show="msg">
<m-no-data :msg="msg" v-if="msg"></m-no-data>
</template>
</div>
</template>
<script>
import _ from 'lodash'
import { mapActions } from 'vuex'
import { pie } from './chartConfig'
import Chart from '~/@analysys/ana-charts'
import mNoData from '@/module/components/noData/noData'
import { stateType } from '@/conf/home/pages/projects/pages/_source/instanceConditions/common'
export default {
name: 'task-ctatus-count',
data () {
return {
isSpin: true,
msg: '',
taskCtatusList: []
}
},
props: {
searchParams: Object
},
methods: {
...mapActions('projects', ['getTaskCtatusCount']),
_goTask (name) {
this.$router.push({
name: 'task-instance',
query: {
stateType: _.find(stateType, ['label', name])['code'],
startDate: this.scheduleTime[0],
endDate: this.scheduleTime[1]
}
})
},
_handleTaskCtatus (res) {
let data = res.data.taskCountDtos
this.taskCtatusList = _.map(data, v => {
return {
key: _.find(stateType, ['code', v.taskStateType])['label'],
value: v.count,
type: 'type'
}
})
const myChart = Chart.pie('#task-status-pie', this.taskCtatusList, { title: '' })
myChart.echart.setOption(pie)
//
if (this.id) {
myChart.echart.on('click', e => {
this._goTask(e.data.name)
})
}
}
},
watch: {
'searchParams': {
deep: true,
immediate: true,
handler (o) {
this.isSpin = true
this.getTaskCtatusCount(o).then(res => {
this.taskCtatusList = []
this._handleTaskCtatus(res)
this.isSpin = false
}).catch(e => {
this.msg = e.msg || 'error'
this.isSpin = false
})
}
}
},
beforeCreate () {
},
created () {
},
beforeMount () {
},
mounted () {
},
beforeUpdate () {
},
updated () {
},
beforeDestroy () {
},
destroyed () {
},
computed: {},
components: { mNoData }
}
</script>
<style lang="scss" rel="stylesheet/scss">
.task-ctatus-count-model {
}
</style>

141
escheduler-ui/src/js/conf/home/pages/projects/pages/index/index.vue

@ -1,12 +1,84 @@
<template>
<m-list-construction :title="$t('Project Home')">
<template slot="content">
<m-project-chart :id="id"></m-project-chart>
<div class="perject-home-content">
<div class="time-model" v-show="searchParams.projectId !== 0">
<x-datepicker
:panel-num="2"
placement="bottom-end"
@on-change="_datepicker"
:value="[searchParams.startDate,searchParams.endDate]"
type="daterange"
:placeholder="$t('Select date range')"
format="YYYY-MM-DD HH:mm:ss">
</x-datepicker>
</div>
<div class="row" >
<div class="col-md-6">
<div class="chart-title">
<span>{{$t('Task status statistics')}}</span>
</div>
<div class="row">
<m-task-ctatus-count :search-params="searchParams">
</m-task-ctatus-count>
</div>
</div>
<div class="col-md-6">
<div class="chart-title">
<span>{{$t('Process Status Statistics')}}</span>
</div>
<div class="row">
<m-process-state-count :search-params="searchParams">
</m-process-state-count>
</div>
</div>
</div>
<div class="row" style="padding-top: 20px;">
<div class="col-md-6">
</div>
<div class="col-md-6">
<div class="chart-title">
<span>队列统计</span>
</div>
<div class="row">
<m-queue-count :search-params="searchParams">
</m-queue-count>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="chart-title" style="margin-bottom: 20px;margin-top: 30px">
<span>命令状态统计</span>
</div>
<div>
<m-command-state-count :search-params="searchParams">
</m-command-state-count>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="chart-title" style="margin-bottom: -20px;margin-top: 30px">
<span>{{$t('Process Definition Statistics')}}</span>
</div>
<div>
<m-define-user-count :project-id="searchParams.projectId">
</m-define-user-count>
</div>
</div>
</div>
</div>
</template>
</m-list-construction>
</template>
<script>
import mProjectChart from './_source/projectChart'
import dayjs from 'dayjs'
import mDefineUserCount from './_source/defineUserCount'
import mCommandStateCount from './_source/CommandStateCount'
import mTaskCtatusCount from './_source/taskCtatusCount'
import mProcessStateCount from './_source/processStateCount'
import mQueueCount from './_source/queueCount'
import localStore from '@/module/util/localStorage'
import mSecondaryMenu from '@/module/components/secondaryMenu/secondaryMenu'
import mListConstruction from '@/module/components/listConstruction/listConstruction'
@ -15,9 +87,70 @@
name: 'projects-index-index',
data () {
return {
id: localStore.getItem('projectId') || null
searchParams: {
projectId: null,
startDate: '',
endDate: ''
}
}
},
components: { mSecondaryMenu, mListConstruction, mProjectChart }
props: {
id: Number
},
methods: {
_datepicker (val) {
this.searchParams.startDate = val[0]
this.searchParams.endDate = val[1]
}
},
created () {
this.searchParams.projectId = this.id === 0 ? 0 : localStore.getItem('projectId')
this.searchParams.startDate = dayjs().format('YYYY-MM-DD 00:00:00')
this.searchParams.endDate = dayjs().format('YYYY-MM-DD HH:mm:ss')
},
components: {
mSecondaryMenu,
mListConstruction,
mDefineUserCount,
mCommandStateCount,
mTaskCtatusCount,
mProcessStateCount,
mQueueCount
}
}
</script>
<style lang="scss" rel="stylesheet/scss">
.perject-home-content {
padding: 10px 20px;
position: relative;
.time-model {
position: absolute;
right: 8px;
top: -40px;
.ans-input {
>input {
width: 344px;
}
}
}
.chart-title {
text-align: center;
height: 60px;
line-height: 60px;
span {
font-size: 22px;
color: #333;
font-weight: bold;
}
}
}
.table-small-model {
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
}
</style>

29
escheduler-ui/src/js/conf/home/pages/security/pages/token/index.vue

@ -5,35 +5,6 @@
import mToken from '@/conf/home/pages/user/pages/token'
export default {
name: 'token-index',
data () {
return {}
},
props: {},
methods: {},
watch: {},
beforeCreate () {
},
created () {
},
beforeMount () {
},
mounted () {
},
beforeUpdate () {
},
updated () {
},
beforeDestroy () {
},
destroyed () {
},
computed: {},
components: { mToken }
}
</script>
<style lang="scss" rel="stylesheet/scss">
.index-model {
}
</style>

6
escheduler-ui/src/js/conf/home/pages/security/pages/workerGroups/_source/createWorker.vue

@ -2,7 +2,7 @@
<m-popup
ref="popup"
:ok-text="item ? $t('Edit') : $t('Submit')"
:nameText="item ? '编辑Worker分组' : '创建Worker分组'"
:nameText="item ? $t('Edit worker group') : $t('Create worker group')"
@ok="_ok">
<template slot="content">
<div class="create-worker-model">
@ -23,10 +23,10 @@
:autosize="{ minRows: 4, maxRows: 6 }"
type="textarea"
v-model="ipList"
:placeholder="'请输入IP地址多个用逗号隔开'">
:placeholder="$t('Please enter the IP address separated by commas')">
</x-input>
<div class="ipt-tip">
<span>注意多个IP地址已逗号分割</span>
<span>{{$t('Note: Multiple IP addresses have been comma separated')}}</span>
</div>
</template>
</m-list-box-f>

4
escheduler-ui/src/js/conf/home/pages/security/pages/workerGroups/_source/list.vue

@ -7,7 +7,7 @@
<span>{{$t('#')}}</span>
</th>
<th>
<span>分组名称</span>
<span>{{$t('Group')}}</span>
</th>
<th>
<span>IPList</span>
@ -62,8 +62,6 @@
</div>
</template>
<script>
// import _ from 'lodash'
// import i18n from '@/module/i18n'
import { mapActions } from 'vuex'
export default {

4
escheduler-ui/src/js/conf/home/pages/security/pages/workerGroups/index.vue

@ -1,9 +1,9 @@
<template>
<m-list-construction :title="'worker分组管理'">
<m-list-construction :title="$t('Worker group manage')">
<template slot="conditions">
<m-conditions @on-conditions="_onConditions">
<template slot="button-group">
<x-button type="ghost" size="small" @click="_create('')">创建worker分组</x-button>
<x-button type="ghost" size="small" @click="_create('')">{{$t('Create worker group')}}</x-button>
</template>
</m-conditions>
</template>

12
escheduler-ui/src/js/conf/home/pages/user/pages/token/_source/createToken.vue

@ -2,12 +2,12 @@
<m-popup
ref="popup"
:ok-text="item ? $t('Edit') : $t('Submit')"
:nameText="item ? '编辑令牌' : '创建令牌'"
:nameText="item ? $t('Edit token') : $t('Create token')"
@ok="_ok">
<template slot="content">
<div class="create-token-model">
<m-list-box-f>
<template slot="name"><b>*</b>失效时间</template>
<template slot="name"><b>*</b>{{$t('Failure time')}}</template>
<template slot="content">
<x-datepicker
:disabled-date="disabledDate"
@ -19,7 +19,7 @@
</template>
</m-list-box-f>
<m-list-box-f v-if="auth">
<template slot="name"><b>*</b>用户</template>
<template slot="name"><b>*</b>{{$t('User')}}</template>
<template slot="content">
<x-select v-model="userId" @on-change="_onChange">
<x-option
@ -36,12 +36,12 @@
<template slot="content">
<x-input
readonly
style="width: 330px;"
style="width: 306px;"
type="input"
v-model="token"
placeholder="请输入Token">
:placeholder="$t('Please enter token')">
</x-input>
<x-button type="ghost" @click="_generateToken" :loading="tokenLoading">生成Token</x-button>
<x-button type="ghost" @click="_generateToken" :loading="tokenLoading">{{$t('Generate token')}}</x-button>
</template>
</m-list-box-f>
</div>

12
escheduler-ui/src/js/conf/home/pages/user/pages/token/_source/list.vue

@ -4,25 +4,25 @@
<table>
<tr>
<th>
<span>编号</span>
<span>{{$t('#')}}</span>
</th>
<th>
<span>用户</span>
<span>{{$t('User')}}</span>
</th>
<th>
<span>Token</span>
</th>
<th>
<span>开始时间</span>
<span>{{$t('Start Time')}}</span>
</th>
<th>
<span>失效时间</span>
<span>{{$t('Failure time')}}</span>
</th>
<th>
<span>创建时间</span>
<span>{{$t('Create Time')}}</span>
</th>
<th>
<span>更新时间</span>
<span>{{$t('Update Time')}}</span>
</th>
<th width="70">
<span>{{$t('Operation')}}</span>

4
escheduler-ui/src/js/conf/home/pages/user/pages/token/index.vue

@ -1,9 +1,9 @@
<template>
<m-list-construction :title="'令牌管理'">
<m-list-construction :title="$t('Token manage')">
<template slot="conditions">
<m-conditions @on-conditions="_onConditions">
<template slot="button-group">
<x-button type="ghost" size="small" @click="_create('')">创建令牌</x-button>
<x-button type="ghost" size="small" @click="_create('')">{{$t('Create token')}}</x-button>
</template>
</m-conditions>
</template>

16
escheduler-ui/src/js/conf/home/router/index.js

@ -323,7 +323,7 @@ const router = new Router({
name: 'worker-groups-manage',
component: resolve => require(['../pages/security/pages/workerGroups/index'], resolve),
meta: {
title: `workerGroups`
title: `${i18n.$t('Worker group manage')}`
}
},
{
@ -331,7 +331,7 @@ const router = new Router({
name: 'token-manage',
component: resolve => require(['../pages/security/pages/token/index'], resolve),
meta: {
title: `token`
title: `${i18n.$t('Token manage')}`
}
}
]
@ -368,7 +368,7 @@ const router = new Router({
name: 'token',
component: resolve => require(['../pages/user/pages/token/index'], resolve),
meta: {
title: `令牌管理`
title: `${i18n.$t('Token manage')}`
}
}
]
@ -405,7 +405,7 @@ const router = new Router({
name: 'servers-alert',
component: resolve => require(['../pages/monitor/pages/servers/alert'], resolve),
meta: {
title: `alert`
title: `Alert`
}
},
{
@ -413,7 +413,7 @@ const router = new Router({
name: 'servers-rpcserver',
component: resolve => require(['../pages/monitor/pages/servers/rpcserver'], resolve),
meta: {
title: `rpcserver`
title: `Rpcserver`
}
},
{
@ -421,7 +421,7 @@ const router = new Router({
name: 'servers-zookeeper',
component: resolve => require(['../pages/monitor/pages/servers/zookeeper'], resolve),
meta: {
title: `zookeeper`
title: `Zookeeper`
}
},
{
@ -429,7 +429,7 @@ const router = new Router({
name: 'servers-apiserver',
component: resolve => require(['../pages/monitor/pages/servers/apiserver'], resolve),
meta: {
title: `apiserver`
title: `Apiserver`
}
},
{
@ -437,7 +437,7 @@ const router = new Router({
name: 'servers-mysql',
component: resolve => require(['../pages/monitor/pages/servers/mysql'], resolve),
meta: {
title: `mysql`
title: `Mysql`
}
}
]

13
escheduler-ui/src/js/conf/home/store/dag/actions.js

@ -112,6 +112,8 @@ export default {
state.tasks = processDefinitionJson.tasks
// global params
state.globalParams = processDefinitionJson.globalParams
// timeout
state.timeout = processDefinitionJson.timeout
resolve(res.data)
}).catch(res => {
@ -141,6 +143,8 @@ export default {
state.tasks = processInstanceJson.tasks
// global params
state.globalParams = processInstanceJson.globalParams
// timeout
state.timeout = processInstanceJson.timeout
resolve(res.data)
}).catch(res => {
@ -155,7 +159,8 @@ export default {
return new Promise((resolve, reject) => {
let data = {
globalParams: state.globalParams,
tasks: state.tasks
tasks: state.tasks,
timeout: state.timeout
}
io.post(`projects/${state.projectName}/process/save`, {
processDefinitionJson: JSON.stringify(data),
@ -177,7 +182,8 @@ export default {
return new Promise((resolve, reject) => {
let data = {
globalParams: state.globalParams,
tasks: state.tasks
tasks: state.tasks,
timeout: state.timeout
}
io.post(`projects/${state.projectName}/process/update`, {
processDefinitionJson: JSON.stringify(data),
@ -200,7 +206,8 @@ export default {
return new Promise((resolve, reject) => {
let data = {
globalParams: state.globalParams,
tasks: state.tasks
tasks: state.tasks,
timeout: state.timeout
}
io.post(`projects/${state.projectName}/instance/update`, {
processInstanceJson: JSON.stringify(data),

7
escheduler-ui/src/js/conf/home/store/dag/mutations.js

@ -52,6 +52,12 @@ export default {
setName (state, payload) {
state.name = payload
},
/**
* set timeout
*/
setTimeout (state, payload) {
state.timeout = payload
},
/**
* set global params
*/
@ -93,6 +99,7 @@ export default {
state.tasks = payload && payload.tasks || []
state.name = payload && payload.name || ''
state.desc = payload && payload.desc || ''
state.timeout = payload && payload.timeout || 0
state.processListS = payload && payload.processListS || []
state.resourcesListS = payload && payload.resourcesListS || []
state.isDetails = payload && payload.isDetails || false

2
escheduler-ui/src/js/conf/home/store/dag/state.js

@ -29,6 +29,8 @@ export default {
globalParams: [],
// Node information
tasks: [],
// Timeout alarm
timeout: 0,
// Node location information
locations: {},
// Node-to-node connection

2
escheduler-ui/src/js/module/components/nav/nav.vue

@ -35,7 +35,7 @@
<div class="clearfix list">
<div class="nav-links">
<router-link :to="{ path: '/monitor'}" tag="a" active-class="active">
<span><i class="iconfont">&#xe65f;</i>监控中心</span><b></b>
<span><i class="iconfont">&#xe65f;</i>{{$t('Monitor')}}</span><b></b>
</router-link>
</div>
</div>

6
escheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js

@ -110,7 +110,7 @@ let menu = {
children: []
},
{
name: `worker分组管理`,
name: `${i18n.$t('Worker group manage')}`,
id: 4,
path: 'worker-groups-manage',
isOpen: true,
@ -119,7 +119,7 @@ let menu = {
children: []
},
{
name: `令牌管理`,
name: `${i18n.$t('Token manage')}`,
id: 2,
path: 'token-manage',
isOpen: true,
@ -181,7 +181,7 @@ let menu = {
disabled: true
},
{
name: `令牌管理`,
name: `${i18n.$t('Token manage')}`,
id: 2,
path: 'token',
isOpen: true,

19
escheduler-ui/src/js/module/i18n/locale/en_US.js

@ -151,7 +151,7 @@ export default {
'Please enter name': 'Please enter name',
'Owned Users': 'Owned Users',
'Process Pid': 'Process Pid',
'zk registration directory': 'zk registration directory',
'Zk registration directory': 'Zk registration directory',
'cpuUsage': 'cpuUsage',
'memoryUsage': 'memoryUsage',
'Last heartbeat time': 'Last heartbeat time',
@ -411,5 +411,20 @@ export default {
'History task record': 'History task record',
'Please go online': 'Please go online',
'Queue value': 'Queue value',
'Please enter queue value': 'Please enter queue value'
'Please enter queue value': 'Please enter queue value',
'Worker group manage': 'Worker group manage',
'Create worker group': 'Create worker group',
'Edit worker group': 'Edit worker group',
'Token manage': 'Token manage',
'Create token': 'Create token',
'Edit token': 'Edit token',
'Please enter the IP address separated by commas': 'Please enter the IP address separated by commas',
'Note: Multiple IP addresses have been comma separated': 'Note: Multiple IP addresses have been comma separated',
'Failure time': 'Failure time',
'User': 'User',
'Please enter token': 'Please enter token',
'Generate token': 'Generate token',
'Monitor': 'Monitor',
'Group': 'Group'
}

19
escheduler-ui/src/js/module/i18n/locale/zh_CN.js

@ -151,7 +151,7 @@ export default {
'Please enter name': '请输入名称',
'Owned Users': '所属用户',
'Process Pid': '进程pid',
'zk registration directory': 'zk注册目录',
'Zk registration directory': 'zk注册目录',
'cpuUsage': 'cpuUsage',
'memoryUsage': 'memoryUsage',
'Last heartbeat time': '最后心跳时间',
@ -411,5 +411,20 @@ export default {
'History task record': '历史任务记录',
'Please go online': '不要忘记上线',
'Queue value': '队列值',
'Please enter queue value': '请输入队列值'
'Please enter queue value': '请输入队列值',
'Worker group manage': 'Worker分组管理',
'Create worker group': '创建Worker分组',
'Edit worker group': '编辑Worker分组',
'Token manage': '令牌管理',
'Create token': '创建令牌',
'Edit token': '编辑令牌',
'Please enter the IP address separated by commas': '请输入IP地址多个用逗号隔开',
'Note: Multiple IP addresses have been comma separated': '注意多个IP地址已逗号分割',
'Failure time': '失效时间',
'User': '用户',
'Please enter token': '请输入令牌',
'Generate token': '生成令牌',
'Monitor': '监控中心',
'Group': '分组'
}

Loading…
Cancel
Save