blob: 018d5880401b00fbbabf1f2fe5e4cc5e8853f5de [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>
<a-table
size="small"
:loading="loading"
:columns="columns"
:dataSource="items"
:rowKey="record => record.id || record.name"
:pagination="false"
:rowSelection="['vm', 'event', 'alert'].includes($route.name) ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange} : null"
:rowClassName="getRowClassName"
style="overflow-y: auto"
>
<template slot="footer">
<span v-if="hasSelected">
{{ `Selected ${selectedRowKeys.length} items` }}
</span>
</template>
<div slot="expandedRowRender" slot-scope="resource">
<info-card :resource="resource" style="margin-left: 0px; width: 50%">
<div slot="actions" style="padding-top: 12px">
<a-tooltip
v-for="(action, actionIndex) in $route.meta.actions"
:key="actionIndex"
placement="bottom">
<template slot="title">
{{ $t(action.label) }}
</template>
<a-button
v-if="action.api in $store.getters.apis && action.dataView &&
('show' in action ? action.show(resource, $store.getters.userInfo) : true)"
:icon="action.icon"
:type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')"
shape="circle"
style="margin-right: 5px; margin-top: 12px"
@click="$parent.execAction(action)"
>
</a-button>
</a-tooltip>
</div>
</info-card>
</div>
<a slot="name" slot-scope="text, record" href="javascript:;">
<div style="min-width: 120px">
<span v-if="$route.path.startsWith('/project')" style="margin-right: 5px">
<a-button type="dashed" size="small" shape="circle" icon="login" @click="changeProject(record)" />
</span>
<os-logo v-if="record.ostypename" :osName="record.ostypename" size="1x" style="margin-right: 5px" />
<console :resource="record" size="small" />
<router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
<router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link>
</div>
</a>
<a slot="displayname" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
</a>
<a slot="username" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
</a>
<a slot="ipaddress" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
<span v-if="record.issourcenat">
&nbsp;
<a-tag>source-nat</a-tag>
</span>
</a>
<a slot="publicip" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
</a>
<a slot="traffictype" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: $route.path + '/' + record.id + '?physicalnetworkid=' + record.physicalnetworkid }">{{ text }}</router-link>
</a>
<a slot="vmname" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/vm/' + record.virtualmachineid }">{{ text }}</router-link>
</a>
<template slot="state" slot-scope="text">
<status :text="text ? text : ''" displayText />
</template>
<a slot="guestnetworkname" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/guestnetwork/' + record.guestnetworkid }">{{ text }}</router-link>
</a>
<a slot="vpcname" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/vpc/' + record.vpcid }">{{ text }}</router-link>
</a>
<a slot="account" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/account/' + record.accountid }" v-if="record.accountid">{{ text }}</router-link>
<router-link :to="{ path: '/account', query: { name: record.account, domainid: record.domainid } }" v-else>{{ text }}</router-link>
</a>
<a slot="domain" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/domain/' + record.domainid }">{{ text }}</router-link>
</a>
<a slot="hostname" slot-scope="text, record" href="javascript:;">
<router-link v-if="record.hostid" :to="{ path: '/host/' + record.hostid }">{{ text }}</router-link>
<router-link v-else-if="record.hostname" :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
<span v-else>{{ text }}</span>
</a>
<a slot="clustername" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link>
</a>
<a slot="podname" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link>
</a>
<a slot="zonename" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link>
</a>
<div slot="order" slot-scope="text, record" class="shift-btns">
<a-tooltip placement="top">
<template slot="title">Move to top</template>
<a-button
shape="round"
icon="double-left"
@click="moveItemTop(record)"
class="shift-btn shift-btn--rotated"></a-button>
</a-tooltip>
<a-tooltip placement="top">
<template slot="title">Move to bottom</template>
<a-button
shape="round"
icon="double-right"
@click="moveItemBottom(record)"
class="shift-btn shift-btn--rotated"></a-button>
</a-tooltip>
<a-tooltip placement="top">
<template slot="title">Move up one row</template>
<a-button shape="round" icon="caret-up" @click="moveItemUp(record)" class="shift-btn"></a-button>
</a-tooltip>
<a-tooltip placement="top">
<template slot="title">Move down one row</template>
<a-button shape="round" icon="caret-down" @click="moveItemDown(record)" class="shift-btn"></a-button>
</a-tooltip>
</div>
<template slot="value" slot-scope="text, record">
<a-input
v-if="editableValueKey === record.key"
:defaultValue="record.value"
v-model="editableValue"
@keydown.esc="editableValueKey = null"
@pressEnter="saveValue(record)">
</a-input>
<div v-else style="width: 200px; word-break: break-all">
{{ text }}
</div>
</template>
<template slot="actions" slot-scope="text, record">
<a-button
shape="circle"
v-if="editableValueKey !== record.key"
icon="edit"
@click="editValue(record)" />
<a-button
shape="circle"
@click="saveValue(record)"
v-if="editableValueKey === record.key" >
<a-icon type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
</a-button>
<a-button
shape="circle"
size="default"
@click="editableValueKey = null"
v-if="editableValueKey === record.key" >
<a-icon type="close-circle" theme="twoTone" twoToneColor="#f5222d" />
</a-button>
</template>
</a-table>
</template>
<script>
import { api } from '@/api'
import Console from '@/components/widgets/Console'
import OsLogo from '@/components/widgets/OsLogo'
import Status from '@/components/widgets/Status'
import InfoCard from '@/components/view/InfoCard'
export default {
name: 'ListView',
components: {
Console,
OsLogo,
Status,
InfoCard
},
props: {
columns: {
type: Array,
required: true
},
items: {
type: Array,
required: true
},
loading: {
type: Boolean,
default: false
}
},
inject: ['parentFetchData', 'parentToggleLoading'],
data () {
return {
selectedRowKeys: [],
editableValueKey: null,
editableValue: ''
}
},
computed: {
hasSelected () {
return this.selectedRowKeys.length > 0
}
},
methods: {
getRowClassName (record, index) {
if (index % 2 === 0) {
return 'light-row'
}
return 'dark-row'
},
onSelectChange (selectedRowKeys) {
console.log('selectedRowKeys changed: ', selectedRowKeys)
this.selectedRowKeys = selectedRowKeys
},
changeProject (project) {
this.$store.dispatch('SetProject', project)
this.$store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark')
this.$message.success(`Switched to "${project.name}"`)
this.$router.push({ name: 'dashboard' })
},
saveValue (record) {
api('updateConfiguration', {
name: record.name,
value: this.editableValue
}).then(() => {
this.editableValueKey = null
this.$message.success('Setting Updated: ' + record.name)
this.$notification.warning({
message: 'Status',
description: 'Please restart your management server(s) for your new settings to take effect.'
})
}).catch(error => {
console.error(error)
this.$message.error('There was an error saving this setting.')
})
.finally(() => {
this.$emit('refresh')
})
},
editValue (record) {
this.editableValueKey = record.key
this.editableValue = record.value
},
handleUpdateOrder (id, index) {
this.parentToggleLoading()
let apiString = ''
switch (this.$route.name) {
case 'template':
apiString = 'updateTemplate'
break
case 'iso':
apiString = 'updateIso'
break
case 'zone':
apiString = 'updateZone'
break
case 'computeoffering':
case 'systemoffering':
apiString = 'updateServiceOffering'
break
case 'diskoffering':
apiString = 'updateDiskOffering'
break
case 'networkoffering':
apiString = 'updateNetworkOffering'
break
case 'vpcoffering':
apiString = 'updateVPCOffering'
break
default:
apiString = 'updateTemplate'
}
api(apiString, {
id,
sortKey: index
}).catch(error => {
console.error(error)
}).finally(() => {
this.parentFetchData()
this.parentToggleLoading()
})
},
moveItemUp (record) {
const data = this.items
const index = data.findIndex(item => item.id === record.id)
if (index === 0) return
data.splice(index - 1, 0, data.splice(index, 1)[0])
data.forEach((item, index) => {
this.handleUpdateOrder(item.id, index + 1)
})
},
moveItemDown (record) {
const data = this.items
const index = data.findIndex(item => item.id === record.id)
if (index === data.length - 1) return
data.splice(index + 1, 0, data.splice(index, 1)[0])
data.forEach((item, index) => {
this.handleUpdateOrder(item.id, index + 1)
})
},
moveItemTop (record) {
const data = this.items
const index = data.findIndex(item => item.id === record.id)
if (index === 0) return
data.unshift(data.splice(index, 1)[0])
data.forEach((item, index) => {
this.handleUpdateOrder(item.id, index + 1)
})
},
moveItemBottom (record) {
const data = this.items
const index = data.findIndex(item => item.id === record.id)
if (index === data.length - 1) return
data.push(data.splice(index, 1)[0])
data.forEach((item, index) => {
this.handleUpdateOrder(item.id, index + 1)
})
}
}
}
</script>
<style scoped>
/deep/ .ant-table-thead {
background-color: #f9f9f9;
}
/deep/ .ant-table-small > .ant-table-content > .ant-table-body {
margin: 0;
}
/deep/ .light-row {
background-color: #fff;
}
/deep/ .dark-row {
background-color: #f9f9f9;
}
</style>
<style scoped lang="scss">
.shift-btns {
display: flex;
}
.shift-btn {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
font-size: 12px;
&:not(:last-child) {
margin-right: 5px;
}
&--rotated {
font-size: 10px;
transform: rotate(90deg);
}
}
</style>