Merge pull request #2155 from jumpserver/pr@v3@perf_account

pref: 继续优化 accounts view
This commit is contained in:
老广 2022-11-08 19:10:44 +08:00 committed by GitHub
commit eb550aeec3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 140 additions and 102 deletions

View File

@ -31,8 +31,19 @@ export default {
columns: ['secret', 'secret_type', 'version'],
columnsMeta: {
secret: {
formatter: ShowKeyCopyFormatter
formatter: ShowKeyCopyFormatter,
formatterArgs: {
hasDownload: false,
name: this.account.name
}
},
secret_type: {
width: '200px'
},
version: {
width: '100px'
}
}
},
headerActions: {

View File

@ -1,7 +1,7 @@
<template>
<Dialog
width="50"
:title="this.$tc('assets.UpdateAssetUserToken')"
:title="this.$tcc('assets.UpdateAssetUserToken')"
:visible.sync="visible"
:destroy-on-close="true"
@confirm="handleConfirm()"
@ -51,7 +51,7 @@ export default {
},
data() {
return {
authInfo: {
secretInfo: {
password: '',
private_key: '',
passphrase: ''
@ -61,12 +61,12 @@ export default {
methods: {
handleConfirm() {
const data = {}
if (this.authInfo.password !== '') {
data.password = encryptPassword(this.authInfo.password)
if (this.secretInfo.password !== '') {
data.password = encryptPassword(this.secretInfo.password)
}
if (this.authInfo.private_key !== '') {
data.private_key = encryptPassword(this.authInfo.private_key)
if (this.authInfo.passphrase) data.passphrase = this.authInfo.passphrase
if (this.secretInfo.private_key !== '') {
data.private_key = encryptPassword(this.secretInfo.private_key)
if (this.secretInfo.passphrase) data.passphrase = this.secretInfo.passphrase
}
this.$axios.patch(
`/api/v1/assets/accounts/${this.account.id}/`,
@ -74,12 +74,12 @@ export default {
{ disableFlashErrorMsg: true }
).then(res => {
this.authInfo = { password: '', private_key: '' }
this.$message.success(this.$tc('common.updateSuccessMsg'))
this.$message.success(this.$tcc('common.updateSuccessMsg'))
this.$emit('updateAuthDone', res)
this.$emit('update:visible', false)
}).catch(err => {
const errMsg = Object.values(err.response.data).join(', ')
this.$message.error(this.$tc('common.updateErrorMsg') + ' ' + errMsg)
this.$message.error(this.$tcc('common.updateErrorMsg') + ' ' + errMsg)
this.$emit('update:visible', true)
})
},
@ -87,7 +87,7 @@ export default {
this.$emit('update:visible', false)
},
getFile(file) {
this.authInfo.private_key = file
this.secretInfo.private_key = file
}
}
}

View File

@ -11,29 +11,28 @@
:show-cancel="false"
:destroy-on-close="true"
:width="'50'"
:visible.sync="showAuthInfo"
:visible.sync="showSecret"
v-bind="$attrs"
v-on="$listeners"
>
<el-form class="password-form" label-position="right" label-width="80px" :model="authInfo">
<el-form class="password-form" label-position="right" label-width="100px" :model="secretInfo">
<el-form-item :label="this.$tc('assets.Name')">
<span>{{ account['name'] }}</span>
</el-form-item>
<el-form-item :label="this.$tc('assets.Username')">
<span>{{ account['username'] }}</span>
</el-form-item>
<el-form-item v-if="secretTypePassword" :label="this.$tc('assets.Password')">
<ShowKeyCopyFormatter v-model="authInfo.secret" />
<el-form-item :label="secretTypeLabel">
<ShowKeyCopyFormatter
:cell-value="secretInfo.secret"
:col="{ formatterArgs: {
name: account['name'],
}}"
/>
</el-form-item>
<el-form-item v-if="secretType === 'ssh_key'" :label="this.$tc('assets.sshKeyFingerprint')">
<span>{{ sshKeyFingerprint }}</span>
</el-form-item>
<div v-else>
<el-form-item :label="this.$tc('assets.SSHSecretKey')">
<ShowKeyCopyFormatter v-model="authInfo.secret" :has-show="false" />
</el-form-item>
<el-form-item :label="this.$tc('assets.sshKeyFingerprint')">
<span>{{ sshKeyFingerprint }}</span>
<el-button type="text" @click="onDownload">{{ $t('common.Download') }}</el-button>
</el-form-item>
</div>
<el-form-item :label="this.$tc('common.DateCreated')">
<span>{{ account['date_created'] | date }}</span>
</el-form-item>
@ -41,7 +40,7 @@
<span>{{ account['date_updated'] | date }}</span>
</el-form-item>
<el-form-item :label="this.$tc('accounts.PasswordRecord')">
<el-button type="text" @click="onShowPasswordHistory">{{ authInfo.version }}</el-button>
<el-button type="text" @click="onShowPasswordHistory">{{ secretInfo.version }}</el-button>
</el-form-item>
</el-form>
</Dialog>
@ -58,7 +57,6 @@ import Dialog from '@/components/Dialog'
import PasswordHistoryDialog from './PasswordHistoryDialog'
import UserConfirmDialog from '@/components/UserConfirmDialog'
import { ShowKeyCopyFormatter } from '@/components/TableFormatters'
import { downloadFile } from '@/utils/common.js'
export default {
name: 'ShowSecretInfo',
@ -81,24 +79,27 @@ export default {
data() {
return {
dialogTitle: this.$tc('assets.AccountDetail'),
authInfo: {},
showAuthInfo: false,
secretInfo: {},
showSecret: false,
sshKeyFingerprint: '',
showPasswordHistoryDialog: false,
url: `/api/v1/assets/account-secrets/${this.account.id}/`
}
},
computed: {
secretTypePassword() {
return this.authInfo.secret_type === 'password'
secretTypeLabel() {
return this.account['secret_type'].label || 'Password'
},
secretType() {
return this.account['secret_type'].value
}
},
methods: {
getAuthInfo() {
this.$axios.get(this.url, { disableFlashErrorMsg: true }).then(resp => {
this.authInfo = resp
this.secretInfo = resp
this.sshKeyFingerprint = resp.specific.ssh_key_fingerprint
this.showAuthInfo = true
this.showSecret = true
})
},
exit() {
@ -106,10 +107,6 @@ export default {
},
onShowPasswordHistory() {
this.showPasswordHistoryDialog = true
},
onDownload() {
const { secret, secret_type } = this.authInfo || {}
downloadFile(secret, secret_type + '.key')
}
}
}
@ -121,11 +118,16 @@ export default {
}
.el-form-item {
border-bottom: 1px solid #EBEEF5;
padding: 3px 0;
padding: 5px 0;
margin-bottom: 0;
&:hover {
background-color: #F5F7FA;
}
>>> .el-form-item__label {
padding-right: 20px;
}
}
ul {

View File

@ -10,7 +10,7 @@
/>
<Dialog
v-if="dialogVisible"
:title="this.$t('assets.Assets')"
:title="this.$tc('assets.Assets')"
:visible.sync="dialogVisible"
custom-class="asset-select-dialog"
width="80vw"

View File

@ -2,7 +2,7 @@
<template>
<div>
<el-tabs type="border-card">
<el-tab-pane v-if="shouldHide('min')" :label="this.$t('common.CronTab.min')">
<el-tab-pane v-if="shouldHide('min')" :label="this.$tc('common.CronTab.min')">
<CrontabMin
ref="cronmin"
:check="checkNumber"
@ -11,7 +11,7 @@
/>
</el-tab-pane>
<el-tab-pane v-if="shouldHide('hour')" :label="this.$t('common.CronTab.hour')">
<el-tab-pane v-if="shouldHide('hour')" :label="this.$tc('common.CronTab.hour')">
<CrontabHour
ref="cronhour"
:check="checkNumber"
@ -20,7 +20,7 @@
/>
</el-tab-pane>
<el-tab-pane v-if="shouldHide('day')" :label="this.$t('common.CronTab.day')">
<el-tab-pane v-if="shouldHide('day')" :label="this.$tc('common.CronTab.day')">
<CrontabDay
ref="cronday"
:check="checkNumber"
@ -29,7 +29,7 @@
/>
</el-tab-pane>
<el-tab-pane v-if="shouldHide('month')" :label="this.$t('common.CronTab.month')">
<el-tab-pane v-if="shouldHide('month')" :label="this.$tc('common.CronTab.month')">
<CrontabMonth
ref="cronmonth"
:check="checkNumber"
@ -38,7 +38,7 @@
/>
</el-tab-pane>
<el-tab-pane v-if="shouldHide('week')" :label="this.$t('common.CronTab.week')">
<el-tab-pane v-if="shouldHide('week')" :label="this.$tc('common.CronTab.week')">
<CrontabWeek
ref="cronweek"
:check="checkNumber"

View File

@ -25,7 +25,7 @@
<el-form-item>
<el-radio v-model="radioValue" :label="7">
{{ this.$t('common.CronTab.appoint') }}
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
<el-select v-model="checkboxList" clearable :placeholder="this.$tc('common.CronTab.manyChoose')" multiple style="width:100%">
<el-option v-for="item in 31" :key="item" :value="item">{{ item }}</el-option>
</el-select>
</el-radio>

View File

@ -25,7 +25,7 @@
<el-form-item>
<el-radio v-model="radioValue" :label="4">
{{ this.$t('common.CronTab.appoint') }}
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
<el-select v-model="checkboxList" clearable :placeholder="this.$tc('common.CronTab.manyChoose')" multiple style="width:100%">
<el-option v-for="item in 24" :key="item" :value="item-1">{{ item-1 }}</el-option>
</el-select>
</el-radio>

View File

@ -25,7 +25,7 @@
<el-form-item>
<el-radio v-model="radioValue" :label="4">
{{ this.$t('common.CronTab.appoint') }}
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%" size="small">
<el-select v-model="checkboxList" clearable :placeholder="this.$tc('common.CronTab.manyChoose')" multiple style="width:100%" size="small">
<el-option v-for="item in 60" :key="item" :value="item-1">{{ item-1 }}</el-option>
</el-select>
</el-radio>

View File

@ -25,7 +25,7 @@
<el-form-item>
<el-radio v-model="radioValue" :label="4">
{{ this.$t('common.CronTab.appoint') }}
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
<el-select v-model="checkboxList" clearable :placeholder="this.$tc('common.CronTab.manyChoose')" multiple style="width:100%">
<el-option v-for="item in 12" :key="item" :value="item">{{ item }}</el-option>
</el-select>
</el-radio>

View File

@ -18,7 +18,7 @@
<el-form-item>
<el-radio v-model="radioValue" :label="6">
{{ this.$t('common.CronTab.appoint') }}
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
<el-select v-model="checkboxList" clearable :placeholder="this.$tc('common.CronTab.manyChoose')" multiple style="width:100%">
<el-option v-for="(item,index) of weekList" :key="index" :value="index+1">{{ item }}</el-option>
</el-select>
</el-radio>

View File

@ -3,7 +3,7 @@
<div class="box">
<el-input v-model="input" clearable @focus="showDialog" @clear="onClear" />
</div>
<el-dialog :title="this.$t('common.CronTab.newCron')" :visible.sync="showCron" top="8vh" width="580px" append-to-body>
<el-dialog :title="this.$tc('common.CronTab.newCron')" :visible.sync="showCron" top="8vh" width="580px" append-to-body>
<Crontab
:expression="expression"
@hide="showCron = false"

View File

@ -2,9 +2,9 @@
<el-date-picker
v-model="value"
type="datetimerange"
:range-separator="this.$t('common.To')"
:start-placeholder="this.$t('common.DateStart')"
:end-placeholder="this.$t('common.DateEnd')"
:range-separator="this.$tc('common.To')"
:start-placeholder="this.$tc('common.DateStart')"
:end-placeholder="this.$tc('common.DateEnd')"
size="small"
:clearable="false"
class="datepicker"

View File

@ -20,7 +20,7 @@
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
<el-form-item class="export-form" :label="this.$tc('common.imExport.ExportRange')" :label-width="'100px'">
<el-radio-group v-model="exportOption">
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
</el-radio-group>

View File

@ -31,11 +31,11 @@ export default {
false: this.$t('common.No')
},
getKey({ row, cellValue }) {
return typeof cellValue === 'object' ? cellValue.value : cellValue
return (cellValue && typeof cellValue === 'object') ? cellValue.value : cellValue
},
getText({ row, cellValue }) {
const key = this.getKey({ row, cellValue })
return typeof cellValue === 'object' ? cellValue.label : this.textChoices[key] || cellValue
return (cellValue && typeof cellValue === 'object') ? cellValue.label : this.textChoices[key] || cellValue
},
getIcon({ row, cellValue }) {
return this.faChoices[cellValue]

View File

@ -1,71 +1,86 @@
<template>
<div class="content">
<span class="text">{{ currentValue }}</span>
<span v-if="iValue" class="action">
<pre class="text">{{ currentValue }}</pre>
<span v-if="cellValue" class="action">
<el-tooltip
v-if="hasShow"
effect="dark"
placement="top"
:content="this.$t('common.View')"
:content="this.$tc('common.View')"
>
<i class="el-icon-view" @click="onShow()" />
<i class="fa" :class="isShow ? 'fa-eye-slash' : 'fa-eye'" @click="onShow()" />
</el-tooltip>
<el-tooltip
v-if="hasDownload"
effect="dark"
placement="top"
:content="this.$tc('common.Download')"
>
<i class="fa fa-download" @click="onDownload()" />
</el-tooltip>
<el-tooltip
v-if="hasCopy"
effect="dark"
placement="top"
:content="this.$t('common.Copy')"
:content="this.$tc('common.Copy')"
>
<i class="el-icon-copy-document" @click="onCopy()" />
<i class="fa fa-clone" @click="onCopy()" />
</el-tooltip>
</span>
</div>
</template>
<script>
import { downloadText } from '@/utils/common'
import BaseFormatter from '@/components/TableFormatters/base'
export default {
name: 'ShowKeyCopyFormatter',
extends: BaseFormatter,
props: {
value: {
type: String,
default: () => ''
},
cellValue: {
type: [String, Number],
default: ''
},
hasShow: {
type: Boolean,
default: true
},
hasCopy: {
type: Boolean,
default: true
formatterArgsDefault: {
type: Object,
default() {
return {
name: 'key',
hasShow: true,
hasDownload: true,
hasCopy: true,
defaultShow: false
}
}
}
},
data() {
return {
currentValue: this.switchShowValue()
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs || {}),
isShow: false
}
},
computed: {
iValue() {
return this.value || this.cellValue
hasShow: function() { return this.formatterArgs.hasShow },
hasDownload: function() { return this.formatterArgs.hasDownload },
hasCopy: function() { return this.formatterArgs.hasCopy },
name: function() { return this.formatterArgs.name },
currentValue() {
if (this.isShow) {
return this.cellValue
} else {
return '******'
}
}
},
mounted() {
this.isShow = this.formatterArgs.defaultShow
},
methods: {
switchShowValue() {
const value = this.value || this.cellValue
return value ? '******' + value.replace(/[\s\S]/g, '') : ''
},
onShow() {
const { currentValue, switchShowValue } = this
this.currentValue = currentValue === this.iValue ? switchShowValue() : this.iValue
this.isShow = !this.isShow
},
onCopy: _.throttle(function() {
const inputDom = document.createElement('input')
inputDom.id = 'createInputDom'
inputDom.value = this.iValue
inputDom.value = this.cellValue
document.body.appendChild(inputDom)
inputDom.select()
document?.execCommand('copy')
@ -75,7 +90,10 @@ export default {
duration: 1400
})
document.body.removeChild(inputDom)
}, 1800)
}, 1800),
onDownload() {
downloadText(this.cellValue, this.name + '.txt')
}
}
}
</script>
@ -84,19 +102,26 @@ export default {
display: flex;
width: 100%;
overflow: hidden;
white-space: nowrap;
//white-space: nowrap;
text-overflow: ellipsis;
font-size: 13px;
.text {
flex: 1;
line-height: 1.3;
overflow: hidden;
white-space: nowrap;
//white-space: nowrap;
text-overflow: ellipsis;
}
.action {
float: right;
font-size: 15px;
cursor: pointer;
.el-icon-view, .el-icon-copy-document {
margin-left: 5px;
.fa {
margin-right: 10px;
&:hover {
color: var(--color-primary);
}

View File

@ -24,7 +24,7 @@ export default {
},
tableData: {
type: Array,
default: () => ({})
default: () => ([])
},
url: {
type: String,

View File

@ -14,7 +14,7 @@
<el-row :gutter="24" style="margin: 0 auto;">
<el-col :md="24" :sm="24">
<el-alert
:title="this.$t('auth.ReLoginTitle')"
:title="this.$tc('auth.ReLoginTitle')"
type="info"
center
:closable="false"

View File

@ -1,6 +1,6 @@
<template>
<Dialog
:title="this.$t('common.updateSelected')"
:title="this.$tc('common.updateSelected')"
:visible.sync="iVisible"
width="70%"
top="1vh"

View File

@ -2,17 +2,17 @@
<div class="navbar">
<ul class="navbar-right">
<li class="header-item header-icon">
<el-tooltip effect="dark" :content="this.$t('route.SiteMessageList')">
<el-tooltip effect="dark" :content="this.$tc('route.SiteMessageList')">
<SiteMessages />
</el-tooltip>
</li>
<li v-perms="['rbac.view_webterminal']" class="header-item header-icon">
<el-tooltip effect="dark" :content="this.$t('route.WebTerminal')">
<el-tooltip effect="dark" :content="this.$tc('route.WebTerminal')">
<WebTerminal />
</el-tooltip>
</li>
<li v-perms="'settings.view_setting'" class="header-item header-icon">
<el-tooltip effect="dark" :content="this.$t('route.SystemSetting')">
<el-tooltip effect="dark" :content="this.$tc('route.SystemSetting')">
<SystemSetting />
</el-tooltip>
</li>

View File

@ -316,7 +316,7 @@ export { BASE_URL }
* @param {String} content
* @param {String} fileName
*/
export function downloadFile(content, filename) {
export function downloadText(content, filename) {
const a = document.createElement('a')
const blob = new Blob([content])
const url = window.URL.createObjectURL(blob)

View File

@ -1,7 +1,7 @@
<template>
<Dialog
v-if="iVisible"
:title="this.$t('assets.AddAccount')"
:title="this.$tc('assets.AddAccount')"
:visible.sync="iVisible"
:destroy-on-close="true"
:show-cancel="false"

View File

@ -3,7 +3,7 @@
<GenericListTable :table-config="tableConfig" :header-actions="headerActions" />
<Dialog
v-if="dialogVisible"
:title="this.$t('assets.TestGatewayTestConnection')"
:title="this.$tc('assets.TestGatewayTestConnection')"
:visible.sync="dialogVisible"
width="40%"
top="35vh"

View File

@ -1,7 +1,7 @@
<template>
<div>
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" />
<Dialog v-if="relationDialog.show" :visible.sync="relationDialog.show" :title="this.$t('audits.Hosts')" :show-cancel="false" :show-confirm="false">
<Dialog v-if="relationDialog.show" :visible.sync="relationDialog.show" :title="this.$tc('audits.Hosts')" :show-cancel="false" :show-confirm="false">
<ListTable :table-config="relationDialog.tableConfig" :header-actions="relationDialog.headerActions" />
</Dialog>
</div>

View File

@ -15,7 +15,7 @@
<Dialog
:visible.sync="dialogLicenseImport"
top="20vh"
:title="this.$t('setting.ImportLicense')"
:title="this.$tc('setting.ImportLicense')"
@cancel="dialogLicenseImport = false"
@confirm="importLicense"
>

View File

@ -1,7 +1,7 @@
<template>
<Dialog
v-if="setting.InviteDialogVisible"
:title="this.$t('users.InviteUserInOrg')"
:title="this.$tc('users.InviteUserInOrg')"
:visible.sync="setting.InviteDialogVisible"
:show-cancel="false"
:show-confirm="false"