Merge pull request #2972 from jumpserver/pr@dev@perf_account_template

perf: 优化账号批量创建
This commit is contained in:
老广
2023-04-04 09:58:42 +08:00
committed by GitHub
10 changed files with 212 additions and 51 deletions

View File

@@ -50,21 +50,21 @@ export default {
protocols: [
{
name: 'ssh',
secret_types: ['password', 'ssh_key', 'token', 'api_key']
secret_types: ['password', 'ssh_key', 'token', 'access_key']
}
]
},
url: '/api/v1/accounts/accounts/',
form: this.account || {},
form: Object.assign(this.account, { on_invalid: 'skip' }),
encryptedFields: ['secret'],
fields: [
[this.$t('assets.Asset'), ['assets']],
[this.$t('common.Basic'), ['name', 'username', 'privileged', 'su_from']],
[this.$t('assets.Secret'), [
'secret_type', 'secret', 'ssh_key', 'token',
'api_key', 'passphrase'
'secret_type', 'secret', 'ssh_key',
'token', 'access_key', 'passphrase'
]],
[this.$t('common.Other'), ['push_now', 'strategy', 'is_active', 'comment']]
[this.$t('common.Other'), ['push_now', 'on_invalid', 'is_active', 'comment']]
],
fieldsMeta: {
assets: {
@@ -78,7 +78,7 @@ export default {
return this.platform || this.asset
}
},
strategy: {
on_invalid: {
rules: [Required],
label: this.$t('ops.RunasPolicy'),
helpText: this.$t('accounts.BulkCreateStrategy'),
@@ -154,11 +154,11 @@ export default {
component: UploadSecret,
hidden: (formValue) => formValue.secret_type !== 'token'
},
api_key: {
id: 'api_key',
access_key: {
id: 'access_key',
label: this.$t('assets.AccessKey'),
component: UploadSecret,
hidden: (formValue) => formValue.secret_type !== 'api_key'
hidden: (formValue) => formValue.secret_type !== 'access_key'
},
secret_type: {
type: 'radio-group',
@@ -210,7 +210,7 @@ export default {
},
{
label: this.$t('assets.AccessKey'),
value: 'api_key'
value: 'access_key'
}
]
const secretTypes = []

View File

@@ -59,6 +59,9 @@ export default {
}
},
computed: {
protocols() {
return this.asset ? this.asset.protocol : []
},
iVisible: {
get() {
return this.visible
@@ -66,46 +69,62 @@ export default {
set(val) {
this.$emit('update:visible', val)
}
},
protocols() {
return this.asset ? this.asset.protocol : []
}
},
methods: {
addAccount(form) {
const formValue = Object.assign({}, form)
let assets = []
let data, url
if (this.asset) {
assets = [this.asset.id]
data = {
asset: this.asset.id,
...formValue
}
url = `/api/v1/accounts/accounts/`
} else {
assets = formValue.assets
data = formValue
url = `/api/v1/accounts/accounts/bulk/`
if (data.assets.length === 0) {
this.$message.error(this.$tc('assets.PleaseSelectAsset'))
return
}
}
delete formValue.assets
if (assets.length === 0) {
this.$message.error(this.$tc('assets.PleaseSelectAsset'))
return
}
const data = []
for (const asset of assets) {
data.push({
...formValue,
asset
})
}
this.$axios.post(`/api/v1/accounts/accounts/`, data).then(() => {
this.iVisible = false
this.$emit('add', true)
this.$message.success(this.$tc('common.createSuccessMsg'))
}).catch(error => this.setFieldError(error))
this.$axios.post(url, data).then((data) => {
this.handleResult(data, null)
}).catch(error => {
this.handleResult(null, error)
})
},
editAccount(form) {
const data = { ...form }
this.$axios.patch(`/api/v1/accounts/accounts/${this.account.id}/`, data).then(() => {
this.iVisible = false
this.$emit('add', true)
this.$message.success(this.$tc('common.updateSuccessMsg'))
}).catch(error => this.setFieldError(error))
},
handleResult(resp, error) {
let bulkCreate = !this.asset
if (error && !Array.isArray(error)) {
bulkCreate = false
}
// if (resp && !Array.isArray(resp)) {
// bulkCreate = false
// }
if (!bulkCreate) {
if (!error) {
this.$message.success(this.$tc('common.createSuccessMsg'))
} else {
this.setFieldError(error)
}
} else {
let result
if (error) {
result = error.response.data
} else {
result = resp
}
this.$emit('bulk-create-done', result)
}
},
setFieldError(error) {
const response = error.response
const data = response.data

View File

@@ -20,6 +20,12 @@
:title="accountCreateUpdateTitle"
:visible.sync="showAddDialog"
@add="addAccountSuccess"
@bulk-create-done="showBulkCreateResult($event)"
/>
<ResultDialog
v-if="showResultDialog"
:result="createAccountResults"
:visible.sync="showResultDialog"
/>
</div>
</template>
@@ -32,10 +38,12 @@ import UpdateSecretInfo from './UpdateSecretInfo'
import AccountCreateUpdate from './AccountCreateUpdate'
import { connectivityMeta } from './const'
import { openTaskPage } from '@/utils/jms'
import ResultDialog from './BulkCreateResultDialog.vue'
export default {
name: 'AccountListTable',
components: {
ResultDialog,
ListTable,
UpdateSecretInfo,
ViewSecret,
@@ -95,7 +103,9 @@ export default {
return {
showViewSecretDialog: false,
showUpdateSecretDialog: false,
showResultDialog: false,
showAddDialog: false,
createAccountResults: [],
accountCreateUpdateTitle: this.$t('assets.AddAccount'),
iAsset: this.asset,
account: {},
@@ -298,11 +308,6 @@ export default {
}
},
...this.headerExtraActions
// {
// name: 'autocreate',
// title: this.$t('accounts.AutoCreate'),
// type: 'default'
// }
],
extraMoreActions: [
{
@@ -365,6 +370,13 @@ export default {
},
refresh() {
this.$refs.ListTable.reloadTable()
},
showBulkCreateResult(results) {
this.showResultDialog = false
this.createAccountResults = results
setTimeout(() => {
this.showResultDialog = true
}, 100)
}
}
}

View File

@@ -0,0 +1,116 @@
<template>
<Dialog
:show-cancel="false"
:title="title"
v-bind="$attrs"
@confirm="closeDialog"
v-on="$listeners"
>
<el-alert style="margin-bottom: 10px" type="success">
<span v-for="item of summary" :key="item.key"><b>{{ item.label }}</b>: {{ item.value }} </span>
</el-alert>
<DataTable :config="config" />
</Dialog>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import DataTable from '@/components/DataTable/index.vue'
export default {
name: 'ResultDialog',
components: {
DataTable,
Dialog
},
props: {
result: {
type: Array,
default: () => []
}
},
data() {
const errorProp = this.$t('common.Error')
const stateMap = {
'created': this.$tc('common.Created'),
'updated': this.$tc('common.Updated'),
'skipped': this.$tc('common.Skipped')
}
const stateClsMap = {
'created': 'color-primary',
'updated': 'color-success',
'skipped': 'color-default'
}
return {
title: this.$t('accounts.AddAccountResult'),
config: {
columns: [
{
prop: 'asset',
label: this.$t('assets.Asset')
},
{
prop: 'state',
label: this.$t('common.Status'),
width: '200px',
formatter: (row) => {
if (row.error) {
return <span class='color-error'>{ errorProp }: { row.error }</span>
} else if (row.state) {
const colorCls = stateClsMap[row.state]
const state = stateMap[row.state]
return <span class={ colorCls }>{ state }</span>
}
}
}
],
totalData: this.result
}
}
},
computed: {
summary() {
const labels = {
total: this.$tc('common.Total'),
created: this.$tc('common.Created'),
updated: this.$tc('common.Updated'),
skipped: this.$tc('common.Skipped'),
error: this.$tc('common.Error')
}
const grouped = _.groupBy(this.result, 'state')
const groupedLength = _.mapValues(grouped, 'length')
groupedLength['total'] = this.result.length
return _.map(groupedLength, (value, key) => {
return {
label: labels[key],
value: value,
key: key
}
})
}
},
methods: {
closeDialog() {
this.$emit('update:visible', false)
}
}
}
</script>
<style scoped>
.color-error {
color: var(--color-danger);
}
.color-primary {
color: var(--color-primary);
}
.color-success {
color: var(--color-success);
}
.color-default {
}
</style>

View File

@@ -63,6 +63,9 @@ export default {
dateEnd: extraQuery.date_to
}, this.headerActions.datePicker)
}
if (this.$route.query.order) {
extraQuery['order'] = this.$route.query.order
}
return {
selectedRows: [],
init: false,

View File

@@ -1,6 +1,7 @@
{
"": "",
"accounts": {
"AddAccountResult": "账号批量添加结果",
"BulkCreateStrategy": "创建时对于不符合要求的账号,如:密钥类型不合规,唯一键约束,可选择以上策略。",
"AccountTemplate": "账号模版",
"HistoryDate": "日期",
@@ -442,6 +443,10 @@
"ReLoginErr": "登录时长已超过 5 分钟,请重新登录"
},
"common": {
"Created": "已创建",
"Updated": "已更新",
"Skipped": "已跳过",
"Error": "错误",
"ServerError": "服务器错误",
"CommunityEdition": "社区版",
"EnterpriseEdition": "企业版",

View File

@@ -1,5 +1,6 @@
import i18n from '@/i18n/i18n'
import { message } from '@/utils/message'
const _ = require('lodash')
const moment = require('moment')
@@ -244,10 +245,10 @@ export function getDayFuture(days, now) {
export function getErrorResponseMsg(error) {
let msg = ''
let data = ''
if (error.response.status === 500) {
if (error?.response?.status === 500) {
data = i18n.t('common.ServerError')
} else {
data = error.response && error.response.data || error
data = error?.response && error?.response.data || error
}
if (data && (data.error || data.msg || data.detail)) {
msg = data.error || data.msg || data.detail

View File

@@ -137,11 +137,16 @@ export default {
handleConfirm() {
this.iVisible = false
// 过滤掉添加里还没有id的账号
const hasIdAccounts = this.accounts.filter(i => i?.id).map(item => item.id)
const templates = this.accounts.filter(i => i?.template).map(item => item.template)
const newAddAccounts = this.accountsSelected.filter(i => {
if (!hasIdAccounts.includes(i.id)) {
i.template = true
return i
return templates.indexOf(i.id) === -1
}).map(item => {
return {
template: item.id,
name: item.name,
username: item.username,
secret_type: item.secret_type.value,
privileged: item.privileged
}
})
this.accounts.push(...newAddAccounts)
@@ -168,7 +173,7 @@ export default {
})
if (status) {
this.$refs.dataTable.$refs.dataTable.toggleRowSelection(row, false)
this.$message.error(this.$t('accounts.SameTypeAccountTip'))
this.$message.error(this.$tc('accounts.SameTypeAccountTip'))
}
return status
},

View File

@@ -20,7 +20,7 @@
<el-table-column :label="$tc('common.Actions')" align="right" class-name="buttons" fixed="right" width="135">
<template v-slot="scope">
<el-button icon="el-icon-minus" size="mini" type="danger" @click="removeAccount(scope.row)" />
<el-button icon="el-icon-edit" size="mini" type="primary" @click="onEditClick(scope.row)" />
<el-button :disabled="scope.row.template" icon="el-icon-edit" size="mini" type="primary" @click="onEditClick(scope.row)" />
</template>
</el-table-column>
</el-table>
@@ -28,7 +28,7 @@
<el-button size="mini" type="primary" @click="onAddClick">
{{ $t('common.Add') }}
</el-button>
<el-button size="mini" type="success" :disabled="!$hasPerm('accounts.view_accounttemplate')" @click="onAddFromTemplateClick">
<el-button :disabled="!$hasPerm('accounts.view_accounttemplate')" size="mini" type="success" @click="onAddFromTemplateClick">
{{ $t('common.TemplateAdd') }}
</el-button>
</div>

View File

@@ -15,8 +15,8 @@
/>
<AccountTemplateDialog
v-if="templateDialogVisible"
:show-create="false"
:asset="object"
:show-create="false"
:visible.sync="templateDialogVisible"
@onConfirm="onConfirm"
/>
@@ -73,7 +73,7 @@ export default {
methods: {
onConfirm(data) {
data = data?.map(i => {
i.template = true
i.template = i.id
i.asset = this.object.id
return i
})