perf: 修改 tree menu (#2536)

* perf: 修改 ztree

* perf: 修改 tree menu

---------

Co-authored-by: ibuler <ibuler@qq.com>
This commit is contained in:
fit2bot
2023-02-06 16:41:42 +08:00
committed by GitHub
parent 5cd45f3493
commit 291b15956b
12 changed files with 180 additions and 138 deletions

View File

@@ -235,10 +235,11 @@ export default {
setDefaultFormatterIfNeed(col) {
if (!col.formatter) {
col.formatter = (row, column, cellValue) => {
const value = cellValue || '-'
let value = cellValue
let padding = '0'
if (value === '-') {
if (!value && value !== 0) {
padding = '6px'
value = '-'
}
return <span style={{ marginLeft: padding }}>{value}</span>
}

View File

@@ -169,7 +169,9 @@ export default {
treeNode.meta.data = res
this.zTree.updateNode(treeNode)
this.$message.success(this.$tc('common.updateSuccessMsg'))
}).finally(() => { this.refreshTree() })
}).finally(() => {
this.refreshTree()
})
},
onBodyMouseDown: function(event) {
const rMenuID = this.$refs.dataztree.$refs.ztree.iRMenuID
@@ -305,11 +307,13 @@ export default {
text-decoration: none;
background-color: #f5f5f5;
}
.rmenu:hover{
.rmenu:hover {
background-color: #f5f7fa;
}
.data-z-tree >>> .fa {
width: 10px;
margin-right: 3px;
}
</style>

View File

@@ -19,9 +19,9 @@
<input
id="searchInput"
v-model="treeSearchValue"
type="text"
autocomplete="off"
class="tree-input"
type="text"
>
</a>
<span v-if="treeSetting.showRefresh" class="icon-refresh" @click="refresh">
@@ -29,10 +29,10 @@
</span>
</span>
</div>
<ul v-show="loading" class="ztree">
<ul v-if="loading" class="ztree">
{{ this.$t('common.tree.Loading') }}...
</ul>
<ul v-show="!loading" :id="iZTreeID" class="ztree" />
<ul v-else :id="iZTreeID" class="ztree tree-obj" />
<div v-if="treeSetting.treeUrl===''" class="tree-empty">
{{ this.$t('common.tree.Empty') }}
</div>
@@ -42,10 +42,10 @@
<el-input
v-if="treeSetting.showSearch && showTreeSearch"
v-model="treeSearchValue"
:placeholder="$tc('common.Search')"
class="fixed-tree-search"
prefix-icon="fa fa-search"
size="mini"
class="fixed-tree-search"
:placeholder="$tc('common.Search')"
@input="treeSearchHandle"
>
<span slot="suffix">
@@ -62,7 +62,7 @@
<ul v-show="loading" class="ztree">
{{ this.$t('common.tree.Loading') }}...
</ul>
<ul v-show="!loading" :id="iZTreeID" class="ztree" />
<ul v-show="!loading" :id="iZTreeID" :key="iZTreeID" class="ztree" />
<div v-if="treeSetting.treeUrl===''" class="tree-empty">
{{ this.$t('common.tree.Empty') }}
<a id="tree-refresh"><i class="fa fa-refresh" /></a>
@@ -123,18 +123,25 @@ export default {
$.fn.zTree.destroy(this.iZTreeID)
},
methods: {
initTree: function() {
async initTree(refresh = false) {
const vm = this
let treeUrl
if (this.init) {
this.loading = true
}
if (this.init && this.treeSetting.treeUrl.indexOf('/perms/') !== -1 && this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1) {
treeUrl = (this.treeSetting.treeUrl.indexOf('?') === -1) ? `${this.treeSetting.treeUrl}?rebuild_tree=1` : `${this.treeSetting.treeUrl}&rebuild_tree=1`
this.loading = true
if (refresh && this.treeSetting.treeUrl.indexOf('/perms/') !== -1 &&
this.treeSetting.treeUrl.indexOf('rebuild_tree') === -1
) {
treeUrl = (this.treeSetting.treeUrl.indexOf('?') === -1)
? `${this.treeSetting.treeUrl}?rebuild_tree=1`
: `${this.treeSetting.treeUrl}&rebuild_tree=1`
} else {
treeUrl = this.treeSetting.treeUrl
}
this.$axios.get(treeUrl, {
if (refresh) {
$.fn.zTree.destroy(this.iZTreeID)
}
let res = await this.$axios.get(treeUrl, {
'axios-retry': {
retries: 20,
retryCondition: e => {
@@ -145,36 +152,29 @@ export default {
return 5000
}
}
}).then(res => {
if (!res) res = []
if (res.length === 0) {
res.push({
name: this.$t('common.tree.Empty')
})
}
this.treeSetting.treeUrl = treeUrl
if (this.init) {
vm.zTree.destroy()
}
this.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
if (!this.treeSetting.customTreeHeader) {
const rootNode = this.zTree.getNodes()[0]
this.rootNodeAddDom(rootNode)
}
// 手动上报事件, Tree加载完成
this.$emit('TreeInitFinish', this.zTree)
if (this.treeSetting.showMenu) {
this.rMenu = $(`#${this.iRMenuID}`)
}
if (this.treeSetting.otherMenu) {
$('.menu-actions').append(this.otherMenu)
}
}).finally(_ => {
vm.loading = false
vm.init = true
})
vm.loading = false
if (!res) res = []
if (res?.length === 0) {
res?.push({
name: this.$t('common.tree.Empty')
})
}
this.treeSetting.treeUrl = treeUrl
vm.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
if (!this.treeSetting.customTreeHeader) {
const rootNode = this.zTree.getNodes()[0]
this.rootNodeAddDom(rootNode)
}
// 手动上报事件, Tree加载完成
this.$emit('TreeInitFinish', this.zTree)
if (this.treeSetting.showMenu) {
this.rMenu = $(`#${this.iRMenuID}`)
}
if (this.treeSetting?.otherMenu) {
$('.menu-actions').append(this.otherMenu)
}
},
onSearch() {
this.showTreeSearch = !this.showTreeSearch
@@ -187,7 +187,7 @@ export default {
rootNodeAddDom(rootNode) {
const { showSearch, showRefresh } = this.treeSetting
const searchIcon = `
<a class="tree-action-btn" onclick="onSearch()">
<a class="tree-action-btn" id='search-btn' onclick="onSearch()">
<i class='fa fa-search tree-banner-icon'></i>
</a>`
const refreshIcon = `
@@ -204,16 +204,13 @@ export default {
$rootNodeRef.after(icons)
}
},
refresh() {
async refresh() {
this.treeSearchValue = ''
const result = this.treeSetting?.callback?.refresh()
if (result && result.then) {
result.finally(() => {
this.initTree()
})
} else {
this.initTree()
if (this.treeSetting?.callback?.refresh) {
await this.treeSetting.callback.refresh()
}
this.zTree.destroy()
setTimeout(() => this.initTree(true), 200)
},
treeSearch() {
const searchIcon = document.getElementById(`searchIcon`)
@@ -374,7 +371,6 @@ export default {
}
searchNode.open = true
})
return
}
}
@@ -455,7 +451,8 @@ export default {
.treebox {
height: 70vh;
background-color: transparent;
>>> .ztree {
> > > .ztree {
overflow: auto;
background-color: transparent;
height: calc(100% - 50px);
@@ -464,7 +461,7 @@ export default {
background-color: transparent !important;
.button {
background-color: rgba(0,0,0,0);
background-color: rgba(0, 0, 0, 0);
}
ul {
@@ -589,21 +586,21 @@ export default {
.fixed-tree-search {
margin-bottom: 10px;
& >>> .el-input__inner {
& > > > .el-input__inner {
border-radius: 4px;
background: #fafafa;
padding-right: 32px;
}
& >>> .el-input__suffix {
& > > > .el-input__suffix {
padding-right: 8px;
}
& >>> .el-input__prefix {
& > > > .el-input__prefix {
padding-left: 6px;
}
& >>> .el-input__suffix-inner {
& > > > .el-input__suffix-inner {
line-height: 30px;
}
}

View File

@@ -1,17 +1,17 @@
<template>
<div style="font-size: 12px" class="code-editor">
<div class="code-editor" style="font-size: 12px">
<div class="toolbar">
<div
v-for="(item,index) in toolbar.left"
:key="index"
style="display: inline-block; margin: 0 2px"
>
<el-tooltip :content="item.tip" placement="top" :disabled="!item.tip">
<el-tooltip :content="item.tip" :disabled="!item.tip" placement="top">
<el-button
v-if="item.type ==='button'"
size="mini"
:type="item.el&&item.el.type"
:disabled="item.disabled"
:type="item.el&&item.el.type"
size="mini"
@click="item.callback()"
>
<i :class="item.icon" style="margin-right: 4px;" />{{ item.name }}
@@ -20,9 +20,9 @@
<el-autocomplete
v-if="item.type === 'input' && item.el.autoComplete"
v-model="item.value"
size="mini"
class="inline-input"
:fetch-suggestions="item.el.query"
class="inline-input"
size="mini"
@select="item.callback(item.value)"
/>
@@ -34,13 +34,13 @@
v-if="item.type==='select' && item.el && item.el.create"
:key="index"
v-model="item.value"
class="autoWidth-select"
size="mini"
default-first-option
:multiple="item.el.multiple"
:allow-create="item.el.create || false"
:filterable="item.el.create || false"
:multiple="item.el.multiple"
:placeholder="item.name"
class="autoWidth-select"
default-first-option
size="mini"
@change="item.callback(item.value)"
>
<template slot="prefix">
@@ -50,8 +50,8 @@
v-for="(option,id) in item.options"
:key="id"
:label="option.label"
:value="option.value"
:title="option.value"
:value="option.value"
/>
</el-select>
</div>
@@ -63,7 +63,7 @@
item.callback(command)
}"
>
<el-button type="default" size="mini">
<el-button size="mini" type="default">
<b>{{ item.name }}:</b> {{ getLabel(item.value, item.options) }} <i
class="el-icon-arrow-down el-icon--right"
/>
@@ -78,14 +78,14 @@
<el-switch
v-if="item.type === 'switch'"
v-model="item.value"
:disabled="item.disabled"
:active-text="item.name"
:disabled="item.disabled"
@change="item.callback( item.value)"
/>
</el-tooltip>
</div>
<div style="float: right" class="right-side">
<div class="right-side" style="float: right">
<div
v-for="(item,index) in toolbar.right"
:key="index"
@@ -94,10 +94,10 @@
<el-tooltip :content="item.tip">
<el-button
v-if="item.type ==='button'"
size="mini"
type="default"
:disabled="item.disabled"
size="mini"
style="background-color: transparent"
type="default"
@click="item.callback()"
>
<i v-if="item.icon.startsWith('fa')" :class="'fa ' + item.icon" />
@@ -107,7 +107,7 @@
</div>
</div>
</div>
<codemirror ref="myCm" v-model="iValue" class="editor" :options="iOptions" />
<codemirror ref="myCm" v-model="iValue" :options="iOptions" class="editor" />
</div>
</template>
@@ -193,7 +193,7 @@ export default {
line-height: 29px;
vertical-align: bottom;
display: inline-block;
padding: 3px;
padding: 3px 3px 3px 0;
margin-bottom: 5px;
}

View File

@@ -7,45 +7,41 @@
<div v-show="iShowTree" :style="iShowTree?('width:20%;'):('width:0;')" class="left">
<component
:is="component"
ref="AutoDataZTree"
:key="componentTreeKey"
ref="AutoDataZTree"
:setting="treeSetting"
class="auto-data-ztree"
v-bind="treeTabConfig"
v-on="$listeners"
@urlChange="handleUrlChange"
v-on="$listeners"
>
<div slot="rMenu" slot-scope="{data}">
<slot name="rMenu" :data="data" />
<slot :data="data" name="rMenu" />
</div>
</component>
</div>
<div
class="right"
:style="iShowTree?('display: flex;width: calc(100% - 20%);'):('display: flex;width:100%;')"
class="right"
>
<div v-if="showTree" class="mini">
<div class="mini-button" :class="{'is-show': iShowTree}" @click="iShowTree = !iShowTree">
<div :class="{'is-show': iShowTree}" class="mini-button" @click="iShowTree = !iShowTree">
<svg-icon
:icon-class="'double-left'"
class="icon-left"
:style="{'transform': iShowTree ? 'none' : 'rotate(180deg)'}"
class="icon-left"
/>
</div>
</div>
<div class="transition-box" style="width: calc(100% - 7px);">
<slot name="table">
<ListTable
ref="ListTable"
:key="componentKey"
:table-config="iTableConfig"
ref="ListTable"
:header-actions="headerActions"
:table-config="iTableConfig"
v-on="$listeners"
>
<template v-slot:left>
Hello world
</template>
</ListTable>
/>
</slot>
</div>
</div>

View File

@@ -1626,6 +1626,7 @@
},
"tree": {
"AddAssetToNode": "Add asset to node",
"RemoveAssetFromNode": "Remove asset from node",
"CreateNode": "Create node",
"DeleteNode": "Delete node",
"MoveAssetToNode": "move asset to node",

View File

@@ -1617,6 +1617,7 @@
},
"tree": {
"AddAssetToNode": "ノードにアセットを追加",
"RemoveAssetFromNode": "ノードからアセットを削除",
"CreateNode": "ノードの作成",
"DeleteNode": "ノードの削除",
"MoveAssetToNode": "資産をノードに移動する",

View File

@@ -1611,6 +1611,7 @@
},
"tree": {
"AddAssetToNode": "添加资产到节点",
"RemoveAssetFromNode": "从节点移除资产",
"CreateNode": "创建节点",
"DeleteNode": "删除节点",
"MoveAssetToNode": "移动资产到节点",

View File

@@ -36,7 +36,6 @@ export default {
BaseList
},
data() {
const vm = this
const tableUrl = '/api/v1/assets/assets/'
return {
treeRef: null,
@@ -53,35 +52,7 @@ export default {
headerActions: {
hasImport: false
},
addExtraMoreActions: [
{
name: 'RemoveFromCurrentNode',
title: this.$t('assets.RemoveFromCurrentNode'),
can: ({ selectedRows }) => {
if (!vm.$route.query.node_id) {
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_id
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)
}
],
addExtraMoreActions: [],
helpMessage: this.$t('assets.AssetListHelpMessage')
}
},

View File

@@ -2,10 +2,11 @@
<div class="asset-select-dialog">
<AssetDialog
v-if="iVisible"
:base-url="assetsUrl"
:title="$tc('assets.Assets')"
:visible.sync="iVisible"
@cancel="assetTreeTableDialogHandleCancel"
@confirm="assetTreeTableDialogHandleConfirm($event)"
@confirm="assetTreeTableDialogHandleConfirm"
/>
</div>
</template>
@@ -47,17 +48,37 @@ export default {
get() {
return this.visible
}
},
assetsUrl() {
if (this.action === 'remove') {
return '/api/v1/assets/assets/?node_id=' + this.selectNode.meta.data.id
} else {
return `/api/v1/assets/assets/`
}
}
},
methods: {
assetTreeTableDialogHandleConfirm(assetsSelected) {
if (!assetsSelected) {
return
}
const currentNode = this.selectNode
if (!currentNode || assetsSelected.length === 0) {
return
}
let url = `/api/v1/assets/nodes/${currentNode.meta.data.id}/assets/add/`
if (this.action === 'move') {
url = `/api/v1/assets/nodes/${currentNode.meta.data.id}/assets/replace/`
let url
switch (this.action) {
case 'add':
url = `/api/v1/assets/nodes/${currentNode.meta.data.id}/assets/add/`
break
case 'move':
url = `/api/v1/assets/nodes/${currentNode.meta.data.id}/assets/replace/`
break
case 'remove':
url = `/api/v1/assets/nodes/${currentNode.meta.data.id}/assets/remove/`
break
default:
return
}
this.$axios.put(
url, { assets: assetsSelected }

View File

@@ -1,16 +1,50 @@
<template>
<div>
<li id="m_add_asset_to_node" v-perms="'assets.add_assettonode'" class="rmenu" tabindex="-1" @click="rMenuAddAssetToNode">
<li class="divider" />
<li
id="m_add_asset_to_node"
v-perms="'assets.add_assettonode'"
class="rmenu"
tabindex="-1"
@click="rMenuAddAssetToNode"
>
<i class="fa fa-clone" /> {{ this.$t('tree.AddAssetToNode') }}
</li>
<li id="m_move_asset_to_node" v-perms="'assets.move_assettonode'" class="rmenu" tabindex="-1" @click="rMenuMoveAssetToNode">
<li
id="m_move_asset_to_node"
v-perms="'assets.move_assettonode'"
class="rmenu"
tabindex="-1"
@click="rMenuMoveAssetToNode"
>
<i class="fa fa-scissors" /> {{ this.$t('tree.MoveAssetToNode') }}
</li>
<li
id="m_remove_asset_from_node"
v-perms="'assets.move_assettonode'"
class="rmenu"
tabindex="-1"
@click="rMenuRemoveAssetFromNode"
>
<i class="fa fa-minus-square-o" /> {{ this.$t('tree.RemoveAssetFromNode') }}
</li>
<li v-if="$hasPerm('assets.move_assettonode | assets.add_assettonode')" class="divider" />
<li id="m_update_node_asset_hardware_info" v-perms="'assets.refresh_assethardwareinfo'" class="rmenu" tabindex="-1" @click="rMenuUpdateNodeAssetHardwareInfo">
<li
id="m_update_node_asset_hardware_info"
v-perms="'assets.refresh_assethardwareinfo'"
class="rmenu"
tabindex="-1"
@click="rMenuUpdateNodeAssetHardwareInfo"
>
<i class="fa fa-refresh" /> {{ this.$t('tree.UpdateNodeAssetHardwareInfo') }}
</li>
<li id="m_test_node_asset_connectivity" v-perms="'assets.test_assetconnectivity'" class="rmenu" tabindex="-1" @click="rMenuTestNodeAssetConnectivity">
<li
id="m_test_node_asset_connectivity"
v-perms="'assets.test_assetconnectivity'"
class="rmenu"
tabindex="-1"
@click="rMenuTestNodeAssetConnectivity"
>
<i class="fa fa-link" /> {{ this.$t('tree.TestNodeAssetConnectivity') }}
</li>
<li v-if="$hasPerm('assets.add_assettonode | assets.test_assetconnectivity')" class="divider" />
@@ -21,22 +55,29 @@
<i class="fa fa-align-justify" /> {{ this.$t('tree.ShowAssetAllChildrenNode') }}
</li>
<li class="divider" />
<li id="m_check_assets_amount" v-perms="'assets.change_node'" class="rmenu" tabindex="-1" @click="rCheckAssetsAmount">
<li
id="m_check_assets_amount"
v-perms="'assets.change_node'"
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>
<NodeAssetsUpdateDialog
v-if="nodeAssetsUpdateDialog.visible"
:visible.sync="nodeAssetsUpdateDialog.visible"
v-bind="nodeAssetsUpdateDialog"
/>
<Dialog
width="50%"
:title="$tc('assets.NodeInformation')"
:visible.sync="nodeInfoDialogSetting.dialogVisible"
:show-cancel="false"
:show-confirm="false"
:title="$tc('assets.NodeInformation')"
:visible.sync="nodeInfoDialogSetting.dialogVisible"
width="50%"
>
<el-row
v-for="item in nodeInfoDialogSetting.items"
@@ -97,6 +138,11 @@ export default {
this.nodeAssetsUpdateDialog.action = 'move'
this.nodeAssetsUpdateDialog.selectNode = this.getSelectedNodes()[0]
},
rMenuRemoveAssetFromNode() {
this.nodeAssetsUpdateDialog.visible = true
this.nodeAssetsUpdateDialog.action = 'remove'
this.nodeAssetsUpdateDialog.selectNode = this.getSelectedNodes()[0]
},
rMenuUpdateNodeAssetHardwareInfo() {
this.hideMenu()
const currentNode = this.getSelectedNodes()[0]
@@ -218,8 +264,9 @@ div.rMenu li {
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
margin-bottom: 0;
}
}

View File

@@ -1,5 +1,5 @@
<template>
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" :help-message="notice" />
<GenericListPage :header-actions="headerActions" :help-message="notice" :table-config="tableConfig" />
</template>
<script>
@@ -14,6 +14,8 @@ export default {
return {
tableConfig: {
url: '/api/v1/assets/domains/',
columnsExclude: ['assets', 'gateway'],
columnsExtra: ['asset_count', 'gateway_count'],
columnsShow: {
min: ['name', 'actions'],
default: ['name', 'asset_count', 'gateway_count', 'comment', 'actions']
@@ -22,7 +24,7 @@ export default {
asset_count: {
label: this.$t('assets.Assets'),
formatter: function(row) {
return <span> { row.assets.length } </span>
return <span> {row.assets.length} </span>
}
},
gateway_count: {