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

This commit is contained in:
ibuler
2020-06-09 16:50:51 +08:00
7 changed files with 297 additions and 182 deletions

View File

@@ -10,31 +10,6 @@
<li id="m_del" class="rmenu" tabindex="-1" @click="removeTreeNode">
<i class="fa fa-minus-square" /> {{ this.$t('tree.DeleteNode') }}
</li>
<li class="divider" />
<li id="m_add_asset_to_node" class="rmenu" tabindex="-1" @click="addAssetToNode">
<i class="fa fa-clone" /> {{ this.$t('tree.AddAssetToNode') }}
</li>
<li id="m_move_asset_to_node" class="rmenu" tabindex="-1" @click="moveAssetToNode">
<i class="fa fa-scissors" /> {{ this.$t('tree.moveAssetToNode') }}
</li>
<li class="divider" />
<li id="m_update_node_asset_hardware_info" class="rmenu" tabindex="-1" @click="updateNodeAssetHardwareInfo">
<i class="fa fa-refresh" /> {{ this.$t('tree.updateNodeAssetHardwareInfo') }}
</li>
<li id="m_test_node_asset_connectivity" class="rmenu" tabindex="-1" @click="testNodeAssetConnectivity">
<i class="fa fa-link" /> {{ this.$t('tree.testNodeAssetConnectivity') }}
</li>
<li class="divider" />
<li id="m_show_asset_only_current_node" class="rmenu" tabindex="-1" @click="showAssetOnlyCurrentNode">
<i class="fa fa-indent" /> {{ this.$t('tree.showAssetOnlyCurrentNode') }}
</li>
<li id="m_show_asset_all_children_node" class="rmenu" tabindex="-1" @click="showAssetAllChildrenNode">
<i class="fa fa-align-justify" /> {{ this.$t('tree.showAssetAllChildrenNode') }}
</li>
<li class="divider" />
<li id="m_show_node_info" class="rmenu" tabindex="-1" @click="showNodeInfo">
<i class="fa fa-info-circle" /> {{ this.$t('tree.showNodeInfo') }}
</li>
<slot name="rMenu" />
</slot>
</DataZTree>
@@ -97,20 +72,7 @@ export default {
return this.$refs.dataztree.rMenu
}
},
mounted() {
this.decorateRMenu()
},
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')
}
},
editTreeNode: function() {
this.hideRMenu()
const currentNode = this.zTree.getSelectedNodes()[0]
@@ -129,14 +91,18 @@ export default {
// Request URL: http://localhost/api/v1/assets/assets/?node_id=d8212328-538d-41a6-bcfd-1e8cc7e3aed4&show_current_asset=null&draw=2&limit=15&offset=0&_=1587022917769
onSelected: function(event, treeNode) {
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
let combinator = '?'
if (this.setting.url.indexOf('?') !== -1) {
combinator = '&'
}
if (treeNode.meta.type === 'node') {
this.currentNode = treeNode
this.currentNodeId = treeNode.meta.node.id
this.$route.query['node'] = this.currentNodeId
this.$emit('urlChange', `${this.setting.url}?node_id=${treeNode.meta.node.id}&show_current_asset=${show_current_asset}`)
this.$emit('urlChange', `${this.setting.url}${combinator}node_id=${treeNode.meta.node.id}&show_current_asset=${show_current_asset}`)
} else if (treeNode.meta.type === 'asset') {
this.$route.query['asset'] = treeNode.meta.asset.id
this.$emit('urlChange', `${this.setting.url}?asset_id=${treeNode.meta.asset.id}&show_current_asset=${show_current_asset}`)
this.$emit('urlChange', `${this.setting.url}${combinator}asset_id=${treeNode.meta.asset.id}&show_current_asset=${show_current_asset}`)
}
},
removeTreeNode: function() {
@@ -154,77 +120,6 @@ export default {
this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
})
},
addAssetToNode: function() {
},
moveAssetToNode: function() {
},
updateNodeAssetHardwareInfo: function() {
this.hideRMenu()
const currentNode = this.zTree.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$axios.post(
`/api/v1/assets/nodes/${currentNode.meta.node.id}/tasks/`,
{ 'action': 'refresh' }
).then((res) => {
window.open(`/core/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
},
testNodeAssetConnectivity: function() {
this.hideRMenu()
const currentNode = this.zTree.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$axios.post(
`/api/v1/assets/nodes/${currentNode.meta.node.id}/tasks/`,
{ 'action': 'test' }
).then((res) => {
window.open(`/core/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
},
showAssetOnlyCurrentNode: function(event) {
console.log(1, event)
this.hideRMenu()
const currentNode = this.zTree.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$cookie.set('show_current_asset', '1', 1)
this.decorateRMenu()
this.$emit('urlChange', `${this.setting.url}?node_id=${currentNode.meta.node.id}&show_current_asset=1`)
},
showAssetAllChildrenNode: function() {
this.hideRMenu()
const currentNode = this.zTree.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$cookie.set('show_current_asset', '0', 1)
this.decorateRMenu()
this.$emit('urlChange', `${this.setting.url}?node_id=${currentNode.meta.node.id}&show_current_asset=0`)
},
showNodeInfo: function() {
this.hideRMenu()
const currentNode = this.zTree.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$axios.get(
`/api/v1/assets/nodes/${currentNode.meta.node.id}/`
).then(res => {
this.$emit('showNodeInfoDialog', res)
}).catch(error => {
this.$message.error(this.$t('common.getErrorMsg' + ' ' + error))
})
},
onRename: function(event, treeId, treeNode, isCancel) {
const url = `${this.treeSetting.nodeUrl}${this.currentNodeId}/`
if (isCancel) {
@@ -246,12 +141,15 @@ export default {
})
},
onBodyMouseDown: function(event) {
if (!(event.target.id === 'rMenu' || $(event.target).parents('#rMenu').length > 0)) {
const rMenuID = this.$refs.dataztree.$refs.ztree.iRMenuID
if (!(event.target.id === 'rMenu' || $(event.target).parents(`#${rMenuID}`).length > 0)) {
this.rMenu.css({ 'visibility': 'hidden' })
}
},
showRMenu: function(type, x, y) {
const offset = $('#ztree').offset()
const rMenuID = this.$refs.dataztree.$refs.ztree.iRMenuID
const zTreeID = this.$refs.dataztree.$refs.ztree.iZTreeID
const offset = $(`#${zTreeID}`).offset()
const scrollTop = document.querySelector('.treebox').scrollTop
x -= offset.left
// Tmp
@@ -259,7 +157,7 @@ export default {
x += document.body.scrollLeft
y += document.body.scrollTop + document.documentElement.scrollTop
this.rMenu.css({ 'top': y + 'px', 'left': x + 'px', 'visibility': 'visible' })
$('#rMenu ul').show()
$(`#${rMenuID} ul`).show()
$('body').bind('mousedown', this.onBodyMouseDown)
},
onRightClick: function(event, treeId, treeNode) {
@@ -368,11 +266,4 @@ export default {
.rmenu:hover{
background-color: #f5f7fa;
}
.divider{
margin: 1px 0;
list-style: none outside none;
background-color: #e5e5e5;
height: 1px
}
</style>

View File

@@ -1,16 +1,15 @@
<template>
<div>
<div class="treebox">
<ul id="ztree" class="ztree">
<ul :id="iZTreeID" class="ztree">
{{ this.$t('common.tree.Loading') }}...
</ul>
<div v-if="treeSetting.treeUrl===''">
{{ this.$t('common.tree.Empty') }}<a id="tree-refresh"><i class="fa fa-refresh" /></a>
</div>
</div>
<div id="rMenu">
<div :id="iRMenuID" class="rMenu">
<ul class="dropdown-menu menu-actions">
<li class="divider" />
<slot name="rMenu" />
</ul>
</div>
@@ -37,6 +36,8 @@ export default {
},
data() {
return {
iZTreeID: `zTree_${this._uid}`,
iRMenuID: `rMenu_${this._uid}`,
zTree: '',
rMenu: ''
}
@@ -61,7 +62,7 @@ export default {
name: this.$t('common.tree.Empty')
})
}
this.zTree = $.fn.zTree.init($('#ztree'), this.treeSetting, res)
this.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
if (this.treeSetting.showRefresh) {
this.rootNodeAddDom(
this.zTree,
@@ -70,7 +71,7 @@ export default {
}
if (this.treeSetting.showMenu) {
this.rMenu = $('#rMenu')
this.rMenu = $(`#${this.iRMenuID}`)
}
if (this.treeSetting.otherMenu) {
$('.menu-actions').append(this.otherMenu)
@@ -112,7 +113,7 @@ export default {
</script>
<style lang='less' scoped>
div#rMenu {
div.rMenu {
position: absolute;
visibility: hidden;
text-align: left;
@@ -129,7 +130,7 @@ export default {
opacity: .9;
border: none;
}
div#rMenu li{
div.rMenu li{
margin: 6px 0;
cursor: pointer;
list-style: none outside none;

View File

@@ -8,7 +8,6 @@
:setting="treeSetting"
class="auto-data-ztree"
@urlChange="handleUrlChange"
@showNodeInfoDialog="showNodeInfoDialog"
>
<div slot="rMenu" slot-scope="{data}">
<slot name="rMenu" :data="data" />
@@ -27,12 +26,6 @@
</slot>
</div>
</div>
<Dialog width="30%" :title="this.$t('assets.NodeInformation')" :visible.sync="nodeInfoDialog.show" :show-cancel="false" :show-confirm="false">
<el-row v-for="item in nodeInfoDialog.items" :key="'card-' + item.key" :gutter="10" class="item">
<el-col :span="6"><div class="item-label"><label>{{ item.label }}: </label></div></el-col>
<el-col :span="18"><div class="item-text">{{ item.value }}</div></el-col>
</el-row>
</Dialog>
</div>
</el-collapse-transition>
</template>
@@ -70,11 +63,7 @@ export default {
return {
iTableConfig: this.tableConfig,
iShowTree: this.showTree,
componentKey: 0,
nodeInfoDialog: {
show: false,
items: []
}
componentKey: 0
}
},
watch: {
@@ -93,15 +82,6 @@ export default {
},
getSelectedNodes: function() {
return this.$refs.AutoDataZTree.getSelectedNodes()
},
showNodeInfoDialog(node) {
this.nodeInfoDialog.show = true
this.nodeInfoDialog.items = [
{ key: 'id', label: 'ID', value: node.id },
{ key: 'name', label: this.$t('assets.Name'), value: node.name },
{ key: 'fullName', label: this.$t('assets.FullName'), value: node.full_value },
{ key: 'key', label: this.$t('assets.Key'), value: node.key }
]
}
}
}
@@ -131,11 +111,4 @@ export default {
overflow: auto;
/*border-right: solid 1px red;*/
}
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
</style>

View File

@@ -678,12 +678,12 @@
"DeleteNode": "删除节点",
"RenameNode": "重命名节点",
"AddAssetToNode": "添加资产到节点",
"moveAssetToNode": "移动资产到节点",
"updateNodeAssetHardwareInfo": "更新节点资产硬件信息",
"testNodeAssetConnectivity": "测试资产节点可连接性",
"showAssetOnlyCurrentNode": "仅显示当前节点资产",
"showAssetAllChildrenNode": "显示所有子节点资产",
"showNodeInfo": "显示节点详情"
"MoveAssetToNode": "移动资产到节点",
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息",
"TestNodeAssetConnectivity": "测试资产节点可连接性",
"ShowAssetOnlyCurrentNode": "仅显示当前节点资产",
"ShowAssetAllChildrenNode": "显示所有子节点资产",
"ShowNodeInfo": "显示节点详情"
},
"audits": {
"View": "查看",

View File

@@ -668,12 +668,12 @@
"DeleteNode": "Delete node",
"RenameNode": "Rename node",
"AddAssetToNode": "Add asset to node",
"moveAssetToNode": "move asset to node",
"updateNodeAssetHardwareInfo": "Update node asset hardware information",
"testNodeAssetConnectivity": "Test node asset connectivity",
"showAssetOnlyCurrentNode": "Show asset only current node",
"showAssetAllChildrenNode": "Show asset all children node",
"showNodeInfo": "Show node information"
"MoveAssetToNode": "move asset to node",
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information",
"TestNodeAssetConnectivity": "Test node asset connectivity",
"ShowAssetOnlyCurrentNode": "Show asset only current node",
"ShowAssetAllChildrenNode": "Show asset all children node",
"ShowNodeInfo": "Show node information"
},
"users": {
"LoginConfirm": "Login confirm",

View File

@@ -1,20 +1,72 @@
<template>
<div>
<GenericTreeListPage ref="TreeList" :table-config="tableConfig" :help-message="helpMessage" :header-actions="headerActions" :tree-setting="treeSetting">
<div slot="rMenu">
<li id="m_del" class="rmenu" tabindex="-1" @click="handleClick">
<i class="fa fa-minus-square" /> {{ this.$t('tree.Demo') }}
<li class="divider" />
<li id="m_add_asset_to_node" class="rmenu" tabindex="-1" @click="rMenuAddAssetToNode">
<i class="fa fa-clone" /> {{ this.$t('tree.AddAssetToNode') }}
</li>
<li id="m_move_asset_to_node" class="rmenu" tabindex="-1" @click="rMenuMoveAssetToNode">
<i class="fa fa-scissors" /> {{ this.$t('tree.MoveAssetToNode') }}
</li>
<li class="divider" />
<li id="m_update_node_asset_hardware_info" class="rmenu" tabindex="-1" @click="rMenuUpdateNodeAssetHardwareInfo">
<i class="fa fa-refresh" /> {{ this.$t('tree.UpdateNodeAssetHardwareInfo') }}
</li>
<li id="m_test_node_asset_connectivity" class="rmenu" tabindex="-1" @click="rMenuTestNodeAssetConnectivity">
<i class="fa fa-link" /> {{ this.$t('tree.TestNodeAssetConnectivity') }}
</li>
<li class="divider" />
<li id="m_show_asset_only_current_node" class="rmenu" tabindex="-1" @click="rMenuShowAssetOnlyCurrentNode">
<i class="fa fa-indent" /> {{ this.$t('tree.ShowAssetOnlyCurrentNode') }}
</li>
<li id="m_show_asset_all_children_node" class="rmenu" tabindex="-1" @click="rMenuShowAssetAllChildrenNode">
<i class="fa fa-align-justify" /> {{ this.$t('tree.ShowAssetAllChildrenNode') }}
</li>
<li class="divider" />
<li id="m_show_node_info" class="rmenu" tabindex="-1" @click="rMenuShowNodeInfo">
<i class="fa fa-info-circle" /> {{ this.$t('tree.ShowNodeInfo') }}
</li>
</div>
</GenericTreeListPage>
<Dialog width="30%" :title="this.$t('assets.NodeInformation')" :visible.sync="nodeInfoDialogSetting.dialogVisible" :show-cancel="false" :show-confirm="false">
<el-row v-for="item in nodeInfoDialogSetting.items" :key="'card-' + item.key" :gutter="10" class="item">
<el-col :span="6"><div class="item-label"><label>{{ item.label }}: </label></div></el-col>
<el-col :span="18"><div class="item-text">{{ item.value }}</div></el-col>
</el-row>
</Dialog>
<Dialog
v-if="assetTreeTableDialogSetting.dialogVisible"
:title="this.$t('assets.Assets')"
:visible.sync="assetTreeTableDialogSetting.dialogVisible"
width="70%"
top="1vh"
@confirm="assetTreeTableDialogHandleConfirm"
@cancel="assetTreeTableDialogHandleCancel"
>
<TreeTable
ref="TreeTable"
:tree-setting="assetTreeTableDialogSetting.treeSetting"
:table-config="assetTreeTableDialogSetting.tableConfig"
:header-actions="assetTreeTableDialogSetting.headerActions"
/>
</Dialog>
</div>
</template>
<script>
import GenericTreeListPage from '@/layout/components/GenericTreeListPage/index'
import { DetailFormatter, ActionsFormatter, BooleanFormatter } from '@/components/ListTable/formatters'
import $ from '@/utils/jquery-vendor'
import Dialog from '@/components/Dialog'
import TreeTable from '@/components/TreeTable'
export default {
components: {
GenericTreeListPage
GenericTreeListPage,
Dialog,
TreeTable
},
data() {
return {
@@ -145,19 +197,202 @@ export default {
}
]
},
helpMessage: this.$t('assets.AssetListHelpMessage')
helpMessage: this.$t('assets.AssetListHelpMessage'),
nodeInfoDialogSetting: {
dialogVisible: false,
items: []
},
assetTreeTableDialogSetting: {
dialogVisible: false,
assetsSelected: [],
action: '',
treeSetting: {
showMenu: false,
showRefresh: true,
showAssets: false,
url: '/api/v1/assets/assets/?fields_size=mini',
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: [
{
prop: 'hostname',
label: this.$t('assets.Hostname'),
sortable: true,
formatter: DetailFormatter,
formatterArgs: {
route: 'AssetDetail'
}
},
{
prop: 'ip',
label: this.$t('assets.ip'),
sortable: 'custom'
}
],
listeners: {
'toggle-row-selection': (isSelected, row) => {
if (isSelected) {
this.addRowToAssetsSelected(row)
} else {
this.removeRowFromAssetsSelected(row)
}
}
}
},
headerActions: {
hasLeftActions: false,
hasRightActions: false
}
}
}
},
mounted() {
this.decorateRMenu()
},
methods: {
handleClick() {
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')
}
},
hideRMenu() {
this.$refs.TreeList.hideRMenu()
console.log(this.$refs.TreeList.getSelectedNodes())
},
getSelectedNodes() {
return this.$refs.TreeList.getSelectedNodes()
},
rMenuAddAssetToNode: function() {
this.assetTreeTableDialogSetting.dialogVisible = true
this.assetTreeTableDialogSetting.action = 'add'
},
rMenuMoveAssetToNode: function() {
this.assetTreeTableDialogSetting.dialogVisible = true
this.assetTreeTableDialogSetting.action = 'move'
},
rMenuUpdateNodeAssetHardwareInfo: function() {
this.hideRMenu()
const currentNode = this.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$axios.post(
`/api/v1/assets/nodes/${currentNode.meta.node.id}/tasks/`,
{ 'action': 'refresh' }
).then((res) => {
window.open(`/core/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
},
rMenuTestNodeAssetConnectivity: function() {
this.hideRMenu()
const currentNode = this.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$axios.post(
`/api/v1/assets/nodes/${currentNode.meta.node.id}/tasks/`,
{ 'action': 'test' }
).then((res) => {
window.open(`/core/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
},
rMenuShowAssetOnlyCurrentNode: function() {
this.hideRMenu()
const currentNode = this.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$cookie.set('show_current_asset', '1', 1)
this.decorateRMenu()
const url = `${this.treeSetting.url}?node_id=${currentNode.meta.node.id}&show_current_asset=1`
this.$refs.TreeList.$refs.TreeTable.handleUrlChange(url)
},
rMenuShowAssetAllChildrenNode: function() {
this.hideRMenu()
const currentNode = this.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$cookie.set('show_current_asset', '0', 1)
this.decorateRMenu()
const url = `${this.treeSetting.url}?node_id=${currentNode.meta.node.id}&show_current_asset=0`
this.$refs.TreeList.$refs.TreeTable.handleUrlChange(url)
},
rMenuShowNodeInfo: function() {
this.hideRMenu()
const currentNode = this.getSelectedNodes()[0]
if (!currentNode) {
return
}
this.$axios.get(
`/api/v1/assets/nodes/${currentNode.meta.node.id}/`
).then(res => {
this.nodeInfoDialogSetting.dialogVisible = true
this.nodeInfoDialogSetting.items = [
{ key: 'id', label: 'ID', value: res.id },
{ key: 'name', label: this.$t('assets.Name'), value: res.name },
{ key: 'fullName', label: this.$t('assets.FullName'), value: res.full_value },
{ key: 'key', label: this.$t('assets.Key'), value: res.key }
]
}).catch(error => {
this.$message.error(this.$t('common.getErrorMsg' + ' ' + error))
})
},
addRowToAssetsSelected(row) {
const selectValueIndex = this.assetTreeTableDialogSetting.assetsSelected.indexOf(row.id)
if (selectValueIndex === -1) {
this.assetTreeTableDialogSetting.assetsSelected.push(row.id)
}
},
removeRowFromAssetsSelected(row) {
const selectValueIndex = this.assetTreeTableDialogSetting.assetsSelected.indexOf(row.id)
if (selectValueIndex > -1) {
this.assetTreeTableDialogSetting.assetsSelected.splice(selectValueIndex, 1)
}
},
assetTreeTableDialogHandleConfirm() {
const currentNode = this.getSelectedNodes()[0]
const assetsSelected = this.assetTreeTableDialogSetting.assetsSelected
if (!currentNode || assetsSelected.length === 0) {
return
}
let url = `/api/v1/assets/nodes/${currentNode.meta.node.id}/assets/add/`
if (this.assetTreeTableDialogSetting.action === 'move') {
url = `/api/v1/assets/nodes/${currentNode.meta.node.id}/assets/replace/`
}
this.$axios.put(
url, { assets: assetsSelected }
).then(res => {
this.assetTreeTableDialogSetting.dialogVisible = false
this.assetTreeTableDialogSetting.assetsSelected = []
$('#tree-refresh').trigger('click')
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
},
assetTreeTableDialogHandleCancel() {
this.assetTreeTableDialogSetting.dialogVisible = false
this.assetTreeTableDialogSetting.assetsSelected = []
}
}
}
</script>
<style>
<style lang="scss" scoped>
.rmenu {
font-size: 12px;
padding: 0 16px;
@@ -180,4 +415,18 @@ export default {
.rmenu:hover{
background-color: #f5f7fa;
}
.divider{
margin: 1px 0;
list-style: none outside none;
background-color: #e5e5e5;
height: 1px
}
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
</style>

View File

@@ -4,7 +4,7 @@
<script>
import ListTable from '@/components/ListTable/index'
import DisplayFormatter from '@/components/ListTable/formatters/DisplayFormatter'
export default {
components: {
ListTable
@@ -22,7 +22,8 @@ export default {
columns: ['name', 'ip', 'port', 'protocol', 'username', 'comment', 'actions'],
columnsMeta: {
name: {
sortable: 'custom'
sortable: 'custom',
formatter: DisplayFormatter
},
protocol: {
sortable: 'custom'