blob: 0be5228d85b48661873b0383d8e9e69d3d383f57 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
<template>
<resource-layout>
<a-spin :spinning="loading" slot="left">
<a-card :bordered="false">
<a-input-search
size="default"
:placeholder="$t('label.search')"
v-model="searchQuery"
@search="onSearch"
>
<a-icon slot="prefix" type="search" />
</a-input-search>
<a-spin :spinning="loadingSearch">
<a-tree
showLine
v-if="treeViewData.length > 0"
class="list-tree-view"
:treeData="treeViewData"
:loadData="onLoadData"
:expandAction="false"
:showIcon="true"
:defaultSelectedKeys="defaultSelected"
:checkStrictly="true"
@select="onSelect"
@expand="onExpand"
:defaultExpandedKeys="arrExpand">
<a-icon slot="parent" type="folder" />
<a-icon slot="leaf" type="block" />
</a-tree>
</a-spin>
</a-card>
</a-spin>
<a-spin :spinning="detailLoading" slot="right">
<a-card
class="spin-content"
:bordered="true"
style="width:100%">
<a-tabs
style="width: 100%"
:animated="false"
:defaultActiveKey="tabs[0].name"
@change="onTabChange" >
<a-tab-pane
v-for="tab in tabs"
:tab="$t('label.' + tab.name)"
:key="tab.name"
v-if="checkShowTabDetail(tab)">
<component
:is="tab.component"
:resource="resource"
:items="items"
:tab="tabActive"
:loading="loading"
:bordered="false" />
</a-tab-pane>
</a-tabs>
</a-card>
</a-spin>
</resource-layout>
</template>
<script>
import store from '@/store'
import { api } from '@/api'
import DetailsTab from '@/components/view/DetailsTab'
import ResourceView from '@/components/view/ResourceView'
import ResourceLayout from '@/layouts/ResourceLayout'
export default {
name: 'TreeView',
components: {
ResourceLayout,
ResourceView
},
props: {
treeData: {
type: Array,
required: true
},
treeSelected: {
type: Object,
required: true
},
tabs: {
type: Array,
default () {
return [{
name: 'details',
component: DetailsTab
}]
}
},
loadedKeys: {
type: Array,
default () {
return []
}
},
loading: {
type: Boolean,
default: false
},
actionData: {
type: Array,
default () {
return []
}
}
},
data () {
return {
detailLoading: false,
loadingSearch: false,
tabActive: 'details',
selectedTreeKey: '',
resource: {},
defaultSelected: [],
treeVerticalData: [],
treeViewData: [],
oldTreeViewData: [],
apiList: '',
apiChildren: '',
apiDetail: '',
metaName: '',
page: 1,
pageSize: 20,
items: [],
showSetting: false,
oldSearchQuery: '',
searchQuery: '',
arrExpand: [],
rootKey: ''
}
},
created: function () {
this.metaName = this.$route.meta.name
this.apiList = this.$route.meta.permission[0] ? this.$route.meta.permission[0] : ''
this.apiChildren = this.$route.meta.permission[1] ? this.$route.meta.permission[1] : ''
},
watch: {
loading () {
this.detailLoading = this.loading
},
treeData () {
if (this.oldTreeViewData.length === 0) {
this.treeViewData = this.treeData
this.treeVerticalData = this.treeData
}
if (this.treeViewData.length > 0) {
this.oldTreeViewData = this.treeViewData
this.rootKey = this.treeViewData[0].key
}
if (Object.keys(this.resource).length > 0) {
const resourceIndex = this.treeVerticalData.findIndex(item => item.id === this.resource.id)
if (resourceIndex === -1) {
this.$el.querySelector(`[title=${this.resource.parentdomainname}]`).click()
}
}
},
treeSelected () {
if (Object.keys(this.treeSelected).length === 0) {
return
}
if (Object.keys(this.resource).length > 0) {
this.selectedTreeKey = this.resource.key
this.$emit('change-resource', this.resource)
// set default expand
if (this.defaultSelected.length > 1) {
const arrSelected = this.defaultSelected
this.defaultSelected = []
this.defaultSelected.push(arrSelected[0])
}
return
}
this.resource = this.treeSelected
this.resource = this.createResourceData(this.resource)
this.selectedTreeKey = this.treeSelected.key
this.defaultSelected.push(this.selectedTreeKey)
// set default expand
if (this.defaultSelected.length > 1) {
const arrSelected = this.defaultSelected
this.defaultSelected = []
this.defaultSelected.push(arrSelected[0])
}
},
actionData (newData, oldData) {
if (!newData || newData.length === 0) {
return
}
this.reloadTreeData(newData)
}
},
methods: {
onLoadData (treeNode) {
if (this.searchQuery !== '' && treeNode.eventKey !== this.rootKey) {
return new Promise(resolve => {
resolve()
})
}
const params = {
listAll: true,
id: treeNode.eventKey
}
return new Promise(resolve => {
api(this.apiChildren, params).then(json => {
const dataResponse = this.getResponseJsonData(json)
const dataGenerate = this.generateTreeData(dataResponse)
treeNode.dataRef.children = dataGenerate
if (this.treeVerticalData.length === 0) {
this.treeVerticalData = this.treeViewData
}
this.treeViewData = [...this.treeViewData]
this.oldTreeViewData = this.treeViewData
for (let i = 0; i < dataGenerate.length; i++) {
const resource = this.treeVerticalData.filter(item => item.id === dataGenerate[i].id)
if (!resource || resource.length === 0) {
this.treeVerticalData.push(dataGenerate[i])
} else {
this.treeVerticalData.filter((item, index) => {
if (item.id === dataGenerate[i].id) {
// replace all value of tree data
Object.keys(dataGenerate[i]).forEach((value, idx) => {
this.$set(this.treeVerticalData[index], value, dataGenerate[i][value])
})
}
})
}
}
resolve()
})
})
},
onSelect (selectedKeys, event) {
if (!event.selected) {
setTimeout(() => { event.node.$refs.selectHandle.click() })
return
}
// check item tree selected, set selectedTreeKey
if (selectedKeys && selectedKeys[0]) {
this.selectedTreeKey = selectedKeys[0]
}
this.getDetailResource(this.selectedTreeKey)
},
onExpand (treeExpand) {
this.arrExpand = treeExpand
},
onSearch (value) {
if (this.searchQuery === '' && this.oldSearchQuery === '') {
return
}
this.searchQuery = value
this.newTreeData = this.treeViewData
this.treeVerticalData = this.newTreeData
// set parameter for the request
const params = {}
params.listall = true
// Check the search query to set params and variables using reset data
if (this.searchQuery !== '') {
this.oldSearchQuery = this.searchQuery
params.keyword = this.searchQuery
} else if (this.metaName === 'domain') {
this.oldSearchQuery = ''
params.id = this.$store.getters.userInfo.domainid
}
this.arrExpand = []
this.treeViewData = []
this.loadingSearch = true
api(this.apiList, params).then(json => {
const listDomains = this.getResponseJsonData(json)
this.treeVerticalData = this.treeVerticalData.concat(listDomains)
if (!listDomains || listDomains.length === 0) {
return
}
if (listDomains[0].id === this.rootKey) {
const rootDomain = this.generateTreeData(listDomains)
this.treeViewData = rootDomain
return
}
this.recursiveTreeData(listDomains)
if (this.treeViewData && this.treeViewData[0]) {
this.defaultSelected = []
this.defaultSelected.push(this.treeViewData[0].key)
this.resource = this.treeViewData[0]
this.$emit('change-resource', this.resource)
}
// check treeViewData, set to expand first children
if (this.treeViewData &&
this.treeViewData[0] &&
this.treeViewData[0].children &&
this.treeViewData[0].children.length > 0
) {
this.arrExpand.push(this.treeViewData[0].children[0].key)
}
}).finally(() => {
this.loadingSearch = false
})
},
onTabChange (key) {
this.tabActive = key
},
reloadTreeData (objData) {
if (objData && objData[0].isDel) {
this.treeVerticalData = this.treeVerticalData.filter(item => item.id !== objData[0].id)
this.treeVerticalData = this.treeVerticalData.filter(item => item.parentdomainid !== objData[0].id)
} else {
// data response from action
let jsonResponse = this.getResponseJsonData(objData[0])
jsonResponse = this.createResourceData(jsonResponse)
// resource for check create or edit
const resource = this.treeVerticalData.filter(item => item.id === jsonResponse.id)
// when edit
if (resource && resource[0]) {
this.treeVerticalData.filter((item, index) => {
if (item.id === jsonResponse.id) {
// replace all value of tree data
Object.keys(jsonResponse).forEach((value, idx) => {
this.$set(this.treeVerticalData[index], value, jsonResponse[value])
})
}
})
} else {
// when create
let resourceExists = true
// check is searching data
if (this.searchQuery !== '') {
resourceExists = jsonResponse.title.indexOf(this.searchQuery) > -1
}
// push new resource to tree data
if (this.resource.haschild && resourceExists) {
this.treeVerticalData.push(jsonResponse)
}
// set resource is currently active as a parent
this.treeVerticalData.filter((item, index) => {
if (item.id === this.resource.id) {
this.$set(this.treeVerticalData[index], 'isLeaf', false)
this.$set(this.treeVerticalData[index], 'haschild', true)
}
})
}
}
this.recursiveTreeData(this.treeVerticalData)
},
getDetailResource (selectedKey) {
// set api name and parameter
const apiName = this.$route.meta.permission[0]
const params = {}
// set id to parameter
params.id = selectedKey
params.listAll = true
params.page = 1
params.pageSize = 1
this.detailLoading = true
api(apiName, params).then(json => {
const jsonResponse = this.getResponseJsonData(json)
// check json response is empty
if (!jsonResponse || jsonResponse.length === 0) {
this.resource = []
} else {
this.resource = jsonResponse[0]
this.resource = this.createResourceData(this.resource)
// set all value of resource tree data
this.treeVerticalData.filter((item, index) => {
if (item.id === this.resource.id) {
this.treeVerticalData[index] = this.resource
}
})
}
// emit change resource to parent
this.$emit('change-resource', this.resource)
}).finally(() => {
this.detailLoading = false
})
},
getResponseJsonData (json) {
let responseName
let objectName
let hasJobId = false
for (const key in json) {
if (key.includes('response')) {
for (const res in json[key]) {
if (res === 'jobid') {
hasJobId = true
break
}
}
responseName = key
break
}
}
for (const key in json[responseName]) {
if (key === 'count') {
continue
}
objectName = key
break
}
if (hasJobId) {
return {}
}
return json[responseName][objectName]
},
checkShowTabDetail (tab) {
if ('show' in tab) {
return tab.show(this.resource, this.$route, store.getters.userInfo)
}
// get permission from the route
const permission = tab.permission ? tab.permission[0] : ''
// check permission not exists
if (!permission || permission === '') {
return true
}
// Check the permissions to see the tab for a user
if (!Object.prototype.hasOwnProperty.call(store.getters.apis, permission)) {
return false
}
return true
},
generateTreeData (jsonData) {
if (!jsonData || jsonData.length === 0) {
return []
}
for (let i = 0; i < jsonData.length; i++) {
jsonData[i] = this.createResourceData(jsonData[i])
}
return jsonData
},
createResourceData (resource) {
if (!resource || Object.keys(resource) === 0) {
return {}
}
Object.keys(resource).forEach((value, idx) => {
if (resource[value] === 'Unlimited') {
this.$set(resource, value, '-1')
}
})
this.$set(resource, 'title', resource.name)
this.$set(resource, 'key', resource.id)
resource.slots = {
icon: 'parent'
}
if (!resource.haschild) {
this.$set(resource, 'isLeaf', true)
resource.slots = {
icon: 'leaf'
}
}
return resource
},
recursiveTreeData (treeData) {
const maxLevel = Math.max.apply(Math, treeData.map((o) => { return o.level }))
const items = treeData.filter(item => item.level <= maxLevel)
this.treeViewData = this.getNestedChildren(items, 0, maxLevel)
this.oldTreeViewData = this.treeViewData
},
getNestedChildren (dataItems, level, maxLevel, id) {
if (level > maxLevel) {
return
}
let items = []
if (!id || id === '') {
items = dataItems.filter(item => item.level === level)
} else {
items = dataItems.filter(item => {
let parentKey = ''
const arrKeys = Object.keys(item)
for (let i = 0; i < arrKeys.length; i++) {
if (arrKeys[i].indexOf('parent') > -1 && arrKeys[i].indexOf('id') > -1) {
parentKey = arrKeys[i]
break
}
}
return parentKey ? item[parentKey] === id : item.level === level
})
}
if (items.length === 0) {
return this.getNestedChildren(dataItems, (level + 1), maxLevel)
}
for (let i = 0; i < items.length; i++) {
items[i] = this.createResourceData(items[i])
if (items[i].haschild) {
items[i].children = this.getNestedChildren(dataItems, (level + 1), maxLevel, items[i].key)
}
}
return items
}
}
}
</script>
<style lang="less" scoped>
.list-tree-view {
overflow-y: hidden;
}
/deep/.ant-tree.ant-tree-directory {
li.ant-tree-treenode-selected {
span.ant-tree-switcher {
color: rgba(0, 0, 0, 0.65);
}
span.ant-tree-node-content-wrapper.ant-tree-node-selected > span {
color: rgba(0, 0, 0, 0.65);
background-color: #bae7ff;
}
span.ant-tree-node-content-wrapper::before {
background: #ffffff;
}
}
.ant-tree-child-tree {
li.ant-tree-treenode-selected {
span.ant-tree-switcher {
color: rgba(0, 0, 0, 0.65);
}
span.ant-tree-node-content-wrapper::before {
background: #ffffff;
}
}
}
}
/deep/.ant-tree li span.ant-tree-switcher.ant-tree-switcher-noop {
display: none;
}
/deep/.ant-tree-node-content-wrapper-open > span:first-child,
/deep/.ant-tree-node-content-wrapper-close > span:first-child {
display: none;
}
/deep/.ant-tree-icon__customize {
color: rgba(0, 0, 0, 0.45);
background: #fff;
padding-right: 5px;
}
/deep/.ant-tree li .ant-tree-node-content-wrapper {
padding-left: 0;
margin-left: 3px;
}
</style>