Compare commits

..

97 Commits
v2.5 ... v2.7.1

Author SHA1 Message Date
Orange
607eec6c44 perf: 允许用户修改Source 2021-02-02 04:41:54 -06:00
fit2bot
28daf41fb5 fix: 临时调整因为Chrome的兼容性问题导致的日期计算错误 (#607)
* fix: 临时调整因为Chrome的兼容性问题导致的日期计算错误

* fix: 修复工单字段展示问题

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-28 19:28:55 +08:00
fit2bot
e45720af1b fix: 修复工单列表字段展示错误 (#604)
Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-26 18:08:31 +08:00
Jiangjie.Bai
2c69b36291 Merge pull request #598 from jumpserver/dev
Dev
2021-01-21 15:35:46 +08:00
Orange
d267cd1f5e fix: 移除提交应用申请工单时多余的字段 2021-01-21 15:33:02 +08:00
Orange
807e3a407a fix: 移除重复的权限提醒 2021-01-21 15:33:02 +08:00
Orange
1ba790e680 fix: 调整工单主机名字段变成主机名组 2021-01-21 15:33:02 +08:00
Orange
44b701edbc fix: 修复命令过滤规则创建提示有详情的问题 2021-01-21 15:33:02 +08:00
fit2bot
8619ab8bca fix: 修改测试产生的bugs (#596)
* fix: 修改创建录像存储的条件为必填

* fix: 修改最后一次执行的log类型为Ansible

* fix: 修复批量测试资产可连接性的权限问题

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-21 12:43:36 +08:00
Jiangjie.Bai
79e92fa46b Merge pull request #595 from jumpserver/dev
Dev
2021-01-20 19:27:59 +08:00
fit2bot
f19c863440 feat: 添加管理用户列表 (#588)
* feat: 添加管理用户列表

* fix: 整理管理用户详情页面

* fix: 整理管理用户详情页面

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 19:07:00 +08:00
fit2bot
fff8b79a45 fix: 修正测试产生的Bugs (#593)
* fix: 调整登录复核工单字段

* fix: 移除创建网关的详情链接展示

* fix: 调整我发起的工单的API字段

* fix: 移除工单申请表单的非必选字段

* fix: 修正创建密码匣子时表单错误

* fix: 调整申请应用时推荐应用对应字段

* fix: 移除旧版本请求

* fix: 优化内部变量写法

* fix: 干掉旧刷新方法

* fix: 优化写法

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 17:32:52 +08:00
fit2bot
cf810b3d3e revert: 回滚表格自定义列功能 (#594)
* revert: 回滚表格自定义列功能

* fix: 优化写法

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 17:12:04 +08:00
fit2bot
f58e37a76a feat: 添加批量测试资产可连接性功能 (#590)
* feat: 添加批量测试资产可连接性功能

* feat: 添加批量测试资产可连接性功能

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 14:56:03 +08:00
ibuler
5889e20aae fix(perms): 修复应用详情中移除系统用户的错误 2021-01-20 14:39:00 +08:00
fit2bot
7caa2c8264 fix: 禁止用户更新用户名与登录名相同选项 (#591)
* fix: 禁止用户更新用户名与登录名相同选项

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-20 11:27:29 +08:00
Jiangjie.Bai
865388dedc Merge pull request #589 from jumpserver/dev
Dev
2021-01-19 19:52:52 +08:00
Orange
35c1077eed fix: 修复新版工单的Bugs 2021-01-19 19:50:35 +08:00
Orange
487e199995 fix: 修复资产列表无克隆创建的bug 2021-01-19 03:31:23 -06:00
fit2bot
f584e96675 fix: 暂时移除应用的克隆创建 (#587)
Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-19 17:29:17 +08:00
fit2bot
1f4f1d3712 fix: 调整Session过期检查 10mins -> 30s (#586)
* fix: 调整Session过期检查 10mins -> 30s

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-19 17:23:02 +08:00
fit2bot
08facb1eda fix: 添加WS链接类型 (#585)
* fix: 添加WS链接类型


Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-19 17:12:52 +08:00
Jiangjie.Bai
34cb9424d4 Merge pull request #582 from jumpserver/dev
Dev
2021-01-17 17:59:42 +08:00
fit2bot
36767cd265 feat: 添加组织详情统计 (#580)
* feat: 添加组织详情统计

* feat: 添加组织详情统计

Co-authored-by: Orange <orangemtony@gmail.com>
2021-01-13 20:48:25 +08:00
Orange
86b9fc8f5a fix: 修复工单详情问题 2021-01-13 19:05:50 +08:00
Orange
49054e5dc0 fix: 修复工单详情问题 2021-01-13 19:05:50 +08:00
Orange
bac7cef23c fix: 修复工单详情问题 2021-01-13 19:05:50 +08:00
Orange
4013ea6212 fix: 修复表单生成问题 2021-01-13 18:51:13 +08:00
Orange
37153ebe1d fix: 修复用户界面加载问题 2021-01-13 18:41:51 +08:00
Orange
da35d9be25 修改版权时间 2021-01-12 17:16:33 +08:00
Orange
c3c24b0ad1 feat: 创建资产授权时按协议过滤资产 2021-01-12 17:16:17 +08:00
Orange
98da517724 fix: 移除命令过滤规则的克隆功能 2021-01-12 17:16:04 +08:00
Orange
f002c7f917 feat: 优化重构工单 2021-01-12 15:07:42 +08:00
Orange
0d4e4324ce feat: 重构申请工单系统 2021-01-12 15:07:42 +08:00
Orange
fdeab46970 feat: 重构申请工单系统 2021-01-12 15:07:42 +08:00
Orange
5acbdd5679 feat: 修改录像存储和会话存储接口 2021-01-12 15:07:25 +08:00
Orange
3a64120241 fix: 移除多余的Console.log 2021-01-12 15:04:31 +08:00
Orange
81d1cbf3a1 perf: 修复表格生成Error并记录页码数据 2021-01-12 15:04:31 +08:00
Orange
cec17bbef8 Merge pull request #537 from jumpserver/pr@dev@feat_custom_col_list
feat: 允许用户自定义表格列显示功能
2021-01-12 02:10:29 +08:00
Orange
fbc3373e1b Merge branch 'dev' into pr@dev@feat_custom_col_list 2021-01-12 02:10:02 +08:00
老广
b4a935ab15 Merge pull request #563 from jumpserver/pr@dev@fix_test_storage_notify
fix: 统一测试存储时的命令提醒
2021-01-06 14:33:14 +08:00
老广
7fbff42067 Merge pull request #560 from jumpserver/pr@dev@fix_remove_unuse_func
fix: 移除未使用的函数
2021-01-06 14:08:36 +08:00
老广
5077fec5a8 Merge pull request #564 from jumpserver/pr@dev@fix_create_ticeket
fix: 修复创建工单提示信息
2021-01-06 11:20:27 +08:00
老广
c4619af96f Merge pull request #566 from jumpserver/pr@dev@fix_tree_rightclick
fix: 修复资产树超过屏幕,右键菜单显示不全问题
2021-01-06 11:03:30 +08:00
Orange
025d0abeae fix: 修复资产树超过屏幕,右键菜单显示不全问题 2021-01-05 16:45:22 +08:00
Orange
5735a591ba feat: 允许用户自定义表格列显示功能 2020-12-30 20:08:58 +08:00
Orange
3b664ee1dc feat: 允许用户自定义表格列显示功能 2020-12-30 19:59:23 +08:00
Orange
a3f6de330e fix: 修复创建工单提示信息 2020-12-25 11:15:14 +08:00
Orange
cbc67a5a4c fix: 统一测试存储时的命令提醒 2020-12-23 15:53:44 +08:00
Orange
dec0593907 fix: 移除未使用的函数 2020-12-21 18:23:23 +08:00
Orange
1ed432b1e2 feat: 添加资产编号字段 2020-12-21 14:32:24 +08:00
Jiangjie.Bai
b65664f9c4 Merge pull request #557 from jumpserver/dev
Merge Dev
2020-12-17 18:13:49 +08:00
Orange
f64def0bec fix: 修改箭头颜色 2020-12-17 18:13:09 +08:00
Orange
06f6202bc4 fix: Filter使用后端搜索 2020-12-17 18:13:09 +08:00
Jiangjie.Bai
6fa7800d6b Merge pull request #554 from jumpserver/dev
Chore: Merge Dev
2020-12-17 15:14:11 +08:00
ibuler
54aa252c20 fix(some): 修复没有clone的列表 2020-12-17 15:12:53 +08:00
ibuler
c083f6c4a4 fix(clone): 修复一下clone丢失的问题 2020-12-17 15:12:53 +08:00
Orange
73bb854ebb fix: 去除批量移除, 去除简单写法 2020-12-17 15:03:28 +08:00
Orange
d6f9df277e fix: 修复样式异常和Bug 2020-12-17 15:03:28 +08:00
Jiangjie.Bai
ba78e33f89 Merge pull request #551 from jumpserver/dev
chore: Merge master from dev
2020-12-16 18:42:40 +08:00
Orange
09ef15cff0 fix: 修复工单提示报错 2020-12-16 17:54:38 +08:00
Orange
62d520e625 fix: 修复工单提示报错 2020-12-16 17:54:38 +08:00
Orange
f07a857813 fix: 修复命令过滤器添加系统用户问题 2020-12-16 17:54:38 +08:00
Orange
2699d5e8eb fix: 禁用命令存储文档类型修改 2020-12-16 17:54:38 +08:00
Orange
fb398ca3e4 调整页面errorMessage 2020-12-16 17:54:38 +08:00
Orange
3230c37318 fix: 调整页面保存设置, 优化页面Bug 2020-12-16 17:54:38 +08:00
Jiangjie.Bai
bc258a7ff8 Merge pull request #549 from jumpserver/dev
chore: Merge master from dev
2020-12-15 20:33:14 +08:00
Orange
64d610e282 fix: 修改系统监控页面 2020-12-15 20:32:04 +08:00
Orange
4d95461b5c fix: 添加组件负载状态 2020-12-15 20:32:04 +08:00
Orange
f364c8fdf9 fix: 批量修复2.6版本测试产生的Bug 2020-12-15 20:32:04 +08:00
Orange
88dc2d9271 Merge pull request #547 from jumpserver/dev
fix: 批量修复2.6版本测试产生的Bug
2020-12-14 19:05:38 +08:00
Orange
3aced25da4 fix: 批量修复2.6版本测试产生的Bug 2020-12-14 19:00:16 +08:00
老广
fcf142b696 Merge pull request #544 from jumpserver/dev
Dev
2020-12-11 19:33:54 +08:00
fit2bot
2123037897 fix: 添加组件总量显示 (#543)
Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-11 19:30:58 +08:00
Orange
f5bc2842ec fix: 修复日期显示问题及用户应用授权更新路由问题 2020-12-11 19:28:26 +08:00
Orange
2c95e5f10b fix: 移除中断心跳间隔设置并添加认证方式字段 2020-12-11 19:28:08 +08:00
Jiangjie.Bai
b3ff9c5bcb Merge pull request #539 from jumpserver/dev
chore(merge): dev 合并到 master
2020-12-10 23:47:30 +08:00
Orange
905e5e00b1 fix: 去掉多余JS引用 2020-12-10 23:43:43 +08:00
Orange
81db3d86fa feat: 添加系统监控页面 2020-12-10 23:43:43 +08:00
Orange
ea15515264 feat: 系统用户详情页面增加资产分页面,增加指定资产推送功能 2020-12-10 20:54:14 +08:00
fit2bot
4048a000c7 feat: 添加表格过滤字段选项 (#527)
* feat: 添加表格过滤字段选项

* fix: 如果已有Filter的情况下去掉过滤

Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-10 17:52:12 +08:00
xinwen
a60693c41c perf(asset): 资产树,右击增加计算节点数量的菜单,可以让后台去计算 #527 2020-12-10 17:51:10 +08:00
Orange
06d6c54db8 feat: 允许用户自定义表格列显示功能 2020-12-10 14:26:17 +08:00
fit2bot
57d5c893d3 fix: 修复自动生成表格字段冲突问题 (#536)
Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-09 16:32:52 +08:00
fit2bot
435ce24c75 fix: 修复删除source字段引起的无法更新密码的问题 (#534)
Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-09 16:21:59 +08:00
Orange
4a757bb6bc fix: 不允许用户修改source字段 2020-12-08 20:56:11 +08:00
fit2bot
32fa4f0b11 feat: 资产允许编辑更多信息 (#530)
* feat: 资产允许编辑更多信息

* feat: 资产允许编辑更多资产信息

* feat: 资产允许编辑更多资产信息

* fix: 修改组件名称

Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-08 20:25:54 +08:00
Orange
9f12e1aa18 perf: 禁用应用的导入导出 2020-12-08 11:12:00 +08:00
jym503558564
6165709747 perf(assetBatchOperation): 资产模块添加批量删除操作 2020-12-08 11:10:15 +08:00
fit2bot
0ad1eef196 feat: 添加xlsx格式支持 (#526)
* feat: 添加xlsx格式支持

* feat: 添加xlsx格式支持

* feat: 添加xlsx格式支持

* feat: 添加xlsx格式支持

Co-authored-by: Orange <orangemtony@gmail.com>
2020-12-07 15:22:56 +08:00
Orange
d2d07555b5 fix: 修复批量更新终端存储时附带的保存并继续添加 2020-12-07 14:06:48 +08:00
Orange
7f60224c6d fix: 修复会话时间显示问题 2020-12-07 14:04:26 +08:00
Orange
bbf502c85d fix: 调整select2组件默认长度 2020-12-07 14:03:30 +08:00
Orange
e6aaa52506 perf: 恢复Web终端入口 2020-11-26 12:38:28 +08:00
ibuler
70affacfde fix(list): 修复列表克隆的bug 2020-11-26 12:37:28 +08:00
Orange
40a8da5e58 fix: 修复批量更新组件的问题 2020-11-23 14:00:34 +08:00
Orange
24266bb929 fix: 修复批量批量更新组件的问题 2020-11-22 16:53:01 +08:00
107 changed files with 2192 additions and 616 deletions

View File

@@ -46,7 +46,7 @@ server {
## License & Copyright
Copyright (c) 2014-2020 飞致云 FIT2CLOUD, All rights reserved.
Copyright (c) 2014-2021 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

View File

@@ -2,7 +2,7 @@ import request from '@/utils/request'
export function getTicketOpenCount(assign) {
return request({
url: `/api/v1/tickets/tickets/?assign=${assign}&status=open&offset=0&limit=15&display=1&draw=1/`,
url: `/api/v1/tickets/tickets/?assignees__id=${assign}&status=open&offset=0&limit=15&display=1&draw=1/`,
method: 'get'
})
}

View File

@@ -1,7 +1,7 @@
<template>
<div :class="grouped ? 'el-button-group' : ''">
<el-button v-for="item in iActions" :key="item.name" :size="size" v-bind="item" @click="handleClick(item.name)">
<el-tooltip v-if="['actionExport', 'actionImport', 'actionRefresh'].indexOf(item.name) !== -1" effect="dark" :content="item.tip" placement="top">
<el-tooltip v-if="item.tip" effect="dark" :content="item.tip" placement="top">
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
</el-tooltip>
<span v-else>

View File

@@ -62,9 +62,11 @@ export default {
switch (type) {
case 'choice':
type = 'radio-group'
field.options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (!fieldMeta.read_only) {
field.options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
}
break
case 'datetime':
type = 'date-picker'
@@ -91,12 +93,14 @@ export default {
break
}
if (type === 'radio-group') {
const options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (options.length > 4) {
type = 'select'
field.el.filterable = true
if (!fieldMeta.read_only) {
const options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (options.length > 4) {
type = 'select'
field.el.filterable = true
}
}
}
field.type = type
@@ -188,9 +192,16 @@ export default {
if (!field) {
return
}
if (field.attrs.error === error) {
error += '.'
if (typeof error === 'object') {
const str = error
error = ''
Object.keys(str).forEach(key => {
error += `${parseInt(key) + 1}.${str[key][0]} `
})
}
// if (field.attrs.error === error) {
// error += '.'
// }
field.attrs.error = error
}
}

View File

@@ -1,5 +1,5 @@
<template>
<DataTable v-if="!loading" ref="dataTable" v-loading="loading" :config="iConfig" v-bind="$attrs" v-on="$listeners" />
<DataTable v-if="!loading" ref="dataTable" v-loading="loading" :config="iConfig" v-bind="$attrs" v-on="$listeners" @filter-change="filterChange" />
</template>
<script type="text/jsx">
@@ -15,6 +15,10 @@ export default {
config: {
type: Object,
default: () => ({})
},
filterTable: {
type: Function,
default: () => ({})
}
},
data() {
@@ -118,6 +122,37 @@ export default {
}
return col
},
addFilterIfNeed(col) {
if (col.prop) {
const column = this.meta[col.prop] || {}
if (!column.filter) {
return col
}
if (column.type === 'boolean') {
col.filters = [
{ text: this.$t('common.Yes'), value: true },
{ text: this.$t('common.No'), value: false }
]
col.sortable = false
col['column-key'] = col.prop
}
if (column.type === 'choice' && column.choices) {
col.filters = column.choices.map(item => {
if (typeof (item.value) === 'boolean') {
if (item.value) {
return { text: item.display_name, value: 'True' }
} else {
return { text: item.display_name, value: 'False' }
}
}
return { text: item.display_name, value: item.value }
})
col.sortable = false
col['column-key'] = col.prop
}
}
return col
},
generateColumn(name) {
const colMeta = this.meta[name] || {}
const customMeta = this.config.columnsMeta ? this.config.columnsMeta[name] : {}
@@ -127,6 +162,7 @@ export default {
col = this.generateColumnByType(colMeta.type, col)
col = Object.assign(col, customMeta)
col = this.addHelpTipsIfNeed(col)
col = this.addFilterIfNeed(col)
return col
},
generateColumns() {
@@ -142,6 +178,12 @@ export default {
}
config.columns = columns
this.iConfig = config
},
filterChange(filters) {
const key = Object.keys(filters)[0]
const attr = {}
attr[key] = filters[key][0]
this.filterTable(attr)
}
}
}

View File

@@ -157,6 +157,11 @@ export default {
y -= (offset.top + scrollTop) / 3 - 10
x += document.body.scrollLeft
y += document.body.scrollTop + document.documentElement.scrollTop
if (y + $(`#${rMenuID} ul`).height() >= window.innerHeight) {
y -= $(`#${rMenuID} ul`).height()
}
this.rMenu.css({ 'top': y + 'px', 'left': x + 'px', 'visibility': 'visible' })
$(`#${rMenuID} ul`).show()
$('body').bind('mousedown', this.onBodyMouseDown)
@@ -235,10 +240,7 @@ export default {
})
},
refresh: function() {
this.$axios.post(
'/api/v1/assets/nodes/00000000-0000-0000-0000-000000000000/tasks/',
{ action: 'refresh_cache' }
)
},
getSelectedNodes: function() {
return this.zTree.getSelectedNodes()

View File

@@ -11,6 +11,7 @@
v-bind="tableAttrs"
:data="data"
:row-class-name="rowClassName"
v-on="$listeners"
@selection-change="selectStrategy.onSelectionChange"
@select="selectStrategy.onSelect"
@select-all="selectStrategy.onSelectAll($event, canSelect)"
@@ -95,6 +96,9 @@
v-for="col in columns"
:key="col.prop"
:formatter="typeof col.formatter === 'function' ? col.formatter : null"
:filters="col.filters || null"
:filter-multiple="false"
:filter-method="typeof col.filterMethod === 'function' ? col.filterMethod : null"
v-bind="{align: columnsAlign, ...col}"
>
<template v-if="col.formatter && typeof col.formatter !== 'function'" v-slot:default="{row, column, index}">
@@ -1006,7 +1010,7 @@ export default {
},
handleSizeChange(val) {
if (this.size === val) return
this.$emit('sizeChange', val)
this.page = defaultFirstPage
this.size = val
this.getList()

View File

@@ -1,9 +1,17 @@
<template>
<ElDatableTable ref="table" class="el-table" v-bind="tableConfig" @update="onUpdate" v-on="iListeners" />
<ElDatableTable
ref="table"
class="el-table"
v-bind="tableConfig"
@update="onUpdate"
v-on="iListeners"
@sizeChange="handleSizeChange"
/>
</template>
<script>
import { default as ElDatableTable } from './compenents/el-data-table'
import { mapGetters } from 'vuex'
export default {
name: 'DataTable',
@@ -58,7 +66,6 @@ export default {
pageCount: 5,
paginationLayout: 'total, sizes, prev, pager, next',
paginationSizes: [15, 30, 50, 100],
paginationSize: 15,
paginationBackground: true,
transformQuery: query => {
if (query.page && query.size) {
@@ -85,12 +92,17 @@ export default {
},
computed: {
tableConfig() {
const config = Object.assign(this.defaultConfig, this.config)
const tableDefaultConfig = this.defaultConfig
tableDefaultConfig.paginationSize = _.get(this.globalTableConfig, 'paginationSize', 15)
const config = Object.assign(tableDefaultConfig, this.config)
return config
},
iListeners() {
return Object.assign({}, this.$listeners, this.tableConfig.listeners)
}
},
...mapGetters({
'globalTableConfig': 'tableConfig'
})
},
watch: {
config: {
@@ -131,6 +143,14 @@ export default {
this.toggleRowSelection(row, true)
}
}
},
handleSizeChange(val) {
this.$store.commit('table/SET_TABLE_CONFIG',
{
key: 'paginationSize',
value: val
}
)
}
}
}

View File

@@ -1,6 +1,11 @@
<template>
<Dialog v-if="showExportDialog" :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
<el-form label-position="left" style="padding-left: 50px">
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
<el-radio-group v-model="exportTypeOption">
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
<el-radio-group v-model="exportOption">
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
@@ -52,6 +57,7 @@ export default {
return {
showExportDialog: false,
exportOption: 'all',
exportTypeOption: 'csv',
meta: {}
}
},
@@ -92,6 +98,20 @@ export default {
can: this.tableHasQuery && this.canExportFiltered
}
]
},
exportTypeOptions() {
return [
{
label: 'CSV',
value: 'csv',
can: true
},
{
label: 'Excel',
value: 'xlsx',
can: true
}
]
}
},
mounted() {
@@ -123,7 +143,7 @@ export default {
// delete query['limit']
// delete query['offset']
}
query['format'] = 'csv'
query['format'] = this.exportTypeOption
const queryStr =
(url.indexOf('?') > -1 ? '&' : '?') +
queryUtil.stringify(query, '=', '&')

View File

@@ -8,6 +8,11 @@
@cancel="handleImportCancel()"
>
<el-form label-position="left" style="padding-left: 50px">
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
<el-radio-group v-model="importTypeOption">
<el-radio v-for="option of importTypeOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('common.Import' )" :label-width="'100px'">
<el-radio v-model="importOption" class="export-item" label="1">{{ this.$t('common.Create') }}</el-radio>
<el-radio v-model="importOption" class="export-item" label="2">{{ this.$t('common.Update') }}</el-radio>
@@ -33,7 +38,7 @@
:before-upload="beforeUpload"
>
<el-button size="mini" type="default">{{ this.$t('common.SelectFile') }}</el-button>
<div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>
<!-- <div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>-->
</el-upload>
</el-form-item>
</el-form>
@@ -71,18 +76,34 @@ export default {
importOption: '1',
isCsv: true,
errorMsg: '',
loadStatus: false
loadStatus: false,
importTypeOption: 'csv'
}
},
computed: {
hasSelected() {
return this.selectedRows.length > 0
},
importTypeOptions() {
return [
{
label: 'CSV',
value: 'csv',
can: true
},
{
label: 'Excel',
value: 'xlsx',
can: true
}
]
},
upLoadUrl() {
return this.url
},
downloadImportTempUrl() {
const url = (this.url.indexOf('?') === -1) ? `${this.url}?format=csv&template=import&limit=1` : `${this.url}&format=csv&template=import&limit=1`
const format = this.importTypeOption === 'csv' ? 'format=csv&template=import&limit=1' : 'format=xlsx&template=import&limit=1'
const url = (this.url.indexOf('?') === -1) ? `${this.url}?${format}` : `${this.url}&${format}`
return url
},
uploadHelpTextClass() {
@@ -103,7 +124,7 @@ export default {
this.$axios.put(
this.upLoadUrl,
item.file,
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
).then((data) => {
const msg = this.$t('common.imExport.updateSuccessMsg', { count: data.length })
this.onSuccess(msg)
@@ -117,7 +138,7 @@ export default {
this.$axios.post(
this.upLoadUrl,
item.file,
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
).then((data) => {
const msg = this.$t('common.imExport.createSuccessMsg', { count: data.length })
this.onSuccess(msg)
@@ -176,7 +197,8 @@ export default {
}
const spm = await createSourceIdCache(resources)
const baseUrl = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
const url = `${baseUrl}?format=csv&template=update&spm=` + spm.spm
const format = this.importTypeOption === 'csv' ? '?format=csv&template=update&spm=' : '?format=xlsx&template=update&spm='
const url = `${baseUrl}${format}` + spm.spm
return this.downloadCsv(url)
},
async handleImportConfirm() {
@@ -186,7 +208,12 @@ export default {
this.showImportDialog = false
},
beforeUpload(file) {
this.isCsv = _.endsWith(file.name, 'csv')
this.isCsv = this.importTypeOption === 'csv' ? _.endsWith(file.name, 'csv') : _.endsWith(file.name, 'xlsx')
if (!this.isCsv) {
this.$message.error(
this.$t('common.NeedSpecifiedFile')
)
}
return this.isCsv
}
}

View File

@@ -2,7 +2,7 @@
<div>
<TableAction :table-url="iTableConfig.url" :search-table="search" :date-pick="handleDateChange" v-bind="headerActions" :selected-rows="selectedRows" :reload-table="reloadTable" />
<IBox class="table-content">
<AutoDataTable ref="dataTable" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
<AutoDataTable ref="dataTable" :filter-table="filter" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
</IBox>
</div>
</template>
@@ -13,6 +13,7 @@ import IBox from '../IBox'
import TableAction from './TableAction'
import Emitter from '@/mixins/emitter'
import deepmerge from 'deepmerge'
export default {
name: 'ListTable',
components: {
@@ -41,6 +42,7 @@ export default {
}
},
computed: {
dataTable() {
return this.$refs.dataTable.$refs.dataTable
},
@@ -55,20 +57,9 @@ export default {
}
return true
},
hasCloneAction() {
const hasClone = _.get(this.tableConfig, 'columnsMeta.actions.formatterArgs.hasClone', null)
if (hasClone) {
return true
}
if (this.hasCreateAction && hasClone == null) {
return true
}
return false
},
iTableConfig() {
const config = deepmerge(this.tableConfig, { extraQuery: this.extraQuery })
this.$log.debug('Header actions', this.headerActions)
_.set(config, 'columnsMeta.actions.formatterArgs.hasClone', this.hasCloneAction)
this.$log.debug('ListTable: iTableConfig change', config)
return config
}
@@ -79,12 +70,14 @@ export default {
this.$log.debug('ListTable: found extraQuery change')
},
deep: true
},
tableColConfig: {
handler() {
this.$log.debug('ListTable: found colConfig change')
},
deep: true
}
},
mounted() {
this.$log.debug(this.headerActions)
this.$log.debug(this.iTableConfig)
},
methods: {
handleSelectionChange(val) {
this.selectedRows = val
@@ -95,6 +88,9 @@ export default {
search(attrs) {
return this.dataTable.search(attrs, true)
},
filter(attrs) {
this.$refs.dataTable.$refs.dataTable.search(attrs, true)
},
handleDateChange(attrs) {
this.$set(this.extraQuery, 'date_from', attrs[0].toISOString())
this.$set(this.extraQuery, 'date_to', attrs[1].toISOString())
@@ -117,28 +113,28 @@ export default {
<style lang="scss" scoped>
.table-content {
margin-top: 10px;
.table-content {
margin-top: 10px;
& >>> .el-card__body {
padding: 0;
}
& >>> .el-table__header thead > tr > th {
background-color: white;
}
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
/*background: white;*/
/*}*/
/*& >>> .el-table th, .el-table tr {*/
/*background-color: red;*/
/*!*background-color: #FAFAFA;*!*/
/*}*/
& >>> .el-card__body {
padding: 0;
}
& >>> .el-table__header thead > tr > th {
background-color: white;
}
//修改颜色
// .el-button--text{
// color: #409EFF;
// }
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
/*background: white;*/
/*}*/
/*& >>> .el-table th, .el-table tr {*/
/*background-color: red;*/
/*!*background-color: #FAFAFA;*!*/
/*}*/
}
//修改颜色
// .el-button--text{
// color: #409EFF;
// }
</style>

View File

@@ -20,6 +20,7 @@
},
"appPath": "应用路径",
"appType": "应用类型",
"appName": "应用名称",
"asset": "资产",
"database": "数据库",
"host": "主机",
@@ -58,8 +59,10 @@
"AdminUserDetail": "管理用户详情",
"AdminUserListHelpMessage": "管理用户是资产(被控服务器)上的 root或拥有 NOPASSWD: ALL sudo 权限的用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。\n",
"Asset": "资产",
"HardwareInfo": "硬件信息",
"AssetDetail": "资产详情",
"AssetList": "资产列表",
"ReplaceNodeAssetsAdminUser":"替换节点资产的管理员",
"AssetListHelpMessage": "左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产\n",
"TestGatewayTestConnection":"测试连接网关",
"TestGatewayHelpMessage": "如果使用了nat端口映射请设置为ssh真实监听的端口",
@@ -68,6 +71,7 @@
"AssetUserList": "资产用户列表",
"Assets": "资产",
"Auth": "认证",
"AccountList": "账号列表",
"AutoGenerateKey": "自动生成密钥",
"AutoPush": "自动推送",
"BasePlatform": "基础平台",
@@ -113,6 +117,7 @@
"OnlyLatestVersion": "仅最新版本",
"Os": "操作系统",
"Other": "其它",
"Hardware": "硬件信息",
"Password": "密码",
"PasswordWithoutSpecialCharHelpText": "不能包含特殊字符",
"Pending": "等待",
@@ -182,17 +187,25 @@
"Action": "动作",
"RequestTickets": "申请工单",
"Actions": "操作",
"CustomCol":"自定义列表字段",
"Activate": "激活",
"NeedSpecifiedFile": "需上传指定格式文件",
"TestPortErrorMsg":"端口错误,请重新输入",
"Active": "激活中",
"actionsTips":"剪切板权限控制目前仅支持 RDP/VNC 协议的连接",
"TableColSettingInfo": "请选择您想显示的列表详细信息。",
"Add": "添加",
"UpdateAssetDetail": "配置更多信息",
"AddSuccessMsg": "添加成功",
"Auth": "认证",
"PushSelected":"推送所选",
"BadRequestErrorMsg": "请求错误,请检查填写内容",
"BadRoleErrorMsg": "请求错误,无该操作权限",
"BadConflictErrorMsg": "正在刷新中,请稍后再试",
"Basic": "基本",
"PleaseAgreeToTheTerms": "请同意条款",
"BasicInfo": "基本信息",
"ApplyInfo": "申请信息",
"Cancel": "取消",
"Close": "关闭",
"Command filter": "命令过滤器",
@@ -289,6 +302,7 @@
"onlyCSVFilesTips": "仅支持csv文件导入",
"updateSuccessMsg": "导入更新成功,总共:{count}"
},
"fileType": "文件类型",
"isValid": "有效",
"nav": {
"APIKey": "API Key",
@@ -323,7 +337,8 @@
"NUMBER_REQUIRED": "须包含数字",
"SPECIAL_CHAR_REQUIRED": "须包含特殊字符",
"MIN_LENGTH_ERROR": "密码最小长度 {0} 位"
}
},
"lastCannotBeDeleteMsg": "最后一项,不能被删除"
},
"dashboard": {
"ActiveAsset": "近期被登录过",
@@ -414,8 +429,10 @@
"SystemUser": "系统用户",
"User": "用户",
"UserGroups": "用户组",
"PermName":"授权名称",
"DatabaseAppPermission": "数据库授权",
"RemoteAppPermission": "远程应用授权",
"addApplicationToThisPermission": "添加应用",
"KubernetesAppPermission": "Kubernetes授权",
"addAssetToThisPermission": "添加资产",
"addDatabaseAppToThisPermission": "添加数据库应用",
@@ -607,6 +624,10 @@
"name": "名称",
"protocol": "协议",
"region": "地域",
"sessionActiveCount": "在线会话数量",
"systemCpuLoad": "CPU负载",
"systemDiskUsedPercent": "硬盘使用率",
"systemMemoryUsedPercent": "内存使用率",
"remoteAddr": "远端地址",
"replay": "回放",
"replaySession": "回放会话",
@@ -779,6 +800,8 @@
"Assignee": "处理人",
"Assignees": "待处理人",
"Close": "关闭",
"OpenStatus":"开启",
"CloseStatus":"关闭",
"Comment": "备注",
"MyTickets": "我发起的",
"RequestPerm":"授权申请",
@@ -787,10 +810,14 @@
"reply": "回复",
"status": "状态",
"title": "标题",
"action": "动作",
"IPGroup": "IP 组",
"type": "类型",
"user": "用户",
"Status": "状态",
"Open": "待处理",
"OrgName":"组织名称",
"AssignedInfo":"审批信息",
"OpenTicket": "创建工单",
"HandleTicket": "处理工单",
"FinishedTicket": "完成工单",
@@ -799,6 +826,7 @@
"Asset": "资产",
"SystemUser": "系统用户",
"RequestAssetPerm": "申请资产授权",
"RequestApplicationPerm": "申请应用授权",
"Applicant": "申请人",
"Pending": "待处理",
"Approved": "已同意",
@@ -806,7 +834,8 @@
"Closed": "已完成",
"helpText": {
"ips": "请输入逗号分割的IP地址组",
"fuzzySearch": "支持模糊搜索"
"fuzzySearch": "支持模糊搜索",
"application": "请输入逗号分割的应用名称组"
}
},
"tree": {
@@ -817,6 +846,7 @@
"RenameNode": "重命名节点",
"ShowAssetAllChildrenNode": "显示所有子节点资产",
"ShowAssetOnlyCurrentNode": "仅显示当前节点资产",
"CheckAssetsAmount": "校对资产数量",
"ShowNodeInfo": "显示节点详情",
"TestNodeAssetConnectivity": "测试资产节点可连接性",
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息"
@@ -1026,6 +1056,13 @@
"ImportLicenseTip": "请导入许可证",
"InterfaceSettings": "界面设置",
"License": "许可证",
"SystemMonitor": "系统监控",
"ServiceRatio": "组件负载统计",
"LoadStatus":"组件状态",
"NormalLoad":"正常",
"HighLoad":"较高",
"CriticalLoad":"严重",
"Offline": "离线",
"LicenseDetail": "许可证详情",
"LicenseFile": "许可证文件",
"NoLicense": "暂无许可证",
@@ -1039,6 +1076,14 @@
"DeleteOrgTitle": "请确保组织内的以下信息已删除",
"DeleteOrgMsg": "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权规则",
"OrgRole": "组织角色",
"users_amount": "用户数量",
"groups_amount": "用户组数量",
"assets_amount": "资产数量",
"admin_users_amount": "管理用户数量",
"system_users_amount": "系统用户数量",
"applications_amount": "应用数量",
"asset_perms_amount": "资产授权数量",
"app_perms_amount": "应用授权数量",
"CreateOrgMsg": "请去组织详情内添加用户",
"AddOrgMembers": "添加组织成员"
},

View File

@@ -20,6 +20,7 @@
},
"appPath": "App path",
"appType": "App type",
"appName": "App name",
"asset": "Asset",
"database": "Database",
"host": "Host",
@@ -55,9 +56,13 @@
"Action": "Action",
"ActiveSelected": "Active selected",
"AdminUser": "Admin user",
"ReplaceNodeAssetsAdminUser":"Replace node assets admin user with this",
"AdminUserDetail": "Admin user detail",
"AdminUserListHelpMessage": "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, JumpServer users of the system using the user to `push system user`, `get assets hardware information`, etc.\n",
"Asset": "Asset",
"HardwareInfo": "Hardware info",
"Hardware": "Hardware",
"AccountList": "Account list",
"AssetDetail": "Asset detail",
"AssetList": "Asset list",
"AssetListHelpMessage": "The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node\n",
@@ -180,12 +185,19 @@
"common": {
"Nothing": "Nothing",
"Action": "Action",
"CustomCol":"Custom table col",
"RequestTickets": "Request tickets",
"Actions": "Actions",
"NeedSpecifiedFile": "Required to upload the specified format file",
"TestPortErrorMsg":"Port Error, please check",
"Activate": "Activate",
"actionsTips":"Clipboard's copy and paste control only support RDP/VNC protocol.",
"Active": "Active",
"TableColSettingInfo": "Please select the list details you want to display",
"Add": "Add",
"PleaseAgreeToTheTerms": "Please agree to the terms",
"PushSelected":"Push selected",
"UpdateAssetDetail": "Update more detail",
"AddSuccessMsg": "Add success",
"Auth": "Authorization",
"BadRequestErrorMsg": "Bad request, please check again",
@@ -193,6 +205,7 @@
"BadConflictErrorMsg": "Refreshing, please try again later",
"Basic": "Basic",
"BasicInfo": "Basic info",
"ApplyInfo": "Apply info",
"Cancel": "Cancel",
"Close": "Close",
"Command filter": "Command filter",
@@ -276,6 +289,7 @@
"disableSelected": "Disable selected",
"fieldRequiredError": "This field is required",
"getErrorMsg": "Get failed",
"fileType": "File type",
"imExport": {
"ExportAll": "Export all",
"ExportOnlyFiltered": "Export only filtered",
@@ -321,7 +335,8 @@
"NUMBER_REQUIRED": "Number required",
"SPECIAL_CHAR_REQUIRED": "Special char required",
"MIN_LENGTH_ERROR": "Password minimum length {}"
}
},
"lastCannotBeDeleteMsg": "The last one can't be delete"
},
"dashboard": {
"ActiveAsset": "Asset active",
@@ -418,12 +433,14 @@
"addAssetToThisPermission": "Add asset to this permission",
"addDatabaseAppToThisPermission": "Add DatabaseApp to this permission",
"addK8sAppToThisPermission": "Add KubernetesApp to this permission",
"addApplicationToThisPermission": "Add Application to this permission",
"addNodeToThisPermission": "Add node to this permission",
"addRemoteAppToThisPermission": "Add RemoteApp to this permission",
"addSystemUserToThisPermission": "System user",
"addUserGroupToThisPermission": "Add user group to this permission",
"addUserToThisPermission": "Add user to this permission",
"all": "All",
"PermName":"Perm name",
"assetAndNode": "Assets and node",
"assetCount": "Asset count",
"connect": "Connect",
@@ -588,6 +605,10 @@
"duration": "Duration",
"endPoint": "Endpoint",
"endpointSuffix": "Endpoint suffix",
"sessionActiveCount": "session active count",
"systemCpuLoad": "cpu load",
"systemDiskUsedPercent": "disk used percent",
"systemMemoryUsedPercent": "memory used percent",
"go": "Go",
"goto": "Goto",
"hosts": "Hosts",
@@ -766,13 +787,18 @@
"AssignedMe": "Assigned me",
"Assignee": "Assignee",
"RequestPerm":"Request Perm",
"AssignedInfo":"Assigned Info",
"OpenTicket": "Open Ticket",
"HandleTicket": "Handle Ticket",
"FinishedTicket": "Finished Ticket",
"Assignees": "Assignees",
"Close": "Close",
"OpenStatus":"Open",
"CloseStatus":"Close",
"Comment": "Comment",
"MyTickets": "My tickets",
"action": "Action",
"IPGroup": "IP 组",
"Reject": "Reject",
"date": "Date",
"reply": "Reply",
@@ -782,19 +808,22 @@
"user": "User",
"Status": "Status",
"Open": "Open",
"OrgName":"Org name",
"IP": "IP",
"Hostname": "Hostname",
"Asset": "Asset",
"SystemUser": "System user",
"Applicant": "Applicant",
"RequestAssetPerm": "Request asset perm",
"RequestApplicationPerm": "Request application perm",
"Pending": "Open",
"Approved": "Approved",
"Rejected": "Rejected",
"Closed": "Closed",
"helpText": {
"ips": "Enter the IP address group, separated by commas",
"fuzzySearch": "Support for fuzzy search"
"fuzzySearch": "Support for fuzzy search",
"application": "Enter the application group, separated by commas"
}
},
"tree": {
@@ -805,6 +834,7 @@
"RenameNode": "Rename node",
"ShowAssetAllChildrenNode": "Show asset all children node",
"ShowAssetOnlyCurrentNode": "Show asset only current node",
"CheckAssetsAmount": "Check assets amount",
"ShowNodeInfo": "Show node information",
"TestNodeAssetConnectivity": "Test node asset connectivity",
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information"
@@ -1015,6 +1045,13 @@
"InterfaceSettings": "Interface setting",
"License": "License",
"LicenseDetail": "License detail",
"SystemMonitor": "System Monitor",
"ServiceRatio": "Service ratio",
"LoadStatus":"Status",
"NormalLoad":"Normal",
"HighLoad":"High",
"Offline": "Offline",
"CriticalLoad":"Critical",
"LicenseFile": "License file",
"NoLicense": "No License",
"Node": "Node",
@@ -1028,7 +1065,15 @@
"DeleteOrgMsg":"User list、User group、Asset list、Domain list、Admin user、System user、Labels、Asset permission",
"OrgRole": "Org role",
"CreateOrgMsg": "Please go to Organization Details to add users",
"AddOrgMembers": "Add organization members"
"AddOrgMembers": "Add organization members",
"users_amount": "Users amount",
"groups_amount": "Groups amount",
"assets_amount": "Assets amount",
"admin_users_amount": "Admin users amount",
"system_users_amount": "System users amount",
"applications_amount": "Applications amount",
"asset_perms_amount": "Asset perms amount",
"app_perms_amount": "App perms amount"
},
"RestoreButton": "Restore Default",
"SubscriptionID": "Subscription ID",

View File

@@ -4,7 +4,7 @@
Version <strong> dev </strong> <span v-if="!publicSettings.XPACK_LICENSE_IS_VALID"> GPLv2. </span>
</div>
<div v-if="!publicSettings.XPACK_LICENSE_IS_VALID" style="padding-left:20px;">
<strong>Copyright</strong> FIT2CLOUD 飞致云 © 2014-2020
<strong>Copyright</strong> FIT2CLOUD 飞致云 © 2014-2021
</div>
</div>
</template>

View File

@@ -147,23 +147,27 @@ export default {
this.$emit('submitSuccess', res)
const h = this.$createElement
this.$log.debug('router is: ', detailRoute)
this.$message({
message: h('p', null, [
h('el-link', {
on: {
click: () => this.$router.push(detailRoute)
},
style: { 'vertical-align': 'top' }
}, msgLinkName),
h('span', { style: {
'padding-left': '5px',
'height': '18px',
'line-height': '18px',
'font-size': '13.5px',
'font-weight': ' 400' }}, msg)
]),
type: 'success'
})
if (this.hasDetailInMsg) {
this.$message({
message: h('p', null, [
h('el-link', {
on: {
click: () => this.$router.push(detailRoute)
},
style: { 'vertical-align': 'top' }
}, msgLinkName),
h('span', { style: {
'padding-left': '5px',
'height': '18px',
'line-height': '18px',
'font-size': '13.5px',
'font-weight': ' 400' }}, msg)
]),
type: 'success'
})
} else {
this.$message.success(msg)
}
if (!addContinue) {
setTimeout(() => this.$router.push(route), 100)
}
@@ -189,6 +193,10 @@ export default {
hasSaveContinue: {
type: Boolean,
default: null
},
hasDetailInMsg: {
type: Boolean,
default: true
}
},
data() {

View File

@@ -9,6 +9,7 @@
<script>
import { getTicketOpenCount } from '@/api/ticket'
import { mapGetters } from 'vuex'
export default {
name: 'WebTerminal',
@@ -17,13 +18,17 @@ export default {
assignedTicketCount: 0
}
},
computed: {
...mapGetters([
'currentUser'
])
},
created() {
this.ticketsOpenedCount()
},
methods: {
ticketsOpenedCount() {
getTicketOpenCount(1).then(data => {
getTicketOpenCount(this.currentUser.id).then(data => {
this.assignedTicketCount = data.count
})
},

View File

@@ -10,7 +10,14 @@
<div class="header-item">
<Language />
</div>
<div v-if="publicSettings.TICKETS_ENABLED&&publicSettings.XPACK_LICENSE_IS_VALID" class="header-item">
<div
v-if="
publicSettings.TICKETS_ENABLED
&& publicSettings.XPACK_LICENSE_IS_VALID
&& !isOrgAuditor
"
class="header-item"
>
<Tickets />
</div>
<div class="header-item">
@@ -31,6 +38,7 @@ import Help from './Help'
import Language from './Language'
import WebTerminal from './WebTerminal'
import Tickets from './Tickets'
import rolc from '@/utils/role'
export default {
components: {
@@ -48,8 +56,11 @@ export default {
},
computed: {
...mapGetters([
'sidebar', 'publicSettings'
])
'sidebar', 'publicSettings', 'currentOrgRoles'
]),
isOrgAuditor() {
return rolc.getRolesDisplay(this.currentOrgRoles).includes('OrgAuditor') || rolc.getRolesDisplay(this.currentOrgRoles).includes('Auditor')
}
},
methods: {
toggleSideBar() {

View File

@@ -24,6 +24,10 @@ export default {
'currentUser'
]),
isExpire() {
// 用户来源不是Local时不显示密码过期提示
if (this.currentUser.source !== 'local') {
return false
}
const intervalTime = this.getIntervalDays(this.currentUser.date_password_last_updated)
const securityPasswordExpirationTime = this.publicSettings.SECURITY_PASSWORD_EXPIRATION_TIME
if (intervalTime >= securityPasswordExpirationTime) {

View File

@@ -34,6 +34,13 @@ export default [
component: () => import('@/views/assets/Asset/AssetCreateUpdate.vue'),
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/assets/assets' },
hidden: true
},
{
path: 'detail/:id/update',
name: 'AssetMoreInformationEdit',
component: () => import('@/views/assets/Asset/AssetMoreInformationEdit.vue'),
meta: { title: i18n.t('common.UpdateAssetDetail'), activeMenu: '/assets/assets' },
hidden: true
}
]
},

View File

@@ -45,5 +45,12 @@ export default [
name: 'CeleryTaskLog',
hidden: true,
meta: { title: i18n.t('route.CeleryTaskLog'), roles: ['SuperAdmin', 'Admin', 'Auditor', 'User'] }
},
{
path: '/ops/task/task/:id/log/',
component: () => import('@/views/ops/CeleryTaskLog'),
name: 'TaskLog',
hidden: true,
meta: { title: i18n.t('route.CeleryTaskLog'), roles: ['SuperAdmin', 'Admin', 'Auditor', 'User'] }
}
]

View File

@@ -56,7 +56,7 @@ const ApplicationPermissionRoutes = [
component: () => import('@/views/perms/ApplicationPermission/ApplicationPermissionDetail/index'),
name: 'ApplicationPermissionDetail',
hidden: true,
meta: { title: i18n.t('route.ApplicationPermissionDetail'), activeMenu: '/perms/remote-app-permissions' }
meta: { title: i18n.t('route.ApplicationPermissionDetail'), activeMenu: '/perms/app-permissions' }
},
{
path: ':id/update',

View File

@@ -27,5 +27,19 @@ export default [
component: () => import('@/views/tickets/RequestAssetPerm/Detail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
hidden: true
},
{
path: 'tickets/request-application-perm/create',
name: 'RequestApplicationPermTicketCreateUpdate',
component: () => import('@/views/tickets/RequestApplicationPerm/RequestApplicationPermTicketCreateUpdate'),
meta: { title: i18n.t('route.TicketCreate'), activeMenu: '/tickets/tickets' },
hidden: true
},
{
path: 'tickets/request-application-perm/:id',
name: 'AppsTicketDetail',
component: () => import('@/views/tickets/RequestApplicationPerm/Detail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets' },
hidden: true
}
]

View File

@@ -103,6 +103,20 @@ export default [
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets', permissions: [rolec.PERM_USE] },
hidden: true
},
{
path: 'tickets/request-application-perm/create',
name: 'RequestApplicationPermTicketCreateUpdate',
component: () => import('@/views/tickets/RequestApplicationPerm/RequestApplicationPermTicketCreateUpdate'),
meta: { title: i18n.t('route.TicketCreate'), activeMenu: '/tickets/tickets', permissions: [rolec.PERM_USE] },
hidden: true
},
{
path: 'tickets/request-application-perm/:id',
name: 'AppsTicketDetail',
component: () => import('@/views/tickets/RequestApplicationPerm/Detail/index'),
meta: { title: i18n.t('route.TicketDetail'), activeMenu: '/tickets/tickets', permissions: [rolec.PERM_USE] },
hidden: true
},
{
path: 'tickets/:id',
name: 'TicketDetail',
@@ -115,7 +129,6 @@ export default [
{
path: `external-luna`,
component: Layout,
hidden: true,
meta: {
permissions: [rolec.PERM_USE]
},

View File

@@ -13,6 +13,7 @@ const getters = {
currentOrgRoles: state => state.users.roles,
currentOrgPerms: state => state.users.perms,
MFAVerifyAt: state => state.users.MFAVerifyAt,
MFA_TTl: state => state.settings.publicSettings.SECURITY_MFA_VERIFY_TTL
MFA_TTl: state => state.settings.publicSettings.SECURITY_MFA_VERIFY_TTL,
tableConfig: state => state.table.tableConfig
}
export default getters

View File

@@ -0,0 +1,28 @@
import VueCookie from 'vue-cookie'
import Vue from 'vue'
function getTableConfigfromCookie() {
return VueCookie.get('tableConfig') ? JSON.parse(VueCookie.get('tableConfig')) : {}
}
const state = {
tableConfig: getTableConfigfromCookie()
}
const mutations = {
SET_TABLE_CONFIG: (state, tableConfig) => {
Vue.set(state.tableConfig, tableConfig.key, tableConfig.value)
VueCookie.set('tableConfig', JSON.stringify(state.tableConfig), 14)
}
}
const actions = {
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@@ -38,7 +38,6 @@ const mutations = {
state.orgs = orgs
},
MODIFY_ORG: (state, org) => {
// console.log(state.orgs)
state.orgs = state.orgs.map(oldOrg => {
if (oldOrg.id === org.id) {
oldOrg.name = org.name

View File

@@ -153,3 +153,7 @@ input[type=file] {
width: 100%;
table-layout: fixed !important;
}
.el-table__column-filter-trigger i {
color: #888888 !important;
}

View File

@@ -90,6 +90,11 @@ export default {
}
}
},
{
prop: 'platform',
label: this.$t('assets.Platform'),
width: '120px'
},
{
prop: 'comment',
label: this.$t('assets.Comment'),
@@ -155,10 +160,10 @@ export default {
},
methods: {
refreshAllFavorites() {
this.tableConfig.columns[4].formatterArgs.loading = true
this.tableConfig.columns[this.tableConfig.columns.length - 1].formatterArgs.loading = true
this.$axios.get('/api/v1/assets/favorite-assets/').then(resp => {
this.allFavorites = resp
this.tableConfig.columns[4].formatterArgs.loading = false
this.tableConfig.columns[this.tableConfig.columns.length - 1].formatterArgs.loading = false
})
},
addOrDeleteFavorite(assetId) {

View File

@@ -8,6 +8,8 @@
:update-success-next-route="updateSuccessNextRoute"
:clean-form-value="cleanFormValue"
:get-method="getMethod"
:on-perform-success="onPerformSuccess"
:perform-submit="performSubmit"
/>
</IBox>
</template>
@@ -86,6 +88,17 @@ export default {
methods: {
getMethod() {
return 'put'
},
performSubmit(validValues) {
if (!validValues.terms) {
this.$message.error(this.$t('common.PleaseAgreeToTheTerms'))
return Promise.reject()
}
return this.$axios['put'](this.url, validValues)
},
onPerformSuccess() {
this.$message.success(this.$t('common.updateSuccessMsg'))
setTimeout(() => this.$router.push({ name: 'UserGuide' }), 100)
}
}
}

View File

@@ -1,12 +1,14 @@
<template>
<IBox>
<GenericCreateUpdateForm
ref="GenericCreateUpdateForm"
:fields="fields"
:fields-meta="fieldsMeta"
:initial="object"
:url="url"
:get-method="getMethod"
:more-buttons="moreButtons"
:on-perform-success="onPerformSuccess"
/>
</IBox>
</template>
@@ -65,6 +67,10 @@ export default {
methods: {
getMethod() {
return 'put'
},
onPerformSuccess() {
this.$refs.GenericCreateUpdateForm.$refs.form.$refs.dataForm.resetForm('form')
this.$message.success(this.$t('common.updateSuccessMsg'))
}
}
}

View File

@@ -73,9 +73,15 @@ function cleanDateStr(d) {
}
export function toSafeLocalDateStr(d) {
if (d === '') {
return ''
}
const date = safeDate(d)
// let date_s = date.toLocaleString(getUserLang(), {hour12: false});
const date_s = date.toLocaleString(getUserLang(), { hourCycle: 'h23' })
// const date_s = date.toLocaleString(getUserLang(), { hourCycle: 'h23' })
const date_s =
date.toLocaleDateString(getUserLang(), { hourCycle: 'h23' }) +
' ' +
date.toLocaleTimeString(getUserLang(), { hourCycle: 'h23' })
return date_s
}

View File

@@ -107,7 +107,7 @@ function refreshSessionAgeDelay(response) {
}
timer = setTimeout(function() {
refreshSessionIdAge()
}, 60 * 10 * 1000)
}, 30 * 1000)
}
// response interceptor

View File

@@ -37,6 +37,7 @@ export default {
actions: {
prop: '',
formatterArgs: {
hasClone: false,
performDelete: function({ row, col, cellValue, reload }) {
this.$axios.delete(
`/api/v1/applications/applications/${row.id}/`
@@ -53,6 +54,8 @@ export default {
},
headerActions: {
hasCreate: false,
hasExport: false,
hasImport: false,
hasBulkDelete: false,
createRoute: 'DatabaseAppCreate',
moreActionsTitle: this.$t('common.Create'),

View File

@@ -29,6 +29,7 @@ export default {
actions: {
prop: '',
formatterArgs: {
hasClone: false,
performDelete: function({ row, col, cellValue, reload }) {
this.$axios.delete(
`/api/v1/applications/applications/${row.id}/`
@@ -45,6 +46,8 @@ export default {
},
headerActions: {
hasBulkDelete: false,
hasExport: false,
hasImport: false,
createRoute: 'KubernetesAppCreate'
}
}

View File

@@ -1,5 +1,5 @@
<template>
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
<GenericListPage ref="GenericListTable" :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
</template>
<script type="text/jsx">
@@ -34,6 +34,7 @@ export default {
},
actions: {
formatterArgs: {
hasClone: false,
onUpdate: ({ row }) => {
vm.$router.push({ name: 'RemoteAppUpdate', params: { id: row.id }, query: { type: row.type }})
},
@@ -55,6 +56,8 @@ export default {
hasCreate: false,
hasMoreActions: false,
hasBulkDelete: false,
hasExport: false,
hasImport: false,
// createRoute: 'RemoteAppCreate',
moreActionsTitle: this.$t('common.Create'),
moreActionsType: 'primary',

View File

@@ -1,23 +1,19 @@
<template><div>
<el-row :gutter="20">
<el-col :span="16">
<AssetUserTable :url="assetUserUrl" :has-import="false" />
</el-col>
<el-col :span="8">
<QuickActions type="primary" :actions="quickActions" />
</el-col>
</el-row>
</div>
<template>
<div>
<el-row :gutter="20">
<el-col :span="16">
<AssetUserTable :url="assetUserUrl" :has-import="false" />
</el-col>
</el-row>
</div>
</template>
<script>
import QuickActions from '@/components/QuickActions/index'
import { AssetUserTable } from '@/components'
export default {
name: 'Detail',
components: {
QuickActions,
AssetUserTable
},
props: {

View File

@@ -0,0 +1,126 @@
<template>
<div>
<el-row :gutter="20">
<el-col :span="16">
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="8">
<QuickActions type="primary" :actions="quickActions" />
<RelationCard ref="RelationCard" type="info" style="margin-top: 15px" v-bind="nodeRelationConfig" />
</el-col>
</el-row>
</div>
</template>
<script>
import QuickActions from '@/components/QuickActions/index'
import ListTable from '@/components/ListTable'
import RelationCard from '@/components/RelationCard'
import { BooleanFormatter } from '@/components/ListTable/formatters'
export default {
name: 'AssetList',
components: {
QuickActions,
ListTable,
RelationCard
},
props: {
object: {
type: Object,
default: () => {}
}
},
data() {
return {
tableConfig: {
url: `/api/v1/assets/assets/?admin_user__id=${this.object.id}`,
columns: [
'hostname', 'ip', 'admin_user_display', 'connectivity'
],
columnsMeta: {
admin_user_display: {
label: this.$t('assets.AdminUser')
},
connectivity: {
label: this.$t('assets.Reachable'),
formatter: BooleanFormatter,
formatterArgs: {
iconChoices: {
0: 'fa-times text-danger',
1: 'fa-check text-primary',
2: 'fa-circle text-warning'
},
typeChange: function(val) {
return val.status
},
hasTips: true
}
}
}
},
headerActions: {
hasLeftActions: false,
hasBulkDelete: false,
hasImport: false,
hasExport: true,
hasCreate: false,
hasSearch: true,
hasMoreActions: false
},
quickActions: [
{
title: this.$t('assets.TestAssetsConnective'),
attrs: {
type: 'primary',
label: this.$t('assets.Test')
},
callbacks: {
click: function() {
this.$axios.get(
`/api/v1/assets/admin-users/${this.object.id}/connective/`
).then(res => {
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
}
)
}.bind(this)
}
}
],
nodeRelationConfig: {
icon: 'fa-info',
title: this.$t('assets.ReplaceNodeAssetsAdminUser'),
objectsAjax: {
url: '/api/v1/assets/nodes/',
transformOption: (item) => {
return { label: item.full_value, value: item.id }
}
},
performAdd: (items) => {
const data = []
const relationUrl = `/api/v1/assets/admin-users/${this.object.id}/nodes/`
items.map(v => {
data.push(v.value)
})
return this.$axios.patch(relationUrl, { nodes: data }).then(res => {
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(err => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
})
},
onAddSuccess: () => {
this.$refs.RelationCard.$refs.select2.clearSelected()
this.$refs.ListTable.reloadTable()
}
}
}
},
methods: {
}
}
</script>
<style lang='less' scoped>
</style>

View File

@@ -9,13 +9,15 @@
<script>
import { GenericDetailPage, TabPage } from '@/layout/components'
import Detail from './Detail.vue'
import AssetList from './AssetsList.vue'
import AccountList from './AccountList.vue'
import AssetList from './AssetList.vue'
export default {
components: {
GenericDetailPage,
TabPage,
Detail,
AssetList
AssetList,
AccountList
},
data() {
return {
@@ -30,6 +32,10 @@ export default {
{
title: this.$t('assets.AssetList'),
name: 'AssetList'
},
{
title: this.$t('assets.AccountList'),
name: 'AccountList'
}
],
hasRightSide: true

View File

@@ -4,7 +4,6 @@
<script>
import { GenericListPage } from '@/layout/components'
import { DetailFormatter, ActionsFormatter } from '@/components/ListTable/formatters/index'
export default {
components: {
@@ -14,53 +13,18 @@ export default {
return {
tableConfig: {
url: '/api/v1/assets/admin-users/',
columns: [
{
prop: 'name',
label: this.$t('common.Name'),
formatter: DetailFormatter,
showOverflowTooltip: true,
sortable: true,
formatterArgs: {
route: 'AdminUserDetail'
}
columns: ['name', 'username', 'assets_amount', 'comment', 'actions'],
columnsMeta: {
username: {
showOverflowTooltip: true
},
{
prop: 'username',
label: this.$t('common.Username'),
showOverflowTooltip: true,
sortable: 'custom'
},
{
prop: 'assets_amount',
label: this.$t('assets.Assets'),
assets_amount: {
width: '80px'
},
{
prop: 'comment',
showOverflowTooltip: true,
label: this.$t('common.Comment'),
sortable: 'custom'
},
{
prop: 'id',
align: 'center',
label: this.$t('common.Action'),
formatter: ActionsFormatter,
width: '200px',
formatterArgs: {
performDelete: ({ row, col }) => {
const id = row.id
const url = `/api/v1/assets/admin-users/${id}/`
return this.$axios.delete(url)
}
}
}
]
}
},
updateRoute: 'AdminUserUpdate',
headerActions: {
hasBulkDelete: false,
createRoute: 'AdminUserCreate'
},
helpMessage: this.$t('assets.AdminUserListHelpMessage')

View File

@@ -24,6 +24,9 @@
<i class="fa fa-align-justify" /> {{ this.$t('tree.ShowAssetAllChildrenNode') }}
</li>
<li class="divider" />
<li id="m_check_assets_amount" class="rmenu" tabindex="-1" @click="rCheckAssetsAmount">
<i class="fa fa-clone" /> {{ this.$t('tree.CheckAssetsAmount') }}
</li>
<li id="m_show_node_info" class="rmenu" tabindex="-1" @click="rMenuShowNodeInfo">
<i class="fa fa-info-circle" /> {{ this.$t('tree.ShowNodeInfo') }}
</li>
@@ -132,11 +135,22 @@ export default {
actions: {
formatter: ActionsFormatter,
formatterArgs: {
hasClone: true,
performDelete: ({ row, col }) => {
const id = row.id
const url = `/api/v1/assets/assets/${id}/`
return this.$axios.delete(url)
}
},
extraActions: [
{
name: 'View',
title: this.$t(`common.UpdateAssetDetail`),
type: 'primary',
callback: function({ cellValue, tableData }) {
return this.$router.push({ name: 'AssetMoreInformationEdit', params: { id: cellValue }})
}
}
]
}
}
}
@@ -449,6 +463,15 @@ export default {
this.$message.error(this.$t('common.getErrorMsg' + ' ' + error))
})
},
rCheckAssetsAmount: function() {
this.$axios.post(
`/api/v1/assets/nodes/launch_check_assets_amount_task/`
).then(res => {
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
}).catch(error => {
this.$message.error(this.$t('common.getErrorMsg' + ' ' + error))
})
},
addRowToAssetsSelected(row) {
const selectValueIndex = this.assetTreeTableDialogSetting.assetsSelected.indexOf(row.id)
if (selectValueIndex === -1) {

View File

@@ -0,0 +1,103 @@
<template>
<GenericCreateUpdatePage v-bind="$data" />
</template>
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
export default {
name: 'AssetMoreInformationEdit',
components: {
GenericCreateUpdatePage
},
data() {
const nodesInitial = []
if (this.$route.query['node']) {
nodesInitial.push(this.$route.query.node)
}
return {
initial: {
is_active: true,
platform: 'Linux',
protocols: ['ssh/22'],
nodes: nodesInitial
},
fields: [
[this.$t('common.Basic'), ['hostname', 'ip', 'platform']],
[this.$t('assets.Hardware'), ['vendor', 'model', 'number', 'cpu_model', 'memory', 'disk_info', 'disk_total']],
[this.$t('assets.Os'), ['sn', 'os', 'os_version', 'os_arch']]
],
fieldsMeta: {
platform: {
el: {
multiple: false,
ajax: {
url: '/api/v1/assets/platforms/',
transformOption: (item) => {
return { label: `${item.name}`, value: item.name }
}
}
}
},
vendor: {
el: {
type: `input`
}
},
model: {
el: {
type: `input`
}
},
cpu_model: {
el: {
type: `input`
}
},
memory: {
el: {
type: `input`
}
},
disk_info: {
el: {
type: `input`
}
},
disk_total: {
el: {
type: `input`
}
},
sn: {
el: {
type: `input`
}
},
os: {
el: {
type: `input`
}
},
os_version: {
el: {
type: `input`
}
},
os_arch: {
el: {
type: `input`
}
}
},
url: '/api/v1/assets/assets/',
updateSuccessNextRoute: { name: 'AssetList' },
createSuccessNextRoute: { name: 'AssetList' }
}
}
}
</script>
<style>
</style>

View File

@@ -32,14 +32,17 @@ export default {
title: this.$t('perms.addSystemUserToThisPermission'),
objectsAjax: {
url: '/api/v1/assets/system-users/',
processResults: (data) => {
let results = data.results
results = results.filter((item) => item.protocol === 'ssh' || item.protocol === 'telnet').map((item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
})
const more = !!data.next
return { results: results, pagination: more, total: data.count }
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}
// processResults: (data) => {
// let results = data.results
// results = results.filter((item) => item.protocol === 'ssh' || item.protocol === 'telnet').map((item) => {
// return { label: item.name + '(' + item.username + ')', value: item.id }
// })
// const more = !!data.next
// return { results: results, pagination: more, total: data.count }
// }
},
hasObjectsId: this.object.system_users,
performAdd: (items) => {

View File

@@ -36,6 +36,7 @@ export default {
},
actions: {
formatterArgs: {
hasClone: false,
updateRoute: {
name: 'CommandFilterRulesUpdate',
query: {
@@ -47,8 +48,8 @@ export default {
}
},
headerActions: {
hasBulkDelete: false,
hasSearch: true,
hasBulkDelete: false,
createRoute: {
name: 'CommandFilterRulesCreate',
query: {

View File

@@ -4,6 +4,7 @@
:initial="initial"
:fields-meta="fieldsMeta"
:url="url"
:has-detail-in-msg="false"
:get-next-route="getNextRoute"
/>
</template>

View File

@@ -4,7 +4,7 @@
<script>
import { GenericListPage } from '@/layout/components'
import { DetailFormatter, ActionsFormatter } from '@/components/ListTable/formatters/index'
import { DetailFormatter } from '@/components/ListTable/formatters/index'
export default {
components: {
@@ -14,69 +14,37 @@ export default {
return {
tableConfig: {
url: '/api/v1/assets/cmd-filters/',
columns: [
{
prop: 'name',
label: this.$t('assets.Name'),
formatter: DetailFormatter,
showOverflowTooltip: true,
sortable: true,
formatterArgs: {
route: 'CommandFilterDetail'
}
},
{
prop: 'rules',
columns: ['name', 'rules', 'system_users', 'comment', 'actions'],
columnsMeta: {
rules: {
label: this.$t('assets.Rules'),
formatter: DetailFormatter,
formatterArgs: {
getTitle: ({ cellValue }) => {
return cellValue.length
},
routeQuery: {
activeTab: 'rules'
},
getTitle: ({ cellValue }) => cellValue.length
}
}
},
{
prop: 'system_users',
system_users: {
label: this.$t('assets.SystemUsers'),
formatter: DetailFormatter,
formatterArgs: {
route: 'CommandFilterDetail',
getTitle: ({ cellValue }) => cellValue.length
}
},
{
prop: 'comment',
showOverflowTooltip: true,
label: this.$t('assets.Comment')
},
{
prop: 'id',
align: 'center',
label: this.$t('assets.Action'),
formatter: ActionsFormatter,
width: '200px',
formatterArgs: {
performDelete: ({ row, col }) => {
const id = row.id
const url = `/api/v1/assets/cmd-filters/${id}/`
return this.$axios.delete(url).then(res => {
this.$message.success(this.$t('common.deleteSuccessMsg'))
window.location.reload()
}).catch(error => {
this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
})
getTitle: ({ cellValue }) => {
return cellValue.length
}
}
}
]
}
},
headerActions: {
hasRightActions: false,
hasExport: false,
hasImport: false,
hasRefresh: true,
hasBulkDelete: false,
hasSearch: true,
hasMoreActions: false,
createRoute: 'CommandFilterCreate'

View File

@@ -1,6 +1,7 @@
<template>
<GenericCreateUpdatePage
:fields="fields"
:has-detail-in-msg="false"
:initial="initial"
:fields-meta="fieldsMeta"
:url="url"

View File

@@ -92,7 +92,6 @@ export default {
}
},
headerActions: {
hasBulkDelete: false,
hasSearch: true,
createRoute: {
name: 'GatewayCreate',
@@ -110,7 +109,14 @@ export default {
methods: {
dialogConfirm() {
this.buttonLoading = true
this.$axios.post(`/api/v1/assets/gateways/${this.cellValue}/test-connective/`, { port: parseInt(this.portInput) }).then(
const port = parseInt(this.portInput)
if (isNaN(port)) {
this.buttonLoading = false
return this.$message.error(this.$t('common.TestPortErrorMsg'))
}
this.$axios.post(`/api/v1/assets/gateways/${this.cellValue}/test-connective/`, { port: port }).then(
res => {
return this.$message.success(this.$t('common.TestSuccessMsg'))
}

View File

@@ -36,7 +36,6 @@ export default {
}
},
headerActions: {
hasBulkDelete: false,
hasMoreActions: false,
createRoute: 'DomainCreate'
},

View File

@@ -1,5 +1,5 @@
<template>
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" :has-detail-in-msg="false" />
</template>
<script>

View File

@@ -4,7 +4,6 @@
<script>
import { GenericListPage } from '@/layout/components'
import { ActionsFormatter } from '@/components/ListTable/formatters/index'
export default {
components: {
@@ -14,42 +13,17 @@ export default {
return {
tableConfig: {
url: '/api/v1/assets/labels/',
columns: [
{
prop: 'name',
label: this.$t('assets.Name'),
showOverflowTooltip: true,
sortable: true
columns: ['name', 'value', 'asset_count', 'actions'],
columnsMeta: {
name: {
formatter: null
},
{
prop: 'value',
label: this.$t('assets.Value'),
showOverflowTooltip: true,
sortable: 'custom'
},
{
prop: 'asset_count',
label: this.$t('assets.Assets'),
width: '80px'
},
{
prop: 'id',
align: 'center',
formatter: ActionsFormatter,
label: this.$t('assets.Action'),
width: '200px',
formatterArgs: {
performDelete: ({ row, col }) => {
const id = row.id
const url = `/api/v1/assets/labels/${id}/`
return this.$axios.delete(url)
}
}
asset_count: {
label: this.$t('assets.Assets')
}
]
}
},
headerActions: {
hasBulkDelete: false,
hasRightActions: false,
hasExport: false,
hasImport: false,

View File

@@ -4,7 +4,6 @@
<script>
import { GenericListPage } from '@/layout/components'
import { DetailFormatter, ActionsFormatter } from '@/components/ListTable/formatters/index'
export default {
components: {
@@ -15,58 +14,22 @@ export default {
tableConfig: {
url: '/api/v1/assets/platforms/',
columns: [
{
prop: 'name',
label: this.$t('assets.Name'),
formatter: DetailFormatter,
showOverflowTooltip: true,
sortable: true,
formatterArgs: {
route: 'PlatformDetail'
}
},
{
prop: 'base',
label: this.$t('assets.BasePlatform'),
sortable: 'custom',
'name', 'base', 'comment', 'actions'
],
columnsMeta: {
base: {
width: '140px'
},
{
prop: 'comment',
showOverflowTooltip: true,
label: this.$t('assets.Comment'),
sortable: 'custom'
},
{
prop: 'id',
align: 'center',
label: this.$t('assets.Action'),
formatter: ActionsFormatter,
width: '200px',
formatterArgs: {
canDelete: (row, vaule) => {
return !row.internal
},
canUpdate: (row, vaule) => {
return !row.internal
},
performDelete: ({ row, col }) => {
const id = row.id
const url = `/api/v1/assets/platforms/${id}/`
return this.$axios.delete(url)
}
}
}
]
}
},
headerActions: {
hasRightActions: false,
hasExport: false,
hasImport: false,
hasRefresh: false,
hasBulkDelete: false,
hasSearch: false,
hasMoreActions: false,
hasBulkDelete: false,
createRoute: 'PlatformCreate'
}
}

View File

@@ -96,6 +96,9 @@ export default {
hidden: (form) => {
this.fieldsMeta.username.el.disabled = form.username_same_with_user
return form.protocol === 'k8s'
},
el: {
disabled: false
}
},
auto_generate_key: {
@@ -233,9 +236,10 @@ export default {
},
mounted() {
const params = this.$route.params
const method = params.id ? 'post' : 'put'
if (method === 'post') {
const method = params.id ? 'update' : 'create'
if (method === 'update') {
this.fieldsMeta.token.rules[0].required = false
this.fieldsMeta.username_same_with_user.el.disabled = true
}
}
}

View File

@@ -0,0 +1,39 @@
<template>
<div>
<el-row :gutter="20">
<el-col :span="16">
<AssetUserTable ref="ListTable" :url="assetUserUrl" :has-import="false" />
</el-col>
<el-col :span="8" />
</el-row>
</div>
</template>
<script>
import { AssetUserTable } from '@/components'
export default {
name: 'AccountList',
components: {
AssetUserTable
},
props: {
object: {
type: Object,
default: () => {}
}
},
data() {
return {
assetUserUrl: `/api/v1/assets/asset-users/?prefer_id=${this.object.id}&prefer=system_user&latest=1`
}
},
computed: {
}
}
</script>
<style lang='less' scoped>
</style>

View File

@@ -2,7 +2,7 @@
<div>
<el-row :gutter="20">
<el-col :span="16">
<AssetUserTable ref="ListTable" :url="assetUserUrl" :other-actions="otherActions" :has-import="false" />
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="8">
<QuickActions type="primary" :actions="quickActions" />
@@ -17,15 +17,16 @@
import QuickActions from '@/components/QuickActions/index'
import RelationCard from '@/components/RelationCard'
import AssetRelationCard from '@/components/AssetRelationCard'
import { AssetUserTable } from '@/components'
import ListTable from '@/components/ListTable'
import { ActionsFormatter } from '@/components/ListTable/formatters'
export default {
name: 'Detail',
name: 'AssetList',
components: {
QuickActions,
RelationCard,
AssetUserTable,
AssetRelationCard
AssetRelationCard,
ListTable
},
props: {
object: {
@@ -36,58 +37,116 @@ export default {
data() {
const vm = this
return {
assetUserUrl: `/api/v1/assets/asset-users/?prefer_id=${this.object.id}&prefer=system_user&latest=1`,
otherActions: [
{
name: 'Push',
title: this.$t('common.Push'),
can: () => vm.object.auto_push,
callback: function({ row }) {
const assetId = row.asset
const username = row.username
const theUrl = `/api/v1/assets/system-users/${vm.object.id}/tasks/?username=${username}`
const data = { action: 'push', asset: assetId }
this.$axios.post(theUrl, data).then(resp => {
window.open(`/#/ops/celery/task/${resp.task}/log/`, '', 'width=900,height=600')
})
}
}
],
AutoPushConfig: {
icon: 'fa-info',
title: this.$t('assets.QuickUpdate'),
url: `/api/v1/assets/system-users/${this.object.id}/`,
content: [
tableConfig: {
url: `/api/v1/assets/system-users-assets-relations/?systemuser=${this.object.id}`,
columns: [
{
name: this.$t('assets.AutoPush'),
auto_push: this.object.auto_push
prop: 'asset_display',
label: this.$t('assets.Hostname')
},
{
prop: 'systemuser_display',
label: this.$t('assets.SystemUsers')
},
{
prop: 'id',
label: this.$t('common.Action'),
align: 'center',
width: 150,
formatter: ActionsFormatter,
formatterArgs: {
hasUpdate: false, // can set function(row, value)
hasDelete: false, // can set function(row, value)
moreActionsTitle: this.$t('common.More'),
extraActions: [
{
name: 'Push',
title: this.$t('common.Push'),
type: 'primary',
can: this.object.auto_push,
callback: ({ row }) => {
const theUrl = `/api/v1/assets/system-users/${vm.object.id}/tasks/`
const data = { action: 'push', assets: [row.asset] }
this.$axios.post(theUrl, data).then(resp => {
window.open(`/#/ops/celery/task/${resp.task}/log/`, '', 'width=900,height=600')
})
}
},
{
name: 'Delete',
title: this.$t('common.Delete'),
type: 'danger',
callback: (val) => {
this.$axios.delete(`/api/v1/assets/system-users-assets-relations/${val.cellValue}/`).then(() => {
this.$message.success(this.$t('common.deleteSuccessMsg'))
this.$refs.ListTable.reloadTable()
})
}
}
]
}
}
]
},
assetRelationConfig: {
icon: 'fa-edit',
title: this.$t('xpack.ChangeAuthPlan.AddAsset'),
performAdd: (items, that) => {
const relationUrl = `/api/v1/assets/system-users-assets-relations/`
const data = [
]
items.map(v =>
data.push({
asset: v,
systemuser: this.object.id
})
)
headerActions: {
hasLeftActions: true,
hasBulkDelete: false,
hasImport: false,
hasExport: true,
hasCreate: false,
hasSearch: true,
hasMoreActions: false,
moreActionsTitle: this.$t('common.More'),
moreActionsType: 'primary',
extraMoreActions: [
{
title: this.$t('common.PushSelected'),
name: 'PushSelected',
can({ selectedRows }) {
return selectedRows.length > 0 && vm.object.auto_push
},
callback: this.bulkPushCallback.bind(this)
},
{
title: this.$t('assets.TestAssetsConnective'),
name: 'TestSelected',
can({ selectedRows }) {
return selectedRows.length > 0
},
callback: this.bulkTestCallback.bind(this)
}
]
},
nodeRelationConfig: {
icon: 'fa-info',
title: this.$t('perms.addNodeToThisPermission'),
objectsAjax: {
url: '/api/v1/assets/nodes/',
transformOption: (item) => {
return { label: item.full_value, value: item.id }
}
},
hasObjectsId: [],
hasObjects: [],
performAdd: (items) => {
const relationUrl = `/api/v1/assets/system-users-nodes-relations/`
const objectId = this.object.id
const data = items.map(v => {
return {
systemuser: objectId,
node: v.value
}
})
if (data.length === 0) {
return this.$message.error(this.$t('assets.UnselectedAssets'))
return this.$message.error(this.$t('assets.UnselectedNodes'))
}
return this.$axios.post(relationUrl, data)
},
onAddSuccess: (items, that) => {
this.$log.debug('AssetSelect value', that.assets)
this.$message.success(this.$t('common.updateSuccessMsg'))
this.$refs.ListTable.$refs.ListTable.reloadTable()
that.$refs.assetSelect.$refs.select2.clearSelected()
performDelete: (item) => {
const itemId = item.value
const objectId = this.object.id
const relationUrl = `/api/v1/assets/system-users-nodes-relations/?systemuser=${objectId}&node=${itemId}`
return this.$axios.delete(relationUrl)
}
},
quickActions: [
@@ -127,59 +186,54 @@ export default {
}
}
],
nodeRelationConfig: {
icon: 'fa-info',
title: this.$t('perms.addNodeToThisPermission'),
objectsAjax: {
url: '/api/v1/assets/nodes/',
transformOption: (item) => {
return { label: item.full_value, value: item.id }
}
},
hasObjectsId: [],
hasObjects: [],
performAdd: (items) => {
const relationUrl = `/api/v1/assets/system-users-nodes-relations/`
const objectId = this.object.id
const data = items.map(v => {
return {
systemuser: objectId,
node: v.value
}
})
assetRelationConfig: {
icon: 'fa-edit',
title: this.$t('xpack.ChangeAuthPlan.AddAsset'),
performAdd: (items, that) => {
const relationUrl = `/api/v1/assets/system-users-assets-relations/`
const data = [
]
items.map(v =>
data.push({
asset: v,
systemuser: this.object.id
})
)
if (data.length === 0) {
return this.$message.error(this.$t('assets.UnselectedNodes'))
return this.$message.error(this.$t('assets.UnselectedAssets'))
}
return this.$axios.post(relationUrl, data)
},
performDelete: (item) => {
const itemId = item.value
const objectId = this.object.id
const relationUrl = `/api/v1/assets/system-users-nodes-relations/?systemuser=${objectId}&node=${itemId}`
return this.$axios.delete(relationUrl)
onAddSuccess: (items, that) => {
this.$log.debug('AssetSelect value', that.assets)
this.$message.success(this.$t('common.updateSuccessMsg'))
vm.$refs.ListTable.reloadTable()
that.$refs.assetSelect.$refs.select2.clearSelected()
}
}
}
},
computed: {
},
mounted() {
this.getNodeList()
},
methods: {
getNodeList() {
this.$axios.get(
`/api/v1/assets/system-users-nodes-relations/?systemuser=${this.object.id}&draw=1&limit=15&offset=0`
).then(data => {
for (const x in data.results) {
this.nodeRelationConfig.hasObjectsId.push(data.results[x].node)
this.nodeRelationConfig.hasObjects.push({
value: data.results[x].node,
label: data.results[x].node_display
})
}
}
)
bulkPushCallback({ selectedRows }) {
const theUrl = `/api/v1/assets/system-users/${this.object.id}/tasks/`
const assets = selectedRows.map((v) => {
return v.asset
})
const data = { action: 'push', assets: assets }
this.$axios.post(theUrl, data).then(resp => {
window.open(`/#/ops/task/task/${resp.task}/log/`, '', 'width=900,height=600')
})
},
bulkTestCallback({ selectedRows }) {
const theUrl = `/api/v1/assets/system-users/${this.object.id}/tasks/`
const assets = selectedRows.map((v) => {
return v.asset
})
const data = { action: 'test', assets: assets }
this.$axios.post(theUrl, data).then(resp => {
window.open(`/#/ops/task/task/${resp.task}/log/`, '', 'width=900,height=600')
})
}
}
}

View File

@@ -9,14 +9,15 @@
<script>
import { GenericDetailPage, TabPage } from '@/layout/components'
import Detail from './Detail.vue'
import AssetList from './AssetList.vue'
import AccountList from './AccountList.vue'
export default {
components: {
GenericDetailPage,
TabPage,
Detail,
AssetList
AssetList,
AccountList
},
data() {
return {
@@ -31,7 +32,12 @@ export default {
{
title: this.$t('assets.AssetList'),
name: 'AssetList'
},
{
title: this.$t('assets.AccountList'),
name: 'AccountList'
}
],
hasRightSide: true
}

View File

@@ -4,7 +4,6 @@
<script>
import { GenericListPage } from '@/layout/components'
import { DetailFormatter, ActionsFormatter } from '@/components/ListTable/formatters/index'
export default {
components: {
@@ -14,63 +13,28 @@ export default {
return {
tableConfig: {
url: '/api/v1/assets/system-users/',
columns: [
{
prop: 'name',
label: this.$t('common.Name'),
formatter: DetailFormatter,
showOverflowTooltip: true,
sortable: true,
formatterArgs: {
route: 'SystemUserDetail'
}
columns: ['name', 'username', 'protocol', 'login_mode', 'assets_amount', 'comment', 'actions'],
columnsMeta: {
username: {
showOverflowTooltip: true
},
{
prop: 'username',
label: this.$t('common.Username'),
showOverflowTooltip: true,
sortable: 'custom'
},
{
prop: 'protocol',
label: this.$t('assets.Protocol'),
sortable: 'custom',
protocol: {
width: '100px'
},
{
prop: 'login_mode_display',
label: this.$t('assets.LoginModel'),
login_mode: {
width: '120px'
},
{
prop: 'assets_amount',
label: this.$t('assets.Assets'),
assets_amount: {
width: '80px'
},
{
prop: 'comment',
showOverflowTooltip: true,
label: this.$t('common.Comment')
},
{
prop: 'id',
align: 'center',
formatter: ActionsFormatter,
width: '200px',
label: this.$t('common.Action'),
updateRoute: 'SystemUserUpdate',
actions: {
formatterArgs: {
performDelete: ({ row, col }) => {
const id = row.id
const url = `/api/v1/assets/system-users/${id}/`
return this.$axios.delete(url)
}
hasClone: true
}
}
]
}
},
headerActions: {
hasBulkDelete: false,
hasMoreActions: false,
createRoute: 'SystemUserCreate'
},

View File

@@ -17,7 +17,7 @@ export default {
return {
tableConfig: {
url: '/api/v1/audits/login-logs/',
columns: ['username', 'type', 'ip', 'city', 'user_agent', 'mfa', 'reason', 'status', 'datetime'],
columns: ['username', 'type', 'backend', 'ip', 'city', 'user_agent', 'mfa', 'reason', 'status', 'datetime'],
columnsMeta: {
username: {
showOverflowTooltip: true

View File

@@ -8,11 +8,14 @@ import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
export default {
name: 'CeleryTaskLog',
props: {
},
data() {
return {
xterm: null,
ws: null,
taskId: this.$route.params.id,
type: this.$route.params.type || this.$route.query.type || 'celery',
url: '/ws/ops/tasks/log/',
failOverPort: process.env.VUE_APP_WS_PORT
}
@@ -71,7 +74,7 @@ export default {
this.xterm.write(data.message)
}
this.ws.onopen = (e) => {
const msg = { 'task': this.taskId }
const msg = { 'task': this.taskId, 'type': this.type }
this.ws.send(JSON.stringify(msg))
}
},

View File

@@ -77,7 +77,7 @@ export default {
value: this.object.id,
formatter: function(row, value) {
const onClick = function() {
window.open(`/#/ops/celery/task/${value}/log/`, '', 'width=900,height=600')
window.open(`/#/ops/task/task/${value}/log/?type=ansible`, '', 'width=900,height=600')
}
const title = this.$t('common.View')
return <a onClick={onClick} >{ title }</a>

View File

@@ -101,7 +101,7 @@ export default {
value: this.object.latest_execution.id,
formatter: function(row, value) {
const onClick = function() {
window.open(`/#/ops/celery/task/${value}/log/`, '', 'width=900,height=600')
window.open(`/#/ops/task/task/${value}/log/?type=ansible`, '', 'width=900,height=600')
}
const title = this.$t('common.View')
return <a onClick={onClick} >{ title }</a>

View File

@@ -54,15 +54,9 @@ export default {
}
},
headerActions: {
hasSearch: true,
hasRefresh: true,
hasLeftActions: true,
hasRightActions: true,
hasLeftActions: false,
hasExport: false,
hasImport: false,
hasCreate: false,
hasBulkDelete: false,
hasBulkUpdate: false
hasImport: false
},
userRelationConfig: {
icon: 'fa-user',

View File

@@ -56,26 +56,20 @@ export default {
}
},
headerActions: {
hasSearch: true,
hasRefresh: true,
hasLeftActions: true,
hasRightActions: true,
hasLeftActions: false,
hasExport: false,
hasImport: false,
hasCreate: false,
hasBulkDelete: false,
hasBulkUpdate: false
hasImport: false
},
remoteAppRelationConfig: {
icon: 'fa-edit',
title: this.$t('perms.addRemoteAppToThisPermission'),
title: this.$t('perms.addApplicationToThisPermission'),
objectsAjax: {
url: `/api/v1/applications/applications/?category=${this.object.category}&type=${this.object.type}`,
transformOption: (item) => {
return { label: item.name + ' (' + item.type_display + ')', value: item.id }
}
},
hasObjectsId: this.object.application,
hasObjectsId: this.object.applications,
showHasObjects: false,
performAdd: (items) => {
const objectId = this.object.id
@@ -117,7 +111,7 @@ export default {
this.$log.debug('Select value', that.select2.value)
that.iHasObjects = [...that.iHasObjects, ...objects]
that.$refs.select2.clearSelected()
this.$message.success(this.$t('common.updateSuccessMsg'))
this.$message.success(this.$tc('common.updateSuccessMsg'))
this.$refs.ListTable.reloadTable()
},
performDelete: (item) => {
@@ -125,6 +119,10 @@ export default {
const relationUrl = `/api/v1/perms/application-permissions/${objectId}/`
const objectOldRelationSystemUsers = this.object.system_users
const objectNewRelationSystemUsers = objectOldRelationSystemUsers.filter(v => v !== item.value)
if (objectNewRelationSystemUsers.length === 0) {
this.$message.error(this.$tc('common.lastCannotBeDeleteMsg'))
return
}
const data = { system_users: objectNewRelationSystemUsers }
return this.$axios.patch(relationUrl, data)
},

View File

@@ -72,6 +72,11 @@ export default {
activeTab: 'RemoteAppPermissionRemoteApp'
}
}
},
actions: {
formatterArgs: {
hasClone: true
}
}
}
},

View File

@@ -82,7 +82,7 @@ export default {
el: {
value: [],
ajax: {
url: '/api/v1/assets/system-users/',
url: '/api/v1/assets/system-users/?protocol__in=rdp,ssh,vnc,telnet',
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}

View File

@@ -8,6 +8,7 @@
:create-success-next-route="successUrl"
:clean-form-value="cleanFormValue"
:object="formData"
:has-detail-in-msg="false"
:fields-meta="fieldsMeta"
/>
</div>
@@ -59,13 +60,16 @@ export default {
doc_type: {
label: this.$t('sessions.docType'),
rules: [Required],
el: {
disabled: true
},
helpText: this.$t('sessions.helpText.esDocType')
}
},
fieldsMap: {
es: [[this.$t('common.Basic'), ['name', 'type', 'hosts', 'index', 'doc_type', 'comment']]]
},
url: '/api/v1/terminal/command-storages/'
url: this.$route.params.id ? '/api/v1/terminal/command-storages/' : `/api/v1/terminal/command-storages/?type=es`
}
},
computed: {
@@ -73,7 +77,7 @@ export default {
return this.fieldsMap[this.currentType]
},
initial() {
return { type: this.currentType }
return { type: this.currentType, doc_type: 'command' }
},
currentType() {
const params = this.$route.params
@@ -134,9 +138,9 @@ export default {
type: 'es',
comment: value.comment,
meta: {
es_hosts: host_array,
es_index: value.index,
es_doc_type: value.doc_type
HOSTS: host_array,
INDEX: value.index,
DOC_TYPE: value.doc_type
}
}
}

View File

@@ -7,6 +7,7 @@
:create-success-next-route="successUrl"
:clean-form-value="cleanFormValue"
:object="formData"
:has-detail-in-msg="false"
:initial="initial"
:fields-meta="fieldsMetas"
/>
@@ -36,7 +37,7 @@ export default {
name: '',
comment: ''
},
url: '/api/v1/terminal/replay-storages/',
url: this.$route.params.id ? `/api/v1/terminal/replay-storages/` : `/api/v1/terminal/replay-storages/?type=${storageType}`,
fields: [
[this.$t('common.Basic'), ['name', 'type']],
[storageTypeMeta.title, storageTypeMeta.meta],
@@ -55,7 +56,8 @@ export default {
rules: [Required]
},
bucket: {
label: this.$t('sessions.bucket')
label: this.$t('sessions.bucket'),
rules: [Required]
},
access_key: {
label: 'Access key',
@@ -67,10 +69,12 @@ export default {
},
endpoint: {
label: this.$t('sessions.endPoint'),
helpText: storageTypeMeta.endpointHelpText
helpText: storageTypeMeta.endpointHelpText,
rules: [Required]
},
region: {
label: this.$t('sessions.region')
label: this.$t('sessions.region'),
rules: [Required]
},
protocol: {
label: this.$t('sessions.protocol'),
@@ -82,13 +86,16 @@ export default {
rules: [Required]
},
container_name: {
label: this.$t('sessions.containerName')
label: this.$t('sessions.containerName'),
rules: [Required]
},
account_name: {
label: this.$t('sessions.accountName')
label: this.$t('sessions.accountName'),
rules: [Required]
},
account_key: {
label: this.$t('sessions.accountKey')
label: this.$t('sessions.accountKey'),
rules: [Required]
},
endpoint_suffix: {
label: this.$t('sessions.endpointSuffix'),
@@ -221,10 +228,10 @@ export default {
type: 's3',
comment: value.comment,
meta: {
s3_bucket: value.bucket,
s3_access_key: value.access_key,
s3_secret_key: value.secret_key,
s3_endpoint: value.endpoint
BUCKET: value.bucket,
ACCESS_KEY: value.access_key,
SECRET_KEY: value.secret_key,
ENDPOINT: value.endpoint
}
}
},
@@ -234,10 +241,10 @@ export default {
type: 'ceph',
comment: value.comment,
meta: {
ceph_bucket: value.bucket,
ceph_access_key: value.access_key,
ceph_secret_key: value.secret_key,
ceph_endpoint: value.endpoint
BUCKET: value.bucket,
ACCESS_KEY: value.access_key,
SECRET_KEY: value.secret_key,
ENDPOINT: value.endpoint
}
}
},
@@ -247,12 +254,12 @@ export default {
type: 'swift',
comment: value.comment,
meta: {
swift_bucket: value.bucket,
swift_access_key: value.access_key,
swift_secret_key: value.secret_key,
swift_region: value.region,
swift_endpoint: value.endpoint,
swift_protocol: value.protocol
BUCKET: value.bucket,
ACCESS_KEY: value.access_key,
SECRET_KEY: value.secret_key,
REGION: value.region,
ENDPOINT: value.endpoint,
PROTOCOL: value.protocol
}
}
},
@@ -262,10 +269,10 @@ export default {
type: 'oss',
comment: value.comment,
meta: {
oss_bucket: value.bucket,
oss_access_key: value.access_key,
oss_secret_key: value.secret_key,
oss_endpoint: value.endpoint
BUCKET: value.bucket,
ACCESS_KEY: value.access_key,
SECRET_KEY: value.secret_key,
ENDPOINT: value.endpoint
}
}
},
@@ -275,10 +282,10 @@ export default {
type: 'azure',
comment: value.comment,
meta: {
azure_container_name: value.container_name,
azure_account_name: value.account_name,
azure_account_key: value.account_key,
azure_endpoint_suffix: value.endpoint_suffix
CONTAINER_NAME: value.container_name,
ACCOUNT_NAME: value.account_name,
ACCOUNT_KEY: value.account_key,
ENDPOINT_SUFFIX: value.endpoint_suffix
}
}
}

View File

@@ -64,7 +64,7 @@ export default {
},
{
key: this.$t('sessions.dateEnd'),
value: toSafeLocalDateStr(this.sessionData.date_end)
value: this.sessionData.date_end ? toSafeLocalDateStr(this.sessionData.date_end) : ''
}
]
},

View File

@@ -73,15 +73,11 @@ export default {
type: 'primary',
callback: function({ row, col, cellValue, reload }) {
TestCommandStorage(cellValue).then(data => {
let success = 'success'
if (!data.is_valid) {
success = 'error'
this.$message.error(data.msg)
} else {
this.$message.success(data.msg)
}
this.$notify({
message: data.msg,
type: success,
duration: 4500
})
})
}
}

View File

@@ -94,15 +94,11 @@ export default {
type: 'primary',
callback: function({ row, col, cellValue, reload }) {
TestReplayStorage(cellValue).then(data => {
let success = 'success'
if (!data.is_valid) {
success = 'error'
this.$message.error(data.msg)
} else {
this.$message.success(data.msg)
}
this.$notify({
message: data.msg,
type: success,
duration: 4500
})
})
}
}

View File

@@ -86,16 +86,45 @@ export default {
}
}
})
}.bind(this)
}.bind(this),
hasSaveContinue: false
}
},
tableConfig: {
url: '/api/v1/terminal/terminals/',
columns: ['name', 'remote_addr', 'session_online', 'is_active', 'is_alive', 'actions'],
columns: ['name', 'remote_addr', 'session_online',
'state.session_active_count',
'state.system_cpu_load_1',
'state.system_disk_used_percent',
'state.system_memory_used_percent',
'status_display',
'is_active',
'is_alive',
'actions'],
columnsMeta: {
name: {
sortable: 'custom'
},
'state.session_active_count': {
label: this.$t('sessions.sessionActiveCount'),
width: '120px'
},
'state.system_cpu_load_1': {
label: this.$t('sessions.systemCpuLoad'),
width: '120px'
},
'state.system_disk_used_percent': {
label: this.$t('sessions.systemDiskUsedPercent'),
width: '120px'
},
'state.system_memory_used_percent': {
label: this.$t('sessions.systemMemoryUsedPercent'),
width: '120px'
},
'status_display': {
label: this.$t('xpack.LoadStatus'),
width: '120px'
},
remote_addr: {
sortable: 'custom'
},

View File

@@ -10,6 +10,7 @@
:object="object"
:fields-meta="fieldsMeta"
:get-method="getMethod"
:has-detail-in-msg="false"
/>
</IBox>
</template>

View File

@@ -10,6 +10,7 @@
:fields-meta="fieldsMeta"
:get-method="getMethod"
:more-buttons="moreButtons"
:has-detail-in-msg="false"
/>
</IBox>
</template>

View File

@@ -9,6 +9,7 @@
:object="object"
:fields-meta="fieldsMeta"
:get-method="getMethod"
:has-detail-in-msg="false"
/>
</IBox>
</template>

View File

@@ -11,6 +11,7 @@
:fields-meta="fieldsMeta"
:get-method="getMethod"
:more-buttons="moreButtons"
:has-detail-in-msg="false"
/>
</IBox>
<Dialog

View File

@@ -10,6 +10,7 @@
:fields-meta="fieldsMeta"
:get-method="getMethod"
:on-perform-success="onPerformSuccess"
:has-detail-in-msg="false"
/>
</IBox>
</template>

View File

@@ -9,6 +9,7 @@
:object="object"
:fields-meta="fieldsMeta"
:get-method="getMethod"
:has-detail-in-msg="false"
/>
</IBox>
</template>
@@ -31,7 +32,7 @@ export default {
data() {
return {
selectFields: [
[this.$t('setting.basicSetting'), ['TERMINAL_PASSWORD_AUTH', 'TERMINAL_PUBLIC_KEY_AUTH', 'TERMINAL_HEARTBEAT_INTERVAL',
[this.$t('setting.basicSetting'), ['TERMINAL_PASSWORD_AUTH', 'TERMINAL_PUBLIC_KEY_AUTH',
'TERMINAL_ASSET_LIST_SORT_BY', 'TERMINAL_ASSET_LIST_PAGE_SIZE', 'TERMINAL_SESSION_KEEP_DURATION',
'TERMINAL_TELNET_REGEX']]
],

View File

@@ -4,15 +4,19 @@
<script>
import TicketListTable from './TicketListTable'
import { mapGetters } from 'vuex'
export default {
name: 'AssignedTicketList',
components: {
TicketListTable
},
data() {
return {
url: '/api/v1/tickets/tickets/?assign=1'
}
computed: {
url() {
return `/api/v1/tickets/tickets/?assignees__id=${this.currentUser.id}`
},
...mapGetters([
'currentUser'
])
}
}
</script>

View File

@@ -4,6 +4,7 @@
<script>
import TicketListTable from './TicketListTable'
import { mapGetters } from 'vuex'
export default {
name: 'MyTicketList',
components: {
@@ -11,8 +12,16 @@ export default {
},
data() {
return {
url: '/api/v1/tickets/tickets/?assign=0'
}
},
computed: {
url() {
return `/api/v1/tickets/tickets/?applicant=${this.currentUser.id}`
},
...mapGetters([
'currentUser'
])
}
}
</script>

View File

@@ -0,0 +1,47 @@
<template>
<el-select v-model="value" v-bind="$attrs" class="select2" v-on="$listeners">
<el-option-group
v-for="group in options"
:key="group.org_name"
:label="group.org_name"
>
<el-option
v-for="item in group.org_admins"
:key="item.id"
:label="item.name + '(' + item.username + ')'"
:value="item.id"
/>
</el-option-group>
</el-select>
</template>
<script>
export default {
name: 'GroupSelectFormatter',
props: {
url: {
type: String,
default: ''
}
},
data() {
return {
value: '',
options: []
}
},
created() {
this.$axios.get(this.url).then(
res => {
this.options = res
}
)
}
}
</script>
<style scoped>
.select2 {
width: 100%;
}
</style>

View File

@@ -0,0 +1,255 @@
<template>
<GenericTicketDetail
:object="object"
:detail-card-items="detailCardItems"
:special-card-items="specialCardItems"
:assigned-card-items="assignedCardItems"
:approve="handleApprove"
:close="handleClose"
:reject="handleReject"
>
<IBox v-if="hasActionPerm&&object.status !== 'closed'" class="box">
<div slot="header" class="clearfix ibox-title">
<i class="fa fa-edit" /> {{ $t('common.Actions') }}
</div>
<template>
<el-form ref="requestForm" :model="requestForm" label-width="140px" label-position="left" class="assets">
<el-form-item :label="$t('perms.PermName')" required>
<el-input v-model="requestForm.name" />
</el-form-item>
<el-form-item :label="$t('assets.Applications')" required>
<Select2 v-model="requestForm.application" v-bind="apps_select2" style="width: 50% !important" />
</el-form-item>
<el-form-item :label="$t('tickets.SystemUser')" required>
<Select2 v-model="requestForm.systemuser" v-bind="systemuser_select2" style="width: 50% !important" />
</el-form-item>
<el-form-item :label="$t('common.dateStart')" required>
<el-date-picker
v-model="requestForm.apply_date_start"
type="datetime"
/>
</el-form-item>
<el-form-item :label="$t('common.dateExpired')" required>
<el-date-picker
v-model="requestForm.apply_date_expired"
type="datetime"
/>
</el-form-item>
</el-form>
</template>
</IBox>
</GenericTicketDetail>
</template>
<script>
import { formatTime, getDateTimeStamp } from '@/utils/index'
import { toSafeLocalDateStr } from '@/utils/common'
import { STATUS_MAP } from '../../const'
import Select2 from '@/components/Select2'
import IBox from '@/components/IBox'
import GenericTicketDetail from '@/views/tickets/components/GenericTicketDetail'
export default {
name: '',
components: { GenericTicketDetail, IBox, Select2 },
props: {
object: {
type: Object,
default: () => ({})
}
},
data() {
return {
statusMap: this.object.status === 'open' ? STATUS_MAP[this.object.status] : STATUS_MAP[this.object.action],
requestForm: {
name: this.object.meta.approve_permission_name,
application: this.object.meta.recommend_applications,
systemuser: this.object.meta.recommend_system_users,
apply_date_expired: this.object.meta.apply_date_expired,
apply_date_start: this.object.meta.apply_date_start
},
comments: '',
assets: [],
apps_select2: {
multiple: true,
value: this.object.meta.recommend_applications,
ajax: {
url: `/api/v1/applications/applications/?oid=${(this.object.org_id === '')
? 'DEFAULT'
: this.object.org_id}&category=${this.object.meta.apply_category}&type=${this.object.meta.apply_type}`,
transformOption: (item) => {
return { label: item.name, value: item.id }
}
}
},
systemuser_select2: {
multiple: true,
value: this.object.meta.recommend_system_users,
ajax: {
url: `/api/v1/assets/system-users/?oid=${(this.object.org_id === '')
? 'DEFAULT'
: this.object.org_id
}&protocol=${(this.object.meta.apply_category === 'remote_app')
? 'rdp'
: this.object.meta.apply_type
}`,
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}
}
}
}
},
computed: {
detailCardItems() {
return [
{
key: this.$t('tickets.status'),
value: this.object.status,
formatter: (item, val) => {
return <el-tag type={this.statusMap.type} size='mini'> { this.statusMap.title }</el-tag>
}
},
{
key: this.$t('tickets.type'),
value: this.object.type_display
},
{
key: this.$t('tickets.user'),
value: this.object.applicant_display
},
{
key: this.$t('tickets.Assignees'),
value: this.object.assignees_display
},
{
key: this.$t('tickets.Assignee'),
value: (this.object.processor_display === 'No') ? '' : this.object.processor_display
},
{
key: this.$t('tickets.OrgName'),
value: this.object.org_name
},
{
key: this.$t('common.dateCreated'),
value: toSafeLocalDateStr(this.object.date_created)
},
{
key: this.$t('common.Comment'),
value: this.object.comment
}
]
},
specialCardItems() {
return [
// {
// key: this.$t('tickets.Assignee'),
// value: this.object.assignee_display
// },
{
key: this.$t('applications.appType'),
value: `${this.object.meta.apply_category_display} / ${this.object.meta.apply_type_display} `
},
{
key: this.$t('applications.appName'),
value: this.object.meta.apply_application_group.toString()
},
{
key: this.$t('tickets.SystemUser'),
value: this.object.meta.apply_system_user_group.toString()
},
{
key: this.$t('common.dateStart'),
value: toSafeLocalDateStr(this.object.meta.apply_date_start)
},
{
key: this.$t('common.dateExpired'),
value: toSafeLocalDateStr(this.object.meta.apply_date_expired)
}
]
},
assignedCardItems() {
return [
{
key: this.$t('applications.appName'),
value: this.object.meta.approve_applications_display
},
{
key: this.$t('tickets.SystemUser'),
value: this.object.meta.approve_system_users_display
},
{
key: this.$t('common.dateStart'),
value: toSafeLocalDateStr(this.object.meta.approve_date_start)
},
{
key: this.$t('common.dateExpired'),
value: toSafeLocalDateStr(this.object.meta.approve_date_expired)
}
]
},
hasActionPerm() {
return this.object.assignees.indexOf(this.$store.state.users.profile.id) !== -1
}
},
methods: {
formatTime(dateStr) {
return formatTime(getDateTimeStamp(dateStr))
},
toSafeLocalDateStr(dataStr) {
return toSafeLocalDateStr(dataStr)
},
reloadPage() {
window.location.reload()
},
handleApprove() {
if (this.requestForm.application.length === 0 || this.requestForm.systemuser.length === 0) {
return this.$message.error(this.$t('common.NeedAssetsAndSystemUserErrMsg'))
} else {
this.$axios.put(`/api/v1/tickets/tickets/${this.object.id}/approve/`, {
meta: {
approve_permission_name: this.requestForm.name,
approve_system_users: this.requestForm.systemuser,
approve_applications: this.requestForm.application,
approve_date_start: this.requestForm.apply_date_start,
approve_date_expired: this.requestForm.apply_date_expired
}
}).then(
() => {
this.$message.success(this.$t('common.updateSuccessMsg'))
this.reloadPage()
}
).catch(
() => this.$message.success(this.$t('common.updateErrorMsg'))
)
}
},
handleClose() {
const url = `/api/v1/tickets/tickets/${this.object.id}/close/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
},
handleReject() {
const url = `/api/v1/tickets/tickets/${this.object.id}/reject/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
}
}
}
</script>
<style scoped>
.assets{
margin-top: 14px;
}
.feed-activity-list .feed-element {
border-bottom: 1px solid #e7eaec;
}
.feed-element > .pull-left {
margin-right: 10px;
}
.feed-element .header-avatar {
width: 38px;
height: 38px;
}
.box {
margin-bottom: 15px;
}
</style>

View File

@@ -0,0 +1,50 @@
<template>
<GenericDetailPage :object.sync="ticket" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
<component :is="config.activeMenu" :object="ticket" />
</GenericDetailPage>
</template>
<script>
import { GenericDetailPage, TabPage } from '@/layout/components'
import TicketDetail from './TicketDetail'
export default {
components: {
GenericDetailPage,
TicketDetail,
TabPage
},
data() {
return {
ticket: { title: '', user_display: '', type_display: '', status: '', assignees_display: '', date_created: '' },
config: {
activeMenu: 'TicketDetail',
url: '',
submenu: [
{
title: this.$t('route.TicketDetail'),
name: 'TicketDetail'
}
],
actions: {
detailApiUrl: `/api/v1/tickets/tickets/${this.$route.params.id}/`
},
getObjectName: this.getObjectName,
hasRightSide: false
}
}
},
mounted() {
},
methods: {
getObjectName() {
return this.ticket.title
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,200 @@
<template>
<GenericCreateUpdatePage
v-bind="$data"
:perform-submit="performSubmit"
:create-success-next-route="createSuccessNextRoute"
/>
</template>
<script>
import { GenericCreateUpdatePage } from '@/layout/components'
import Select2 from '@/components/Select2'
import { getDaysFuture } from '@/utils/common'
import { Required } from '@/components/DataForm/rules'
export default {
components: {
GenericCreateUpdatePage
},
data() {
const now = new Date()
const date_expired = getDaysFuture(7, now).toISOString()
const date_start = now.toISOString()
return {
hasDetailInMsg: false,
initial: {
ips_or_not: true,
apply_date_expired: date_expired,
apply_date_start: date_start,
org_id: 'DEFAULT',
type: 'apply_application',
apply_actions: ['all', 'connect', 'updownload', 'upload_file', 'download_file']
},
fields: [
[this.$t('common.Basic'), ['title', 'type', 'org_id', 'assignees', 'comment']],
[this.$t('tickets.RequestPerm'), ['apply_category_type', 'apply_application_group', 'apply_system_user_group', 'apply_date_start', 'apply_date_expired']]
],
fieldsMeta: {
type: {
hidden: () => true,
el: {
disabled: true
}
},
apply_category_type: {
type: 'cascader',
label: this.$t('applications.appType'),
rules: [Required],
el: {
multiple: false,
options: [
{
label: this.$t(`applications.applicationsCategory.db`),
value: 'db',
children: [
{
label: 'MySQL',
value: 'mysql'
},
{
label: 'Oracle',
value: 'oracle'
},
{
label: 'PostgreSQL',
value: 'postgresql'
},
{
label: 'MariaDB',
value: 'mariadb'
}
]
},
{
label: this.$t(`applications.applicationsCategory.cloud`),
value: 'cloud',
children: [
{
label: 'Kubernetes',
value: 'k8s'
}
]
},
{
label: this.$t(`applications.applicationsCategory.remote_app`),
value: 'remote_app',
children: [
{
label: 'MySQL Workbench',
value: 'mysql_workbench'
},
{
label: 'vSphere Client',
value: 'vmware_client'
},
{
label: 'Custom',
value: 'custom'
}, {
label: 'Chrome',
value: 'chrome'
}
]
}
]
}
},
apply_application_group: {
label: this.$t('applications.appName'),
helpText: this.$t('tickets.helpText.application')
},
apply_system_user_group: {
label: this.$t('assets.SystemUser'),
helpText: this.$t('tickets.helpText.fuzzySearch')
},
apply_date_start: {
label: this.$t('common.DateStart'),
type: 'date-picker',
el: {
type: 'datetime'
}
},
apply_date_expired: {
label: this.$t('common.DateEnd'),
type: 'date-picker',
el: {
type: 'datetime'
}
},
org_id: {
component: Select2,
el: {
multiple: false,
options: this.$store.state.users.profile.user_all_orgs
},
on: {
changeOptions: ([event], updateForm) => {
this.fieldsMeta.assignees.el.ajax.url = `/api/v1/tickets/assignees/?org_id=${event[0].value}`
}
}
},
assignees: {
el: {
multiple: true,
value: [],
ajax: {
url: `/api/v1/tickets/assignees/?org_id=DEFAULT`,
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}
}
}
}
},
url: '/api/v1/tickets/tickets/?type=apply_application&action=open',
createSuccessNextRoute: {
name: 'TicketList'
}
}
},
methods: {
performSubmit(validValues) {
const meta = {}
const applications = validValues.apply_application_group
const systemUser = validValues.apply_system_user_group
if (applications) {
meta.apply_application_group = applications.split(',')
}
delete validValues['apply_application_group']
if (systemUser) {
meta.apply_system_user_group = systemUser.split(',')
}
delete validValues['apply_system_user_group']
meta.apply_category = validValues.apply_category_type[0]
meta.apply_type = validValues.apply_category_type[1]
delete validValues['apply_category_type']
meta.apply_date_start = validValues.apply_date_start
delete validValues['apply_date_start']
meta.apply_date_expired = validValues.apply_date_expired
delete validValues['apply_date_expired']
validValues['meta'] = meta
return this.$axios['post'](`/api/v1/tickets/tickets/open/?type=apply_application`, validValues)
}
}
}
</script>
<style lang="less" scoped>
.el-form ::v-deep .el-cascader {
width: 100%;
}
</style>

View File

@@ -3,6 +3,7 @@
:object="object"
:detail-card-items="detailCardItems"
:special-card-items="specialCardItems"
:assigned-card-items="assignedCardItems"
:approve="handleApprove"
:close="handleClose"
:reject="handleReject"
@@ -13,11 +14,26 @@
</div>
<template>
<el-form ref="requestForm" :model="requestForm" label-width="140px" label-position="left" class="assets">
<el-form-item :label="$t('perms.PermName')" required>
<el-input v-model="requestForm.name" />
</el-form-item>
<el-form-item :label="$t('tickets.Asset')" required>
<Select2 v-model="requestForm.asset" v-bind="asset_select2" style="width: 30% !important" />
<Select2 v-model="requestForm.asset" v-bind="asset_select2" style="width: 50% !important" />
</el-form-item>
<el-form-item :label="$t('tickets.SystemUser')" required>
<Select2 v-model="requestForm.systemuser" v-bind="systemuser_select2" style="width: 30% !important" />
<Select2 v-model="requestForm.systemuser" v-bind="systemuser_select2" style="width: 50% !important" />
</el-form-item>
<el-form-item :label="$t('common.dateStart')" required>
<el-date-picker
v-model="requestForm.apply_date_start"
type="datetime"
/>
</el-form-item>
<el-form-item :label="$t('common.dateExpired')" required>
<el-date-picker
v-model="requestForm.apply_date_expired"
type="datetime"
/>
</el-form-item>
<el-form-item :label="$t('assets.Action')" required>
<AssetPermissionFormActionField v-model="requestForm.actions" style="width: 30% !important" />
@@ -49,17 +65,20 @@ export default {
return {
statusMap: this.object.status === 'open' ? STATUS_MAP[this.object.status] : STATUS_MAP[this.object.action],
requestForm: {
asset: this.object.confirmed_assets,
systemuser: this.object.confirmed_system_users,
actions: this.object.actions
name: this.object.meta.approve_permission_name,
asset: this.object.meta.recommend_assets,
systemuser: this.object.meta.recommend_system_users,
actions: this.object.meta.apply_actions,
apply_date_expired: this.object.meta.apply_date_expired,
apply_date_start: this.object.meta.apply_date_start
},
comments: '',
assets: [],
asset_select2: {
multiple: true,
value: this.object.confirmed_assets,
value: this.object.meta.recommend_assets,
ajax: {
url: this.object.assets_waitlist_url,
url: `/api/v1/assets/assets/?oid=${(this.object.org_id === '') ? 'DEFAULT' : this.object.org_id}&protocol__in=rdp,vnc,ssh,telnet`,
transformOption: (item) => {
return { label: item.hostname, value: item.id }
}
@@ -67,9 +86,9 @@ export default {
},
systemuser_select2: {
multiple: true,
value: this.object.confirmed_system_users,
value: this.object.meta.recommend_system_users,
ajax: {
url: this.object.system_users_waitlist_url,
url: `/api/v1/assets/system-users/?oid=${(this.object.org_id === '') ? 'DEFAULT' : this.object.org_id}&protocol__in=rdp,vnc,ssh,telnet`,
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}
@@ -93,7 +112,7 @@ export default {
},
{
key: this.$t('tickets.user'),
value: this.object.user_display
value: this.object.applicant_display
},
{
key: this.$t('tickets.Assignees'),
@@ -101,7 +120,11 @@ export default {
},
{
key: this.$t('tickets.Assignee'),
value: this.object.assignee_display
value: (this.object.processor_display === 'No') ? '' : this.object.processor_display
},
{
key: this.$t('tickets.OrgName'),
value: this.object.org_name
},
{
key: this.$t('common.dateCreated'),
@@ -121,23 +144,43 @@ export default {
// },
{
key: this.$t('tickets.IP'),
value: this.object.ips
value: this.object.meta.apply_ip_group.toString()
},
{
key: this.$t('tickets.Hostname'),
value: this.object.hostname
value: this.object.meta.apply_hostname_group.toString()
},
{
key: this.$t('tickets.SystemUser'),
value: this.object.system_user
value: this.object.meta.apply_system_user_group.toString()
},
{
key: this.$t('common.dateStart'),
value: toSafeLocalDateStr(this.object.date_start)
value: toSafeLocalDateStr(this.object.meta.apply_date_start)
},
{
key: this.$t('common.dateExpired'),
value: toSafeLocalDateStr(this.object.date_expired)
value: toSafeLocalDateStr(this.object.meta.apply_date_expired)
}
]
},
assignedCardItems() {
return [
{
key: this.$t('assets.Asset'),
value: this.object.meta.approve_assets_display
},
{
key: this.$t('tickets.SystemUser'),
value: this.object.meta.approve_system_users_display
},
{
key: this.$t('common.dateStart'),
value: toSafeLocalDateStr(this.object.meta.approve_date_start)
},
{
key: this.$t('common.dateExpired'),
value: toSafeLocalDateStr(this.object.meta.approve_date_expired)
}
]
},
@@ -159,31 +202,32 @@ export default {
if (this.requestForm.asset.length === 0 || this.requestForm.systemuser.length === 0) {
return this.$message.error(this.$t('common.NeedAssetsAndSystemUserErrMsg'))
} else {
this.$axios.patch(`/api/v1/tickets/tickets/request-asset-perm/${this.object.id}/`, {
confirmed_system_users: this.requestForm.systemuser,
confirmed_assets: this.requestForm.asset,
actions: this.requestForm.actions
}).then(res => {
this.$axios.post(`/api/v1/tickets/tickets/request-asset-perm/${this.object.id}/approve/`).then(
() => {
this.$message.success(this.$t('common.updateSuccessMsg'))
this.reloadPage()
}
)
}).catch(
this.$axios.put(`/api/v1/tickets/tickets/${this.object.id}/approve/`, {
meta: {
approve_permission_name: this.requestForm.name,
approve_system_users: this.requestForm.systemuser,
approve_assets: this.requestForm.asset,
approve_actions: this.requestForm.actions,
approve_date_start: this.requestForm.apply_date_start,
approve_date_expired: this.requestForm.apply_date_expired
}
}).then(
() => {
this.$message.success(this.$t('common.updateSuccessMsg'))
this.reloadPage()
}
).catch(
() => this.$message.success(this.$t('common.updateErrorMsg'))
)
}
},
handleClose() {
const url = `/api/v1/tickets/tickets/request-asset-perm/${this.object.id}/close/`
const data = { status: 'closed' }
this.$axios.post(url, data).then(res => this.reloadPage()).catch(err => this.$message.error(err))
const url = `/api/v1/tickets/tickets/${this.object.id}/close/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
},
handleReject() {
const url = `/api/v1/tickets/tickets/request-asset-perm/${this.object.id}/reject/`
const data = { action: 'reject' }
this.$axios.post(url, data).then(res => this.reloadPage()).catch(err => this.$message.error(err))
const url = `/api/v1/tickets/tickets/${this.object.id}/reject/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
}
}
}

View File

@@ -19,12 +19,16 @@ export default {
ticket: { title: '', user_display: '', type_display: '', status: '', assignees_display: '', date_created: '' },
config: {
activeMenu: 'TicketDetail',
url: '',
submenu: [
{
title: this.$t('route.TicketDetail'),
name: 'TicketDetail'
}
],
actions: {
detailApiUrl: `/api/v1/tickets/tickets/${this.$route.params.id}/`
},
getObjectName: this.getObjectName,
hasRightSide: false
}

View File

@@ -17,33 +17,59 @@ export default {
const date_expired = getDaysFuture(7, now).toISOString()
const date_start = now.toISOString()
return {
// 工单创建 隐藏提示信息中的跳转连接
hasDetailInMsg: false,
initial: {
ips_or_not: true,
date_expired: date_expired,
date_start: date_start,
apply_date_expired: date_expired,
apply_date_start: date_start,
org_id: 'DEFAULT',
actions: ['all', 'connect', 'updownload', 'upload_file', 'download_file']
type: 'apply_asset',
apply_actions: ['all', 'connect', 'updownload', 'upload_file', 'download_file']
},
fields: [
[this.$t('common.Basic'), ['title', 'org_id', 'assignees', 'comment']],
[this.$t('tickets.RequestPerm'), ['ips', 'hostname', 'system_user', 'actions', 'date_start', 'date_expired']]
[this.$t('common.Basic'), ['title', 'type', 'org_id', 'assignees', 'comment']],
[this.$t('tickets.RequestPerm'), ['apply_ip_group', 'apply_hostname_group', 'apply_system_user_group', 'apply_actions', 'apply_date_start', 'apply_date_expired']]
],
fieldsMeta: {
ips: {
type: {
hidden: () => true,
el: {
disabled: true
}
},
apply_ip_group: {
label: this.$t('tickets.IPGroup'),
helpText: this.$t('tickets.helpText.ips')
},
hostname: {
apply_hostname_group: {
label: this.$t('assets.Hostname'),
helpText: this.$t('tickets.helpText.fuzzySearch')
},
system_user: {
apply_system_user_group: {
label: this.$t('assets.SystemUser'),
helpText: this.$t('tickets.helpText.fuzzySearch')
},
actions: {
apply_actions: {
label: this.$t('perms.Actions'),
component: AssetPermissionFormActionField,
helpText: this.$t('common.actionsTips')
},
apply_date_start: {
label: this.$t('common.DateStart'),
type: 'date-picker',
el: {
type: 'datetime'
}
},
apply_date_expired: {
label: this.$t('common.DateEnd'),
type: 'date-picker',
el: {
type: 'datetime'
}
},
org_id: {
component: Select2,
el: {
@@ -52,8 +78,7 @@ export default {
},
on: {
changeOptions: ([event], updateForm) => {
// console.log(event[0].value) // output: input value
this.fieldsMeta.assignees.el.ajax.url = `/api/v1/tickets/tickets/request-asset-perm/assignees/?org_id=${event[0].value}`
this.fieldsMeta.assignees.el.ajax.url = `/api/v1/tickets/assignees/?org_id=${event[0].value}`
}
}
},
@@ -62,7 +87,7 @@ export default {
multiple: true,
value: [],
ajax: {
url: '/api/v1/tickets/tickets/request-asset-perm/assignees/',
url: `/api/v1/tickets/assignees/?org_id=DEFAULT`,
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}
@@ -70,7 +95,7 @@ export default {
}
}
},
url: '/api/v1/tickets/tickets/request-asset-perm/',
url: '/api/v1/tickets/tickets/?type=apply_asset&action=open',
createSuccessNextRoute: {
name: 'TicketList'
}
@@ -78,14 +103,37 @@ export default {
},
methods: {
performSubmit(validValues) {
const ips = validValues.ips
const meta = {}
const ips = validValues.apply_ip_group
if (ips) {
validValues.ips = ips.split(',')
meta.apply_ip_group = ips.split(',')
}
if (ips === '') {
delete validValues['ips']
delete validValues['apply_ip_group']
}
return this.$axios['post'](this.url, validValues)
if (validValues.apply_hostname_group) {
meta.apply_hostname_group = validValues.apply_hostname_group.split(',')
delete validValues['apply_hostname_group']
}
if (validValues.apply_system_user_group) {
meta.apply_system_user_group = validValues.apply_system_user_group.split(',')
delete validValues['apply_system_user_group']
}
if (validValues.apply_actions) {
meta.apply_actions = validValues.apply_actions
delete validValues['apply_actions']
}
meta.apply_date_start = validValues.apply_date_start
delete validValues['apply_date_start']
meta.apply_date_expired = validValues.apply_date_expired
delete validValues['apply_date_expired']
validValues['meta'] = meta
return this.$axios['post'](`/api/v1/tickets/tickets/open/?type=apply_asset`, validValues)
}
}
}

View File

@@ -33,7 +33,7 @@ export default {
return [
{
key: this.$t('tickets.Applicant'),
value: this.object.user_display
value: this.object.applicant_display
},
{
key: this.$t('tickets.type'),
@@ -52,7 +52,7 @@ export default {
},
{
key: this.$t('tickets.Assignee'),
value: this.object.assignee_display
value: this.object.processor_display
},
{
key: this.$t('common.dateCreated'),

View File

@@ -16,7 +16,14 @@ export default {
},
data() {
return {
ticket: { title: '', user_display: '', type_display: '', status: '', assignees_display: '', date_created: '' },
ticket: {
title: '',
user_display: '',
type_display: '',
status: '',
assignees_display: '',
date_created: ''
},
config: {
activeMenu: 'TicketDetail',
submenu: [

View File

@@ -9,6 +9,7 @@
<script>
import { TabPage } from '@/layout/components'
import { mapGetters } from 'vuex'
import AssignedTicketList from './AssignedTicketList'
import MyTicketList from './MyTicketList'
import { getTicketOpenCount } from '@/api/ticket'
@@ -38,12 +39,17 @@ export default {
}
}
},
computed: {
...mapGetters([
'currentUser'
])
},
mounted() {
this.getTicketOpenCount()
},
methods: {
getTicketOpenCount() {
getTicketOpenCount(1).then(data => {
getTicketOpenCount(this.currentUser.id).then(data => {
this.assignedTicketCount = data.count
})
},

View File

@@ -32,8 +32,10 @@ export default {
sortable: 'custom',
formatterArgs: {
getRoute: function({ row }) {
if (row.type === 'request_asset') {
if (row.type === 'apply_asset') {
return 'AssetsTicketDetail'
} else if (row.type === 'apply_application') {
return 'AppsTicketDetail'
} else {
return 'TicketDetail'
}
@@ -41,14 +43,14 @@ export default {
}
},
{
prop: 'user_display',
prop: 'applicant_display',
label: this.$t('tickets.user'),
sortable: 'custom'
},
{
prop: 'type_display',
label: this.$t('tickets.type'),
width: '110px'
width: '160px'
},
{
prop: 'status',
@@ -56,6 +58,20 @@ export default {
align: 'center',
width: '90px',
sortable: 'custom',
formatter: row => {
if (row.status === 'open') {
return <el-tag type='primary' size='mini'style='align-items:center; display: flex; justify-content:center;'> { this.$t('tickets.OpenStatus') }</el-tag>
} else {
return <el-tag type='danger' size='mini'style='align-items:center; display: flex; justify-content:center;'> { this.$t('tickets.CloseStatus') }</el-tag>
}
}
},
{
prop: 'action',
label: this.$t('tickets.action'),
align: 'center',
width: '90px',
sortable: 'custom',
formatter: row => {
if (row.status === 'open') {
return <el-tag type='success' size='mini'style='align-items:center; display: flex; justify-content:center;'> { this.$t('tickets.Pending') }</el-tag>
@@ -69,7 +85,6 @@ export default {
return <el-tag type='info' size='mini' style='align-items:center; display: flex; justify-content:center;'> { this.$t('tickets.Closed') }</el-tag>
}
}
},
{
prop: 'date_created',
@@ -89,9 +104,9 @@ export default {
default: {
status: {
key: 'status',
label: this.$t('tickets.Status'),
label: this.$t('tickets.action'),
value: 'open',
valueLabel: this.$t('tickets.Open')
valueLabel: this.$t('tickets.Pending')
}
}
},
@@ -105,16 +120,20 @@ export default {
genExtraMoreActions() {
return [
{
name: '',
name: 'RequestAssetPerm',
title: this.$t('tickets.RequestAssetPerm'),
type: 'primary',
can: true,
callback: this.onCallback
callback: () => this.$router.push({ name: 'RequestAssetPermTicketCreateUpdate' })
},
{
name: 'RequestApplicationPerm',
title: this.$t('tickets.RequestApplicationPerm'),
type: 'primary',
can: true,
callback: () => this.$router.push({ name: 'RequestApplicationPermTicketCreateUpdate' })
}
]
},
onCallback() {
this.$router.push({ name: 'RequestAssetPermTicketCreateUpdate' })
}
}
}

View File

@@ -4,7 +4,7 @@
<i class="fa fa-comments" /> {{ $t('common.Message') }}
</div>
<template v-if="comments">
<div v-for="item in comments" :key="item.user_display + item.body" class="feed-activity-list">
<div v-for="item in comments" :key="item.id" class="feed-activity-list">
<div class="feed-element">
<a href="#" class="pull-left">
<el-avatar :src="imageUrl" class="header-avatar" />
@@ -117,7 +117,7 @@ export default {
},
getComment() {
this.loading = true
const url = `/api/v1/tickets/tickets/${this.object.id}/comments/`
const url = `/api/v1/tickets/comments/?ticket_id=${this.object.id}`
this.$axios.get(url).then(res => {
this.comments = res
}).catch(err => {
@@ -130,25 +130,22 @@ export default {
defaultApprove() {
this.createComment(function() {
})
const url = `/api/v1/tickets/tickets/${this.object.id}/`
const data = { action: 'approve' }
this.$axios.patch(url, data).then(res => this.reloadPage()).catch(err => this.$message.error(err))
const url = `/api/v1/tickets/tickets/${this.object.id}/approve/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
},
defaultReject() {
this.createComment(function() {})
const url = `/api/v1/tickets/tickets/${this.object.id}/`
const data = { action: 'reject' }
this.$axios.patch(url, data).then(res => this.reloadPage()).catch(err => this.$message.error(err))
const url = `/api/v1/tickets/tickets/${this.object.id}/reject/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
},
defaultClose() {
const url = `/api/v1/tickets/tickets/${this.object.id}/`
const data = { status: 'closed' }
this.$axios.patch(url, data).then(res => this.reloadPage()).catch(err => this.$message.error(err))
const url = `/api/v1/tickets/tickets/${this.object.id}/close/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
},
createComment(successCallback) {
const commentText = this.form.comments
const ticketId = this.object.id
const commentUrl = `/api/v1/tickets/tickets/${ticketId}/comments/`
const commentUrl = `/api/v1/tickets/comments/?ticket_id=${this.object.id}`
if (!commentText) { return }
const body = {
body: commentText,

View File

@@ -1,7 +1,7 @@
<template>
<IBox class="box">
<div slot="header" class="clearfix ibox-title">
<i class="fa fa-info-circle" /> {{ $t('common.BasicInfo') }}
<i class="fa fa-info-circle" /> {{ title }}
</div>
<div class="content">
<el-row :gutter="10">
@@ -54,6 +54,10 @@ export default {
detailCardItems: {
type: Array,
default: () => ([])
},
title: {
type: String,
default: ''
}
},
data() {

View File

@@ -1,7 +1,9 @@
<template>
<el-row>
<el-col :span="17">
<Details :detail-card-items="detailCardItems" :special-card-items="specialCardItems" />
<Details :detail-card-items="detailCardItems" :title="$t('common.BasicInfo')" />
<Details v-if="specialCardItems.length > 0" :detail-card-items="specialCardItems" :title="$t('common.ApplyInfo')" />
<Details v-if="object.action === 'approve' && assignedCardItems.length > 0" :detail-card-items="assignedCardItems" :title="$t('tickets.AssignedInfo')" />
<slot id="MoreDetails" />
<Comments :object="object" v-bind="$attrs" />
</el-col>
@@ -30,7 +32,12 @@ export default {
detailCardItems: {
type: Array,
default: () => ([])
},
assignedCardItems: {
type: Array,
default: () => ([])
}
},
data() {
return {}

View File

@@ -7,7 +7,7 @@
:description="`${this.$t('tickets.Applicant')}${object.user_display}`"
>
<div slot="description">
<div>{{ `${this.$t('tickets.Applicant')}${object.user_display}` }}</div>
<div>{{ `${this.$t('tickets.Applicant')}${object.applicant_display}` }}</div>
<div>{{ `${this.$t('common.dateCreated')}: ${toSafeLocalDateStr(object.date_created)}` }}</div>
</div>
</el-step>
@@ -20,7 +20,7 @@
:description="ticketSteps===STATUS.close ? `${this.$t('tickets.Assignee')}${object.assignee_display}`:'' "
>
<div v-if="ticketSteps===STATUS.close" slot="description">
<div>{{ `${this.$t('tickets.Assignee')}${object.assignee_display}` }}</div>
<div>{{ `${this.$t('tickets.Assignee')}${object.processor_display}` }}</div>
<div>{{ `${this.$t('common.dateFinished')}: ${toSafeLocalDateStr(object.date_updated)}` }}</div>
</div>
</el-step>

View File

@@ -22,8 +22,8 @@ export default {
initial: {
password_strategy: 0,
mfa_level: 0,
source: 'local',
role: 'User',
source: 'local',
org_roles: ['User'],
date_expired: getDayFuture(36500, new Date()).toISOString()
},

View File

@@ -18,7 +18,7 @@ export default {
data() {
return {
tableConfig: {
url: `/api/v1/perms/application-permissions/?user_id=${this.$route.params.id}&draw=1`,
url: `/api/v1/perms/application-permissions/?user_id=${this.object.id}&draw=1`,
columns: [
'name', 'category_display', 'users_amount', 'user_groups_amount',
'applications_amount', 'system_users_amount',
@@ -49,7 +49,9 @@ export default {
},
actions: {
formatterArgs: {
updateRoute: 'RemoteAppPermissionUpdate',
onUpdate: ({ row, col, cellValue }) => {
this.$router.push({ name: 'ApplicationPermissionUpdate', params: { id: cellValue }})
},
performDelete: ({ row, col }) => {
const id = row.id
const url = `/api/v1/perms/application-permissions/${id}/?user_id=${this.object.id}&draw=1`

View File

@@ -19,7 +19,7 @@ export default {
data() {
return {
tableConfig: {
url: `/api/v1/perms/asset-permissions/?user_id=${this.$route.params.id}`,
url: `/api/v1/perms/asset-permissions/?user_id=${this.object.id}`,
hasSelection: false,
hasTree: true,
columns: [

View File

@@ -18,7 +18,7 @@ export default {
data() {
return {
tableConfig: {
url: `/api/v1/perms/users/${this.$route.params.id}/applications/`,
url: `/api/v1/perms/users/${this.object.id}/applications/`,
columns: ['name', 'category_display', 'type', 'asset', 'comment'],
columnsMeta: {
name: {

View File

@@ -18,8 +18,8 @@ export default {
},
data() {
return {
treeUrl: `/api/v1/perms/users/${this.$route.params.id}/nodes/children/tree/?cache_policy=1`,
tableUrl: `/api/v1/perms/users/${this.$route.params.id}/assets/?cache_policy=1&all=1`
treeUrl: `/api/v1/perms/users/${this.object.id}/nodes/children/tree/?cache_policy=1`,
tableUrl: `/api/v1/perms/users/${this.object.id}/assets/?cache_policy=1&all=1`
}
}
}

View File

@@ -265,11 +265,11 @@ export default {
}
this.$message.success(successMsg)
} catch (error) {
let errorMsg = this.$t('common.bulkDeleteErrorMsg')
if (!this.currentOrgIsDefault) {
errorMsg = this.$t('common.bulkRemoveErrorMsg')
}
this.$message.error(errorMsg + error)
// let errorMsg = this.$t('common.bulkDeleteErrorMsg')
// if (!this.currentOrgIsDefault) {
// errorMsg = this.$t('common.bulkRemoveErrorMsg')
// }
// this.$message.error(errorMsg + error)
} finally {
instance.confirmButtonLoading = false
}

View File

@@ -21,6 +21,7 @@ export default {
is_periodic: true,
interval: 24
},
hasDetailInMsg: false,
fieldsMeta: {
crontab: {
hidden: (formValue) => {

Some files were not shown because too many files have changed in this diff Show More