mirror of
https://github.com/jumpserver/lina.git
synced 2025-09-09 02:40:18 +00:00
@@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="mfaDialogVisible">
|
||||
<UserConfirmDialog
|
||||
:url="url"
|
||||
@UserConfirmCancel="exit"
|
||||
@UserConfirmDone="getAuthInfo"
|
||||
/>
|
||||
</div>
|
||||
<Dialog
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
@@ -67,7 +60,6 @@
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
|
||||
import UserConfirmDialog from '@/components/Apps/UserConfirmDialog/index.vue'
|
||||
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
|
||||
@@ -76,7 +68,6 @@ export default {
|
||||
components: {
|
||||
Dialog,
|
||||
PasswordHistoryDialog,
|
||||
UserConfirmDialog,
|
||||
ShowKeyCopyFormatter
|
||||
},
|
||||
props: {
|
||||
@@ -128,7 +119,10 @@ export default {
|
||||
const url = `/api/v1/accounts/account-secrets/${this.account.id}/histories/?limit=1`
|
||||
this.$axios.get(url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.versions = resp.count
|
||||
this.showSecretDialog()
|
||||
})
|
||||
} else {
|
||||
this.showSecretDialog()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -146,10 +140,10 @@ export default {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
})
|
||||
},
|
||||
getAuthInfo() {
|
||||
this.$axios.get(this.url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.secretInfo = resp
|
||||
this.sshKeyFingerprint = resp?.spec_info?.ssh_key_fingerprint || '-'
|
||||
showSecretDialog() {
|
||||
return this.$axios.get(this.url, { disableFlashErrorMsg: true }).then((res) => {
|
||||
this.secretInfo = res
|
||||
this.sshKeyFingerprint = res?.spec_info?.ssh_key_fingerprint || '-'
|
||||
this.showSecret = true
|
||||
})
|
||||
},
|
||||
|
@@ -120,7 +120,6 @@ export default {
|
||||
},
|
||||
async mounted() {
|
||||
await this.getUrlMeta()
|
||||
await this.handleFieldChange()
|
||||
},
|
||||
methods: {
|
||||
async getUrlMeta() {
|
||||
|
@@ -1,35 +1,31 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
:destory-on-close="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:width="'36%'"
|
||||
class="dialog-content"
|
||||
v-bind="$attrs"
|
||||
width="600px"
|
||||
@confirm="visible = false"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<div v-if="ConfirmType === 'relogin'">
|
||||
<div v-if="confirmTypeRequired === 'relogin'">
|
||||
<el-row :gutter="24" style="margin: 0 auto;">
|
||||
<el-col :md="24" :sm="24">
|
||||
<el-alert
|
||||
:closable="false"
|
||||
:title="$tc('auth.ReLoginTitle')"
|
||||
center
|
||||
style="margin-bottom: 20px;"
|
||||
type="info"
|
||||
type="error"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" style="margin: 0 auto;">
|
||||
<el-col :md="24" :sm="24">
|
||||
<el-button
|
||||
size="mini"
|
||||
style="width: 100%; line-height:20px;"
|
||||
type="primary"
|
||||
@click="logOut"
|
||||
>
|
||||
<el-button class="confirm-btn" size="mini" type="primary" @click="logout">
|
||||
{{ this.$t('auth.ReLogin') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
@@ -39,14 +35,13 @@
|
||||
<el-row :gutter="24" style="margin: 0 auto;">
|
||||
<el-col :md="24" :sm="24" :span="24" class="add">
|
||||
<el-select
|
||||
v-model="Select"
|
||||
:disabled="ConfirmType === 'password'"
|
||||
v-model="subTypeSelected"
|
||||
style="width: 100%; margin-bottom: 20px;"
|
||||
@change="helpText(Select)"
|
||||
@change="handleSubTypeChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, i) of Content"
|
||||
:key="i"
|
||||
v-for="item of subTypeChoices"
|
||||
:key="item.name"
|
||||
:disabled="item.disabled"
|
||||
:label="item.display_name"
|
||||
:value="item.name"
|
||||
@@ -56,28 +51,23 @@
|
||||
</el-row>
|
||||
<el-row :gutter="24" style="margin: 0 auto;">
|
||||
<el-col :md="24" :sm="24" style="display: flex; margin-bottom: 20px;">
|
||||
<el-input v-model="SecretKey" :placeholder="HelpText" :show-password="showPassword" />
|
||||
<span v-if="Select === 'sms'" style="margin: -1px 0 0 20px;">
|
||||
<el-input v-model="secretValue" :placeholder="inputPlaceholder" :show-password="showPassword" />
|
||||
<span v-if="subTypeSelected === 'sms'" style="margin: -1px 0 0 20px;">
|
||||
<el-button
|
||||
:disabled="smsBtndisabled"
|
||||
:disabled="smsBtnDisabled"
|
||||
size="mini"
|
||||
style="line-height:20px; float: right;"
|
||||
type="primary"
|
||||
@click="sendChallengeCode"
|
||||
@click="sendSMSCode"
|
||||
>
|
||||
{{ smsBtnText }}
|
||||
</el-button>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" style="margin: 0 auto;">
|
||||
<el-row :gutter="24" style="margin: 10px auto;">
|
||||
<el-col :md="24" :sm="24">
|
||||
<el-button
|
||||
size="mini"
|
||||
style="width: 100%; line-height:20px;"
|
||||
type="primary"
|
||||
@click="userConfirm"
|
||||
>
|
||||
<el-button class="confirm-btn" size="mini" type="primary" @click="handleConfirm">
|
||||
{{ this.$t('common.Confirm') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
@@ -96,125 +86,116 @@ export default {
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
default: ''
|
||||
},
|
||||
handler: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '',
|
||||
title: this.$t('common.CurrentUserVerify'),
|
||||
smsWidth: 0,
|
||||
Select: '',
|
||||
Level: null,
|
||||
HelpText: '',
|
||||
smsBtnText: '',
|
||||
smsBtndisabled: false,
|
||||
ConfirmType: '',
|
||||
Content: null,
|
||||
SecretKey: '',
|
||||
visible: false
|
||||
subTypeSelected: '',
|
||||
inputPlaceholder: '',
|
||||
smsBtnText: this.$t('common.SendVerificationCode'),
|
||||
smsBtnDisabled: false,
|
||||
confirmTypeRequired: '',
|
||||
subTypeChoices: [],
|
||||
secretValue: '',
|
||||
visible: false,
|
||||
callback: null,
|
||||
cancel: null,
|
||||
processing: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
showPassword() {
|
||||
if (this.ConfirmType === 'password') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (!val) {
|
||||
this.$emit('UserConfirmCancel', true)
|
||||
}
|
||||
return this.confirmTypeRequired === 'password'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.smsBtnText = this.$t('common.SendVerificationCode')
|
||||
this.$axios.get(`${this.url}`, { disableFlashErrorMsg: true }).then(
|
||||
() => { this.$emit('UserConfirmDone', true) }).catch((err) => {
|
||||
const confirm_type = err.response.data.code
|
||||
this.$axios.get('/api/v1/authentication/confirm/', { params: { confirm_type: confirm_type }}).then((data) => {
|
||||
this.ConfirmType = data.confirm_type
|
||||
this.Content = data.content
|
||||
if (this.ConfirmType === 'relogin') {
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/confirm/`,
|
||||
{
|
||||
confirm_type: this.ConfirmType,
|
||||
secret_key: ''
|
||||
},
|
||||
{ disableFlashErrorMsg: true },
|
||||
).then(() => { this.$emit('UserConfirmDone', true) }).catch(() => {
|
||||
// const onRecvCallback = _.debounce(this.performConfirm, 500)
|
||||
this.$eventBus.$on('showConfirmDialog', this.performConfirm)
|
||||
},
|
||||
methods: {
|
||||
handleSubTypeChange(val) {
|
||||
this.inputPlaceholder = this.subTypeChoices.filter(item => item.name === val)[0]?.placeholder
|
||||
this.smsWidth = val === 'sms' ? 6 : 0
|
||||
},
|
||||
performConfirm({ response, callback, cancel }) {
|
||||
if (this.processing || this.visible) {
|
||||
return
|
||||
}
|
||||
this.processing = true
|
||||
this.callback = callback
|
||||
this.cancel = cancel
|
||||
this.$log.debug('perform confirm action')
|
||||
const confirmType = response.data?.code
|
||||
const confirmUrl = '/api/v1/authentication/confirm/'
|
||||
this.$axios.get(confirmUrl, { params: { confirm_type: confirmType }}).then((data) => {
|
||||
this.confirmTypeRequired = data.confirm_type
|
||||
|
||||
if (this.confirmTypeRequired === 'relogin') {
|
||||
this.$axios.post(confirmUrl, { 'confirm_type': 'relogin', 'secret_key': 'x' }).then(() => {
|
||||
this.callback()
|
||||
this.visible = false
|
||||
}).catch(() => {
|
||||
this.title = this.$t('auth.NeedReLogin')
|
||||
this.visible = true
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.ConfirmType === 'mfa') {
|
||||
this.Select = this.Content.filter(item => !item.disabled)[0].name
|
||||
if (this.Select === 'sms') {
|
||||
this.smsWidth = 6
|
||||
}
|
||||
this.HelpText = this.Content.filter(item => !item.disabled)[0].placeholder
|
||||
} else if (this.ConfirmType === 'password') {
|
||||
this.Select = this.$t('setting.password')
|
||||
this.HelpText = this.$t('common.PasswordRequireForSecurity')
|
||||
this.Content = [{ 'name': 'password' }]
|
||||
}
|
||||
this.title = this.$t('common.CurrentUserVerify')
|
||||
this.subTypeChoices = data.content
|
||||
const defaultSubType = this.subTypeChoices.filter(item => !item.disabled)[0]
|
||||
this.subTypeSelected = defaultSubType.name
|
||||
this.inputPlaceholder = defaultSubType.placeholder
|
||||
this.visible = true
|
||||
}).catch(() => {
|
||||
this.$emit('AuthMFAError', true)
|
||||
}).catch((err) => {
|
||||
const data = err.response?.data
|
||||
const msg = data?.error || data?.detail || data?.msg || this.$t('common.GetConfirmTypeFailed')
|
||||
this.$message.error(msg)
|
||||
this.cancel(err)
|
||||
}).finally(() => {
|
||||
this.processing = false
|
||||
})
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
helpText(val) {
|
||||
this.HelpText = this.Content.filter(item => item.name === val)[0]?.placeholder
|
||||
if (val === 'sms') {
|
||||
this.smsWidth = 6
|
||||
} else {
|
||||
this.smsWidth = 0
|
||||
}
|
||||
},
|
||||
logOut() {
|
||||
logout() {
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
},
|
||||
sendChallengeCode() {
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/mfa/select/`, {
|
||||
type: 'sms'
|
||||
}
|
||||
).then(res => {
|
||||
this.$message.success(this.$t('common.VerificationCodeSent'))
|
||||
sendSMSCode() {
|
||||
this.$axios.post(`/api/v1/authentication/mfa/select/`, { type: 'sms' }).then(res => {
|
||||
this.$message.success(this.$tc('common.VerificationCodeSent'))
|
||||
let time = 60
|
||||
const interval = setInterval(() => {
|
||||
const originText = this.smsBtnText
|
||||
this.smsBtnText = this.$t('common.Pending') + `: ${time}`
|
||||
this.smsBtndisabled = true
|
||||
this.smsBtnDisabled = true
|
||||
time -= 1
|
||||
|
||||
if (time === 0) {
|
||||
this.smsBtnText = this.$t('common.SendVerificationCode')
|
||||
this.smsBtndisabled = false
|
||||
this.smsBtnText = originText
|
||||
this.smsBtnDisabled = false
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
userConfirm() {
|
||||
if (this.Select === 'otp' && this.SecretKey.length !== 6) {
|
||||
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
||||
handleConfirm() {
|
||||
if (this.confirmTypeRequired === 'relogin') {
|
||||
return this.logout()
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/confirm/`, {
|
||||
confirm_type: this.ConfirmType,
|
||||
mfa_type: this.ConfirmType === 'password' ? undefined : this.Select,
|
||||
secret_key: this.SecretKey
|
||||
}
|
||||
).then(res => {
|
||||
this.$emit('UserConfirmDone', true)
|
||||
if (this.subTypeSelected === 'otp' && this.secretValue.length !== 6) {
|
||||
return this.$message.error(this.$tc('common.MFAErrorMsg'))
|
||||
}
|
||||
const data = {
|
||||
confirm_type: this.confirmTypeRequired,
|
||||
mfa_type: this.confirmTypeRequired === 'mfa' ? this.subTypeSelected : '',
|
||||
secret_key: this.secretValue
|
||||
}
|
||||
this.$axios.post(`/api/v1/authentication/confirm/`, data).then(res => {
|
||||
this.callback()
|
||||
this.visible = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -228,5 +209,16 @@ export default {
|
||||
|
||||
.dialog-content >>> .el-dialog {
|
||||
padding: 8px;
|
||||
|
||||
.el-dialog__body {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -16,9 +16,8 @@
|
||||
>
|
||||
<i :class="item.icon" style="margin-right: 4px;" />{{ item.name }}
|
||||
</el-button>
|
||||
|
||||
<el-autocomplete
|
||||
v-if="item.type === 'input' && item.el.autoComplete"
|
||||
v-if="item.type === 'input' &&item.el && item.el.autoComplete"
|
||||
v-model="item.value"
|
||||
:placeholder="item.placeholder"
|
||||
:fetch-suggestions="item.el.query"
|
||||
@@ -27,7 +26,14 @@
|
||||
@select="item.callback(item.value)"
|
||||
@change="item.callback(item.value)"
|
||||
/>
|
||||
|
||||
<el-input
|
||||
v-else-if="item.type==='input'"
|
||||
v-model="item.value"
|
||||
:placeholder="item.placeholder"
|
||||
class="inline-input"
|
||||
size="mini"
|
||||
@change="item.callback(item.value)"
|
||||
/>
|
||||
<div v-if="item.type==='select' && item.el && item.el.create" class="select-content">
|
||||
<span class="filter-label">
|
||||
{{ item.name }}:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="input-text">
|
||||
{{ value.toString() }}
|
||||
<div :class="bolder ? 'bolder' : ''" class="input-text">
|
||||
{{ value.toString() || text }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -9,7 +9,15 @@ export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Boolean],
|
||||
default: () => false
|
||||
default: ''
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
bolder: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -20,12 +28,14 @@ export default {
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.input-text {
|
||||
border: solid 1px #dcdfe6;
|
||||
line-height: 32px;
|
||||
padding-left: 5px;
|
||||
padding-left: 8px;
|
||||
height: 32px;
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.bolder {
|
||||
border: solid 1px #dcdfe6;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -1,13 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="mfaDialogShow">
|
||||
<UserConfirmDialog
|
||||
:url="url"
|
||||
@AuthMFAError="handleAuthMFAError"
|
||||
@UserConfirmCancel="handleExportCancel"
|
||||
@UserConfirmDone="showExportDialog"
|
||||
/>
|
||||
</div>
|
||||
<Dialog
|
||||
v-if="exportDialogShow"
|
||||
:destroy-on-close="true"
|
||||
@@ -40,7 +32,9 @@
|
||||
:disabled="!option.can"
|
||||
:label="option.value"
|
||||
class="export-item"
|
||||
>{{ option.label }}</el-radio>
|
||||
>
|
||||
{{ option.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -50,15 +44,13 @@
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import UserConfirmDialog from '@/components/Apps/UserConfirmDialog/index.vue'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/Table/DataTable/compenents/el-data-table/utils/query'
|
||||
|
||||
export default {
|
||||
name: 'ExportDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
UserConfirmDialog
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
selectedRows: {
|
||||
@@ -107,12 +99,12 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
meta: {},
|
||||
exportDialogShow: false,
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
meta: {},
|
||||
mfaVerified: false,
|
||||
mfaDialogShow: false
|
||||
mfaDialogShow: false,
|
||||
confirmUrl: '/api/v1/accounts/account-secrets/?limit=1'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -136,7 +128,6 @@ export default {
|
||||
delete query[key]
|
||||
}
|
||||
}
|
||||
|
||||
return query
|
||||
},
|
||||
tableHasQuery() {
|
||||
@@ -189,12 +180,9 @@ export default {
|
||||
this.exportDialogShow = true
|
||||
return
|
||||
}
|
||||
// 这是需要校验 MFA 的
|
||||
if (!this.mfaDialogShow) {
|
||||
this.mfaDialogShow = true
|
||||
} else {
|
||||
this.$axios.get('/api/v1/authentication/confirm/check/?confirm_type=mfa').then(() => {
|
||||
this.exportDialogShow = true
|
||||
}
|
||||
})
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
@@ -231,13 +219,11 @@ export default {
|
||||
async handleExportConfirm() {
|
||||
await this.handleExport()
|
||||
this.exportDialogShow = false
|
||||
this.mfaDialogShow = false
|
||||
},
|
||||
handleExportCancel() {
|
||||
const vm = this
|
||||
setTimeout(() => {
|
||||
vm.exportDialogShow = false
|
||||
vm.mfaDialogShow = false
|
||||
}, 100)
|
||||
},
|
||||
handleAuthMFAError() {
|
||||
|
@@ -5,6 +5,7 @@
|
||||
<script>
|
||||
import BaseFormatter from './base.vue'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'DateFormatter',
|
||||
extends: BaseFormatter,
|
||||
@@ -13,7 +14,7 @@ export default {
|
||||
if (this.cellValue) {
|
||||
value = toSafeLocalDateStr(this.cellValue)
|
||||
} else {
|
||||
value = ''
|
||||
value = '-'
|
||||
}
|
||||
// const locale = this.$i18n.locale
|
||||
// const value = dt.toLocaleString(locale, { hourCycle: 'h23' })
|
||||
|
@@ -36,6 +36,7 @@
|
||||
@focus="focus = true"
|
||||
@change="handleConfirm"
|
||||
@keyup.enter.native="handleConfirm"
|
||||
@keyup.delete.native="handleDelete"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -67,6 +68,7 @@ export default {
|
||||
filterKey: '',
|
||||
filterValue: '',
|
||||
valueLabel: '',
|
||||
emptyCount: 0,
|
||||
filterTags: this.default || {},
|
||||
focus: false
|
||||
}
|
||||
@@ -105,28 +107,32 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
handler(val) {
|
||||
if (val && val.length > 0) {
|
||||
const routeFilter = this.checkInTableColumns()
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal && newVal.length > 0) {
|
||||
const routeFilter = this.checkInTableColumns(newVal)
|
||||
if (oldVal.length > 0 && newVal.length !== oldVal.length) {
|
||||
const beforeRouteFilter = this.checkInTableColumns(oldVal)
|
||||
// 如果2次过滤的参数相同就不在重复请求
|
||||
if (_.isEqual(routeFilter, beforeRouteFilter)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
this.filterTagSearch(routeFilter)
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
if (Object.keys(this.filterMaps).length > 0) {
|
||||
return this.$emit('tagSearch', this.filterMaps)
|
||||
},
|
||||
filterValue(newValue, oldValue) {
|
||||
if (newValue === '' && oldValue !== '') {
|
||||
this.emptyCount = 1
|
||||
}
|
||||
}, 400)
|
||||
// this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取url中的查询条件,判断是不是包含在当前查询条件里
|
||||
checkInTableColumns() {
|
||||
checkInTableColumns(options) {
|
||||
const searchFieldOptions = {}
|
||||
const queryInfoValues = this.options.map((i) => i.value)
|
||||
const queryInfoValues = options.map((i) => i.value)
|
||||
const routeQuery = this.getUrlQuery ? this.$route?.query : {}
|
||||
const routeQueryKeysLength = Object.keys(routeQuery).length
|
||||
if (routeQueryKeysLength < 1) return searchFieldOptions
|
||||
@@ -204,10 +210,10 @@ export default {
|
||||
...asFilterTags,
|
||||
...routeFilter
|
||||
}
|
||||
if (Object.keys(routeFilter).length > 0) {
|
||||
if (Object.keys(this.filterTags).length > 0) {
|
||||
setTimeout(() => {
|
||||
return this.$emit('tagSearch', this.filterMaps)
|
||||
}, 490)
|
||||
}, 400)
|
||||
}
|
||||
},
|
||||
getValueLabel(key, value) {
|
||||
@@ -243,10 +249,22 @@ export default {
|
||||
},
|
||||
handleTagClose(evt) {
|
||||
this.$delete(this.filterTags, evt)
|
||||
this.checkUrlFilds(evt)
|
||||
if (this.getUrlQuery) {
|
||||
this.checkUrlFields(evt)
|
||||
}
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
return true
|
||||
},
|
||||
handleDelete() {
|
||||
const filterTags = Object.keys(this.filterTags)
|
||||
if (this.filterValue === '' && filterTags.length > 0) {
|
||||
if (this.emptyCount === 2) {
|
||||
this.handleTagClose(filterTags[filterTags.length - 1])
|
||||
} else {
|
||||
this.emptyCount++
|
||||
}
|
||||
}
|
||||
},
|
||||
handleConfirm() {
|
||||
if (this.filterValue === '') {
|
||||
this.handleTagClose(this.filterKey)
|
||||
@@ -256,6 +274,9 @@ export default {
|
||||
if (this.filterValue && !this.filterKey) {
|
||||
this.filterKey = 'search' + '_' + this.filterValue
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.emptyCount = 2
|
||||
}, 10)
|
||||
const tag = {
|
||||
key: this.filterKey,
|
||||
label: this.keyLabel,
|
||||
@@ -304,7 +325,7 @@ export default {
|
||||
this.$refs.SearchInput.focus()
|
||||
},
|
||||
// 删除查询条件时改变url
|
||||
checkUrlFilds(evt) {
|
||||
checkUrlFields(evt) {
|
||||
let newQuery = _.omit(this.$route.query, evt)
|
||||
if (this.getUrlQuery && evt.startsWith('search')) {
|
||||
if (newQuery.search) delete newQuery.search
|
||||
|
@@ -116,6 +116,8 @@
|
||||
"AccountExportTips": "The exported information contains account secret, which involves sensitive information. The exported format is an encrypted zip file (if no encryption password is set, please go to personal information to set the file encryption password).",
|
||||
"TaskID": "Task ID",
|
||||
"AccountTemplate": "Account template",
|
||||
"Sync": "Sync",
|
||||
"SyncUpdateAccountInfo": "Sync update account info",
|
||||
"AddAccountResult": "Add account result",
|
||||
"AutoPush": "Auto Push",
|
||||
"GeneralAccounts": "General Accounts",
|
||||
@@ -228,6 +230,7 @@
|
||||
"NoSQLProtocol": "NoSQL Protocol"
|
||||
},
|
||||
"assets": {
|
||||
"SyncProtocolToAsset": "Sync protocol to asset",
|
||||
"CommentHelpText": "Note: Note information will be hovered and displayed in the user authorization asset tree of Luna page, which can be viewed by ordinary users. Please do not fill in sensitive information.",
|
||||
"BulkUpdatePlatformHelpText": "Only when the original platform type of the asset is the same as the selected platform type will it be updated, if the platform type before and after the update is different, it will not be updated.",
|
||||
"ImportMessage": "Please go to the page of mapping type to import data",
|
||||
@@ -983,10 +986,12 @@
|
||||
},
|
||||
"ops": {
|
||||
"EnterRunUser": "Enter run user",
|
||||
"EnterRunningPath": "Enter running path",
|
||||
"Save": "Save",
|
||||
"Reset": "Reset",
|
||||
"SystemError": "System Error",
|
||||
"RunasHelpText": "Fill in the user name to run the script",
|
||||
"RunningPathHelpText": "Fill in the running path of the script. This setting only takes effect for shell scripts.",
|
||||
"RunasPolicyHelpText": "Indicates the account selection strategy when there is no running user on the current asset",
|
||||
"StatusGreen": "Recently in good condition",
|
||||
"StatusRed": "The last task execution failed",
|
||||
@@ -1021,6 +1026,7 @@
|
||||
"ratio": "Ratio",
|
||||
"run": "Run",
|
||||
"runAs": "Run as",
|
||||
"runningPath": "Running path",
|
||||
"runTimes": "Run times",
|
||||
"selectAssetsMessage": "Select the left asset, select the running system user, execute command in batch",
|
||||
"selectedAssets": "Selected assets:",
|
||||
@@ -1726,7 +1732,9 @@
|
||||
"securityPasswordUpperCase": "After opening, the user password changes and resets must contain uppercase letters",
|
||||
"securityServiceAccountRegistration": "Allow using bootstrap token register service account, when terminal setup, can disable it",
|
||||
"SecurityInsecureCommand": "After it is enabled, when a dangerous command is executed on the asset, an email alarm notification will be sent",
|
||||
"IP": "192.168.1.1,192.168.1.2 | 192.168.1.1-12 | 192.168.1.1-192.168.1.12 | 192.168.1.0/30 | 192.168.1.1"
|
||||
"IP": "192.168.1.1,192.168.1.2 | 192.168.1.1-12 | 192.168.1.1-192.168.1.12 | 192.168.1.0/30 | 192.168.1.1",
|
||||
"CustomParams": "On the left are the parameters received by the SMS platform, and on the right are the JumpServer parameters to be formatted, as follows::<br/>{\"phone_numbers\": \"123,134\", \"content\": \"The verification code is: 666666\"}",
|
||||
"CustomFile": "Save the customized file to the specified directory (data/sms/main.py) and enable the configuration item SMS_CUSTOM_FILE_MD5=<file md5> in config.txt"
|
||||
},
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo3": "Ensure this value is greater than or equal to 3",
|
||||
@@ -1925,8 +1933,7 @@
|
||||
"UserName": "Name",
|
||||
"Account": "Account",
|
||||
"Existing": "Existing",
|
||||
"AccountInformation": "Account information",
|
||||
"PersonalSetting": "Personal setting",
|
||||
"UserInformation": "User information",
|
||||
"Authentication": "Account",
|
||||
"Comment": "Comment",
|
||||
"ConfirmPassword": "Confirm password",
|
||||
@@ -1955,7 +1962,7 @@
|
||||
"OrgAuditor": "Org Auditor",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "Enable multi-factor authentication to make the account more secure <br/> After is enabled, you will enter the multi-factor authentication binding process on your next login <br/> You can also bind directly in (personal information -> fast modifier -> modifier multiple factor Settings)",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "To protect the security of you and the company <br/> please properly keep your account, password, key and other important and sensitive information <br/> (e.g., set a complex password and enable multi-factor authentication)",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "To protect the security of you and the company <br/> please properly keep your account, password, key and other important and sensitive information <br/> (e.g., set a complex password and enable multi-factor authentication) <br> Personal information such as email, phone number, WeChat, etc. is only used for user authentication and platform internal message notifications.",
|
||||
"SSHKeyOfProfileSSHUpdatePage": "Copy your public key here",
|
||||
"OrgRoleHelpText": "Organizational roles are the user's role in the current organization",
|
||||
"addRolePermissions": "After add or update success, set permissions in detail page"
|
||||
@@ -1973,6 +1980,7 @@
|
||||
"OldSSHKey": "Old SSH key",
|
||||
"Profile": "Profile",
|
||||
"ProfileSetting": "Profile setting",
|
||||
"AuthSetting": "Auth setting",
|
||||
"Remove": "Remove",
|
||||
"ResetAndDownloadSSHKey": "Reset and download SSH Key",
|
||||
"ResetPublicKeyAndDownload": "Reset public key and download",
|
||||
@@ -2306,5 +2314,10 @@
|
||||
"applets": {
|
||||
"PublishStatus": "Publish status",
|
||||
"NoPublished": "Unpublished"
|
||||
},
|
||||
"profile": {
|
||||
"CreateAccessKey": "Create Access key",
|
||||
"ApiKeyWarning": "To reduce the risk of AccessKey exposure, Secret is provided only during creation and cannot be queried again later. Please keep it safe.",
|
||||
"PasskeyAddDisableInfo": "Your authentication source is {source}, and Passkey addition is not supported."
|
||||
}
|
||||
}
|
||||
|
@@ -116,6 +116,8 @@
|
||||
"AccountExportTips": "エクスポート情報には機密情報を含むアカウント暗号文が含まれており、エクスポートされたフォーマットは暗号化されたzipファイルです(暗号化パスワードが設定されていない場合は、個人情報にファイル暗号化パスワードを設定してください)。",
|
||||
"TaskID": "タスク ID",
|
||||
"AccountTemplate": "账号模版",
|
||||
"Sync": "同期",
|
||||
"SyncUpdateAccountInfo": "アカウント情報の同期更新",
|
||||
"AddAccountResult": "账号批量添加结果",
|
||||
"AutoPush": "自動プッシュ",
|
||||
"GeneralAccounts": "一般アカウント",
|
||||
@@ -228,6 +230,7 @@
|
||||
"NoSQLProtocol": "非リレーショナルデータベース"
|
||||
},
|
||||
"assets": {
|
||||
"SyncProtocolToAsset": "プロトコルをアセットに同期",
|
||||
"CommentHelpText": "注意:コメント情報はLunaページのユーザー認可資産ツリーに表示されます。一般ユーザーは表示できますので、機密情報を記入しないでください。",
|
||||
"BulkUpdatePlatformHelpText": "アセットの元のプラットフォーム タイプが選択したプラットフォーム タイプと同じ場合にのみ更新され、更新の前後のプラットフォーム タイプが異なる場合は更新されません。",
|
||||
"ImportMessage": "ミラータイプのページにデータをインポートしてください",
|
||||
@@ -981,11 +984,13 @@
|
||||
},
|
||||
"ops": {
|
||||
"EnterRunUser": "実行ユーザーの入力",
|
||||
"EnterRunningPath": "実行パスの入力",
|
||||
"Save": "保存#ホゾン#",
|
||||
"Reset": "リストア",
|
||||
"SystemError": "システムエラー",
|
||||
"RunasHelpText": "スクリプトを実行するユーザー名を入力",
|
||||
"RunasPolicyHelpText": "現在の資産にこの実行ユーザーが存在しない場合にアカウント選択ポリシーを実行することを示します",
|
||||
"RunningPathHelpText": "スクリプトの実行パスを入力します。この設定はシェル スクリプトにのみ有効です",
|
||||
"StatusGreen": "最近調子がいい",
|
||||
"StatusRed": "最後のタスクの実行に失敗しました",
|
||||
"StatusYellow": "最近の存在の実行に失敗しました",
|
||||
@@ -1019,6 +1024,7 @@
|
||||
"ratio": "スケール",
|
||||
"run": "実行",
|
||||
"runAs": "実行ユーザー",
|
||||
"runningPath": "実行パス",
|
||||
"runTimes": "実行回数",
|
||||
"selectAssetsMessage": "左側の資産を選択し、実行するシステムユーザーを選択し、コマンドを一括実行します",
|
||||
"selectedAssets": "選択済アセット:",
|
||||
@@ -1731,7 +1737,9 @@
|
||||
"securityPasswordUpperCase": "オンにすると、ユーザーパスワードの変更、リセットに大文字を含める必要があります",
|
||||
"securityServiceAccountRegistration": "Bootstrap tokenを使用して端末を登録できるようにし、端末の登録が成功すると禁止できるようにします",
|
||||
"SecurityInsecureCommand": "オンにすると、資産に危険なコマンドが実行されたときに、メールの警告通知が送信されます",
|
||||
"IP": "192.168.1.1,192.168.1.2 | 192.168.1.1-12 | 192.168.1.1-192.168.1.12 | 192.168.1.0/30 | 192.168.1.1"
|
||||
"IP": "192.168.1.1,192.168.1.2 | 192.168.1.1-12 | 192.168.1.1-192.168.1.12 | 192.168.1.0/30 | 192.168.1.1",
|
||||
"CustomParams": "左側はSMSプラットフォームで受信されるパラメータ、右側はJumpServerフォーマットされるパラメータで、最終的には次のようになります。<br/>{\"phone_numbers\": \"123,134\", \"content\": \"認証コードは: 666666\"}",
|
||||
"CustomFile": "カスタムファイルを指定のディレクトリ(data/sms/main.py)に置き、config.txtで設定項目SMS_CUSTOM_FILE_MD5=<ファイルmd5値>を有効にします"
|
||||
},
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo3": "この値が3以上であることを確認してください",
|
||||
@@ -1914,8 +1922,7 @@
|
||||
"AuthSettings": "認証設定",
|
||||
"UserName": "氏名",
|
||||
"Account": "アカウント",
|
||||
"AccountInformation": "アカウント情報",
|
||||
"PersonalSetting": "個人設定",
|
||||
"UserInformation": "アカウント情報",
|
||||
"Authentication": "認証",
|
||||
"Comment": "備考",
|
||||
"ConfirmPassword": "パスワードの確認",
|
||||
@@ -1944,7 +1951,7 @@
|
||||
"setFeiShu": "飛書認証を設定する",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "アカウントをより安全にするために、マルチファクタ认证を有効にします。 <Br/> 有効にすると、次回のログイン時に多因子認証バインディングプロセスに入ります。また、 (個人情報-> クイック修正-> 多因子設定の変更) もできます。で直接紐付けます",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "あなたと会社の安全を守るために、あなたのアカウント、パスワード、鍵などの重要な機密情報を大切に保管してください (例: 複雑なパスワードを設定し、多因子認証を有効にする)",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "あなたと会社の安全を守るために、あなたのアカウント、パスワード、鍵などの重要な機密情報を大切に保管してください (例: 複雑なパスワードを設定し、多因子認証を有効にする) <br> メールボックス、携帯電話番号、微信などの個人情報は、ユーザー認証やプラットフォーム内部のメッセージ通知としてのみ使用されています。",
|
||||
"SSHKeyOfProfileSSHUpdatePage": "公開鍵をここにコピーします",
|
||||
"OrgRoleHelpText": "組織ロールは、現在の組織におけるユーザーのロールです",
|
||||
"addRolePermissions": "作成/更新に成功したら、詳細に権限を追加します"
|
||||
@@ -1963,6 +1970,7 @@
|
||||
"OldSSHKey": "元のSSH公開鍵",
|
||||
"Profile": "個人情報",
|
||||
"ProfileSetting": "個人情報設定",
|
||||
"AuthSetting": "資格認定の設定",
|
||||
"Remove": "削除",
|
||||
"ResetAndDownloadSSHKey": "キーをリセットしてダウンロードします",
|
||||
"ResetPublicKeyAndDownload": "SSHキーをリセットしてダウンロードします",
|
||||
@@ -2297,5 +2305,10 @@
|
||||
"applets": {
|
||||
"PublishStatus": "投稿ステータス",
|
||||
"NoPublished": "未発表"
|
||||
},
|
||||
"profile": {
|
||||
"CreateAccessKey": "Create Access key",
|
||||
"ApiKeyWarning": "AccessKeyの漏洩リスクを低減するため、Secretは作成時にのみ提供され、後で再度クエリできません。安全に保管してください。",
|
||||
"PasskeyAddDisableInfo": "あなたの認証元は {source} であり、Passkeyの追加はサポートされていません。"
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"AutoPush": "自动推送",
|
||||
"GeneralAccounts": "普通账号",
|
||||
@@ -11,6 +10,8 @@
|
||||
"GenerateSuccessMsg": "账号生成成功",
|
||||
"GenerateAccounts": "重新生成账号",
|
||||
"UpdateSecret": "更新密文",
|
||||
"Sync": "同步",
|
||||
"SyncUpdateAccountInfo": "同步更新账号信息",
|
||||
"AddAccountResult": "账号批量添加结果",
|
||||
"AccountPolicy": "账号策略",
|
||||
"BulkCreateStrategy": "创建时对于不符合要求的账号,如:密钥类型不合规,唯一键约束,可选择以上策略。",
|
||||
@@ -229,6 +230,7 @@
|
||||
},
|
||||
"assets": {
|
||||
"CustomType": "自定义类型",
|
||||
"SyncProtocolToAsset": "同步协议到资产",
|
||||
"CustomHelpMessage": "自定义类型资产,依赖于远程应用,请前往系统设置在远程应用中配置",
|
||||
"CustomFields": "自定义属性",
|
||||
"CommentHelpText": "注意:备注信息会在 Luna 页面的用户授权资产树中进行悬停显示,普通用户可以查看,请不要填写敏感信息。",
|
||||
@@ -960,10 +962,12 @@
|
||||
},
|
||||
"ops": {
|
||||
"EnterRunUser": "输入运行用户",
|
||||
"EnterRunningPath": "输入运行路径",
|
||||
"Save": "保存",
|
||||
"Reset": "还原",
|
||||
"SystemError": "系统错误",
|
||||
"RunasHelpText": "填写运行脚本的用户名",
|
||||
"RunningPathHelpText": "填写脚本的运行路径,此设置仅 shell 脚本生效",
|
||||
"RunasPolicyHelpText": "当前资产上没此运行用户时,采取什么账号选择策略。跳过:不执行。优先特权账号:如果有特权账号先选特权账号,如果没有就选普通账号。仅特权账号:只从特权账号中选择,如果没有则不执行",
|
||||
"StatusGreen": "近期状态良好",
|
||||
"StatusRed": "上一次任务执行失败",
|
||||
@@ -1057,6 +1061,7 @@
|
||||
"ratio": "比例",
|
||||
"run": "执行",
|
||||
"runAs": "运行用户",
|
||||
"runningPath": "运行路径",
|
||||
"RunasPolicy": "账号策略",
|
||||
"runTimes": "执行次数",
|
||||
"selectAssetsMessage": "选择左侧资产, 选择运行的系统用户,批量执行命令",
|
||||
@@ -1676,7 +1681,7 @@
|
||||
"LDAPUser": "LDAP 用户",
|
||||
"helpText": {
|
||||
"TempPassword": "临时密码有效期为 300 秒,使用后立刻失效",
|
||||
"ApiKeyList": "使用 Api key 签名请求头进行认证,每个请求的头部是不一样的, 相对于 Token 方式,更加安全,请查阅文档使用",
|
||||
"ApiKeyList": "使用 Api key 签名请求头进行认证,每个请求的头部是不一样的, 相对于 Token 方式,更加安全,请查阅文档使用;<br>为降低泄露风险,Secret 仅在生成时可以查看, 每个用户最多支持创建 10 个",
|
||||
"ConnectionTokenList": "连接令牌是将身份验证和连接资产结合起来使用的一种认证信息,支持用户一键登录到资产,目前支持的组件包括:KoKo、Lion、Magnus、Razor 等",
|
||||
"authLdapSearchFilter": "可能的选项是(cn或uid或sAMAccountName=%(user)s)",
|
||||
"authLdapSearchOu": "使用|分隔各OU",
|
||||
@@ -1692,7 +1697,7 @@
|
||||
"securityLoginLimitTime": "提示:(单位:分)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录",
|
||||
"securityMaxIdleTime": "提示:如果超过该配置没有操作,连接会被断开 (单位:分)",
|
||||
"securityPasswordExpirationTime": "提示:(单位:天)如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期提醒邮件将在密码过期前5天内由系统(每天)自动发送给用户",
|
||||
"siteUrl": "eg: http://jumpserver.abc.com:8080",
|
||||
"siteUrl": "eg: https://jumpserver.example.com:8080",
|
||||
"terminalHeartbeatInterval": "单位: 秒",
|
||||
"terminalSessionKeepDuration": "单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不受影响)",
|
||||
"terminalTelnetRegex": "登录telnet服务器成功后的提示正则表达式,如: Last\\s*login|success|成功",
|
||||
@@ -1710,7 +1715,9 @@
|
||||
"securityPasswordUpperCase": "开启后,用户密码修改、重置必须包含大写字母",
|
||||
"securityServiceAccountRegistration": "允许使用bootstrap token注册终端, 当组件注册成功后可以禁止",
|
||||
"SecurityInsecureCommand": "开启后,当资产上有危险命令执行时,会发送邮件告警通知",
|
||||
"IP": "192.168.1.1,192.168.1.2 | 192.168.1.1-12 | 192.168.1.1-192.168.1.12 | 192.168.1.0/30 | 192.168.1.1"
|
||||
"IP": "192.168.1.1,192.168.1.2 | 192.168.1.1-12 | 192.168.1.1-192.168.1.12 | 192.168.1.0/30 | 192.168.1.1",
|
||||
"CustomParams": "左侧为短信平台接收的参数,右侧为JumpServer待格式化参数,最终如下:<br/>{\"phone_numbers\": \"123,134\", \"content\": \"验证码为: 666666\"}",
|
||||
"CustomFile": "请将自定义的文件放到指定目录下(data/sms/main.py),并在 config.txt 中启用配置项 SMS_CUSTOM_FILE_MD5=<文件md5值>"
|
||||
},
|
||||
"validatorMessage": {
|
||||
"EnsureThisValueIsGreaterThanOrEqualTo3": "请确保该值大于或者等于 3",
|
||||
@@ -1870,10 +1877,15 @@
|
||||
"TestNodeAssetConnectivity": "测试资产节点可连接性",
|
||||
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息"
|
||||
},
|
||||
"profile": {
|
||||
"CreateAccessKey": "创建访问密钥",
|
||||
"ApiKeyWarning": "为降低 AccessKey 泄露的风险,只在创建时提供 Secret,后续不可再进行查询,请妥善保存。",
|
||||
"PasskeyAddDisableInfo": "你的认证来源是 {source}, 不支持添加 Passkey"
|
||||
},
|
||||
"users": {
|
||||
"LunaSettingUpdate": "Luna 配置设置",
|
||||
"KokoSettingUpdate": "Koko 配置设置",
|
||||
"UserSetting": "个人设置",
|
||||
"UserSetting": "偏好设置",
|
||||
"AllMembers": "全部成员",
|
||||
"UnbindHelpText": "本地用户为此认证来源用户,无法解绑",
|
||||
"SetStatus": "设置状态",
|
||||
@@ -1890,8 +1902,7 @@
|
||||
"AuthSettings": "认证配置",
|
||||
"UserName": "姓名",
|
||||
"Account": "账户",
|
||||
"AccountInformation": "账号信息",
|
||||
"PersonalSetting": "个人设置",
|
||||
"UserInformation": "用户信息",
|
||||
"Authentication": "认证",
|
||||
"Comment": "备注",
|
||||
"ConfirmPassword": "确认密码",
|
||||
@@ -1920,7 +1931,7 @@
|
||||
"setFeiShu": "设置飞书认证",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "启用多因子认证,使账号更加安全。<br/> 启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速修改->更改多因子设置)中直接绑定!",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)<br/> 邮箱、手机号、微信等个人信息,仅作为用户认证和平台内部消息通知使用。",
|
||||
"SSHKeyOfProfileSSHUpdatePage": "复制你的公钥到这里",
|
||||
"OrgRoleHelpText": "组织角色是用户在当前组织中的角色",
|
||||
"addRolePermissions": "创建/更新成功后,详情中添加权限"
|
||||
@@ -1939,6 +1950,7 @@
|
||||
"OldSSHKey": "原来SSH公钥",
|
||||
"Profile": "个人信息",
|
||||
"ProfileSetting": "个人信息设置",
|
||||
"AuthSetting": "认证设置",
|
||||
"Remove": "移除",
|
||||
"ResetAndDownloadSSHKey": "重置并下载密钥",
|
||||
"ResetPublicKeyAndDownload": "重置并下载SSH密钥",
|
||||
|
1
src/icons/svg/preference.svg
Normal file
1
src/icons/svg/preference.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1697615639399" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7535" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M122.88 232.106667l-2.56-2.986667H42.666667v-106.666667l81.066666 2.56v-4.266666a175.36 175.36 0 0 1 331.946667 0l2.56 2.56h532.053333v104.533333H454.826667v4.266667a174.933333 174.933333 0 0 1-331.946667 0z m212.437333-122.112A80.64 80.64 0 0 0 290.986667 95.573333h-1.706667a80.64 80.64 0 1 0 46.037333 14.421334zM515.413333 564.906667l2.56 2.986666a175.36 175.36 0 0 0 331.946667 0v-4.266666h140.373333v-104.533334H853.333333l-2.56-2.986666a175.36 175.36 0 0 0-331.946666 0v4.266666L42.666667 457.386667v107.52h472.746666z m170.666667-133.546667a80.597333 80.597333 0 0 1 54.698667 138.24A80.64 80.64 0 1 1 682.666667 431.36h3.413333z m-467.2 469.333333l2.56 2.56a175.36 175.36 0 0 0 331.946667 0v-4.266666l436.48 2.133333v-106.24H556.8l-2.133333-2.986667a174.933333 174.933333 0 0 0-331.946667 0v4.266667L42.666667 793.173333v107.52h176.213333z m170.666667-133.546666a80.64 80.64 0 1 1-82.346667 80.64A80.64 80.64 0 0 1 387.84 768l1.706667-0.853333z" fill="#515151" p-id="7536"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
@@ -8,9 +8,13 @@
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="profile">
|
||||
<svg-icon class="icon" icon-class="personal" />
|
||||
<svg-icon class="icon" icon-class="attestation" />
|
||||
{{ $t('common.nav.Profile') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="UserSetting">
|
||||
<svg-icon class="icon" icon-class="preference" />
|
||||
{{ $t('users.UserSetting') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="$hasPerm('authentication.view_accesskey')" command="apiKey">
|
||||
<svg-icon class="icon" icon-class="key" />
|
||||
{{ $t('common.nav.APIKey') }}
|
||||
@@ -22,10 +26,6 @@
|
||||
<svg-icon class="icon" icon-class="unlock-one" />
|
||||
{{ $t('common.nav.TempPassword') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="$hasPerm('authentication.view_connectiontoken')" command="connectionToken">
|
||||
<svg-icon class="icon" icon-class="token" />
|
||||
{{ $t('common.nav.ConnectionToken') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="logout" divided>
|
||||
<svg-icon class="icon" icon-class="logout" />
|
||||
{{ $t('common.nav.Logout') }}
|
||||
@@ -64,13 +64,13 @@ export default {
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
break
|
||||
case 'apiKey':
|
||||
this.$router.push('/profile/key')
|
||||
this.$router.push('/profile/api-keys')
|
||||
break
|
||||
case 'tempPassword':
|
||||
this.$router.push('/profile/temp-password')
|
||||
break
|
||||
case 'connectionToken':
|
||||
this.$router.push('/profile/connection-token')
|
||||
case 'UserSetting':
|
||||
this.$router.push('/profile/user/setting')
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
|
@@ -42,10 +42,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getIntervalDays(date) {
|
||||
date = new Date(date)
|
||||
const dateExpired = this.$moment(date, 'YYYY-MM-DD').format('YYYY-MM-DD')
|
||||
const dateNow = this.$moment(new Date()).format('YYYY-MM-DD')
|
||||
const intervalTime = this.$moment(dateNow).diff(this.$moment(dateExpired), 'days')
|
||||
return intervalTime
|
||||
return this.$moment(dateNow).diff(this.$moment(dateExpired), 'days')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,16 +15,19 @@
|
||||
</el-alert>
|
||||
<slot />
|
||||
</PageContent>
|
||||
<UserConfirmDialog />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeading from './PageHeading'
|
||||
import PageContent from './PageContent'
|
||||
import UserConfirmDialog from '@/components/Apps/UserConfirmDialog/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'Page',
|
||||
components: {
|
||||
UserConfirmDialog,
|
||||
PageHeading,
|
||||
PageContent
|
||||
},
|
||||
|
@@ -28,7 +28,7 @@
|
||||
</template>
|
||||
</el-tabs>
|
||||
|
||||
<transition appear mode="out-in" name="fade-transform">
|
||||
<transition v-if="loading" appear mode="out-in" name="fade-transform">
|
||||
<slot>
|
||||
<keep-alive>
|
||||
<component :is="computeActiveComponent" />
|
||||
@@ -60,6 +60,11 @@ export default {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActiveMenu: {
|
||||
get() {
|
||||
@@ -90,6 +95,18 @@ export default {
|
||||
return needActiveComponent
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
const activeTab = to.query?.activeTab
|
||||
if (activeTab && this.iActiveMenu !== activeTab) {
|
||||
this.iActiveMenu = activeTab
|
||||
this.loading = false
|
||||
setTimeout(() => {
|
||||
this.loading = true
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.iActiveMenu = this.getPropActiveTab()
|
||||
},
|
||||
|
@@ -8,6 +8,7 @@ import App from './App'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
import i18n from './i18n/i18n'
|
||||
import { eventBus } from './utils/const'
|
||||
|
||||
import '@/icons' // icon
|
||||
import '@/guards' // permission control
|
||||
@@ -65,7 +66,7 @@ Vue.prototype.$message = message
|
||||
Vue.prototype.$xss = xss
|
||||
|
||||
// 注册全局事件总线
|
||||
Vue.prototype.$eventBus = new Vue()
|
||||
Vue.prototype.$eventBus = eventBus
|
||||
new Vue({
|
||||
el: '#app',
|
||||
i18n,
|
||||
|
@@ -20,7 +20,7 @@ export default {
|
||||
name: 'ProfileInfo',
|
||||
component: () => import('@/views/profile/ProfileInfo'),
|
||||
meta: {
|
||||
title: i18n.t('users.AccountInformation'),
|
||||
title: i18n.t('users.UserInformation'),
|
||||
icon: 'attestation',
|
||||
permissions: []
|
||||
}
|
||||
@@ -30,11 +30,21 @@ export default {
|
||||
name: 'ProfileSetting',
|
||||
component: () => import('@/views/profile/ProfileUpdate/index'),
|
||||
meta: {
|
||||
title: i18n.t('users.Profile'),
|
||||
title: i18n.t('users.AuthSetting'),
|
||||
icon: 'personal',
|
||||
permissions: []
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/profile/user/setting',
|
||||
name: 'UserSetting',
|
||||
component: () => import('@/views/profile/UserSettingUpdate/index'),
|
||||
meta: {
|
||||
title: i18n.t('users.UserSetting'),
|
||||
icon: 'preference',
|
||||
permissions: []
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/profile/improvement',
|
||||
component: () => import('@/views/profile/ProfileImprovement'),
|
||||
@@ -43,7 +53,7 @@ export default {
|
||||
meta: { title: i18n.t('route.PersonalInformationImprovement'), permissions: [] }
|
||||
},
|
||||
{
|
||||
path: '/profile/key',
|
||||
path: '/profile/api-keys',
|
||||
component: () => import('@/views/profile/ApiKey'),
|
||||
name: 'ApiKey',
|
||||
meta: {
|
||||
@@ -86,16 +96,6 @@ export default {
|
||||
hidden: ({ settings }) => !settings['AUTH_PASSKEY'],
|
||||
permissions: ['authentication.view_connectiontoken']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/profile/user/setting',
|
||||
name: 'UserSetting',
|
||||
component: () => import('@/views/profile/UserSettingUpdate/index'),
|
||||
meta: {
|
||||
title: i18n.t('users.UserSetting'),
|
||||
icon: 'setting',
|
||||
permissions: []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -5,7 +5,8 @@ const getDefaultState = () => {
|
||||
metaMap: {},
|
||||
metaPromiseMap: {},
|
||||
isRouterAlive: true,
|
||||
sqlQueryCounter: []
|
||||
sqlQueryCounter: [],
|
||||
confirmDialogVisible: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +28,9 @@ const mutations = {
|
||||
if (state.sqlQueryCounter.length > 10) {
|
||||
state.sqlQueryCounter.shift()
|
||||
}
|
||||
},
|
||||
setConfirmDialogVisible: (state, show) => {
|
||||
state.confirmDialogVisible = show
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +78,9 @@ const actions = {
|
||||
return
|
||||
}
|
||||
commit('addSQLQueryCounter', { url, count: sqlCount })
|
||||
},
|
||||
showConfirmDialog({ commit, state }, show) {
|
||||
commit('setConfirmDialogVisible', show)
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
src/styles/icons/db2.png
Normal file
BIN
src/styles/icons/db2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 199 B |
@@ -88,6 +88,9 @@
|
||||
background: url('./icons/unix.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
&.db2_ico_docu {
|
||||
background: url('./icons/db2.png') no-repeat center left transparent;
|
||||
}
|
||||
|
||||
&.private_ico_docu {
|
||||
background: url('./icons/private.png') no-repeat center left transparent;
|
||||
|
6
src/utils/const.js
Normal file
6
src/utils/const.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
export const eventBus = new Vue()
|
||||
export default {
|
||||
eventBus
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import { eventBus } from '@/utils/const'
|
||||
import { getTokenFromCookie } from '@/utils/auth'
|
||||
import { getErrorResponseMsg } from '@/utils/common'
|
||||
import { refreshSessionIdAge } from '@/api/users'
|
||||
@@ -119,6 +120,17 @@ function refreshSessionAgeDelay(response) {
|
||||
}, 30 * 1000)
|
||||
}
|
||||
|
||||
function ifConfirmRequired({ response, error }) {
|
||||
if (response.status !== 412) {
|
||||
return null
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = () => resolve()
|
||||
const cancel = () => reject()
|
||||
eventBus.$emit('showConfirmDialog', { response, callback, cancel })
|
||||
})
|
||||
}
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
/**
|
||||
@@ -142,16 +154,27 @@ service.interceptors.response.use(
|
||||
}
|
||||
return res
|
||||
},
|
||||
error => {
|
||||
async error => {
|
||||
// NProgress.done()
|
||||
if (!error.response) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
const response = error.response
|
||||
ifUnauthorized({ response, error })
|
||||
ifBadRequest({ response, error })
|
||||
flashErrorMsg({ response, error })
|
||||
|
||||
const confirming = ifConfirmRequired({ response, error })
|
||||
if (confirming) {
|
||||
return new Promise((resolve, reject) => {
|
||||
confirming.then(() => {
|
||||
resolve(service(error.config))
|
||||
}).catch(() => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
await ifUnauthorized({ response, error })
|
||||
await ifBadRequest({ response, error })
|
||||
await flashErrorMsg({ response, error })
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
@@ -47,11 +47,11 @@ export default {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& > > > .table-content {
|
||||
& >>> .table-content {
|
||||
margin-left: 21px;
|
||||
}
|
||||
|
||||
& ::v-deep .el-table__row {
|
||||
& >>> .el-table__row {
|
||||
height: 40px;
|
||||
|
||||
& > td {
|
||||
|
@@ -60,7 +60,7 @@ export default {
|
||||
{ assets: [row.id] }
|
||||
).then(res => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
reload()
|
||||
this.$store.commit('common/reload')
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$tc('common.deleteErrorMsg') + ' ' + error)
|
||||
})
|
||||
@@ -92,7 +92,7 @@ export default {
|
||||
title: this.$t('accounts.AccountChangeSecret.AddAsset'),
|
||||
disabled: this.$store.getters.currentOrgIsRoot,
|
||||
canSelect: (row, index) => {
|
||||
return this.object.assets.indexOf(row.id) === -1
|
||||
return (this.object.assets?.map(i => i.id) || []).indexOf(row.id) === -1
|
||||
},
|
||||
performAdd: (items, that) => {
|
||||
const relationUrl = `/api/v1/accounts/change-secret/${this.object.id}/asset/add/`
|
||||
@@ -104,7 +104,7 @@ export default {
|
||||
onAddSuccess: (items, that) => {
|
||||
this.$log.debug('AssetSelect value', that.assets)
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
window.location.reload()
|
||||
this.$store.commit('common/reload')
|
||||
}
|
||||
},
|
||||
nodeRelationConfig: {
|
||||
|
@@ -14,6 +14,14 @@ export default {
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
const isUpdate = vm.$route.path.indexOf('/update') > -1 && vm.$route.params?.id
|
||||
const formFields = templateFields(vm)
|
||||
for (const [key, value] of formFields) {
|
||||
if (key === vm.$t('assets.Secret')) {
|
||||
isUpdate && value.push('is_sync_account')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initial: {
|
||||
secret_type: 'password',
|
||||
@@ -21,11 +29,28 @@ export default {
|
||||
},
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
hasDetailInMsg: false,
|
||||
fields: [
|
||||
...templateFields(vm)
|
||||
],
|
||||
fields: formFields,
|
||||
fieldsMeta: {
|
||||
...templateFieldsMeta(vm)
|
||||
...templateFieldsMeta(vm),
|
||||
is_sync_account: {
|
||||
label: this.$t('accounts.SyncUpdateAccountInfo'),
|
||||
el: {
|
||||
icon: 'fa fa-external-link',
|
||||
type: 'primary',
|
||||
size: 'mini'
|
||||
},
|
||||
component: 'el-button',
|
||||
on: {
|
||||
click: () => {
|
||||
vm.$router.push({
|
||||
name: 'AccountTemplateDetail',
|
||||
query: {
|
||||
activeTab: 'Account'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
Object.keys(value).forEach((item, index, arr) => {
|
||||
@@ -35,6 +60,7 @@ export default {
|
||||
}
|
||||
})
|
||||
value['secret'] = encryptPassword(value['secret'])
|
||||
delete value.is_sync_account
|
||||
return value
|
||||
},
|
||||
createSuccessNextRoute: { name: 'AccountTemplateList' },
|
||||
|
@@ -20,10 +20,6 @@
|
||||
:url="secretUrl"
|
||||
:visible.sync="showViewSecretDialog"
|
||||
/>
|
||||
<AccountTemplateChangeSecretDialog
|
||||
:object="object"
|
||||
:visible.sync="visible"
|
||||
/>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
@@ -31,17 +27,16 @@
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import AccountTemplateChangeSecretDialog from './AccountTemplateChangeSecretDialog'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import ViewSecret from '@/components/Apps/AccountListTable/ViewSecret'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'AccountTemplateChangeSecret',
|
||||
components: {
|
||||
ViewSecret,
|
||||
QuickActions,
|
||||
GenericListTable,
|
||||
AccountTemplateChangeSecretDialog
|
||||
GenericListTable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -58,14 +53,18 @@ export default {
|
||||
showViewSecretDialog: false,
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('accounts.UpdateSecret'),
|
||||
title: this.$t('accounts.SyncUpdateAccountInfo'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Update')
|
||||
label: this.$t('accounts.Sync')
|
||||
},
|
||||
callbacks: Object.freeze({
|
||||
click: () => {
|
||||
vm.visible = true
|
||||
this.$axios.patch(
|
||||
`/api/v1/accounts/account-templates/${this.object.id}/sync-related-accounts/`
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -1,93 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="iVisible"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:title="$tc('accounts.UpdateSecret')"
|
||||
:visible.sync="iVisible"
|
||||
width="50%"
|
||||
>
|
||||
<AutoDataForm
|
||||
:form="form"
|
||||
v-bind="config"
|
||||
@submit="onSubmit"
|
||||
/>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { AutoDataForm, Dialog } from '@/components'
|
||||
import { templateFieldsMeta } from '../const.js'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
|
||||
export default {
|
||||
name: 'AccountTemplateChangeSecretDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
AutoDataForm
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
object: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: this.object,
|
||||
secretType: this.object?.secret_type?.value,
|
||||
config: {
|
||||
hasSaveContinue: false,
|
||||
url: '/api/v1/accounts/account-templates/',
|
||||
fields: [
|
||||
['', [
|
||||
'secret_type', 'secret', 'ssh_key', 'token',
|
||||
'access_key', 'passphrase', 'api_key'
|
||||
]]
|
||||
],
|
||||
fieldsMeta: {
|
||||
...templateFieldsMeta(this),
|
||||
secret_type: {
|
||||
disabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit(form) {
|
||||
const { object, secretType } = this
|
||||
const currentSecretType = secretType === 'password' ? form?.['secret'] : form?.[secretType]
|
||||
const params = {
|
||||
secret: currentSecretType ? encryptPassword(currentSecretType) : '',
|
||||
is_sync_account: true,
|
||||
...(secretType === 'ssh_key' && { passphrase: encryptPassword(form?.['passphrase']) })
|
||||
}
|
||||
|
||||
this.$axios.patch(
|
||||
`/api/v1/accounts/account-templates/${object.id}/`,
|
||||
params
|
||||
).then(() => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
}).finally(() => {
|
||||
this.iVisible = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -67,7 +67,9 @@ export default {
|
||||
}
|
||||
},
|
||||
reviewers: {
|
||||
hidden: (item) => item.action !== 'review',
|
||||
hidden: (formValue) => {
|
||||
return !['review', 'notice'].includes(formValue.action)
|
||||
},
|
||||
rules: [rules.RequiredChange],
|
||||
el: {
|
||||
value: [],
|
||||
@@ -80,7 +82,16 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
url: '/api/v1/acls/login-asset-acls/'
|
||||
url: '/api/v1/acls/login-asset-acls/',
|
||||
cleanFormValue(value) {
|
||||
if (!Array.isArray(value.rules.ip_group)) {
|
||||
value.rules.ip_group = value.rules.ip_group ? value.rules.ip_group.split(',') : []
|
||||
}
|
||||
if (!['review', 'notice'].includes(value.action)) {
|
||||
value.reviewers = []
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
|
@@ -44,8 +44,15 @@ export default {
|
||||
width: '160px',
|
||||
formatter: AmountFormatter,
|
||||
formatterArgs: {
|
||||
routeQuery: {
|
||||
activeTab: 'GroupUser'
|
||||
route: 'AccountGatherList',
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'CommandFilterAclList',
|
||||
query: {
|
||||
activeTab: 'CommandGroup',
|
||||
command_filters: row.id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,9 +11,11 @@ export default {
|
||||
ListTable
|
||||
},
|
||||
data() {
|
||||
const _id = this.$route.query.command_filters
|
||||
const url = `/api/v1/acls/command-groups/${_id ? `?command_filters=${_id}` : ''}`
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/acls/command-groups/',
|
||||
url: url,
|
||||
permissions: {
|
||||
app: 'acls',
|
||||
resource: 'commandgroup'
|
||||
|
@@ -51,7 +51,7 @@ export default {
|
||||
},
|
||||
rules: [Required],
|
||||
hidden: (formValue) => {
|
||||
return formValue.action !== 'review'
|
||||
return !['review', 'notice'].includes(formValue.action)
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
@@ -86,7 +86,7 @@ export default {
|
||||
if (!Array.isArray(value.rules.ip_group)) {
|
||||
value.rules.ip_group = value.rules.ip_group ? value.rules.ip_group.split(',') : []
|
||||
}
|
||||
if (value.action !== 'review') {
|
||||
if (!['review', 'notice'].includes(value.action)) {
|
||||
value.reviewers = []
|
||||
}
|
||||
return value
|
||||
|
@@ -17,6 +17,9 @@ export default {
|
||||
addFieldsMeta: this.getAddFieldsMeta()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.url = `${this.url}?platform=${this.$route.query.platform}`
|
||||
},
|
||||
methods: {
|
||||
getAddFields() {
|
||||
const platform = this.$route.query.type
|
||||
|
@@ -131,6 +131,22 @@ export default {
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
title: this.$t(`assets.SyncProtocolToAsset`),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('accounts.Sync')
|
||||
},
|
||||
callbacks: Object.freeze({
|
||||
click: () => {
|
||||
const data = { platform_id: this.object.id }
|
||||
this.$axios.post(
|
||||
'/api/v1/assets/assets/sync-platform-protocols/', data).then(res => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
]
|
||||
this.quickActions = quickActions
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<div class="head">
|
||||
<Title :config="config" />
|
||||
</div>
|
||||
<LineChart v-bind="lineChartConfig" />
|
||||
<LineChart v-if="loading" v-bind="lineChartConfig" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,6 +20,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
config: {
|
||||
title: this.$t('dashboard.UserAssetActivity'),
|
||||
tip: this.$t('dashboard.UserAssetActivity')
|
||||
@@ -34,7 +35,11 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getMetricData()
|
||||
try {
|
||||
this.getMetricData()
|
||||
} finally {
|
||||
this.loading = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getMetricData() {
|
||||
|
@@ -81,6 +81,7 @@ export default {
|
||||
DataZTree: 0,
|
||||
runas: '',
|
||||
runasPolicy: 'skip',
|
||||
chdir: '',
|
||||
command: '',
|
||||
module: 'shell',
|
||||
timeout: -1,
|
||||
@@ -200,6 +201,17 @@ export default {
|
||||
callback: (option) => {
|
||||
this.timeout = option
|
||||
}
|
||||
},
|
||||
chdir: {
|
||||
type: 'input',
|
||||
name: this.$t('ops.runningPath'),
|
||||
align: 'left',
|
||||
value: '',
|
||||
placeholder: this.$tc('ops.EnterRunningPath'),
|
||||
tip: this.$tc('ops.RunningPathHelpText'),
|
||||
callback: (val) => {
|
||||
this.chdir = val
|
||||
}
|
||||
}
|
||||
},
|
||||
right: {
|
||||
@@ -401,6 +413,9 @@ export default {
|
||||
is_periodic: false,
|
||||
timeout: this.timeout
|
||||
}
|
||||
if (this.chdir) {
|
||||
data.chdir = this.chdir
|
||||
}
|
||||
createJob(data).then(res => {
|
||||
this.executionInfo.timeCost = 0
|
||||
this.executionInfo.status = 'running'
|
||||
|
@@ -58,7 +58,15 @@ export default {
|
||||
width: 150,
|
||||
objects: this.object.assets,
|
||||
formatter: DeleteActionFormatter,
|
||||
deleteUrl: `/api/v1/perms/asset-permissions-assets-relations/?assetpermission=${this.object.id}&asset=`
|
||||
onDelete: function(col, row, cellValue, reload) {
|
||||
const url = `/api/v1/perms/asset-permissions-assets-relations/?assetpermission=${this.object.id}&asset=${cellValue}`
|
||||
this.$axios.delete(url).then(res => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$store.commit('common/reload')
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$tc('common.deleteErrorMsg') + ' ' + error)
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
@@ -81,7 +89,7 @@ export default {
|
||||
hasObjectsId: this.object.assets?.map(i => i.id) || [],
|
||||
disabled: this.$store.getters.currentOrgIsRoot,
|
||||
canSelect: (row, index) => {
|
||||
return this.object.assets.indexOf(row.id) === -1
|
||||
return (this.object.assets?.map(i => i.id) || []).indexOf(row.id) === -1
|
||||
},
|
||||
performAdd: (items, that) => {
|
||||
const relationUrl = `/api/v1/perms/asset-permissions-assets-relations/`
|
||||
|
@@ -1,38 +1,70 @@
|
||||
<template>
|
||||
<GenericListPage
|
||||
ref="GenericListTable"
|
||||
:header-actions="headerActions"
|
||||
:help-message="helpMessage"
|
||||
:table-config="tableConfig"
|
||||
/>
|
||||
<div>
|
||||
<GenericListPage
|
||||
ref="GenericListTable"
|
||||
:header-actions="headerActions"
|
||||
:help-message="helpMessage"
|
||||
:table-config="tableConfig"
|
||||
/>
|
||||
<Dialog
|
||||
:show-cancel="false"
|
||||
:title="$tc('profile.CreateAccessKey')"
|
||||
:visible.sync="visible"
|
||||
width="700px"
|
||||
@confirm="visible = false"
|
||||
>
|
||||
<el-alert type="warning">
|
||||
{{ warningText }}
|
||||
<div class="secret">
|
||||
<div class="row">
|
||||
<span class="col">ID:</span>
|
||||
<span class="value">{{ key.id }}</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="col">Secret:</span>
|
||||
<span class="value">{{ key.secret }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-alert>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
import { DateFormatter, ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
|
||||
import { DateFormatter } from '@/components/Table/TableFormatters'
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dialog,
|
||||
GenericListPage
|
||||
},
|
||||
data() {
|
||||
const ajaxUrl = '/api/v1/authentication/access-keys/'
|
||||
return {
|
||||
mfaUrl: '',
|
||||
mfaDialogVisible: false,
|
||||
helpMessage: this.$t('setting.helpText.ApiKeyList'),
|
||||
warningText: this.$t('profile.ApiKeyWarning'),
|
||||
visible: false,
|
||||
key: { id: '', secret: '' },
|
||||
tableConfig: {
|
||||
hasSelection: true,
|
||||
url: ajaxUrl,
|
||||
columns: ['id', 'secret', 'is_active', 'date_created', 'date_last_used', 'actions'],
|
||||
columnsShow: {
|
||||
min: ['id', 'actions'],
|
||||
default: ['id', 'secret', 'is_active', 'date_created', 'actions']
|
||||
min: ['id', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
id: {
|
||||
label: 'Access Key'
|
||||
label: 'ID'
|
||||
},
|
||||
secret: {
|
||||
label: 'Secret Key',
|
||||
formatter: ShowKeyCopyFormatter
|
||||
label: 'Secret',
|
||||
formatter: () => {
|
||||
return '********'
|
||||
}
|
||||
},
|
||||
date_created: {
|
||||
label: this.$t('common.DateCreated'),
|
||||
@@ -65,7 +97,7 @@ export default {
|
||||
this.getRefsListTable.reloadTable()
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$tc('common.updateErrorMsg' + ' ' + error))
|
||||
this.$message.error(this.$t('common.updateErrorMsg') + ' ' + error)
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
@@ -75,9 +107,7 @@ export default {
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasSearch: true,
|
||||
hasRightActions: true,
|
||||
hasRefresh: true,
|
||||
hasMoreActions: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasBulkDelete: false,
|
||||
@@ -90,10 +120,8 @@ export default {
|
||||
can: () => this.$hasPerm('authentication.add_accesskey'),
|
||||
callback: function() {
|
||||
this.$axios.post(ajaxUrl).then(res => {
|
||||
this.getRefsListTable.reloadTable()
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$tc('common.updateErrorMsg' + ' ' + error))
|
||||
this.key = res
|
||||
this.visible = true
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
@@ -105,9 +133,29 @@ export default {
|
||||
getRefsListTable() {
|
||||
return this.$refs.GenericListTable.$refs.ListTable.$refs.ListTable || {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.secret {
|
||||
color: #2b2f3a;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 100px;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
@@ -52,7 +52,7 @@ export default {
|
||||
name: 'Expired',
|
||||
title: this.$t('setting.Expire'),
|
||||
type: 'info',
|
||||
can: ({ row }) => !row['is_expired'] && this.$hasPerm('authentication.change_connectiontoken'),
|
||||
can: ({ row }) => !row['is_expired'] && this.$hasPerm('authentication.expire_connectiontoken'),
|
||||
callback: function({ row }) {
|
||||
this.$axios.patch(`${ajaxUrl}${row.id}/expire/`,
|
||||
).then(res => {
|
||||
|
@@ -9,8 +9,12 @@
|
||||
:show-buttons="false"
|
||||
:title="$tc('auth.AddPassKey')"
|
||||
:visible.sync="dialogVisible"
|
||||
width="600px"
|
||||
>
|
||||
<AutoDataForm v-bind="form" @submit="onAddConfirm" />
|
||||
<el-alert v-if="!isLocalUser" :closable="false" class="source-alert" type="error">
|
||||
{{ $t('profile.PasskeyAddDisableInfo', {source: source.label}) }}
|
||||
</el-alert>
|
||||
<AutoDataForm v-else v-bind="form" @submit="onAddConfirm" />
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -115,6 +119,12 @@ export default {
|
||||
computed: {
|
||||
getRefsListTable() {
|
||||
return this.$refs.GenericListTable.$refs.ListTable.$refs.ListTable || {}
|
||||
},
|
||||
isLocalUser() {
|
||||
return this.source?.value === 'local'
|
||||
},
|
||||
source() {
|
||||
return this.$store.getters.currentUser?.source
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -133,6 +143,9 @@ export default {
|
||||
this.getRefsListTable.reloadTable()
|
||||
this.$message.success(this.$tc('common.createSuccessMsg'))
|
||||
}).catch((error) => {
|
||||
if (error.response.status === 412) {
|
||||
return
|
||||
}
|
||||
alert(error)
|
||||
})
|
||||
},
|
||||
@@ -149,5 +162,8 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang='scss' scoped>
|
||||
.source-alert >>> .el-alert__content {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<Page v-bind="$attrs">
|
||||
<UserConfirmDialog
|
||||
v-if="showPasswordDialog"
|
||||
:url="confirmUrl"
|
||||
:visible.sync="showPasswordDialog"
|
||||
@UserConfirmCancel="exit"
|
||||
@UserConfirmDone="verifyDone"
|
||||
/>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="14" :sm="24">
|
||||
@@ -35,7 +28,6 @@
|
||||
import Page from '@/layout/components/Page'
|
||||
import DetailCard from '@/components/Cards/DetailCard'
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import UserConfirmDialog from '@/components/Apps/UserConfirmDialog'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import store from '@/store'
|
||||
|
||||
@@ -44,8 +36,7 @@ export default {
|
||||
components: {
|
||||
Page,
|
||||
DetailCard,
|
||||
QuickActions,
|
||||
UserConfirmDialog
|
||||
QuickActions
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -72,7 +63,7 @@ export default {
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.currentEdit = 'wecom'
|
||||
this.showPasswordDialog = true
|
||||
this.verifyDone()
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
@@ -89,7 +80,7 @@ export default {
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.currentEdit = 'dingtalk'
|
||||
this.showPasswordDialog = true
|
||||
this.verifyDone()
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
@@ -106,7 +97,7 @@ export default {
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.currentEdit = 'feishu'
|
||||
this.showPasswordDialog = true
|
||||
this.verifyDone()
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
@@ -298,7 +289,7 @@ export default {
|
||||
confirmUrl() {
|
||||
return '/api/v1/authentication/confirm-oauth/'
|
||||
},
|
||||
bindOrUNBindUrl() {
|
||||
bindOrUnbindUrl() {
|
||||
let url = ''
|
||||
if (!this.object[`${this.currentEdit}_id`]) {
|
||||
url = `/core/auth/${this.currentEdit}/qr/bind/?redirect_url=${this.$route.fullPath}`
|
||||
@@ -344,16 +335,17 @@ export default {
|
||||
return backendList
|
||||
},
|
||||
verifyDone() {
|
||||
const url = this.bindOrUNBindUrl
|
||||
if (!this.object[`${this.currentEdit}_id`]) {
|
||||
window.location.href = url
|
||||
} else {
|
||||
this.$axios.post(url).then(res => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
this.$store.dispatch('users/getProfile')
|
||||
})
|
||||
}
|
||||
this.showPasswordDialog = false
|
||||
this.$axios.get(this.confirmUrl).then(() => {
|
||||
const url = this.bindOrUnbindUrl
|
||||
if (!this.object[`${this.currentEdit}_id`]) {
|
||||
window.open(url, 'Bind', 'width=800,height=600')
|
||||
} else {
|
||||
this.$axios.post(url).then(res => {
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
this.$store.dispatch('users/getProfile')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
exit() {
|
||||
this.$emit('update:visible', false)
|
||||
|
@@ -7,6 +7,7 @@
|
||||
:initial="object"
|
||||
:submit-method="submitMethod"
|
||||
:url="url"
|
||||
:clean-form-value="cleanFormValue"
|
||||
class="password-update"
|
||||
/>
|
||||
</IBox>
|
||||
@@ -61,7 +62,9 @@ export default {
|
||||
for (const k in this.remoteMeta) {
|
||||
if (this.remoteMeta.hasOwnProperty(k)) {
|
||||
fields.push([this.remoteMeta[k].label, [k]])
|
||||
fieldsMeta[k] = { 'fields': Object.keys(this.remoteMeta[k].children) }
|
||||
fieldsMeta[k] = { 'fields': Object.keys(this.remoteMeta[k].children).filter(
|
||||
// todo: remove this when we have a better solution
|
||||
(key) => key !== 'terminal_theme_name') }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +86,13 @@ export default {
|
||||
},
|
||||
submitMethod() {
|
||||
return 'patch'
|
||||
},
|
||||
cleanFormValue(value) {
|
||||
if (this.category === 'koko') {
|
||||
// todo: remove this when we have a better solution
|
||||
delete value['basic']['terminal_theme_name']
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,7 +26,8 @@ export default {
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.Ticket'),
|
||||
name: 'Ticket'
|
||||
name: 'Ticket',
|
||||
hidden: !this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.AppOps'),
|
||||
|
@@ -16,11 +16,13 @@
|
||||
:title="$tc('setting.ImportLicense')"
|
||||
:visible.sync="dialogLicenseImport"
|
||||
top="20vh"
|
||||
width="600px"
|
||||
@cancel="dialogLicenseImport = false"
|
||||
@confirm="importLicense"
|
||||
>
|
||||
{{ this.$t('setting.LicenseFile') }}
|
||||
<br>
|
||||
<div style="padding-bottom: 10px">
|
||||
{{ this.$t('setting.LicenseFile') }}
|
||||
</div>
|
||||
<input type="file" @change="fileChange">
|
||||
</Dialog>
|
||||
</div>
|
||||
|
@@ -99,18 +99,14 @@ export default {
|
||||
}
|
||||
],
|
||||
cleanFormValue(data) {
|
||||
const submitValue = {}
|
||||
submitValue['EMAIL_RECIPIENT'] = data['EMAIL_RECIPIENT']
|
||||
submitValue['EMAIL_FROM'] = data['EMAIL_FROM']
|
||||
submitValue['EMAIL_SUBJECT_PREFIX'] = data['EMAIL_SUBJECT_PREFIX']
|
||||
Object.keys(submitValue).forEach(
|
||||
Object.keys(data).forEach(
|
||||
function(key) {
|
||||
if (submitValue[key] === null) {
|
||||
delete submitValue[key]
|
||||
if (data[key] === null) {
|
||||
delete data[key]
|
||||
}
|
||||
}
|
||||
)
|
||||
return submitValue
|
||||
return data
|
||||
},
|
||||
submitMethod() {
|
||||
return 'patch'
|
||||
|
@@ -52,7 +52,8 @@ export default {
|
||||
fieldsMeta: {
|
||||
'CUSTOM_SMS_API_PARAMS': {
|
||||
label: this.$t('common.Params'),
|
||||
component: JsonEditor
|
||||
component: JsonEditor,
|
||||
helpText: this.$t('setting.helpTip.CustomParams')
|
||||
},
|
||||
SMS_TEST_PHONE: {
|
||||
component: PhoneInput
|
||||
|
67
src/views/settings/Msg/SMS/SMSFileCustom.vue
Normal file
67
src/views/settings/Msg/SMS/SMSFileCustom.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert :title="helpMessage" type="success" :closable="false" />
|
||||
<BaseSMS ref="baseSms" :config="$data" :title="$tc('setting.Custom')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseSMS from './Base.vue'
|
||||
import { PhoneInput } from '@/components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'SMSFileCustom',
|
||||
components: {
|
||||
BaseSMS
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
helpMessage: this.$t('setting.helpTip.CustomFile'),
|
||||
url: `/api/v1/settings/setting/?category=custom_file`,
|
||||
hasDetailInMsg: false,
|
||||
visible: false,
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('common.Test'),
|
||||
loading: false,
|
||||
callback: function(value, form, btn) {
|
||||
btn.loading = true
|
||||
vm.$axios.post(
|
||||
`/api/v1/settings/sms/custom_file/testing/`,
|
||||
value
|
||||
).then(res => {
|
||||
vm.$message.success(res['msg'])
|
||||
}).catch((error) => {
|
||||
vm.$log.error('err occur')
|
||||
vm.$refs.baseSms.testPerformError(error)
|
||||
}).finally(() => { btn.loading = false })
|
||||
}
|
||||
}
|
||||
],
|
||||
fields: [
|
||||
[
|
||||
this.$t('common.Other'),
|
||||
[
|
||||
'SMS_TEST_PHONE'
|
||||
]
|
||||
]
|
||||
],
|
||||
fieldsMeta: {
|
||||
SMS_TEST_PHONE: {
|
||||
component: PhoneInput
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
return 'patch'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -10,6 +10,7 @@ import SMSAlibaba from './SMSAlibaba.vue'
|
||||
import SMSTencent from './SMSTencent.vue'
|
||||
import SMSHuawei from './SMSHuawei.vue'
|
||||
import SMSCustom from './SMSCustom.vue'
|
||||
import SMSFileCustom from './SMSFileCustom.vue'
|
||||
import CMPP2 from './CMPP2.vue'
|
||||
import { IBox } from '@/components'
|
||||
|
||||
@@ -25,12 +26,12 @@ export default {
|
||||
fields: [
|
||||
[
|
||||
this.$t('setting.Basic'), [
|
||||
'SMS_ENABLED', 'SMS_BACKEND'
|
||||
'SMS_ENABLED', 'SMS_BACKEND', 'SMS_CODE_LENGTH'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('setting.SMSProvider'), [
|
||||
'ALIYUN', 'QCLOUD', 'HUAWEICLOUD', 'CMPP2', 'SMSCustom'
|
||||
'ALIYUN', 'QCLOUD', 'HUAWEICLOUD', 'CMPP2', 'SMSCustom', 'SMSFileCustom'
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -69,6 +70,13 @@ export default {
|
||||
hidden: (form) => {
|
||||
return form['SMS_BACKEND'] !== 'custom'
|
||||
}
|
||||
},
|
||||
SMSFileCustom: {
|
||||
label: this.$t('setting.Custom'),
|
||||
component: SMSFileCustom,
|
||||
hidden: (form) => {
|
||||
return form['SMS_BACKEND'] !== 'custom_file'
|
||||
}
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { Text } from '@/components/Form/FormFields'
|
||||
import { TextReadonly } from '@/components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -33,9 +33,10 @@ export default {
|
||||
readonly: true
|
||||
},
|
||||
permissions: {
|
||||
component: Text,
|
||||
component: TextReadonly,
|
||||
el: {
|
||||
text: this.$t('users.HelpText.addRolePermissions')
|
||||
text: this.$t('users.HelpText.addRolePermissions'),
|
||||
bolder: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -181,7 +181,7 @@ export default {
|
||||
key: this.$t('users.Phone'),
|
||||
formatter: () => {
|
||||
const phoneObj = this.object.phone
|
||||
return <div>{phoneObj?.code}{phoneObj?.phone}</div>
|
||||
return <div>{phoneObj?.code} {phoneObj?.phone}</div>
|
||||
}
|
||||
},
|
||||
'wecom_id', 'dingtalk_id', 'feishu_id',
|
||||
|
Reference in New Issue
Block a user