Rohit Yadav 12c82f5be5 component refactoring
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2021-01-20 07:06:13 +05:30

531 lines
15 KiB
Vue

<template>
<div>
<breadcrumb v-if="device !== 'desktop'" style="margin-left: -16px; margin-right: -16px; margin-top: -16px" />
<a-row>
<a-col :span="17">
<a-tooltip placement="bottom" v-for="(action, actionIndex) in actions" :key="actionIndex" v-if="(!dataView && (action.listView || action.groupAction && selectedRowKeys.length > 0)) || (dataView && action.dataView)">
<template slot="title">
{{ action.label }}
</template>
<a-button
:icon="action.icon"
:type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')"
shape="circle"
style="margin-right: 5px"
@click="execAction(action)"
:disabled="'hidden' in action ? dataView && action.hidden(resource) : false"
>
</a-button>
</a-tooltip>
<span v-if="!dataView" style="float: right; padding-right: 8px; margin-top: -2px">
<a-tooltip placement="bottom">
<template slot="title">
{{ "Auto-Refresh" }}
</template>
<a-switch
style="margin: 8px;"
:loading="loading"
:checked="autoRefresh"
@change="toggleAutoRefresh"
/>
</a-tooltip>
<a-tooltip placement="bottom">
<template slot="title">
{{ "Refresh" }}
</template>
<a-button
@click="fetchData()"
:loading="loading"
shape="circle"
icon="reload"
/>
</a-tooltip>
<a-button-group style="margin-left: 10px" v-if="!dataView">
<a-button icon="bars" @click="tableView=true" />
<a-button icon="appstore" @click="tableView=false" />
</a-button-group>
</span>
</a-col>
<a-col :span="7">
<a-tooltip placement="bottom" v-if="dataView">
<template slot="title">
{{ "Refresh" }}
</template>
<a-button
style="float: right"
@click="fetchData()"
:loading="loading"
shape="circle"
icon="reload"
/>
</a-tooltip>
<a-tooltip placement="bottom" v-if="dataView">
<template slot="title">
{{ "Auto-Refresh" }}
</template>
<a-switch
v-if="dataView"
style="float: right; margin: 5px;"
:loading="loading"
:checked="autoRefresh"
@change="toggleAutoRefresh"
/>
</a-tooltip>
<a-input-search
size="default"
placeholder="Search"
@search="onSearch"
v-if="!dataView"
>
<a-icon slot="prefix" type="search" />
</a-input-search>
</a-col>
</a-row>
<div v-show="showAction">
<keep-alive v-if="currentAction.component">
<a-modal
:title="currentAction.label"
:visible="showAction"
:closable="true"
style="top: 20px;"
@ok="handleSubmit"
@cancel="closeAction"
:confirmLoading="currentAction.loading"
centered
>
<component :is="currentAction.component"/></component>
</a-modal>
</keep-alive>
<a-modal
:title="currentAction.label"
:visible="showAction"
:closable="true"
style="top: 20px;"
@ok="handleSubmit"
@cancel="closeAction"
:confirmLoading="currentAction.loading"
centered
>
<a-spin :spinning="currentAction.loading">
<a-form
:form="form"
@submit="handleSubmit"
layout="vertical" >
<a-form-item
v-for="(field, fieldIndex) in currentAction.params"
:key="fieldIndex"
:label="$t(field.name)"
:v-bind="field.name"
v-if="field.name !== 'id'"
>
<span v-if="field.type==='boolean'">
<a-switch
v-decorator="[field.name, {
rules: [{ required: field.required, message: 'Please provide input' }]
}]"
:placeholder="field.description"
/>
</span>
<span v-else-if="field.type==='uuid' || field.name==='account'">
<a-select
:loading="field.loading"
v-decorator="[field.name, {
rules: [{ required: field.required, message: 'Please select option' }]
}]"
:placeholder="field.description"
>
<a-select-option v-for="(opt, optIndex) in field.opts" :key="optIndex">
{{ opt.name }}
</a-select-option>
</a-select>
</span>
<span v-else-if="field.type==='long'">
<a-input-number
v-decorator="[field.name, {
rules: [{ required: field.required, message: 'Please enter a number' }]
}]"
:placeholder="field.description"
/>
</span>
<span v-else>
<a-input
v-decorator="[field.name, {
rules: [{ required: field.required, message: 'Please enter input' }]
}]"
:placeholder="field.description"
/>
</span>
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</div>
<div v-if="dataView">
<instance-view :vm="resource" v-if="routeName == 'vm'" />
<data-view :resource="resource" v-else />
</div>
<div style="margin-top: 12px" v-else>
<list-view
:loading="loading"
:columns="columns"
:items="items"
v-show="tableView" />
<card-view
:items="items"
:loading="loading"
v-show="!tableView" />
</div>
</div>
</template>
<script>
import { api } from '@/api'
import { mixinDevice } from '@/utils/mixin.js'
import store from '@/store'
import Breadcrumb from '@/components/widgets/Breadcrumb'
import CardView from '@/components/widgets/CardView'
import ChartCard from '@/components/chart/ChartCard'
import DataView from '@/components/widgets/DataView'
import InstanceView from '@/components/widgets/InstanceView'
import ListView from '@/components/widgets/ListView'
import Status from '@/components/widgets/Status'
export default {
name: 'Resource',
components: {
Breadcrumb,
CardView,
ChartCard,
DataView,
InstanceView,
ListView,
Status
},
mixins: [mixinDevice],
data () {
return {
apiName: '',
loading: false,
autoRefresh: false,
columns: [],
items: [],
resource: {},
selectedRowKeys: [],
currentAction: {},
showAction: false,
dataView: false,
actions: [],
tableView: true
}
},
computed: {
hasSelected () {
return this.selectedRowKeys.length > 0
}
},
mounted () {
this.fetchData()
},
watch: {
'$route' (to, from) {
if (to.fullPath !== from.fullPath) {
this.fetchData()
}
},
'$i18n.locale' (to, from) {
if (to !== from) {
this.fetchData()
}
}
},
beforeCreate () {
this.form = this.$form.createForm(this)
},
methods: {
fetchData (search = '') {
this.routeName = this.$route.name
if (!this.routeName) {
this.routeName = this.$route.matched[this.$route.matched.length - 1].parent.name
}
this.apiName = ''
this.actions = []
this.columns = []
this.columnKeys = []
var params = { listall: true }
if (Object.keys(this.$route.query).length > 0) {
Object.assign(params, this.$route.query)
} else if (this.$route.meta.params) {
Object.assign(params, this.$route.meta.params)
}
if (search !== '') {
params['keyword'] = search
}
if (this.$route && this.$route.params && this.$route.params.id) {
this.dataView = true
} else {
this.dataView = false
}
if (this.$route && this.$route.meta && this.$route.meta.permission) {
this.apiName = this.$route.meta.permission[0]
if (this.$route.meta.columns) {
this.columnKeys = this.$route.meta.columns
}
if (this.$route.meta.actions) {
this.actions = this.$route.meta.actions
}
}
if (this.apiName === '' || this.apiName === undefined) {
return
}
if (!this.columnKeys || this.columnKeys.length === 0) {
for (const field of store.getters.apis[this.apiName]['response']) {
this.columnKeys.push(field.name)
}
this.columnKeys = [...new Set(this.columnKeys)]
this.columnKeys.sort(function (a, b) {
if (a === 'name' && b !== 'name') { return -1 }
if (a < b) { return -1 }
if (a > b) { return 1 }
return 0
})
}
var counter = 0
for (var key of this.columnKeys) {
if (typeof key === 'object') {
key = Object.keys(key)[0]
}
this.columns.push({
title: this.$t(key),
dataIndex: key,
key: counter++,
scopedSlots: { customRender: key },
sorter: (a, b) => String(a[key]).length - String(b[key]).length
})
}
this.loading = true
if (this.$route.params && this.$route.params.id) {
params['id'] = this.$route.params.id
}
api(this.apiName, params).then(json => {
this.loading = false
var responseName
var objectName
for (const key in json) {
if (key.includes('response')) {
responseName = key
break
}
}
for (const key in json[responseName]) {
if (key === 'count') continue
objectName = key
break
}
this.items = json[responseName][objectName]
if (!this.items || this.items.length === 0) {
this.items = []
}
for (let idx = 0; idx < this.items.length; idx++) {
this.items[idx]['key'] = idx
}
if (this.items.length > 0) {
this.resource = this.items[0]
} else {
this.resource = {}
}
})
},
onSearch (value) {
this.fetchData(value)
},
toggleAutoRefresh () {
this.autoRefresh = !this.autoRefresh
this.doRefresh()
},
doRefresh () {
if (!this.autoRefresh) {
return
}
const doRefresh = this.doRefresh
const fetchData = this.fetchData
setTimeout(function () {
fetchData()
doRefresh()
}, 5000)
},
closeAction () {
this.currentAction.loading = false
this.showAction = false
this.currentAction = {}
},
execAction (action) {
this.currentAction = action
var params = store.getters.apis[this.currentAction.api]['params']
params.sort(function (a, b) {
if (a.name === 'name' && b.name !== 'name') { return -1 }
if (a.name !== 'name' && b.name === 'name') { return -1 }
if (a.name === 'id') { return -1 }
if (a.name < b.name) { return -1 }
if (a.name > b.name) { return 1 }
return 0
})
if (action.args && action.args.length > 0) {
this.currentAction['params'] = action.args.map(function (arg) {
return params.filter(function (param) {
return param.name === arg
})[0]
})
} else {
this.currentAction['params'] = params
}
this.showAction = true
for (const param of this.currentAction['params']) {
if (param.type === 'uuid' || param.name === 'account') {
this.listUuidOpts(param)
}
}
this.currentAction.loading = false
},
listUuidOpts (param) {
var paramName = param.name
const possibleName = 'list' + paramName.replace('id', '').toLowerCase() + 's'
var possibleApi
if (paramName === 'id') {
possibleApi = this.apiName
} else {
for (const api in store.getters.apis) {
if (api.toLowerCase().startsWith(possibleName)) {
possibleApi = api
break
}
}
}
if (!possibleApi) {
return
}
param.loading = true
param.opts = []
var params = { listall: true }
if (possibleApi === 'listTemplates') {
params['templatefilter'] = 'executable'
}
api(possibleApi, params).then(json => {
param.loading = false
for (const obj in json) {
if (obj.includes('response')) {
for (const res in json[obj]) {
if (res === 'count') {
continue
}
param.opts = json[obj][res]
this.$forceUpdate()
break
}
break
}
}
}).catch(function (error) {
console.log(error.stack)
param.loading = false
}).then(function () {
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
this.currentAction.loading = true
const params = {}
for (const key in values) {
const input = values[key]
for (const param of this.currentAction['params']) {
if (param.name === key) {
if (input === undefined) {
if (param.type === 'boolean') {
params[key] = false
}
break
}
if (param.type === 'uuid') {
params[key] = param.opts[input]['id']
} else {
params[key] = input
}
break
}
}
}
if ('id' in this.resource) {
params['id'] = this.resource['id']
}
const closeAction = this.closeAction
const showError = this.$notification['error']
api(this.currentAction.api, params).then(json => {
for (const obj in json) {
if (obj.includes('response')) {
for (const res in json[obj]) {
if (res === 'jobid') {
this.$store.dispatch('AddAsyncJob', { 'title': this.currentAction.label, 'jobid': json[obj][res], 'description': this.resource.name, 'status': 'progress' })
break
}
}
break
}
}
closeAction()
}).catch(function (error) {
closeAction()
console.log(error)
showError({
message: 'Request Failed',
description: error.response.headers['x-description']
})
})
const fetchData = this.fetchData
setTimeout(function () {
fetchData()
}, 2500)
}
})
},
start () {
this.loading = true
this.fetchData()
setTimeout(() => {
this.loading = false
this.selectedRowKeys = []
}, 1000)
}
}
}
</script>
<style>
.info-card {
margin-top: 12px;
}
.ant-breadcrumb {
vertical-align: text-bottom;
margin-bottom: 8px;
}
.ant-breadcrumb .anticon {
margin-left: 8px;
}
</style>