This commit is contained in:
“huailei000”
2022-10-25 13:55:24 +08:00
25 changed files with 456 additions and 273 deletions

View File

@@ -1,117 +0,0 @@
<template>
<AutoDataForm v-bind="$data" @submit="confirm" />
</template>
<script>
import AutoDataForm from '@/components/AutoDataForm'
import { UpdateToken } from '@/components/FormFields'
export default {
name: 'AccountCreateForm',
components: {
AutoDataForm
},
props: {
protocols: {
type: Array,
default: () => ([])
},
accounts: {
type: Array,
default: () => ([])
},
account: {
type: Object,
default: null
}
},
data() {
return {
url: '/api/v1/assets/accounts/',
form: this.account || { },
fields: [
['Basic', ['username', 'privileged']],
['Auth', ['secret_type', 'password', 'ssh_key', 'token', 'api_key', 'passphrase']],
['Other', ['push_now', 'name', 'comment']]
],
defaultPrivilegedAccounts: ['root', 'administrator'],
fieldsMeta: {
username: {
on: {
change: ([value], updateForm) => {
if (this.defaultPrivilegedAccounts.indexOf(value.toLowerCase()) > -1) {
updateForm({ privileged: true })
}
}
}
},
password: {
label: 'Password',
component: UpdateToken,
hidden: (formValue) => formValue['secret_type'] !== 'password'
},
ssh_key: {
label: 'SSH private key',
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue['secret_type'] !== 'ssh_key'
},
passphrase: {
label: 'Passphrase',
component: UpdateToken,
hidden: (formValue) => formValue['secret_type'] !== 'ssh_key'
},
token: {
label: 'Token',
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue['secret_type'] !== 'token'
},
api_key: {
id: 'api_key',
label: 'Secret key',
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue['secret_type'] !== 'api_key'
},
secret_type: {
label: 'Secret Type',
type: 'radio-group',
options: [
{ label: 'Password', value: 'password' },
{ label: 'SSH key', value: 'ssh_key' },
{ label: 'Token', value: 'token' },
{ label: 'Api key', value: 'api_key' }
]
}
},
hasSaveContinue: false
}
},
mounted() {
console.log('protocols: ', this.protocols)
console.log('this.account: ', this.account)
},
methods: {
confirm(form) {
console.log('Account form: accout is: ', this.account)
if (this.account?.name) {
this.$emit('edit', form)
} else {
this.$emit('add', form)
}
},
hasProtocol(name) {
return this.protocols.find(item => item.name === name)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,137 @@
<template>
<AutoDataForm
v-bind="$data"
@submit="confirm"
@afterRemoteMeta="afterGetRemoteMeta"
/>
</template>
<script>
import AutoDataForm from '@/components/AutoDataForm'
import { UpdateToken } from '@/components/FormFields'
export default {
name: 'AccountCreateForm',
components: {
AutoDataForm
},
props: {
platform: {
type: Object,
required: true
},
account: {
type: Object,
default: null
}
},
data() {
return {
loading: true,
usernameChanged: false,
url: '/api/v1/assets/accounts/',
form: this.account || { },
fields: [
[this.$t('common.Basic'), ['name', 'username', 'privileged']],
[this.$t('assets.Secret'), ['secret_type', 'password', 'ssh_key', 'token', 'api_key', 'passphrase']],
[this.$t('common.Other'), ['push_now', 'comment']]
],
defaultPrivilegedAccounts: ['root', 'administrator'],
fieldsMeta: {
name: {
on: {
input: ([value], updateForm) => {
if (!this.usernameChanged) {
updateForm({ username: value })
}
}
}
},
username: {
on: {
input: ([value], updateForm) => {
this.usernameChanged = true
},
change: ([value], updateForm) => {
if (this.defaultPrivilegedAccounts.indexOf(value.toLowerCase()) > -1) {
updateForm({ privileged: true })
}
}
}
},
password: {
label: this.$t('assets.Password'),
component: UpdateToken,
hidden: (formValue) => formValue.secret_type !== 'password'
},
ssh_key: {
label: this.$t('assets.PrivateKey'),
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue.secret_type !== 'ssh_key'
},
passphrase: {
label: 'Passphrase',
component: UpdateToken,
hidden: (formValue) => formValue.secret_type !== 'ssh_key'
},
token: {
label: 'Token',
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue.secret_type !== 'token'
},
api_key: {
id: 'api_key',
label: this.$t('assets.SecretKey'),
el: {
type: 'textarea',
rows: 4
},
hidden: (formValue) => formValue.secret_type !== 'api_key'
},
secret_type: {
type: 'radio-group',
options: []
},
push_now: {
hidden: () => {
return !this.platform.automation?.['push_account_enabled']
}
}
},
hasSaveContinue: false
}
},
mounted() {
},
methods: {
afterGetRemoteMeta(meta) {
const choices = meta.secret_type.choices
const secretTypes = []
this.platform.protocols?.forEach(p => {
secretTypes.push(...p['secret_types'])
})
if (!this.form.secret_type) {
this.form.secret_type = secretTypes[0]
}
this.fieldsMeta.secret_type.options = choices.filter(item => {
return secretTypes.indexOf(item.value) > -1
})
},
confirm(form) {
if (this.account?.name) {
this.$emit('edit', form)
} else {
this.$emit('add', form)
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -7,7 +7,7 @@
<script>
import { ActionsFormatter, DetailFormatter } from '@/components/TableFormatters'
import ShowSecretInfo from '../AccountListTable/ShowSecretInfo'
import ShowSecretInfo from '../AccountListTable/ViewSecret'
import { GenericListPage } from '@/layout/components'
export default {

View File

@@ -10,8 +10,9 @@
v-bind="$attrs"
v-on="$listeners"
>
<AccountCreateForm
:protocols="protocols"
<AccountCreateUpdateForm
v-if="!loading"
:platform="platform"
:account="account"
@add="addAccount"
@edit="editAccount"
@@ -21,12 +22,12 @@
<script>
import Dialog from '@/components/Dialog'
import AccountCreateForm from '@/components/AccountCreateForm'
import AccountCreateUpdateForm from '@/components/AccountCreateUpdateForm'
export default {
name: 'CreateAccountDialog',
components: {
Dialog,
AccountCreateForm
AccountCreateUpdateForm
},
props: {
visible: {
@@ -39,18 +40,16 @@ export default {
},
account: {
type: Object,
default: () => ({
name: '',
username: '',
password: '',
private_key: ''
})
default: () => ({})
}
},
data() {
return {
loading: true,
platform: {}
}
},
computed: {
iVisible: {
get() {
@@ -64,7 +63,17 @@ export default {
return this.asset ? this.asset.protocol : []
}
},
async mounted() {
try {
await this.getPlatform()
} finally {
this.loading = false
}
},
methods: {
async getPlatform() {
this.platform = await this.$axios.get(`/api/v1/assets/platforms/${this.asset?.platform?.id}/`)
},
addAccount(form) {
const data = { asset: this.asset.id, ...form }
this.$axios.post(`/api/v1/assets/accounts/`, data).then(() => {

View File

@@ -1,7 +1,7 @@
<template>
<Dialog
width="50"
:title="this.$t('assets.UpdateAssetUserToken')"
:title="this.$tc('assets.UpdateAssetUserToken')"
:visible.sync="visible"
:destroy-on-close="true"
@confirm="handleConfirm()"

View File

@@ -20,7 +20,7 @@
<el-form-item :label="this.$t('assets.Name')">
<el-input v-model="account.asset.name" readonly />
</el-form-item>
<el-form-item :label="this.$t('assets.Username')">
<el-form-item :label="this.$tc('assets.Username')">
<el-input v-model="account['username']" readonly />
</el-form-item>
<el-form-item v-if="secretTypePassword" :label="this.$t('assets.Password')">
@@ -68,7 +68,7 @@ export default {
},
data() {
return {
dialogTitle: this.$t('common.ViewSecret'),
dialogTitle: this.$tc('common.ViewSecret'),
authInfo: {},
showAuthInfo: false,
url: `/api/v1/assets/account-secrets/${this.account.id}/`

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
<ShowSecretInfo v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" />
<ViewSecret v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" />
<UpdateSecretInfo
v-if="showUpdateSecretDialog"
:visible.sync="showUpdateSecretDialog"
@@ -26,7 +26,7 @@
<script>
import ListTable from '@/components/ListTable/index'
import { ActionsFormatter } from '@/components/TableFormatters'
import ShowSecretInfo from './ShowSecretInfo'
import ViewSecret from './ViewSecret'
import UpdateSecretInfo from './UpdateSecretInfo'
import AddAccount from './AddAccount'
import { connectivityMeta } from './const'
@@ -39,9 +39,9 @@ export default {
components: {
ListTable,
UpdateSecretInfo,
ShowSecretInfo,
AddAccount,
PasswordHistoryDialog
PasswordHistoryDialog,
ViewSecret,
AddAccount
},
props: {
url: {
@@ -138,7 +138,7 @@ export default {
{
name: 'View',
title: this.$t('common.View'),
can: this.$hasPerm('assets.view_assetaccountsecret'),
can: this.$hasPerm('assets.view_accountsecret'),
type: 'primary',
callback: ({ row }) => {
vm.account = row
@@ -176,7 +176,7 @@ export default {
{
name: 'Update',
title: this.$t('common.Update'),
can: this.$hasPerm('assets.change_assetaccountsecret') && !this.$store.getters.currentOrgIsRoot,
can: this.$hasPerm('assets.change_account') && !this.$store.getters.currentOrgIsRoot,
callback: ({ row }) => {
vm.account = row
vm.showAddDialog = false
@@ -252,6 +252,8 @@ export default {
actionColumn.formatterArgs.extraActions.push(item)
}
}
console.log('Has perm: ', this.$hasPerm('assets.change_account'))
},
methods: {
onUpdateAuthDone(account) {

View File

@@ -76,13 +76,15 @@ export default {
data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
}
this.remoteMeta = data.actions[this.method.toUpperCase()] || {}
this.$emit('afterRemoteMeta', this.remoteMeta)
this.generateColumns()
this.$emit('afterGenerateColumns', this.totalFields)
this.cleanFormValue()
this.loading = false
console.log('Loading: ', this.groups)
},
generateColumns() {
const generator = new FormFieldGenerator()
const generator = new FormFieldGenerator(this.$emit)
this.totalFields = generator.generateFields(this.fields, this.fieldsMeta, this.remoteMeta)
this.groups = generator.groups
this.$log.debug('Total fields: ', this.totalFields)

View File

@@ -2,12 +2,13 @@ import Vue from 'vue'
import Select2 from '@/components/FormFields/Select2'
import ObjectSelect2 from '@/components/FormFields/NestedObjectSelect2'
import NestedField from '@/components/AutoDataForm/components/NestedField'
import Swicher from '@/components/FormFields/Swicher'
import Switcher from '@/components/FormFields/Switcher'
import rules from '@/components/DataForm/rules'
import { assignIfNot } from '@/utils/common'
export class FormFieldGenerator {
constructor() {
constructor(emit) {
this.$emite = emit
this.groups = []
}
generateFieldByType(type, field, fieldMeta, fieldRemoteMeta) {
@@ -62,7 +63,7 @@ export class FormFieldGenerator {
break
case 'boolean':
type = ''
field.component = Swicher
field.component = Switcher
break
case 'object_related_field':
field.component = ObjectSelect2

View File

@@ -1,159 +0,0 @@
<template>
<div v-if="isUpdate(this)">
在资产详情中更新账号信息
</div>
<div v-else class="accounts">
<el-table :data="accounts" style="width: 100%">
<el-table-column prop="username" label="用户名" width="180" />
<el-table-column prop="privileged" label="特权账号">
<template v-slot="scope">
<i class="fa text-primary" :class="scope.row['privileged'] ? 'fa-check' : ''" />
</template>
</el-table-column>
<el-table-column fixed="right" align="right" label="操作" width="135" class-name="buttons">
<template v-slot="scope">
<el-button type="danger" icon="el-icon-minus" size="mini" @click="removeAccount(scope.row)" />
<el-button type="primary" icon="el-icon-edit" size="mini" @click="onEditClick(scope.row)" />
</template>
</el-table-column>
</el-table>
<div class="actions">
<el-button size="mini" type="primary" @click="onAddClick">添加</el-button>
<el-button size="mini" type="success" @click="onAddFromTemplateClick">模版添加</el-button>
</div>
<Dialog
v-if="visible"
:title="this.$tc('assets.AddAccount')"
:visible.sync="visible"
:destroy-on-close="true"
:show-cancel="false"
:show-confirm="false"
width="70%"
>
<AccountCreateForm
:protocols="protocols"
:account="account"
@add="addAccount"
@edit="editAccount"
/>
</Dialog>
<Dialog
v-if="templateTable.visible"
:title="'选择模版'"
:visible.sync="templateTable.visible"
:destroy-on-close="true"
width="70%"
@submit="onSelectTemplate"
>
<AutoDataTable :config="templateTable.tableConfig" />
</Dialog>
</div>
</template>
<script>
import Dialog from '@/components/Dialog'
import AccountCreateForm from '@/components/AccountCreateForm'
import AutoDataTable from '@/components/AutoDataTable'
export default {
name: 'AssetAccounts',
components: {
Dialog,
AutoDataTable,
AccountCreateForm
},
props: {
protocols: {
type: Array,
default: () => []
},
value: {
type: [Array],
default: () => []
},
isUpdate: {
type: Function,
default: (vm) => {
return vm.$route.params.id
}
}
},
data() {
return {
visible: false,
accounts: [],
account: {},
initial: false,
templateTable: {
visible: false,
tableConfig: {
url: '/api/v1/assets/account-templates/',
columns: ['name', 'username', 'privileged', 'actions'],
columnsMeta: {
privileged: {
width: '100px'
}
}
}
}
}
},
watch: {
accounts: {
handler(value) {
if (value.length > 0 || this.initial) {
this.$emit('input', value)
}
if (value) {
this.initial = true
}
},
immediate: true,
deep: true
}
},
methods: {
addAccount(account) {
const i = this.accounts.findIndex(item => item.username === account.username)
if (i !== -1) {
this.accounts.splice(i, 1)
}
this.accounts.push(account)
this.visible = false
},
removeAccount(account) {
this.accounts = this.accounts.filter((item) => {
return item._id !== account._id
})
},
editAccount(form) {
const i = this.accounts.findIndex(item => item.username === this.account.username)
this.accounts.splice(i, 1, form)
this.visible = false
},
onEditClick(account) {
this.account = account
setTimeout(() => {
this.visible = true
})
},
onAddClick() {
this.account = null
setTimeout(() => {
this.visible = true
})
},
onAddFromTemplateClick() {
this.templateTable.visible = true
},
onSelectTemplate() {
}
}
}
</script>
<style scoped lang="scss">
.accounts >>> .buttons .cell {
padding-right: 2px;
}
</style>

View File

@@ -137,12 +137,9 @@ export default {
},
setDefaultItems(choices) {
const defaults = choices.filter(item => item.required || item.primary || item.default)
console.log('defaults1: ', defaults)
console.log('this value: ', this.value)
const notInDefaults = this.value.filter(item => {
return !defaults.find(d => d.name === item.name)
})
console.log('Not defaults: ', notInDefaults)
this.items = [...defaults, ...notInDefaults]
},
onSettingClick(item) {
@@ -158,7 +155,7 @@ export default {
}
.el-select {
width: 100px;
max-width: 120px;
}
.input-with-select {
@@ -171,7 +168,7 @@ export default {
}
.el-select ::v-deep .el-input__inner {
width: 100px;
width: 110px;
}
.input-button {

View File

@@ -10,7 +10,7 @@
<script>
export default {
name: 'Switcher',
name: 'Switcher', // Switch js
props: {
type: {
type: String,

View File

@@ -1,45 +1,45 @@
import Link from './Link'
import PasswordInput from './PasswordInput'
import Select2 from './Select2'
import Swicher from './Swicher'
import UploadField from './UploadField'
import UploadKey from './UploadKey'
import UserPassword from './UserPassword'
import WeekCronSelect from './WeekCronSelect'
import UpdateToken from './UpdateToken'
import JsonEditor from './JsonEditor'
import Text from './Text'
import Select2 from './Select2'
import Switcher from './Switcher'
import UploadKey from './UploadKey'
import JsonEditor from './JsonEditor'
import UploadField from './UploadField'
import UpdateToken from './UpdateToken'
import UserPassword from './UserPassword'
import PasswordInput from './PasswordInput'
import WeekCronSelect from './WeekCronSelect'
import NestedObjectSelect2 from './NestedObjectSelect2'
import DatetimeRangePicker from './DatetimeRangePicker'
export default {
DatetimeRangePicker,
NestedObjectSelect2,
Text,
Link,
PasswordInput,
Switcher,
Select2,
Swicher,
UploadKey,
JsonEditor,
UpdateToken,
UploadField,
UserPassword,
PasswordInput,
WeekCronSelect,
UpdateToken,
JsonEditor,
Text
NestedObjectSelect2,
DatetimeRangePicker
}
export {
DatetimeRangePicker,
NestedObjectSelect2,
Text,
Link,
PasswordInput,
Switcher,
Select2,
Swicher,
UploadKey,
JsonEditor,
UpdateToken,
UploadField,
UserPassword,
PasswordInput,
WeekCronSelect,
UpdateToken,
JsonEditor,
Text
NestedObjectSelect2,
DatetimeRangePicker
}

View File

@@ -3,14 +3,31 @@
<td>{{ action.title }}:</td>
<td>
<span>
<component :is="iType" v-model="action.attrs.model" v-bind="action.attrs" v-on="callbacks">{{ label }}</component>
<component
:is="iType"
v-model="action.attrs.model"
v-bind="action.attrs"
v-on="callbacks"
>
{{ label }}
</component>
</span>
</td>
</tr>
</template>
<script>
import Switcher from '../FormFields/Swicher'
import Switcher from '../FormFields/Switcher'
class Action {
constructor() {
this.title = ''
this.type = ''
this.attrs = {}
this.callbacks = ''
}
}
export default {
name: 'ActionItem',
components: {
@@ -18,8 +35,8 @@ export default {
},
props: {
action: {
type: Object,
default: () => ({})
type: [Action, Object],
default: () => ({ title: '' })
}
},
data() {
@@ -30,7 +47,7 @@ export default {
computed: {
iType() {
switch (this.action.type) {
case 'switcher':
case 'switch':
return 'Switcher'
default:
return 'el-button'

View File

@@ -19,7 +19,7 @@ export { default as SvgIcon } from './SvgIcon'
export { default as TreeTable } from './TreeTable'
export { default as IBox } from './IBox'
export { default as QuickActions } from './QuickActions'
export { default as Switcher } from './FormFields/Swicher'
export { default as Switcher } from './FormFields/Switcher'
export { default as SummaryCard } from './SummaryCard'
export { default as UploadField } from './FormFields/UploadField'
export { default as AccountListTable } from './AccountListTable/index'