Merge branch 'v3' of github.com:jumpserver/lina into v3

This commit is contained in:
ibuler
2022-08-29 18:30:54 +08:00
20 changed files with 876 additions and 405 deletions

View File

@@ -96,6 +96,8 @@
"NoSQLProtocol": "非关系数据库"
},
"assets": {
"All": "所有",
"CloudPlatform": "云平台",
"AddAccount": "添加账号",
"Account": "账号",
"Defaults": "默认值",

View File

@@ -55,21 +55,21 @@ export default [
{
path: '',
name: 'HostList',
component: () => import('@/views/assets/Host/HostList.vue'),
component: () => import('@/views/assets/Asset/AssetList/HostList.vue'),
hidden: true,
meta: { title: i18n.t('route.HostList'), activeMenu: '/console/assets/assets' }
},
{
path: 'create',
name: 'HostCreate',
component: () => import('@/views/assets/Host/HostCreateUpdate.vue'),
component: () => import('@/views/assets/Asset/AssetCreateUpdate/HostCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetCreate'), activeMenu: '/console/assets/assets' }
},
{
path: ':id/update',
name: 'HostUpdate',
component: () => import('@/views/assets/Host/HostCreateUpdate.vue'),
component: () => import('@/views/assets/Asset/AssetCreateUpdate/HostCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/console/assets/assets' }
}
@@ -85,21 +85,111 @@ export default [
{
path: '',
name: 'DatabaseList',
component: () => import('@/views/assets/Database/HostList.vue'),
component: () => import('@/views/assets/Asset/AssetList/DatabaseList.vue'),
hidden: true,
meta: { title: i18n.t('route.HostList'), activeMenu: '/console/assets/assets' }
},
{
path: 'create',
name: 'DatabaseCreate',
component: () => import('@/views/assets/Database/DatabaseCreateUpdate.vue'),
component: () => import('@/views/assets/Asset/AssetCreateUpdate/DatabaseCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetCreate'), activeMenu: '/console/assets/assets' }
},
{
path: ':id/update',
name: 'DatabaseUpdate',
component: () => import('@/views/assets/Database/DatabaseCreateUpdate.vue'),
component: () => import('@/views/assets/Asset/AssetCreateUpdate/DatabaseCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/console/assets/assets' }
}
]
},
{
path: 'networking',
component: empty,
redirect: '',
hidden: true,
meta: { title: i18n.t('route.networking'), permissions: ['assets.view_asset'] },
children: [
{
path: '',
name: 'DatabaseList',
component: () => import('@/views/assets/Asset/AssetList/NetworkList.vue'),
hidden: true,
meta: { title: i18n.t('route.HostList'), activeMenu: '/console/assets/assets' }
},
{
path: 'create',
name: 'DatabaseCreate',
component: () => import('@/views/assets/Asset/AssetCreateUpdate/NetworkingCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetCreate'), activeMenu: '/console/assets/assets' }
},
{
path: ':id/update',
name: 'DatabaseUpdate',
component: () => import('@/views/assets/Asset/AssetCreateUpdate/NetworkingCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/console/assets/assets' }
}
]
},
{
path: 'CloudCreate',
component: empty,
redirect: '',
hidden: true,
meta: { title: i18n.t('route.networking'), permissions: ['assets.view_asset'] },
children: [
{
path: '',
name: 'CloudsPlatformList',
component: () => import('@/views/assets/Asset/AssetList/CloudsPlatformList.vue'),
hidden: true,
meta: { title: i18n.t('route.HostList'), activeMenu: '/console/assets/assets' }
},
{
path: 'create',
name: 'CloudCreate',
component: () => import('@/views/assets/Asset/AssetCreateUpdate/CloudsPlatformCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetCreate'), activeMenu: '/console/assets/assets' }
},
{
path: ':id/update',
name: 'CloudCreate',
component: () => import('@/views/assets/Asset/AssetCreateUpdate/CloudsPlatformCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/console/assets/assets' }
}
]
},
{
path: 'web',
component: empty,
redirect: '',
hidden: true,
meta: { title: i18n.t('route.networking'), permissions: ['assets.view_asset'] },
children: [
{
path: '',
name: 'WebList',
component: () => import('@/views/assets/Asset/AssetList/WebList.vue'),
hidden: true,
meta: { title: i18n.t('route.HostList'), activeMenu: '/console/assets/assets' }
},
{
path: 'create',
name: 'WebCreate',
component: () => import('@/views/assets/Asset/AssetCreateUpdate/WebCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetCreate'), activeMenu: '/console/assets/assets' }
},
{
path: ':id/update',
name: 'WebUpdate',
component: () => import('@/views/assets/Asset/AssetCreateUpdate/WebCreateUpdate.vue'),
hidden: true,
meta: { title: i18n.t('route.AssetUpdate'), activeMenu: '/console/assets/assets' }
}

View File

@@ -0,0 +1,72 @@
<template>
<GenericCreateUpdatePage v-bind="$data" />
</template>
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
import { assetFieldsMeta } from '@/views/assets/const'
export default {
name: 'DatabaseCreateUpdate',
components: {
GenericCreateUpdatePage
},
data() {
const nodesInitial = []
if (this.$route.query['node']) {
nodesInitial.push(this.$route.query.node)
}
const platformId = this.$route.query['platform'] || 1
return {
title: this.$t('assets.CreateDatabase'),
initial: {
is_active: true,
platform: parseInt(platformId),
protocols: ['mysql/22'],
nodes: nodesInitial
},
url: '/api/v1/assets/databases/',
createSuccessNextRoute: { name: 'AssetDetail' },
hasDetailInMsg: false,
fields: [
[this.$t('common.Basic'), ['name', 'ip', 'platform', 'db_name']],
[this.$t('assets.Protocols'), ['protocols']],
[this.$t('assets.Domain'), ['domain']],
[this.$t('assets.Node'), ['nodes']],
[this.$t('assets.Label'), ['labels']],
[this.$t('common.Other'), ['is_active', 'comment']]
],
fieldsMeta: assetFieldsMeta(this)
}
},
mounted() {
this.setPlatformInitial()
},
methods: {
async setPlatformInitial() {
const nodesInitial = []
if (this.$route.query['node']) {
nodesInitial.push(this.$route.query.node)
}
const platformId = this.$route.query['platform'] || 1
const url = `/api/v1/assets/platforms/${platformId}/`
this.platform = await this.$axios.get(url)
const initial = {
is_active: true,
platform: parseInt(platformId),
protocols: this.platform.protocols_default,
nodes: nodesInitial,
domain: this.platform.domain_default
}
const constraints = this.platform['type_constraints']
this.fieldsMeta.protocols.el.choices = constraints['protocols']
this.initial = initial
this.loading = false
}
}
}
</script>
<style>
</style>

View File

@@ -29,15 +29,39 @@ export default {
createSuccessNextRoute: { name: 'AssetDetail' },
hasDetailInMsg: false,
fields: [
[this.$t('common.Basic'), ['platform', 'hostname', 'ip', 'port', 'db_name']],
[this.$t('common.Basic'), ['name', 'ip', 'platform', 'db_name']],
[this.$t('assets.Protocols'), ['protocols']],
[this.$t('assets.Domain'), ['domain']],
[this.$t('assets.Node'), ['nodes']],
[this.$t('assets.Label'), ['labels']],
[this.$t('common.Other'), ['is_active', 'comment']]
],
fieldsMeta: {
...assetFieldsMeta
fieldsMeta: assetFieldsMeta(this)
}
},
mounted() {
this.setPlatformInitial()
},
methods: {
async setPlatformInitial() {
const nodesInitial = []
if (this.$route.query['node']) {
nodesInitial.push(this.$route.query.node)
}
const platformId = this.$route.query['platform'] || 1
const url = `/api/v1/assets/platforms/${platformId}/`
this.platform = await this.$axios.get(url)
const initial = {
is_active: true,
platform: parseInt(platformId),
protocols: this.platform.protocols_default,
nodes: nodesInitial,
domain: this.platform.domain_default
}
const constraints = this.platform['type_constraints']
this.fieldsMeta.protocols.el.choices = constraints['protocols']
this.initial = initial
this.loading = false
}
}
}

