mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-13 19:35:24 +00:00
perf: cloud sync module adjustment (#3912)
* perf: cloud sync module adjustment * perf: optimize interface settings * fix: cloud edit - previous step and next step will failed
This commit is contained in:
93
src/components/Form/FormFields/Checkbox.vue
Normal file
93
src/components/Form/FormFields/Checkbox.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-checkbox
|
||||
v-if="hasCheckAll && options.length > 0"
|
||||
v-model="checkAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
{{ $tc('SelectAll') }}
|
||||
</el-checkbox>
|
||||
<el-checkbox-group v-model="checked" @change="onChange">
|
||||
<el-alert v-show="tipShow" type="error"> {{ noOptionTip }}</el-alert>
|
||||
<el-checkbox v-for="item in options" :key="item.value" :label="item.value">{{ item.label }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Checkbox',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
processResults: {
|
||||
type: Function,
|
||||
default: (data) => data
|
||||
},
|
||||
noOptionTip: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
hasCheckAll: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
checkAll: false,
|
||||
tipShow: false,
|
||||
isIndeterminate: false,
|
||||
checked: this.value || [],
|
||||
options: []
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.getOptions()
|
||||
this.tipShow = this.options.length === 0 && this.noOptionTip
|
||||
},
|
||||
methods: {
|
||||
async getOptions() {
|
||||
const validateStatus = (status) => {
|
||||
if (status === 403) {
|
||||
return 200
|
||||
}
|
||||
return status
|
||||
}
|
||||
await this.$axios.get(this.url, { validateStatus }).then((data) => {
|
||||
this.processResults(data)?.forEach((v) => {
|
||||
this.options.push(v)
|
||||
})
|
||||
this.refreshCheckboxStatus()
|
||||
})
|
||||
},
|
||||
refreshCheckboxStatus() {
|
||||
const checkedCount = this.checked.length
|
||||
this.checkAll = checkedCount === this.options.length
|
||||
this.isIndeterminate = checkedCount > 0 && checkedCount < this.options.length
|
||||
},
|
||||
onChange() {
|
||||
this.$log.debug('Current checked: ', this.checked)
|
||||
this.refreshCheckboxStatus()
|
||||
this.$emit('input', this.checked)
|
||||
},
|
||||
handleCheckAllChange(val) {
|
||||
this.checked = val ? this.options.map((o) => o.value) : []
|
||||
this.isIndeterminate = false
|
||||
this.$emit('input', this.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -2,6 +2,7 @@ import Link from './Link.vue'
|
||||
import Select2 from './Select2.vue'
|
||||
import TagInput from './TagInput.vue'
|
||||
import Switcher from './Switcher.vue'
|
||||
import Checkbox from './Checkbox.vue'
|
||||
import AttrInput from './AttrInput.vue'
|
||||
import UploadKey from './UploadKey.vue'
|
||||
import JsonEditor from './JsonEditor.vue'
|
||||
@@ -23,6 +24,7 @@ import PasswordRule from './PasswordRule.vue'
|
||||
export default {
|
||||
Link,
|
||||
Switcher,
|
||||
Checkbox,
|
||||
Select2,
|
||||
TagInput,
|
||||
AttrInput,
|
||||
@@ -47,6 +49,7 @@ export default {
|
||||
export {
|
||||
Link,
|
||||
Switcher,
|
||||
Checkbox,
|
||||
Select2,
|
||||
TagInput,
|
||||
AttrInput,
|
||||
|
||||
@@ -60,6 +60,7 @@ const defaultDeleteCallback = function({ row, col, cellValue, reload }) {
|
||||
msg += ' ?'
|
||||
const title = this.$t('Info')
|
||||
const performDelete = this.colActions.performDelete
|
||||
const afterDelete = this.colActions.afterDelete
|
||||
this.$alert(msg, title, {
|
||||
type: 'warning',
|
||||
confirmButtonClass: 'el-button--danger',
|
||||
@@ -71,6 +72,9 @@ const defaultDeleteCallback = function({ row, col, cellValue, reload }) {
|
||||
await performDelete.bind(this)({ row: row, col: col })
|
||||
done()
|
||||
reload()
|
||||
if (afterDelete instanceof Function) {
|
||||
afterDelete({ row: row, col: col })
|
||||
}
|
||||
this.$message.success(this.$tc('DeleteSuccessMsg'))
|
||||
} finally {
|
||||
instance.confirmButtonLoading = false
|
||||
|
||||
@@ -118,10 +118,10 @@ export default {
|
||||
}
|
||||
.action-bar {
|
||||
position: relative;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
height: 0;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-bottom: none;
|
||||
z-index: 999;
|
||||
.action {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
|
||||
@@ -13,6 +13,7 @@ export { default as Hamburger } from './Widgets/Hamburger'
|
||||
export { default as ListTable } from './Table/ListTable'
|
||||
export { default as RelationCard } from './Cards/RelationCard'
|
||||
export { default as Select2 } from './Form/FormFields/Select2'
|
||||
export { default as Checkbox } from './Form/FormFields/Checkbox'
|
||||
export { default as UploadKey } from './Form/FormFields/UploadKey.vue'
|
||||
export { default as AssetSelect } from './Apps/AssetSelect'
|
||||
export { default as AutomationParams } from './Apps/AutomationParams'
|
||||
|
||||
@@ -26,7 +26,7 @@ const clouds = {
|
||||
component: empty,
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('AccountList'),
|
||||
title: i18n.t('CloudAccountList'),
|
||||
permissions: ['xpack.view_account']
|
||||
},
|
||||
children: [
|
||||
@@ -36,7 +36,7 @@ const clouds = {
|
||||
hidden: true,
|
||||
redirect: '/console/assets/cloud',
|
||||
meta: {
|
||||
title: i18n.t('AccountList'),
|
||||
title: i18n.t('CloudAccountList'),
|
||||
permissions: ['xpack.view_account']
|
||||
}
|
||||
},
|
||||
@@ -46,7 +46,7 @@ const clouds = {
|
||||
name: 'AccountCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('Create'),
|
||||
title: i18n.t('CloudAccountCreate'),
|
||||
action: 'create',
|
||||
permissions: ['xpack.add_account']
|
||||
}
|
||||
@@ -57,7 +57,7 @@ const clouds = {
|
||||
name: 'AccountUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('Update'),
|
||||
title: i18n.t('CloudAccountUpdate'),
|
||||
action: 'update',
|
||||
permissions: ['xpack.change_account']
|
||||
}
|
||||
@@ -68,62 +68,12 @@ const clouds = {
|
||||
name: 'AccountDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('Detail'),
|
||||
title: i18n.t('CloudAccountDetail'),
|
||||
permissions: ['xpack.view_account']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'sync-instance-tasks',
|
||||
component: empty,
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('SyncInstanceTaskList'),
|
||||
permissions: ['xpack.view_syncinstancetask']
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/assets/Cloud/'),
|
||||
name: 'SyncInstanceTaskList',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('SyncInstanceTaskList'),
|
||||
permissions: ['xpack.view_syncinstancetask']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskCreateUpdate'),
|
||||
name: 'SyncInstanceTaskCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('SyncInstanceTaskCreate'),
|
||||
permissions: ['xpack.add_syncinstancetask']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskCreateUpdate'),
|
||||
name: 'SyncInstanceTaskUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('SyncInstanceTaskUpdate'),
|
||||
permissions: ['xpack.change_syncinstancetask']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskDetail/index'),
|
||||
name: 'SyncInstanceTaskDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('SyncInstanceTaskDetail')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'strategy',
|
||||
component: empty,
|
||||
|
||||
@@ -12,7 +12,7 @@ import Detail from './Detail'
|
||||
import TaskExecutionList from './TaskExecutionList'
|
||||
|
||||
export default {
|
||||
name: 'SyncInstanceTaskDetail',
|
||||
name: 'AccountGatherTaskDetail',
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TabPage,
|
||||
|
||||
@@ -1,20 +1,50 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:initial="initial"
|
||||
v-bind="$data"
|
||||
/>
|
||||
<Page>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="20" :sm="24">
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm
|
||||
v-show="activeStep===0"
|
||||
ref="baseForm"
|
||||
v-bind="configSettings"
|
||||
/>
|
||||
<GenericCreateUpdateForm
|
||||
v-if="activeStep===1"
|
||||
ref="strategyForm"
|
||||
v-bind="strategySettings"
|
||||
/>
|
||||
</IBox>
|
||||
</el-col>
|
||||
<el-col :md="4" :sm="24">
|
||||
<IBox style="height: 490px;">
|
||||
<el-steps
|
||||
direction="vertical"
|
||||
:active="activeStep"
|
||||
space="400px"
|
||||
>
|
||||
<el-step :title="$tc('Config')" :description="description" />
|
||||
<el-step :title="$tc('Strategy')" />
|
||||
</el-steps>
|
||||
</IBox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { RequiredChange, specialEmojiCheck } from '@/components/Form/DataForm/rules'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun } from '../const'
|
||||
import { UploadKey } from '@/components'
|
||||
import { Checkbox, CronTab, IBox, UploadKey } from '@/components'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
import { GenericCreateUpdateForm, Page } from '@/layout/components'
|
||||
import SyncInstanceTaskStrategy from './components/SyncInstanceTaskStrategy'
|
||||
import { setUrlParam } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
IBox,
|
||||
Page,
|
||||
GenericCreateUpdateForm
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
@@ -33,110 +63,233 @@ export default {
|
||||
}
|
||||
|
||||
return {
|
||||
initial: {
|
||||
attrs: {
|
||||
ip_group: []
|
||||
accountId: '',
|
||||
taskId: '',
|
||||
activeStep: 0,
|
||||
description: accountProviderAttrs.title,
|
||||
configSettings: {
|
||||
initial: {
|
||||
attrs: {
|
||||
ip_group: []
|
||||
},
|
||||
provider: this.$route.query.provider,
|
||||
port: 443
|
||||
},
|
||||
provider: this.$route.query.provider,
|
||||
port: 443
|
||||
},
|
||||
url: '/api/v1/xpack/cloud/accounts/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'provider']],
|
||||
[this.$t(accountProviderAttrs.title), ['attrs']],
|
||||
[this.$t('Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
rules: [RequiredChange, specialEmojiCheck]
|
||||
},
|
||||
attrs: {
|
||||
encryptedFields: ['access_key_secret'],
|
||||
fields: accountProviderAttrs.attrs,
|
||||
fieldsMeta: {
|
||||
// 必须放在最上面,下面特殊制定的字段才会覆盖默认
|
||||
...setFieldAttrs(),
|
||||
service_account_key: {
|
||||
label: this.$t('ServerAccountKey'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
url: '/api/v1/xpack/cloud/accounts/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'provider']],
|
||||
[this.$t(accountProviderAttrs.title), ['attrs']],
|
||||
[this.$t('Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
rules: [RequiredChange, specialEmojiCheck]
|
||||
},
|
||||
attrs: {
|
||||
encryptedFields: ['access_key_secret'],
|
||||
fields: accountProviderAttrs.attrs,
|
||||
fieldsMeta: {
|
||||
// 必须放在最上面,下面特殊制定的字段才会覆盖默认
|
||||
...setFieldAttrs(),
|
||||
service_account_key: {
|
||||
label: this.$t('ServerAccountKey'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
cert_file: {
|
||||
label: this.$t('Certificate'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
key_file: {
|
||||
label: this.$t('SecretKey'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
password: {
|
||||
rules: this.$route.params.id ? [] : [RequiredChange]
|
||||
},
|
||||
public_key: {
|
||||
label: this.$t('PublicKey'),
|
||||
rules: this.$route.params.id ? [] : [RequiredChange]
|
||||
},
|
||||
private_key: {
|
||||
label: this.$t('PrivateKey'),
|
||||
rules: this.$route.params.id ? [] : [RequiredChange]
|
||||
},
|
||||
project: {
|
||||
label: this.$t('Project')
|
||||
}
|
||||
},
|
||||
cert_file: {
|
||||
label: this.$t('Certificate'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
key_file: {
|
||||
label: this.$t('SecretKey'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
password: {
|
||||
rules: this.$route.params.id ? [] : [RequiredChange]
|
||||
},
|
||||
public_key: {
|
||||
label: this.$t('PublicKey'),
|
||||
rules: this.$route.params.id ? [] : [RequiredChange]
|
||||
},
|
||||
private_key: {
|
||||
label: this.$t('PrivateKey'),
|
||||
rules: this.$route.params.id ? [] : [RequiredChange]
|
||||
},
|
||||
project: {
|
||||
label: this.$t('Project')
|
||||
}
|
||||
},
|
||||
provider: {
|
||||
rules: [RequiredChange],
|
||||
el: {
|
||||
disabled: true
|
||||
}
|
||||
}
|
||||
},
|
||||
provider: {
|
||||
rules: [RequiredChange],
|
||||
el: {
|
||||
disabled: true
|
||||
onPerformSuccess: (resp) => {
|
||||
let url = vm.strategySettings.fieldsMeta.regions.el.url
|
||||
if (!vm.accountId) {
|
||||
vm.accountId = resp?.id
|
||||
url = setUrlParam(url, 'account_id', vm.accountId)
|
||||
vm.strategySettings.fieldsMeta.regions.el.url = url
|
||||
}
|
||||
}
|
||||
},
|
||||
updateSuccessNextRoute: { name: 'CloudCenter', params: { activeMenu: 'AccountList' }},
|
||||
createSuccessNextRoute: { name: 'CloudCenter', params: { activeMenu: 'AccountList' }},
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
let url = `/api/v1/xpack/cloud/accounts/`
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
return `${url}?provider=${accountProvider}`
|
||||
},
|
||||
cleanFormValue(values) {
|
||||
const encryptedFields = [
|
||||
'access_key_secret', 'password', 'client_secret',
|
||||
'oc_password', 'sc_password'
|
||||
]
|
||||
const attrs = values.attrs
|
||||
for (const item of encryptedFields) {
|
||||
const value = attrs[item]
|
||||
if (!value) {
|
||||
continue
|
||||
if (!vm.taskId) {
|
||||
vm.taskId = resp?.task?.id
|
||||
vm.strategySettings.url += `${vm.taskId}/`
|
||||
}
|
||||
this.activeStep = this.activeStep === 0 ? 1 : 0
|
||||
},
|
||||
submitBtnText: this.$t('Next'),
|
||||
hasSaveContinue: false,
|
||||
hasReset: false,
|
||||
getUrl: () => {
|
||||
let url = vm.configSettings.url
|
||||
const params = vm.$route.params
|
||||
const instanceId = params?.id || vm.accountId
|
||||
if (instanceId) {
|
||||
url = `${url}${instanceId}/`
|
||||
}
|
||||
return `${url}?provider=${accountProvider}`
|
||||
},
|
||||
submitMethod: () => { return vm.$route.params?.id || vm.accountId ? 'put' : 'post' },
|
||||
cleanFormValue(values) {
|
||||
const encryptedFields = [
|
||||
'access_key_secret', 'password', 'client_secret',
|
||||
'oc_password', 'sc_password'
|
||||
]
|
||||
const attrs = values.attrs
|
||||
for (const item of encryptedFields) {
|
||||
const value = attrs[item]
|
||||
if (!value) {
|
||||
continue
|
||||
}
|
||||
attrs[item] = encryptPassword(value)
|
||||
}
|
||||
if (Array.isArray(attrs.ip_group)) {
|
||||
values.attrs.ip_group = attrs.ip_group.filter(Boolean)
|
||||
}
|
||||
return values
|
||||
},
|
||||
afterGetFormValue(formValue) {
|
||||
if (!formValue.attrs) {
|
||||
return formValue
|
||||
}
|
||||
attrs[item] = encryptPassword(value)
|
||||
}
|
||||
if (Array.isArray(attrs.ip_group)) {
|
||||
values.attrs.ip_group = attrs.ip_group.filter(Boolean)
|
||||
}
|
||||
return values
|
||||
},
|
||||
afterGetFormValue(formValue) {
|
||||
if (!formValue.attrs) {
|
||||
return formValue
|
||||
}
|
||||
return formValue
|
||||
},
|
||||
strategySettings: {
|
||||
initial: {
|
||||
is_periodic: false,
|
||||
interval: 24,
|
||||
hostname_strategy: 'instance_name_partial_ip',
|
||||
ip_network_segment_group: ['*']
|
||||
},
|
||||
fields: [
|
||||
[this.$t('CloudSource'), ['regions']],
|
||||
[this.$t('SaveSetting'), [
|
||||
'hostname_strategy', 'ip_network_segment_group', 'sync_ip_type',
|
||||
'is_always_update', 'fully_synchronous', 'release_assets'
|
||||
]],
|
||||
[this.$t('SyncStrategy'), ['strategy']],
|
||||
[this.$t('Periodic'), ['is_periodic', 'interval', 'crontab']]
|
||||
],
|
||||
url: '/api/v1/xpack/cloud/sync-instance-tasks/',
|
||||
fieldsMeta: {
|
||||
hostname_strategy: {
|
||||
rules: [RequiredChange],
|
||||
helpTip: this.$t('HostnameStrategy')
|
||||
},
|
||||
is_always_update: {
|
||||
type: 'switch',
|
||||
label: this.$t('IsAlwaysUpdate'),
|
||||
helpTip: this.$t('IsAlwaysUpdateHelpTip')
|
||||
},
|
||||
fully_synchronous: {
|
||||
type: 'switch',
|
||||
label: this.$t('FullySynchronous'),
|
||||
helpTip: this.$t('FullySynchronousHelpTip')
|
||||
},
|
||||
release_assets: {
|
||||
type: 'switch',
|
||||
label: this.$t('ReleaseAssets'),
|
||||
helpTips: this.$t('ReleaseAssetsHelpTips')
|
||||
},
|
||||
regions: {
|
||||
component: Checkbox,
|
||||
el: {
|
||||
url: '/api/v1/xpack/cloud/regions/',
|
||||
options: [],
|
||||
value: [],
|
||||
noOptionTip: this.$t('CloudRegionTip'),
|
||||
processResults(data) {
|
||||
return data.regions?.map((item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
is_periodic: {
|
||||
type: 'switch'
|
||||
},
|
||||
crontab: {
|
||||
component: CronTab,
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_periodic === false
|
||||
},
|
||||
helpText: this.$t('CrontabOfCreateUpdatePage'),
|
||||
helpTextAsTip: true
|
||||
},
|
||||
interval: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_periodic === false
|
||||
},
|
||||
helpText: this.$t('IntervalOfCreateUpdatePage')
|
||||
},
|
||||
strategy: {
|
||||
label: this.$t('Strategy'),
|
||||
component: SyncInstanceTaskStrategy,
|
||||
helpTip: this.$t('StrategyHelpTip')
|
||||
}
|
||||
},
|
||||
hasSaveContinue: false,
|
||||
hasReset: false,
|
||||
getUrl: () => { return this.strategySettings.url },
|
||||
submitMethod: () => { return this.$refs.baseForm.form.task?.id ? 'put' : 'post' },
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('Previous'),
|
||||
callback: () => { this.activeStep = this.activeStep === 1 ? 0 : 1 }
|
||||
}
|
||||
],
|
||||
updateSuccessNextRoute: { name: 'CloudCenter' },
|
||||
createSuccessNextRoute: { name: 'CloudCenter' },
|
||||
cleanFormValue(value) {
|
||||
const ipNetworkSegments = value.ip_network_segment_group
|
||||
const strategy = value?.strategy || []
|
||||
if (!Array.isArray(ipNetworkSegments)) {
|
||||
value.ip_network_segment_group = ipNetworkSegments ? ipNetworkSegments.split(',') : []
|
||||
}
|
||||
value.strategy = strategy.map(item => { return item.id })
|
||||
const accountId = this.$route.params?.id || this.accountId
|
||||
if (accountId) {
|
||||
value.account = { pk: accountId }
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="16" :sm="24">
|
||||
<el-col :md="15" :sm="24">
|
||||
<AutoDetailCard :excludes="excludes" :object="object" :url="url" />
|
||||
<AutoDetailCard :fields="detailFields" :object="object" :title="$tc('TaskDetail')" :url="url" />
|
||||
</el-col>
|
||||
<el-col :md="9" :sm="24">
|
||||
<QuickActions :actions="quickActions" type="primary" />
|
||||
<RelationCard
|
||||
ref="StrategyRelation"
|
||||
v-perms="'xpack.change_strategy'"
|
||||
style="margin-top: 15px"
|
||||
type="info"
|
||||
v-bind="strategyRelationConfig"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDetailCard from '@/components/Cards/DetailCard/auto'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import RelationCard from '@/components/Cards/RelationCard'
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'AccountDetail',
|
||||
components: {
|
||||
QuickActions, RelationCard,
|
||||
AutoDetailCard
|
||||
},
|
||||
props: {
|
||||
@@ -23,13 +39,108 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
url: `/api/v1/xpack/cloud/accounts/${this.object.id}/`,
|
||||
excludes: ['attrs']
|
||||
excludes: ['attrs', 'task'],
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('RunTaskManually'),
|
||||
attrs: {
|
||||
showTip: !this.object.task?.id,
|
||||
tip: this.$t('ExecCloudSyncErrorMsg'),
|
||||
type: 'primary',
|
||||
label: this.$t('Execute'),
|
||||
disabled: !this.$hasPerm('xpack.add_syncinstancetaskexecution') || !this.object.task?.id
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.get(
|
||||
`/api/v1/xpack/cloud/sync-instance-tasks/${this.object.task.id}/run/`
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
],
|
||||
strategyRelationConfig: {
|
||||
icon: 'fa-info',
|
||||
title: this.$t('Strategy'),
|
||||
objectsAjax: {
|
||||
url: '/api/v1/xpack/cloud/strategies/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
},
|
||||
hasObjectsId: this.object?.task?.strategy?.map(i => i.id) || [],
|
||||
performAdd: (items) => {
|
||||
const newData = []
|
||||
const value = this.$refs.StrategyRelation.iHasObjects
|
||||
value.map(v => {
|
||||
newData.push(v.value)
|
||||
})
|
||||
const relationUrl = `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/`
|
||||
items.map(v => {
|
||||
newData.push(v.value)
|
||||
})
|
||||
return this.$axios.patch(relationUrl, { strategy: newData })
|
||||
},
|
||||
performDelete: (item) => {
|
||||
const itemId = item.value
|
||||
const newData = []
|
||||
const value = this.$refs.StrategyRelation.iHasObjects
|
||||
value.map(v => {
|
||||
if (v.value !== itemId) {
|
||||
newData.push(v.value)
|
||||
}
|
||||
})
|
||||
const relationUrl = `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/`
|
||||
return this.$axios.patch(relationUrl, { strategy: newData })
|
||||
}
|
||||
},
|
||||
detailFields: [
|
||||
{
|
||||
key: this.$t('Strategy'),
|
||||
value: this.object?.task?.strategy?.map(item => item.name).join(', ')
|
||||
},
|
||||
{
|
||||
key: this.$t('IPNetworkSegment'),
|
||||
value: this.object?.task?.ip_network_segment_group?.join(', ')
|
||||
},
|
||||
{
|
||||
key: this.$t('IsAlwaysUpdate'),
|
||||
value: this.object?.task?.is_always_update
|
||||
},
|
||||
{
|
||||
key: this.$t('CyclePerform'),
|
||||
value: this.object?.task?.is_periodic
|
||||
},
|
||||
{
|
||||
key: this.$t('Region'),
|
||||
value: this.object?.task.regions,
|
||||
formatter(row, value) {
|
||||
return (<div>{
|
||||
value?.map((content) => {
|
||||
return <div>{ content }</div>
|
||||
})}
|
||||
</div>)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: this.$t('ReleaseAssets'),
|
||||
value: this.object?.task.release_assets
|
||||
},
|
||||
{
|
||||
key: this.$t('DateLastSync'),
|
||||
value: this.object?.task?.date_last_sync ? toSafeLocalDateStr(this.object?.task.date_created) : ''
|
||||
},
|
||||
{
|
||||
key: this.$t('DateCreated'),
|
||||
value: this.object?.task.date_created ? toSafeLocalDateStr(this.object?.task.date_created) : ''
|
||||
},
|
||||
'comment'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cardTitle() {
|
||||
return this.object.name
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ActionsFormatter, DateFormatter } from '@/components/Table/TableFormatt
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'HistoryList',
|
||||
name: 'TaskHistoryList',
|
||||
components: { GenericListTable },
|
||||
props: {
|
||||
object: {
|
||||
@@ -29,7 +29,7 @@ export default {
|
||||
}
|
||||
},
|
||||
tableConfig: {
|
||||
url: `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/history/`,
|
||||
url: `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.task?.id}/history/`,
|
||||
columns: [
|
||||
{
|
||||
prop: 'summary.new',
|
||||
@@ -7,7 +7,7 @@ import GenericListTable from '@/layout/components/GenericListTable/index'
|
||||
import { DateFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetList',
|
||||
name: 'TaskSyncAssetList',
|
||||
components: { GenericListTable },
|
||||
props: {
|
||||
object: {
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
]
|
||||
},
|
||||
tableConfig: {
|
||||
url: `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/instances/`,
|
||||
url: `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.task?.id}/instances/`,
|
||||
hasSelection: false,
|
||||
columns: [
|
||||
'instance_id',
|
||||
@@ -8,11 +8,15 @@
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import TaskSyncAssetList from './TaskSyncAssetList'
|
||||
import TaskHistoryList from './TaskHistoryList'
|
||||
import AccountDetail from './AccountDetail'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TaskSyncAssetList,
|
||||
TaskHistoryList,
|
||||
AccountDetail,
|
||||
TabPage
|
||||
},
|
||||
@@ -20,7 +24,7 @@ export default {
|
||||
const vm = this
|
||||
return {
|
||||
Account: {
|
||||
name: '', provider: '', provider_display: '', validity_display: '', comment: '', date_created: '', created_by: ''
|
||||
name: '', provider: '', provider_display: '', validity_display: '', comment: '', date_created: '', created_by: '', task: {}
|
||||
},
|
||||
config: {
|
||||
url: `/api/v1/xpack/cloud/accounts`,
|
||||
@@ -29,6 +33,16 @@ export default {
|
||||
{
|
||||
title: this.$t('Detail'),
|
||||
name: 'AccountDetail'
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncInstanceTaskHistoryList'),
|
||||
name: 'TaskHistoryList',
|
||||
hidden: () => { return !this.Account.task?.id }
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncInstanceTaskHistoryAssetList'),
|
||||
name: 'TaskSyncAssetList',
|
||||
hidden: () => { return !this.Account.task?.id }
|
||||
}
|
||||
],
|
||||
actions: {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div>
|
||||
<GenericListTable ref="regionTable" :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<Dialog
|
||||
:confirm-title="$tc('assets.TestConnection')"
|
||||
:confirm-title="$tc('TestConnection')"
|
||||
:loading-status="testLoading"
|
||||
:title="$tc('assets.TestConnection')"
|
||||
:title="$tc('TestConnection')"
|
||||
:visible.sync="visible"
|
||||
width="50"
|
||||
@cancel="handleCancel()"
|
||||
@@ -12,7 +12,7 @@
|
||||
@confirm="handleConfirm()"
|
||||
>
|
||||
<el-form ref="regionForm" :model="account" label-width="auto">
|
||||
<el-form-item :label="$tc('xpack.Cloud.Region')" :rules="regionRules" prop="region">
|
||||
<el-form-item :label="$tc('Region')" :rules="regionRules" prop="region">
|
||||
<Select2 ref="regionSelect" v-model="account.region" v-bind="select2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -30,9 +30,10 @@ import rules from '@/components/Form/DataForm/rules'
|
||||
import { Select2 } from '@/components/Form/FormFields'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'AccountList',
|
||||
name: 'CloudAccountList',
|
||||
components: {
|
||||
Dialog,
|
||||
Select2,
|
||||
@@ -47,7 +48,7 @@ export default {
|
||||
app: 'xpack',
|
||||
resource: 'account'
|
||||
},
|
||||
columnsExclude: ['attrs'],
|
||||
columnsExclude: ['attrs', 'task'],
|
||||
columnsShow: {
|
||||
default: [
|
||||
'name', 'provider', 'comment', 'validity', 'actions'
|
||||
@@ -70,6 +71,9 @@ export default {
|
||||
onUpdate: ({ row, col }) => {
|
||||
vm.$router.push({ name: 'AccountUpdate', params: { id: row.id }, query: { provider: row.provider?.value }})
|
||||
},
|
||||
afterDelete: () => {
|
||||
this.getCloudPlatforms()
|
||||
},
|
||||
extraActions: [
|
||||
{
|
||||
name: 'TestConnection',
|
||||
@@ -87,6 +91,22 @@ export default {
|
||||
vm.$message.error(err.response.data.msg)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: vm.$t('RunTaskManually'),
|
||||
name: 'execute',
|
||||
type: 'info',
|
||||
can: () => vm.$hasPerm('xpack.add_syncinstancetaskexecution'),
|
||||
callback: function(data) {
|
||||
const taskId = data.row.task?.id
|
||||
if (taskId) {
|
||||
this.$axios.get(`/api/v1/xpack/cloud/sync-instance-tasks/${taskId}/run/`).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
} else {
|
||||
this.$message.error(this.$t('ExecCloudSyncErrorMsg'))
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -101,117 +121,11 @@ export default {
|
||||
getUrlQuery: false
|
||||
},
|
||||
moreCreates: {
|
||||
loading: false,
|
||||
callback: (option) => {
|
||||
vm.$router.push({ name: 'AccountCreate', query: { provider: option.name }})
|
||||
},
|
||||
dropdown: [
|
||||
{
|
||||
name: aliyun,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[aliyun].title,
|
||||
type: 'primary',
|
||||
group: this.$t('PublicCloud'),
|
||||
can: true
|
||||
},
|
||||
{
|
||||
name: qcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qcloud].title,
|
||||
type: 'primary',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
name: qcloud_lighthouse,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qcloud_lighthouse].title
|
||||
},
|
||||
{
|
||||
name: huaweicloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud].title
|
||||
},
|
||||
{
|
||||
name: baiducloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[baiducloud].title
|
||||
},
|
||||
{
|
||||
name: jdcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[jdcloud].title
|
||||
},
|
||||
{
|
||||
name: kingsoftcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[kingsoftcloud].title
|
||||
},
|
||||
{
|
||||
name: aws_china,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[aws_china].title
|
||||
},
|
||||
{
|
||||
name: aws_international,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[aws_international].title
|
||||
},
|
||||
{
|
||||
name: azure,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[azure].title
|
||||
},
|
||||
{
|
||||
name: azure_international,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[azure_international].title
|
||||
},
|
||||
{
|
||||
name: gcp,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[gcp].title
|
||||
},
|
||||
{
|
||||
name: ucloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[ucloud].title
|
||||
},
|
||||
{
|
||||
name: volcengine,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[volcengine].title
|
||||
},
|
||||
{
|
||||
name: vmware,
|
||||
group: this.$t('PrivateCloud'),
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[vmware].title
|
||||
},
|
||||
{
|
||||
name: qingcloud_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qingcloud_private].title
|
||||
},
|
||||
{
|
||||
name: huaweicloud_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud_private].title
|
||||
},
|
||||
{
|
||||
name: ctyun_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[ctyun_private].title
|
||||
},
|
||||
{
|
||||
name: openstack,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[openstack].title
|
||||
},
|
||||
{
|
||||
name: zstack,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[zstack].title
|
||||
},
|
||||
{
|
||||
name: nutanix,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[nutanix].title
|
||||
},
|
||||
{
|
||||
name: fc,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[fc].title
|
||||
},
|
||||
{
|
||||
name: scp,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[scp].title
|
||||
},
|
||||
{
|
||||
name: apsara_stack,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[apsara_stack].title
|
||||
},
|
||||
{
|
||||
name: lan,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[lan].title
|
||||
}
|
||||
]
|
||||
dropdown: []
|
||||
}
|
||||
},
|
||||
account: {},
|
||||
@@ -224,10 +138,148 @@ export default {
|
||||
regionRules: [rules.Required]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getCloudPlatforms()
|
||||
},
|
||||
methods: {
|
||||
fitCloudPlatformAttr(platforms, data, group) {
|
||||
const createdPlatform = []
|
||||
const uncreatedPlatform = []
|
||||
platforms.map((p) => {
|
||||
let created = false
|
||||
for (let i = 0; i < data?.length; i++) {
|
||||
if (p.name === data[i].provider.value) {
|
||||
p['can'] = false
|
||||
created = true
|
||||
createdPlatform.push(p)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!created) {
|
||||
uncreatedPlatform.push(p)
|
||||
}
|
||||
})
|
||||
const result = uncreatedPlatform.concat(createdPlatform)
|
||||
result[0].group = group
|
||||
return result
|
||||
},
|
||||
getCloudPlatforms() {
|
||||
this.headerActions.moreCreates.loading = true
|
||||
const publicPlatforms = [
|
||||
{
|
||||
name: aliyun,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[aliyun].title
|
||||
},
|
||||
{
|
||||
name: qcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qcloud].title
|
||||
},
|
||||
{
|
||||
name: qcloud_lighthouse,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qcloud_lighthouse].title
|
||||
},
|
||||
{
|
||||
name: huaweicloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud].title
|
||||
},
|
||||
{
|
||||
name: baiducloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[baiducloud].title
|
||||
},
|
||||
{
|
||||
name: jdcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[jdcloud].title
|
||||
},
|
||||
{
|
||||
name: kingsoftcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[kingsoftcloud].title
|
||||
},
|
||||
{
|
||||
name: aws_china,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[aws_china].title
|
||||
},
|
||||
{
|
||||
name: aws_international,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[aws_international].title
|
||||
},
|
||||
{
|
||||
name: azure,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[azure].title
|
||||
},
|
||||
{
|
||||
name: azure_international,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[azure_international].title
|
||||
},
|
||||
{
|
||||
name: gcp,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[gcp].title
|
||||
},
|
||||
{
|
||||
name: ucloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[ucloud].title
|
||||
},
|
||||
{
|
||||
name: volcengine,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[volcengine].title
|
||||
}
|
||||
]
|
||||
const privatePlatforms = [
|
||||
{
|
||||
name: vmware,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[vmware].title
|
||||
},
|
||||
{
|
||||
name: qingcloud_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qingcloud_private].title
|
||||
},
|
||||
{
|
||||
name: huaweicloud_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud_private].title
|
||||
},
|
||||
{
|
||||
name: ctyun_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[ctyun_private].title
|
||||
},
|
||||
{
|
||||
name: openstack,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[openstack].title
|
||||
},
|
||||
{
|
||||
name: zstack,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[zstack].title
|
||||
},
|
||||
{
|
||||
name: nutanix,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[nutanix].title
|
||||
},
|
||||
{
|
||||
name: fc,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[fc].title
|
||||
},
|
||||
{
|
||||
name: scp,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[scp].title
|
||||
},
|
||||
{
|
||||
name: apsara_stack,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[apsara_stack].title
|
||||
},
|
||||
{
|
||||
name: lan,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[lan].title
|
||||
}
|
||||
]
|
||||
const url = '/api/v1/xpack/cloud/accounts/?fields_size=mini'
|
||||
this.$axios.get(url).then((resp) => {
|
||||
const pcPlatforms = this.fitCloudPlatformAttr(publicPlatforms, resp, this.$t('PublicCloud'))
|
||||
const paPlatforms = this.fitCloudPlatformAttr(privatePlatforms, resp, this.$t('PrivateCloud'))
|
||||
this.headerActions.moreCreates.dropdown = pcPlatforms.concat(paPlatforms)
|
||||
this.headerActions.moreCreates.loading = false
|
||||
})
|
||||
},
|
||||
valid(status) {
|
||||
if (status !== 200) {
|
||||
this.$message.error(this.$t('AccountTestConnectionError'))
|
||||
this.$message.error(this.$tc('TestAccountConnectionError'))
|
||||
return 200
|
||||
}
|
||||
return status
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage ref="createUpdatePage" v-bind="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { CronTab, Select2 } from '@/components'
|
||||
import rules from '@/components/Form/DataForm/rules'
|
||||
import SyncInstanceTaskStrategy from './components/SyncInstanceTaskStrategy/index'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
initial: {
|
||||
is_periodic: false,
|
||||
interval: 24,
|
||||
hostname_strategy: 'instance_name_partial_ip',
|
||||
ip_network_segment_group: ['*']
|
||||
},
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name']],
|
||||
[this.$t('CloudSource'), ['account', 'regions']],
|
||||
[this.$t('SaveSetting'), [
|
||||
'hostname_strategy', 'ip_network_segment_group',
|
||||
'sync_ip_type', 'is_always_update', 'fully_synchronous'
|
||||
]],
|
||||
[this.$t('SyncStrategy'), ['strategy']],
|
||||
[this.$t('Periodic'), ['is_periodic', 'interval', 'crontab']],
|
||||
[this.$t('Other'), ['comment']]
|
||||
],
|
||||
url: '/api/v1/xpack/cloud/sync-instance-tasks/',
|
||||
fieldsMeta: {
|
||||
account: {
|
||||
label: this.$t('Account'),
|
||||
on: {
|
||||
change: ([event], updateForm) => {
|
||||
vm.fieldsMeta.regions.el.ajax.url = `/api/v1/xpack/cloud/regions/?account_id=${event?.pk}`
|
||||
updateForm({ regions: '' })
|
||||
}
|
||||
},
|
||||
el: {
|
||||
multiple: false,
|
||||
value: [],
|
||||
ajax: {
|
||||
url: '/api/v1/xpack/cloud/accounts/',
|
||||
transformOption: (item) => {
|
||||
const label = `${item.name}(${item.provider.label})`
|
||||
return { label: label, value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hostname_strategy: {
|
||||
rules: [rules.RequiredChange],
|
||||
helpTip: this.$t('HostnameStrategy')
|
||||
},
|
||||
is_always_update: {
|
||||
type: 'switch',
|
||||
label: this.$t('IsAlwaysUpdate'),
|
||||
helpTip: this.$t('IsAlwaysUpdateHelpTip')
|
||||
},
|
||||
fully_synchronous: {
|
||||
type: 'switch',
|
||||
label: this.$t('FullySynchronous'),
|
||||
helpTip: this.$t('FullySynchronousHelpTip')
|
||||
},
|
||||
regions: {
|
||||
component: Select2,
|
||||
el: {
|
||||
multiple: true,
|
||||
allowCreate: true,
|
||||
value: [],
|
||||
ajax: {
|
||||
url: '/api/v1/xpack/cloud/regions/',
|
||||
processResults(data) {
|
||||
const results = data.regions?.map((item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
})
|
||||
const more = !!data.next
|
||||
return { results: results, pagination: more, total: data.count }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
is_periodic: {
|
||||
type: 'switch'
|
||||
},
|
||||
crontab: {
|
||||
component: CronTab,
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_periodic === false
|
||||
},
|
||||
helpTip: this.$t('CrontabOfCreateUpdatePage')
|
||||
},
|
||||
interval: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_periodic === false
|
||||
},
|
||||
helpText: this.$t('IntervalOfCreateUpdatePage')
|
||||
},
|
||||
strategy: {
|
||||
label: this.$t('Strategy'),
|
||||
component: SyncInstanceTaskStrategy,
|
||||
helpTip: this.$t('StrategyHelpTip')
|
||||
}
|
||||
},
|
||||
updateSuccessNextRoute: { name: 'CloudCenter' },
|
||||
createSuccessNextRoute: { name: 'CloudCenter' },
|
||||
afterGetFormValue(formValue) {
|
||||
formValue.protocols = formValue.protocols?.split(' ').map(i => {
|
||||
const [name, port] = i.split('/')
|
||||
return { name, port }
|
||||
})
|
||||
return formValue
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
const ipNetworkSegments = value.ip_network_segment_group
|
||||
const strategy = value?.strategy || []
|
||||
if (!Array.isArray(ipNetworkSegments)) {
|
||||
value.ip_network_segment_group = ipNetworkSegments ? ipNetworkSegments.split(',') : []
|
||||
}
|
||||
value.strategy = strategy.map(item => { return item.id })
|
||||
return value
|
||||
},
|
||||
onPerformError(error, method, vm) {
|
||||
this.$emit('submitError', error)
|
||||
const response = error.response
|
||||
const data = response.data
|
||||
if (response.status === 400) {
|
||||
for (const key of Object.keys(data)) {
|
||||
let value = data[key]
|
||||
if (key === 'protocols') {
|
||||
value = Object.values(data[key])
|
||||
}
|
||||
if (value instanceof Array) {
|
||||
value = value.join(';')
|
||||
}
|
||||
this.$refs.form.setFieldError(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const params = this.$route.params
|
||||
// 更新获取链接
|
||||
if (params.id) {
|
||||
const form = await this.$refs.createUpdatePage.$refs.createUpdateForm.getFormValue()
|
||||
this.fieldsMeta.regions.el.ajax.url = form.account?.id ? `/api/v1/xpack/cloud/regions/?account_id=${form.account.id}` : `/api/v1/xpack/cloud/regions/`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
||||
@@ -1,143 +0,0 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="15" :sm="24">
|
||||
<AutoDetailCard :fields="detailFields" :object="object" :url="url" />
|
||||
</el-col>
|
||||
<el-col :md="9" :sm="24">
|
||||
<QuickActions :actions="quickActions" type="primary" />
|
||||
<RelationCard
|
||||
ref="StrategyRelation"
|
||||
v-perms="'xpack.change_strategy'"
|
||||
style="margin-top: 15px"
|
||||
type="info"
|
||||
v-bind="strategyRelationConfig"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDetailCard from '@/components/Cards/DetailCard/auto'
|
||||
import RelationCard from '@/components/Cards/RelationCard'
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
AutoDetailCard,
|
||||
QuickActions,
|
||||
RelationCard
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('RunTaskManually'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('Execute'),
|
||||
disabled: !this.$hasPerm('xpack.add_syncinstancetaskexecution')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.get(
|
||||
`/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/run/`
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
],
|
||||
strategyRelationConfig: {
|
||||
icon: 'fa-info',
|
||||
title: this.$t('Strategy'),
|
||||
objectsAjax: {
|
||||
url: '/api/v1/xpack/cloud/strategies/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
},
|
||||
hasObjectsId: this.object.strategy?.map(i => i.id) || [],
|
||||
performAdd: (items) => {
|
||||
const newData = []
|
||||
const value = this.$refs.StrategyRelation.iHasObjects
|
||||
value.map(v => {
|
||||
newData.push(v.value)
|
||||
})
|
||||
const relationUrl = `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/`
|
||||
items.map(v => {
|
||||
newData.push(v.value)
|
||||
})
|
||||
return this.$axios.patch(relationUrl, { strategy: newData })
|
||||
},
|
||||
performDelete: (item) => {
|
||||
const itemId = item.value
|
||||
const newData = []
|
||||
const value = this.$refs.StrategyRelation.iHasObjects
|
||||
value.map(v => {
|
||||
if (v.value !== itemId) {
|
||||
newData.push(v.value)
|
||||
}
|
||||
})
|
||||
const relationUrl = `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/`
|
||||
return this.$axios.patch(relationUrl, { strategy: newData })
|
||||
}
|
||||
},
|
||||
url: `/api/v1/xpack/cloud/accounts/${this.object.id}`,
|
||||
detailFields: [
|
||||
'name', 'account_display', 'node_display',
|
||||
{
|
||||
key: this.$t('Strategy'),
|
||||
value: this.object.strategy?.map(item => item.name).join(', ')
|
||||
},
|
||||
{
|
||||
key: this.$t('IPNetworkSegment'),
|
||||
value: this.object.ip_network_segment_group?.join(', ')
|
||||
},
|
||||
'is_always_update', 'is_periodic', 'periodic_display',
|
||||
{
|
||||
key: this.$t('DateLastSync'),
|
||||
value: this.object.date_last_sync ? toSafeLocalDateStr(this.object.date_created) : ''
|
||||
},
|
||||
{
|
||||
key: this.$t('DateCreated'),
|
||||
value: this.object.date_created ? toSafeLocalDateStr(this.object.date_created) : ''
|
||||
},
|
||||
{
|
||||
key: this.$t('Region'),
|
||||
value: this.object.regions,
|
||||
formatter(row, value) {
|
||||
return (<div>{
|
||||
value?.map((content) => {
|
||||
return <div>{ content }</div>
|
||||
})}
|
||||
</div>)
|
||||
}
|
||||
},
|
||||
'comment'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,65 +0,0 @@
|
||||
<template>
|
||||
<GenericDetailPage :active-menu.sync="config.activeMenu" :object.sync="TaskDetail" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="TaskDetail" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import detail from './detail'
|
||||
import HistoryList from './HistoryList'
|
||||
import AssetList from './AssetList'
|
||||
|
||||
export default {
|
||||
name: 'SyncInstanceTaskDetail',
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TabPage,
|
||||
detail,
|
||||
HistoryList,
|
||||
AssetList
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
TaskDetail: {},
|
||||
config: {
|
||||
activeMenu: 'detail',
|
||||
url: '/api/v1/xpack/cloud/sync-instance-tasks',
|
||||
actions: {
|
||||
canDelete: vm.$hasPerm('xpack.delete_syncinstancetask'),
|
||||
canUpdate: vm.$hasPerm('xpack.change_syncinstancetask'),
|
||||
deleteSuccessRoute: 'CloudCenter'
|
||||
},
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('SyncInstanceTaskDetail'),
|
||||
name: 'detail'
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncInstanceTaskHistoryList'),
|
||||
name: 'HistoryList',
|
||||
hidden: () => !this.$hasPerm('xpack.view_syncinstancetaskexecution')
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncInstanceTaskHistoryAssetList'),
|
||||
name: 'AssetList',
|
||||
hidden: () => !this.$hasPerm('xpack.view_syncinstancedetail')
|
||||
|
||||
}
|
||||
],
|
||||
hasRightSide: true
|
||||
// getObjectName: function(obj) {
|
||||
// return obj.hostname + '(' + obj.ip + ')'
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import { DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'SyncInstanceTaskList',
|
||||
components: {
|
||||
GenericListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/xpack/cloud/sync-instance-tasks/',
|
||||
permissions: {
|
||||
app: 'xpack',
|
||||
resource: 'syncinstancetask'
|
||||
},
|
||||
columnsShow: {
|
||||
min: ['name', 'account', 'hostname_strategy', 'actions'],
|
||||
default: [
|
||||
'name', 'account', 'hostname_strategy', 'protocols', 'is_periodic', 'actions'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
sync_ip_type: {
|
||||
width: '120px'
|
||||
},
|
||||
hostname_strategy: {
|
||||
width: '150px',
|
||||
formatter: function(row) {
|
||||
return <span>{ row.hostname_strategy.label }</span>
|
||||
}
|
||||
},
|
||||
account: {
|
||||
label: this.$t('Account'),
|
||||
formatter: function(row) {
|
||||
return <span>{ row.account?.name }</span>
|
||||
}
|
||||
},
|
||||
periodic_display: {
|
||||
width: '150px'
|
||||
},
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
hasClone: false,
|
||||
onUpdate: ({ row }) => {
|
||||
this.$router.push({ name: 'SyncInstanceTaskUpdate', params: { id: row.id }})
|
||||
},
|
||||
extraActions: [
|
||||
{
|
||||
title: vm.$t('Execute'),
|
||||
name: 'execute',
|
||||
type: 'info',
|
||||
can: () => vm.$hasPerm('xpack.add_syncinstancetaskexecution'),
|
||||
callback: function(data) {
|
||||
this.$axios.get(`/api/v1/xpack/cloud/sync-instance-tasks/${data.row.id}/run/`).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
permissions: 'xpack.view_syncinstancedetail',
|
||||
route: 'SyncInstanceTaskDetail'
|
||||
}
|
||||
},
|
||||
history_count: {
|
||||
width: '110px',
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
permissions: 'xpack.view_syncinstancetaskexecution',
|
||||
route: 'SyncInstanceTaskDetail',
|
||||
routeQuery: {
|
||||
tab: 'HistoryList'
|
||||
}
|
||||
}
|
||||
},
|
||||
instance_count: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
permissions: 'xpack.view_syncinstancetask',
|
||||
route: 'SyncInstanceTaskDetail',
|
||||
routeQuery: {
|
||||
tab: 'AssetList'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
date_last_sync: {
|
||||
width: '130px'
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasMoreActions: false,
|
||||
hasImport: false,
|
||||
hasExport: false,
|
||||
createRoute: 'SyncInstanceTaskCreate'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -13,25 +13,19 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
config: {
|
||||
activeMenu: 'SyncInstanceTaskList',
|
||||
activeMenu: 'CloudAccountList',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('SyncTask'),
|
||||
name: 'SyncInstanceTaskList',
|
||||
hidden: () => !this.$hasPerm('xpack.view_syncinstancetask'),
|
||||
component: () => import('@/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskList.vue')
|
||||
title: this.$t('CloudAccountList'),
|
||||
name: 'CloudAccountList',
|
||||
hidden: () => !this.$hasPerm('xpack.view_account'),
|
||||
component: () => import('@/views/assets/Cloud/Account/AccountList.vue')
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncStrategy'),
|
||||
name: 'StrategyList',
|
||||
hidden: () => !this.$hasPerm('xpack.view_strategy'),
|
||||
component: () => import('@/views/assets/Cloud/Strategy/StrategyList.vue')
|
||||
},
|
||||
{
|
||||
title: this.$t('CloudAccountList'),
|
||||
name: 'AccountList',
|
||||
hidden: () => !this.$hasPerm('xpack.view_account'),
|
||||
component: () => import('@/views/assets/Cloud/Account/AccountList.vue')
|
||||
}
|
||||
],
|
||||
actions: {
|
||||
|
||||
@@ -3,45 +3,50 @@
|
||||
<div v-if="isDev" style="margin-bottom: 20px">
|
||||
<div class="dz">
|
||||
<el-button
|
||||
v-for="tp of ['primary', 'success', 'info', 'warning', 'danger']"
|
||||
v-for="(value, tp) in examples"
|
||||
:key="tp"
|
||||
:type="tp"
|
||||
size="small"
|
||||
>
|
||||
{{ tp.toUpperCase() }}
|
||||
{{ value }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="dz">
|
||||
<el-button
|
||||
v-for="tp of ['primary', 'success', 'info', 'warning', 'danger']"
|
||||
v-for="(value, tp) in examples"
|
||||
:key="tp"
|
||||
:type="tp"
|
||||
size="small"
|
||||
disabled
|
||||
>
|
||||
{{ tp.toUpperCase() }}
|
||||
{{ value }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="dz">
|
||||
<el-link
|
||||
v-for="tp of ['primary', 'success', 'info', 'warning', 'danger']"
|
||||
v-for="(value, tp) in examples"
|
||||
:key="tp"
|
||||
:type="tp"
|
||||
style="padding-right: 10px;"
|
||||
>
|
||||
<span style="padding-right: 10px">{{ tp.toUpperCase() }}</span>
|
||||
{{ value }}
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="dz">
|
||||
<el-radio-group v-model="dz.radio">
|
||||
<el-radio :label="3">备选项1</el-radio>
|
||||
<el-radio :label="6">备选项2</el-radio>
|
||||
<el-radio :label="9">备选项3</el-radio>
|
||||
<el-radio v-for="i in 3" :key="i" :label="$tc('Options') + ` ${i}`" />
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-steps :active="1" :space="200" class="dz" finish-status="error">
|
||||
<el-step title="已完成" />
|
||||
<el-step title="进行中" />
|
||||
<el-step title="步骤 3" />
|
||||
</el-steps>
|
||||
<div class="dz">
|
||||
<el-steps :active="1" :space="100">
|
||||
<el-step
|
||||
v-for="(s, i) in stepStatus"
|
||||
:key="s"
|
||||
:title="$tc('Step') + ` ${i+1}`"
|
||||
:status="s"
|
||||
/>
|
||||
</el-steps>
|
||||
</div>
|
||||
<div class="dz" />
|
||||
</div>
|
||||
<IBox v-if="!loading">
|
||||
@@ -77,8 +82,13 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
dz: {},
|
||||
stepStatus: ['wait', 'success', 'finish', 'process', 'error'],
|
||||
loading: true,
|
||||
files: {},
|
||||
examples: {
|
||||
'primary': this.$t('Primary'), 'info': this.$t('Info'), 'warning': this.$t('Warning'),
|
||||
'success': this.$t('Success'), 'danger': this.$t('Danger')
|
||||
},
|
||||
interfaceInfo: {},
|
||||
hasSaveContinue: false,
|
||||
successUrl: { name: 'Settings' },
|
||||
|
||||
Reference in New Issue
Block a user