View File

@@ -4,7 +4,7 @@
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
import { assetFieldsMeta } from '../const'
import { assetFieldsMeta } from '../../const'
export default {
name: 'HostCreateUpdate',

View File

@@ -0,0 +1,65 @@
<template>
<GenericCreateUpdatePage v-if="!loading" v-bind="$data" />
</template>
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
import { assetFieldsMeta } from '../../const'
export default {
name: 'HostCreateUpdate',
components: {
GenericCreateUpdatePage
},
data() {
return {
loading: true,
platform: {},
initial: {
labels: []
},
fields: [
[this.$t('common.Basic'), ['name', 'ip', 'platform', 'domain']],
[this.$t('assets.Protocols'), ['protocols']],
[this.$t('assets.Node'), ['nodes']],
this.$route.params.id ? null : [this.$t('assets.Account'), ['accounts']],
[this.$t('assets.Label'), ['labels']],
[this.$t('common.Other'), ['is_active', 'comment']]
],
fieldsMeta: assetFieldsMeta(this),
url: '/api/v1/assets/assets/',
createSuccessNextRoute: { name: 'AssetDetail' },
hasDetailInMsg: false
}
},
mounted() {
this.setPlatformInitial()
},
methods: {
async setPlatformInitial() {
const nodesInitial = []
if (this.$route.query['node']) {
nodesInitial.push(this.$route.query.node)
}
const platformId = this.$route.query['platform'] || 1
const url = `/api/v1/assets/platforms/${platformId}/`
this.platform = await this.$axios.get(url)
const initial = {
is_active: true,
platform: parseInt(platformId),
protocols: this.platform.protocols_default,
nodes: nodesInitial,
domain: this.platform.domain_default
}
const constraints = this.platform['type_constraints']
this.fieldsMeta.protocols.el.choices = constraints['protocols']
this.initial = initial
this.loading = false
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,65 @@
<template>
<GenericCreateUpdatePage v-if="!loading" v-bind="$data" />
</template>
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
import { assetFieldsMeta } from '../../const'
export default {
name: 'HostCreateUpdate',
components: {
GenericCreateUpdatePage
},
data() {
return {
loading: true,
platform: {},
initial: {
labels: []
},
fields: [
[this.$t('common.Basic'), ['name', 'ip', 'platform', 'domain']],
[this.$t('assets.Protocols'), ['protocols']],
[this.$t('assets.Node'), ['nodes']],
this.$route.params.id ? null : [this.$t('assets.Account'), ['accounts']],
[this.$t('assets.Label'), ['labels']],
[this.$t('common.Other'), ['is_active', 'comment']]
],
fieldsMeta: assetFieldsMeta(this),
url: '/api/v1/assets/assets/',
createSuccessNextRoute: { name: 'AssetDetail' },
hasDetailInMsg: false
}
},
mounted() {
this.setPlatformInitial()
},
methods: {
async setPlatformInitial() {
const nodesInitial = []
if (this.$route.query['node']) {
nodesInitial.push(this.$route.query.node)
}
const platformId = this.$route.query['platform'] || 1
const url = `/api/v1/assets/platforms/${platformId}/`
this.platform = await this.$axios.get(url)
const initial = {
is_active: true,
platform: parseInt(platformId),
protocols: this.platform.protocols_default,
nodes: nodesInitial,
domain: this.platform.domain_default
}
const constraints = this.platform['type_constraints']
this.fieldsMeta.protocols.el.choices = constraints['protocols']
this.initial = initial
this.loading = false
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,295 @@
<template>
<div>
<TreeTable
ref="TreeList"
:table-config="tableConfig"
:help-message="helpMessage"
:header-actions="headerActions"
:tree-setting="treeSetting"
>
<TreeMenu
slot="rMenu"
:tree="treeRef"
@showAll="showAll"
/>
</TreeTable>
<AssetBulkUpdateDialog
:visible.sync="updateSelectedDialogSetting.visible"
v-bind="updateSelectedDialogSetting"
/>
<PlatformDialog :visible.sync="showPlatform" :category="category" />
</div>
</template>
<script>
import { TreeTable } from '@/components'
import { DetailFormatter, ActionsFormatter, TagsFormatter, ChoicesDisplayFormatter } from '@/components/TableFormatters'
import $ from '@/utils/jquery-vendor'
import { mapGetters } from 'vuex'
import { connectivityMeta } from '@/components/AccountListTable/const'
import AssetBulkUpdateDialog from './components/AssetBulkUpdateDialog'
import TreeMenu from './components/TreeMenu'
import PlatformDialog from './components/PlatformDialog'
export default {
components: {
TreeTable,
AssetBulkUpdateDialog,
TreeMenu,
PlatformDialog
},
data() {
const vm = this
return {
treeRef: null,
showPlatform: false,
category: 'all',
treeSetting: {
showMenu: true,
showRefresh: true,
showAssets: false,
showCreate: true,
showUpdate: true,
showDelete: true,
hasRightMenu: true,
showSearch: true,
customTreeHeader: true,
url: '/api/v1/assets/assets/',
nodeUrl: '/api/v1/assets/nodes/',
// ?assets=0不显示资产. =1显示资产
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=0'
},
tableConfig: {
url: '/api/v1/assets/assets/',
hasTree: true,
columns: [
'name', 'ip', 'public_ip', 'admin_user_display',
'protocols', 'platform', 'hardware_info', 'model',
'cpu_model', 'cpu_cores', 'cpu_count', 'cpu_vcpus',
'disk_info', 'disk_total', 'memory', 'os', 'os_arch',
'os_version', 'number', 'vendor', 'sn', 'is_active',
'connectivity', 'labels_display',
'created_by', 'date_created', 'comment', 'org_name', 'actions'
],
columnsShow: {
min: ['hostname', 'ip', 'actions'],
default: [
'hostname', 'ip', 'platform', 'protocols', 'hardware_info',
'connectivity', 'actions'
]
},
columnsMeta: {
type: { formatter: ChoicesDisplayFormatter },
category: { formatter: ChoicesDisplayFormatter },
name: {
formatter: DetailFormatter,
formatterArgs: {
route: 'AssetDetail'
},
showOverflowTooltip: true,
sortable: true
},
platform: {
sortable: true
},
protocols: {
formatter: function(row) {
return <span> {row.protocols.toString()} </span>
}
},
ip: {
sortable: 'custom',
width: '140px'
},
hardware_info: {
showOverflowTooltip: true
},
cpu_model: {
showOverflowTooltip: true
},
sn: {
showOverflowTooltip: true
},
comment: {
showOverflowTooltip: true
},
connectivity: connectivityMeta,
labels_display: {
formatter: TagsFormatter
},
actions: {
formatter: ActionsFormatter,
formatterArgs: {
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',
can: vm.$hasPerm('assets.refresh_assethardwareinfo'),
callback: function({ cellValue, tableData, row }) {
return this.$router.push({
name: 'AssetMoreInformationEdit',
params: { id: row.id }
})
}
}
]
}
}
}
},
headerActions: {
createRoute: () => {
return {
name: 'AssetCreate',
query: this.$route.query
}
},
onCreate: () => {
this.showPlatform = true
},
createInNewPage: true,
searchConfig: {
options: [
{ label: this.$t('assets.Label'), value: 'label' }
]
},
extraActions: [
{
name: this.$t('xpack.Cloud.CloudSync'),
title: this.$t('xpack.Cloud.CloudSync'),
has: () => vm.$hasPerm('xpack.view_account') && vm.$hasLicense(),
callback: () => this.$router.push({ name: 'CloudCenter' })
}
],
extraMoreActions: [
{
name: 'DeactiveSelected',
title: this.$t('assets.DeactiveSelected'),
type: 'primary',
can: ({ selectedRows }) => {
return selectedRows.length > 0 && vm.$hasPerm('assets.change_asset')
},
callback: function({ selectedRows }) {
const ids = selectedRows.map((v) => {
return { pk: v.id, is_active: false }
})
this.$axios.patch(`/api/v1/assets/assets/`, ids).then(res => {
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(err => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
})
}.bind(this)
},
{
name: 'ActiveSelected',
title: this.$t('assets.ActiveSelected'),
type: 'primary',
can: ({ selectedRows }) => {
return selectedRows.length > 0 && vm.$hasPerm('assets.change_asset')
},
callback: function({ selectedRows }) {
const ids = selectedRows.map((v) => {
return { pk: v.id, is_active: true }
})
this.$axios.patch(`/api/v1/assets/assets/`, ids).then(res => {
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(err => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
})
}.bind(this)
},
{
name: 'actionUpdateSelected',
title: this.$t('common.updateSelected'),
can: ({ selectedRows }) => {
return selectedRows.length > 0 &&
!vm.currentOrgIsRoot &&
vm.$hasPerm('assets.change_asset')
},
callback: ({ selectedRows }) => {
vm.updateSelectedDialogSetting.selectedRows = selectedRows
vm.updateSelectedDialogSetting.visible = true
}
},
{
name: 'RemoveFromCurrentNode',
title: this.$t('assets.RemoveFromCurrentNode'),
can: ({ selectedRows }) => {
if (!vm.$route.query.node) {
return false
}
return selectedRows.length > 0 &&
!vm.currentOrgIsRoot &&
vm.$hasPerm('assets.change_node')
},
callback: function({ selectedRows, reloadTable }) {
const assetsId = []
for (const item of selectedRows) {
assetsId.push(item.id)
}
const nodeId = this.$route.query.node
if (!nodeId) {
return
}
const url = `/api/v1/assets/nodes/${nodeId}/assets/remove/`
this.$axios.put(url, { assets: assetsId }).then(res => {
this.$message.success(this.$t('common.removeSuccessMsg'))
reloadTable()
}).catch(err => {
this.$message.error(this.$t('common.removeErrorMsg' + ' ' + err))
})
}.bind(this)
}
]
},
helpMessage: this.$t('assets.AssetListHelpMessage'),
updateSelectedDialogSetting: {
visible: false,
selectedRows: []
}
}
},
computed: {
...mapGetters(['currentOrgIsRoot'])
},
mounted() {
this.decorateRMenu()
this.treeSetting.hasRightMenu = !this.currentOrgIsRoot
this.treeSetting.showCreate = this.$hasPerm('assets.add_node')
this.treeSetting.showUpdate = this.$hasPerm('assets.change_node')
this.treeSetting.showDelete = this.$hasPerm('assets.delete_node')
this.treeRef = this.$refs.TreeList
},
methods: {
decorateRMenu() {
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
if (show_current_asset === '1') {
$('#m_show_asset_all_children_node').css('color', '#606266')
$('#m_show_asset_only_current_node').css('color', 'green')
} else {
$('#m_show_asset_all_children_node').css('color', 'green')
$('#m_show_asset_only_current_node').css('color', '#606266')
}
},
showAll({ node, showCurrentAsset }) {
this.$cookie.set('show_current_asset', showCurrentAsset, 1)
this.decorateRMenu()
const url = `${this.treeSetting.url}?node_id=${node.meta.data.id}&show_current_asset=${showCurrentAsset}`
this.$refs.TreeList.$refs.TreeTable.handleUrlChange(url)
}
}
}
</script>
<style lang="scss" scoped>
.asset-select-dialog >>> .transition-box:first-child {
background-color: #f3f3f3;
}
</style>

View File

@@ -0,0 +1,37 @@
<template>
<BaseList
:category="'cloud'"
:table-config="tableConfig"
:header-actions="headerActions"
/>
</template>
<script>
import BaseList from './components/BaseList'
export default {
components: {
BaseList
},
data() {
return {
tableConfig: {
url: '/api/v1/assets/clouds/',
columns: [
'name', 'ip', 'public_ip', 'admin_user_display',
'protocols', 'category', 'type', 'platform', 'sn',
'is_active', 'connectivity', 'labels_display',
'created_by', 'date_created', 'comment', 'org_name', 'actions'
]
},
headerActions: {
createRoute: 'CloudCreate'
}
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,42 @@
<template>
<BaseList
:category="'database'"
:table-config="tableConfig"
:header-actions="headerActions"
/>
</template>
<script>
import BaseList from './components/BaseList'
export default {
components: {
BaseList
},
data() {
return {
tableConfig: {
url: '/api/v1/assets/databases/',
columns: [
'name', 'ip', 'public_ip', 'admin_user_display',
'protocols', 'category', 'type', 'platform', 'sn',
'is_active', 'connectivity', 'labels_display',
'created_by', 'date_created', 'comment', 'org_name', 'actions'
],
columnsMeta: {
platform: {
sortable: false
}
}
},
headerActions: {
createRoute: 'DatabaseCreate'
}
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,31 @@
<template>
<BaseList
:category="'host'"
:table-config="tableConfig"
:header-actions="headerActions"
/>
</template>
<script>
import BaseList from './components/BaseList'
export default {
components: {
BaseList
},
data() {
return {
tableConfig: {
url: '/api/v1/assets/hosts/'
},
headerActions: {
createRoute: 'HostCreate'
}
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,31 @@
<template>
<BaseList
:category="'networking'"
:table-config="tableConfig"
:header-actions="headerActions"
/>
</template>
<script>
import BaseList from './components/BaseList'
export default {
components: {
BaseList
},
data() {
return {
tableConfig: {
url: '/api/v1/assets/networking/'
},
headerActions: {
createRoute: 'NetworkingCreate'
}
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,31 @@
<template>
<BaseList
:category="'web'"
:table-config="tableConfig"
:header-actions="headerActions"
/>
</template>
<script>
import BaseList from './components/BaseList'
export default {
components: {
BaseList
},
data() {
return {
tableConfig: {
url: '/api/v1/assets/web/'
},
headerActions: {
createRoute: 'WebCreate'
}
}
}
}
</script>
<style>
</style>

View File

@@ -1,40 +1,61 @@
<template>
<GenericListPage
:table-config="tableConfig"
:header-actions="headerActions"
:help-message="notice"
/>
<div>
<ListTable
:table-config="iTableConfig"
:header-actions="iHeaderActions"
/>
<PlatformDialog :visible.sync="showPlatform" :category="category" />
</div>
</template>
<script>
import { GenericListPage } from '@/layout/components'
import { ActionsFormatter, DetailFormatter, TagsFormatter } from '@/components/TableFormatters'
import { ListTable } from '@/components'
import { ActionsFormatter, DetailFormatter, TagsFormatter, ChoicesDisplayFormatter } from '@/components/TableFormatters'
import { connectivityMeta } from '@/components/AccountListTable/const'
import PlatformDialog from '../components/PlatformDialog'
export default {
components: {
GenericListPage
ListTable,
PlatformDialog
},
props: {
tableConfig: {
type: Object,
default: () => ({})
},
headerActions: {
type: Object,
default: () => ({})
},
category: {
type: String,
default: 'all'
}
},
data() {
const vm = this
return {
tableConfig: {
showPlatform: false,
defaultConfig: {
url: '/api/v1/assets/hosts/',
columns: [
'hostname', 'ip', 'public_ip', 'admin_user_display',
'name', 'ip', 'public_ip', 'admin_user_display',
'protocols', 'category', 'type', 'platform', 'sn',
'is_active', 'connectivity', 'labels_display',
'created_by', 'date_created', 'comment', 'org_name', 'actions'
],
columnsShow: {
min: ['hostname', 'ip', 'actions'],
min: ['name', 'ip', 'actions'],
default: [
'hostname', 'ip', 'platform', 'category', 'type',
'name', 'ip', 'platform', 'category', 'type',
'connectivity', 'actions'
]
},
columnsMeta: {
hostname: {
type: { formatter: ChoicesDisplayFormatter },
category: { formatter: ChoicesDisplayFormatter },
name: {
formatter: DetailFormatter,
formatterArgs: {
route: 'AssetDetail'
@@ -93,11 +114,22 @@ export default {
}
}
},
headerActions: {
defaultHeaderActions: {
hasMoreActions: false,
createRoute: 'HostCreate'
createRoute: 'HostCreate',
onCreate: () => {
this.showPlatform = true
}
}
}
},
computed: {
iTableConfig() {
return _.merge(this.defaultConfig, this.tableConfig)
},
iHeaderActions() {
return _.merge(this.defaultHeaderActions, this.headerActions)
}
}
}
</script>

View File

@@ -128,6 +128,7 @@ export default {
host: 'HostCreate',
database: 'DatabaseCreate',
cloud: 'CloudCreate',
web: 'WebCreate',
remote_app: 'RemoteAppCreate'
}
const route = mapper[platform.category.value] || 'HostCreate'

View File

@@ -1,306 +1,66 @@
<template>
<div>
<TabPage :active-menu.sync="tab.activeMenu" :submenu="tab.submenu" @tab-click="handleTabChange">
<TreeTable
v-if="show"
ref="TreeList"
:table-config="tableConfig"
:help-message="helpMessage"
:header-actions="headerActions"
:tree-setting="treeSetting"
:show-tree="showTree"
>
<TreeMenu
slot="rMenu"
:tree="treeRef"
@showAll="showAll"
/>
</TreeTable>
</TabPage>
<AssetBulkUpdateDialog
:visible.sync="updateSelectedDialogSetting.visible"
v-bind="updateSelectedDialogSetting"
/>
<PlatformDialog :visible.sync="showPlatform" :category="category" />
</div>
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu" />
</template>
<script>
import { TabPage } from '@/layout/components'
import { TreeTable } from '@/components'
import $ from '@/utils/jquery-vendor'
import { mapGetters } from 'vuex'
import { connectivityMeta } from '@/components/AccountListTable/const'
import AssetBulkUpdateDialog from './AssetBulkUpdateDialog'
import PlatformDialog from './PlatformDialog'
import TreeMenu from './TreeMenu'
import { Categories } from '@/views/assets/const'
import {
DetailFormatter,
ActionsFormatter,
TagsFormatter,
NestedObjectFormatter,
ChoicesDisplayFormatter
} from '@/components/TableFormatters'
export default {
name: 'Index',
components: {
AssetBulkUpdateDialog,
TreeMenu,
PlatformDialog,
TreeTable,
TabPage
},
data() {
const vm = this
return {
treeRef: null,
showPlatform: false,
showTree: true,
show: true,
tab: {
config: {
activeMenu: 'all',
submenu: [
{ name: 'all', title: '所有' },
...Categories
],
activeMenu: 'all'
},
category: '',
treeSetting: {
showMenu: true,
showRefresh: true,
showAssets: false,
showCreate: true,
showUpdate: true,
showDelete: true,
hasRightMenu: true,
url: '/api/v1/assets/assets/',
nodeUrl: '/api/v1/assets/nodes/',
// ?assets=0不显示资产. =1显示资产
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=0'
},
tableConfig: {
url: '/api/v1/assets/assets/',
hasTree: true,
columns: [
'name', 'ip', 'category', 'type', 'platform',
'labels', 'nodes', 'is_active', 'connectivity',
'created_by', 'date_created', 'comment', 'org_name',
'actions'
],
columnsShow: {
min: ['name', 'ip', 'actions'],
default: [
'name', 'ip', 'platform', 'category', 'type',
'connectivity', 'actions'
]
},
columnsMeta: {
type: { formatter: ChoicesDisplayFormatter },
category: { formatter: ChoicesDisplayFormatter },
name: {
formatter: DetailFormatter,
formatterArgs: {
route: 'AssetDetail'
},
showOverflowTooltip: true,
sortable: true
},
ip: { sortable: 'custom', width: '140px' },
comment: { showOverflowTooltip: true },
connectivity: connectivityMeta,
labels_display: { formatter: TagsFormatter },
nodes: { formatter: NestedObjectFormatter },
actions: {
formatter: ActionsFormatter,
formatterArgs: {
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',
can: vm.$hasPerm('assets.refresh_assethardwareinfo'),
callback: function({ cellValue, tableData, row }) {
return this.$router.push({
name: 'AssetMoreInformationEdit',
params: { id: row.id }
})
}
}
]
}
}
}
},
headerActions: {
createRoute: () => {
return {
name: 'AssetCreate',
query: this.$route.query
}
},
onCreate: () => {
this.showPlatform = true
},
createInNewPage: true,
searchConfig: {
options: [
{ label: this.$t('assets.Label'), value: 'label' }
]
},
extraActions: [
{
name: this.$t('xpack.Cloud.CloudSync'),
title: this.$t('xpack.Cloud.CloudSync'),
has: () => vm.$hasPerm('xpack.view_account') && vm.$hasLicense(),
callback: () => this.$router.push({ name: 'CloudCenter' })
}
],
extraMoreActions: [
{
name: 'DeactiveSelected',
title: this.$t('assets.DeactiveSelected'),
type: 'primary',
can: ({ selectedRows }) => {
return selectedRows.length > 0 && vm.$hasPerm('assets.change_asset')
},
callback: function({ selectedRows }) {
const ids = selectedRows.map((v) => {
return { pk: v.id, is_active: false }
})
this.$axios.patch(`/api/v1/assets/assets/`, ids).then(res => {
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(err => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
})
}.bind(this)
title: this.$t('assets.All'),
name: 'all',
component: () => import('@/views/assets/Asset/AssetList/AllList.vue')
},
{
name: 'ActiveSelected',
title: this.$t('assets.ActiveSelected'),
type: 'primary',
can: ({ selectedRows }) => {
return selectedRows.length > 0 && vm.$hasPerm('assets.change_asset')
},
callback: function({ selectedRows }) {
const ids = selectedRows.map((v) => {
return { pk: v.id, is_active: true }
})
this.$axios.patch(`/api/v1/assets/assets/`, ids).then(res => {
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(err => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
})
}.bind(this)
title: this.$t('applications.host'),
name: 'host',
hidden: () => false,
component: () => import('@/views/assets/Asset/AssetList/HostList.vue')
},
{
name: 'actionUpdateSelected',
title: this.$t('common.updateSelected'),
can: ({ selectedRows }) => {
return selectedRows.length > 0 &&
!vm.currentOrgIsRoot &&
vm.$hasPerm('assets.change_asset')
},
callback: ({ selectedRows }) => {
vm.updateSelectedDialogSetting.selectedRows = selectedRows
vm.updateSelectedDialogSetting.visible = true
}
title: this.$t('route.NetworkDevices'),
name: 'networking',
hidden: () => false,
component: () => import('@/views/assets/Asset/AssetList/NetworkList.vue')
},
{
name: 'RemoveFromCurrentNode',
title: this.$t('assets.RemoveFromCurrentNode'),
can: ({ selectedRows }) => {
if (!vm.$route.query.node) {
return false
}
return selectedRows.length > 0 &&
!vm.currentOrgIsRoot &&
vm.$hasPerm('assets.change_node')
},
callback: function({ selectedRows, reloadTable }) {
const assetsId = []
for (const item of selectedRows) {
assetsId.push(item.id)
}
const nodeId = this.$route.query.node
if (!nodeId) {
return
}
const url = `/api/v1/assets/nodes/${nodeId}/assets/remove/`
this.$axios.put(url, { assets: assetsId }).then(res => {
this.$message.success(this.$t('common.removeSuccessMsg'))
reloadTable()
}).catch(err => {
this.$message.error(this.$t('common.removeErrorMsg' + ' ' + err))
})
}.bind(this)
title: this.$t('route.Databases'),
name: 'database',
hidden: () => false,
component: () => import('@/views/assets/Asset/AssetList/DatabaseList.vue')
},
{
title: this.$t('assets.CloudPlatform'),
name: 'cloud',
hidden: () => false,
component: () => import('@/views/assets/Asset/AssetList/CloudsPlatformList.vue')
},
{
title: 'Web',
name: 'web',
hidden: () => false,
component: () => import('@/views/assets/Asset/AssetList/WebList.vue')
}
]
},
helpMessage: this.$t('assets.AssetListHelpMessage'),
updateSelectedDialogSetting: {
visible: false,
selectedRows: []
}
}
},
computed: {
...mapGetters(['currentOrgIsRoot'])
},
mounted() {
this.decorateRMenu()
this.treeSetting.hasRightMenu = !this.currentOrgIsRoot
this.treeSetting.showCreate = this.$hasPerm('assets.add_node')
this.treeSetting.showUpdate = this.$hasPerm('assets.change_node')
this.treeSetting.showDelete = this.$hasPerm('assets.delete_node')
this.treeRef = this.$refs.TreeList
this.handleTabChange()
},
methods: {
decorateRMenu() {
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
if (show_current_asset === '1') {
$('#m_show_asset_all_children_node').css('color', '#606266')
$('#m_show_asset_only_current_node').css('color', 'green')
} else {
$('#m_show_asset_all_children_node').css('color', 'green')
$('#m_show_asset_only_current_node').css('color', '#606266')
}
},
showAll({ node, showCurrentAsset }) {
this.$cookie.set('show_current_asset', showCurrentAsset, 1)
this.decorateRMenu()
const url = `${this.treeSetting.url}?node_id=${node.meta.data.id}&show_current_asset=${showCurrentAsset}`
this.$refs.TreeList.$refs.TreeTable.handleUrlChange(url)
},
handleTabChange(item) {
const category = item ? item.name : this.tab.activeMenu
this.category = category
console.log('category: ', category)
this.show = false
setTimeout(() => {
let url = '/api/v1/assets/assets/'
const showTree = category === 'all'
if (category !== 'all') {
url = `${url}?category=${category}`
}
this.treeSetting.url = url
this.showTree = showTree
this.tableConfig.url = url
this.show = true
}, 100)
}
}
}
</script>
<style lang="scss" scoped>
.asset-select-dialog >>> .transition-box:first-child {
background-color: #f3f3f3;
}
<style scoped>
</style>

View File

@@ -1,107 +0,0 @@
<template>
<GenericListPage
:table-config="tableConfig"
:header-actions="headerActions"
:help-message="notice"
/>
</template>
<script>
import { GenericListPage } from '@/layout/components'
import { ActionsFormatter, DetailFormatter, TagsFormatter } from '@/components/TableFormatters'
import { connectivityMeta } from '@/components/AccountListTable/const'
export default {
components: {
GenericListPage
},
data() {
const vm = this
return {
tableConfig: {
url: '/api/v1/assets/hosts/',
columns: [
'hostname', 'ip', 'public_ip', 'admin_user_display',
'protocols', 'category', 'type', 'platform', 'sn',
'is_active', 'connectivity', 'labels_display',
'created_by', 'date_created', 'comment', 'org_name', 'actions'
],
columnsShow: {
min: ['hostname', 'ip', 'actions'],
default: [
'hostname', 'ip', 'platform', 'category', 'type',
'connectivity', 'actions'
]
},
columnsMeta: {
hostname: {
formatter: DetailFormatter,
formatterArgs: {
route: 'AssetDetail'
},
showOverflowTooltip: true,
sortable: true
},
platform: {
sortable: true
},
protocols: {
formatter: function(row) {
return <span> {row.protocols.toString()} </span>
}
},
ip: {
sortable: 'custom',
width: '140px'
},
hardware_info: {
showOverflowTooltip: true
},
cpu_model: {
showOverflowTooltip: true
},
sn: {
showOverflowTooltip: true
},
comment: {
showOverflowTooltip: true
},
connectivity: connectivityMeta,
labels_display: {
formatter: TagsFormatter
},
actions: {
formatter: ActionsFormatter,
formatterArgs: {
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',
can: vm.$hasPerm('assets.refresh_assethardwareinfo'),
callback: function({ cellValue, tableData, row }) {
return this.$router.push({ name: 'AssetMoreInformationEdit', params: { id: row.id }})
}
}
]
}
}
}
},
headerActions: {
hasMoreActions: false,
createRoute: 'HostCreate'
}
}
}
}
</script>
<style>
</style>