1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 00:09:16 +08:00

feat: 备份账号页面调整

This commit is contained in:
ssongliu 2023-01-28 16:51:58 +08:00 committed by ssongliu
parent e7af9de9ed
commit dc5b6fba55
25 changed files with 738 additions and 537 deletions

View File

@ -36,13 +36,36 @@ func NewIBackupService() IBackupService {
func (u *BackupService) List() ([]dto.BackupInfo, error) {
ops, err := backupRepo.List(commonRepo.WithOrderBy("created_at desc"))
var dtobas []dto.BackupInfo
ossExist, s3Exist, sftpExist, minioExist := false, false, false, false
for _, group := range ops {
switch group.Type {
case "OSS":
ossExist = true
case "S3":
s3Exist = true
case "SFTP":
sftpExist = true
case "MINIO":
minioExist = true
}
var item dto.BackupInfo
if err := copier.Copy(&item, &group); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtobas = append(dtobas, item)
}
if !ossExist {
dtobas = append(dtobas, dto.BackupInfo{Type: "OSS"})
}
if !s3Exist {
dtobas = append(dtobas, dto.BackupInfo{Type: "S3"})
}
if !sftpExist {
dtobas = append(dtobas, dto.BackupInfo{Type: "SFTP"})
}
if !minioExist {
dtobas = append(dtobas, dto.BackupInfo{Type: "MINIO"})
}
return dtobas, err
}

View File

@ -9,6 +9,7 @@ export namespace Backup {
credential: string;
vars: string;
varsJson: object;
createdAt: Date;
}
export interface BackupOperate {
id: number;

View File

@ -699,6 +699,7 @@ export default {
duplicatePassword: 'The new password cannot be the same as the original password, please re-enter!',
backup: 'Backup',
createBackupAccount: 'Create {0} backup account',
noTypeForCreate: 'No backup type is currently created',
serverDisk: 'Server disks',
currentPath: 'Current path',
@ -734,6 +735,7 @@ export default {
complexityHelper:
'The password must contain at least eight characters and contain at least three uppercase letters, lowercase letters, digits, and special characters',
mfa: 'MFA',
mfaHelper: 'After this function is enabled, the mobile application verification code will be verified',
mfaHelper1: 'Download a MFA verification mobile app such as:',
mfaHelper2: 'Scan the following QR code using the mobile app to obtain the 6-digit verification code',
mfaHelper3: 'Enter six digits from the app',

View File

@ -239,7 +239,7 @@ export default {
database: {
delete: '删除操作无法回滚请输入 "',
deleteHelper: '" 删除此数据库',
create: '建数据库',
create: '建数据库',
noMysql: '当前未检测到 {0} 数据库请进入应用商店点击安装',
mysqlBadStatus: '当前 mysql 应用状态异常请在',
adjust: '中查看原因或修改配置',
@ -713,6 +713,7 @@ export default {
duplicatePassword: '新密码不能与原始密码一致请重新输入',
backup: '备份',
createBackupAccount: '添加 {0} 备份账号',
noTypeForCreate: '当前无可创建备份类型',
serverDisk: '服务器磁盘',
currentPath: '当前路径',
@ -767,8 +768,9 @@ export default {
expiredHelper: '当前密码已过期请重新修改密码',
timeoutHelper: ' {0} 天后 面板密码即将过期过期后需要重新设置密码',
complexity: '密码复杂度验证',
complexityHelper: '密码必须满足密码长度大于 8 位且包含字母数字及特殊字符',
complexityHelper: '开启后密码必须满足密码长度大于 8 位且包含字母数字及特殊字符',
mfa: '两步验证',
mfaHelper: '开启后会验证手机应用验证码',
mfaHelper1: '下载两步验证手机应用 :',
mfaHelper2: '使用手机应用扫描以下二维码获取 6 位验证码',
mfaHelper3: '输入手机应用上的 6 位数字',

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="composeVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-drawer v-model="composeVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('container.compose') }}</span>
@ -39,7 +39,7 @@
placeholder="#Define or paste the content of your docker-compose file here"
:indent-with-tab="true"
:tabSize="4"
style="max-height: 500px; width: 100%; min-height: 200px"
style="width: 100%; height: calc(100vh - 251px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
@ -59,7 +59,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-drawer v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('container.containerCreate') }}</span>
@ -188,7 +188,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -17,7 +17,7 @@
<span class="input-help">{{ $t('container.emptyUser') }}</span>
</el-form-item>
<el-form-item :label="$t('container.custom')" prop="custom">
<el-switch v-model="form.isCustom" @change="form.command = ''" />
<el-switch v-model="form.isCustom" @change="onChangeCommand" />
</el-form-item>
<el-form-item v-if="form.isCustom" label="Command" prop="command" :rules="Rules.requiredInput">
<el-input style="width: 30%" clearable v-model="form.command" />
@ -84,6 +84,11 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
window.addEventListener('resize', changeTerminalSize);
};
const onChangeCommand = async () => {
console.log('addqwd');
form.command = '';
};
const onWSReceive = (message: any) => {
if (!isJson(message.data)) {
return;

View File

@ -1,10 +1,10 @@
<template>
<el-dialog
<el-drawer
v-model="buildVisiable"
:destroy-on-close="true"
@close="onCloseLog"
:close-on-click-modal="false"
width="50%"
size="50%"
>
<template #header>
<div class="card-header">
@ -27,7 +27,7 @@
placeholder="#Define or paste the content of your Dockerfile here"
:indent-with-tab="true"
:tabSize="4"
style="max-height: 500px; width: 100%; min-height: 200px"
style="width: 100%; height: calc(100vh - 350px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
@ -81,7 +81,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,10 +1,10 @@
<template>
<el-dialog
<el-drawer
v-model="pullVisiable"
@close="onCloseLog"
:destroy-on-close="true"
:close-on-click-modal="false"
width="50%"
size="50%"
>
<template #header>
<div class="card-header">
@ -58,7 +58,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,10 +1,10 @@
<template>
<el-dialog
<el-drawer
v-model="pushVisiable"
@close="onCloseLog"
:destroy-on-close="true"
:close-on-click-modal="false"
width="50%"
size="50%"
>
<template #header>
<div class="card-header">
@ -35,7 +35,7 @@
placeholder="Waiting for push output..."
:indent-with-tab="true"
:tabSize="4"
style="max-height: 300px"
style="height: calc(100vh - 301px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
@ -55,7 +55,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="saveVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<el-drawer v-model="saveVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('container.exportImage') }}</span>
@ -40,7 +40,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="tagVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<el-drawer v-model="tagVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<template #header>
<div class="card-header">
<span>Tag {{ $t('container.image') }}</span>
@ -35,7 +35,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<el-drawer v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('container.createNetwork') }}</span>
@ -53,7 +53,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="repoVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-drawer v-model="repoVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ title }}{{ $t('container.repo') }}</span>
@ -60,7 +60,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="templateVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-drawer v-model="templateVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ title }}{{ $t('container.composeTemplate') }}</span>
@ -18,7 +18,7 @@
placeholder="#Define or paste the content of your docker-compose file here"
:indent-with-tab="true"
:tabSize="4"
style="max-height: 500px; width: 100%; min-height: 200px"
style="width: 100%; height: calc(100vh - 251px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
@ -39,7 +39,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<el-drawer v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('container.createVolume') }}</span>
@ -41,7 +41,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="cronjobVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-drawer v-model="cronjobVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ title }}{{ $t('cronjob.cronTask') }}</span>
@ -138,7 +138,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -1,6 +1,6 @@
<template>
<div>
<el-dialog v-model="backupVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-drawer v-model="backupVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('database.backup') }} - {{ dbName }}</span>
@ -27,7 +27,7 @@
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix />
</ComplexTable>
</el-dialog>
</el-drawer>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<el-drawer v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('database.create') }}</span>
@ -9,7 +9,7 @@
<el-form-item :label="$t('commons.table.name')" prop="name">
<el-input clearable v-model.trim="form.name">
<template #append>
<el-select v-model="form.format" style="width: 125px">
<el-select v-model="form.format" style="width: 80px">
<el-option label="utf8mb4" value="utf8mb4" />
<el-option label="utf-8" value="utf8" />
<el-option label="gbk" value="gbk" />
@ -26,7 +26,7 @@
</el-form-item>
<el-form-item :label="$t('database.permission')" prop="permission">
<el-select style="width: 100%" v-model="form.permission">
<el-select v-model="form.permission">
<el-option value="localhost" :label="$t('database.permissionLocal')" />
<el-option value="%" :label="$t('database.permissionAll')" />
<el-option value="ip" :label="$t('database.permissionForIP')" />
@ -47,7 +47,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>

View File

@ -17,79 +17,87 @@
>
<span style="font-size: 14px">{{ $t('commons.service.serviceNotStarted', ['Mysql']) }}</span>
</el-card>
<div style="margin-top: 20px" v-if="mysqlIsExist && !isOnSetting">
<el-button type="primary" @click="onOpenDialog()">
{{ $t('database.create') }}
</el-button>
<el-button @click="onChangeRootPassword" type="primary" plain>
{{ $t('database.rootPassword') }}
</el-button>
<el-button @click="onChangeAccess" type="primary" plain>
{{ $t('database.remoteAccess') }}
</el-button>
<el-button @click="goDashboard" type="primary" plain>phpMyAdmin</el-button>
</div>
<div v-if="mysqlIsExist" :class="{ mask: mysqlStatus != 'Running' }">
<el-card v-if="!isOnSetting" style="margin-top: 20px">
<ComplexTable :pagination-config="paginationConfig" @search="search" :data="data">
<template #toolbar>
<el-button type="primary" icon="Plus" @click="onOpenDialog()">
{{ $t('commons.button.create') }}
</el-button>
<el-button @click="onChangeRootPassword" type="primary" plain>
{{ $t('database.rootPassword') }}
</el-button>
<el-button @click="onChangeAccess" type="primary" plain>
{{ $t('database.remoteAccess') }}
</el-button>
<el-button @click="goDashboard" type="primary" plain icon="Position">phpMyAdmin</el-button>
</template>
<el-table-column :label="$t('commons.table.name')" prop="name" />
<el-table-column :label="$t('commons.login.username')" prop="username" />
<el-table-column :label="$t('commons.login.password')" prop="password">
<template #default="{ row }">
<div>
<span style="float: left; line-height: 25px" v-if="!row.showPassword">***********</span>
<div style="cursor: pointer; float: left" v-if="!row.showPassword">
<el-icon
style="margin-left: 5px; margin-top: 3px"
@click="row.showPassword = true"
:size="16"
>
<View />
</el-icon>
<el-card v-if="!isOnSetting" style="margin-top: 10px">
<LayoutContent :header="'Mysql ' + $t('menu.database')">
<ComplexTable :pagination-config="paginationConfig" @search="search" :data="data">
<el-table-column :label="$t('commons.table.name')" prop="name" />
<el-table-column :label="$t('commons.login.username')" prop="username" />
<el-table-column :label="$t('commons.login.password')" prop="password">
<template #default="{ row }">
<div>
<span style="float: left; line-height: 25px" v-if="!row.showPassword">
***********
</span>
<div style="cursor: pointer; float: left" v-if="!row.showPassword">
<el-icon
style="margin-left: 5px; margin-top: 3px"
@click="row.showPassword = true"
:size="16"
>
<View />
</el-icon>
</div>
<span style="float: left" v-if="row.showPassword">{{ row.password }}</span>
<div style="cursor: pointer; float: left" v-if="row.showPassword">
<el-icon
style="margin-left: 5px; margin-top: 3px"
@click="row.showPassword = false"
:size="16"
>
<Hide />
</el-icon>
</div>
<div style="cursor: pointer; float: left">
<el-icon
style="margin-left: 5px; margin-top: 3px"
:size="16"
@click="onCopyPassword(row)"
>
<DocumentCopy />
</el-icon>
</div>
</div>
<span style="float: left" v-if="row.showPassword">{{ row.password }}</span>
<div style="cursor: pointer; float: left" v-if="row.showPassword">
<el-icon
style="margin-left: 5px; margin-top: 3px"
@click="row.showPassword = false"
:size="16"
>
<Hide />
</el-icon>
</div>
<div style="cursor: pointer; float: left">
<el-icon
style="margin-left: 5px; margin-top: 3px"
:size="16"
@click="onCopyPassword(row)"
>
<DocumentCopy />
</el-icon>
</div>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.description')" prop="description">
<template #default="{ row }">
<fu-read-write-switch :data="row.description" v-model="row.edit" @change="onChange(row)">
<el-input v-model="row.description" @blur="row.edit = false" />
</fu-read-write-switch>
</template>
</el-table-column>
<el-table-column
prop="createdAt"
:label="$t('commons.table.date')"
:formatter="dateFromat"
show-overflow-tooltip
/>
<fu-table-operations
width="300px"
:buttons="buttons"
:ellipsis="10"
:label="$t('commons.table.operate')"
fix
/>
</ComplexTable>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.description')" prop="description">
<template #default="{ row }">
<fu-read-write-switch
:data="row.description"
v-model="row.edit"
@change="onChange(row)"
>
<el-input v-model="row.description" @blur="row.edit = false" />
</fu-read-write-switch>
</template>
</el-table-column>
<el-table-column
prop="createdAt"
:label="$t('commons.table.date')"
:formatter="dateFromat"
show-overflow-tooltip
/>
<fu-table-operations
width="300px"
:buttons="buttons"
:ellipsis="10"
:label="$t('commons.table.operate')"
fix
/>
</ComplexTable>
</LayoutContent>
</el-card>
</div>
<el-dialog v-model="changeVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
@ -175,6 +183,7 @@
</template>
<script lang="ts" setup>
import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue';
import OperateDialog from '@/views/database/mysql/create/index.vue';
import DeleteDialog from '@/views/database/mysql/delete/index.vue';

View File

@ -1,6 +1,6 @@
<template>
<div>
<el-dialog v-model="upVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="70%">
<el-drawer v-model="upVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ $t('commons.button.import') }}</span>
@ -59,7 +59,7 @@
fix
/>
</ComplexTable>
</el-dialog>
</el-drawer>
</div>
</template>

View File

@ -1,71 +1,190 @@
<template>
<div>
<Submenu activeName="backupaccount" />
<el-card style="margin-top: 20px">
<template #header>
<div class="card-header">
<span>{{ $t('setting.backup') }}</span>
</div>
</template>
<el-button type="primary" icon="Plus" @click="onOpenDialog('create')">
{{ $t('commons.button.create') }}
</el-button>
<el-row :gutter="20" class="row-box">
<el-col v-for="item in data" :key="item.id" :span="8" style="margin-top: 20px">
<el-card class="el-card">
<el-form label-position="left" label-width="130px" :v-key="reflash">
<el-row :gutter="20" style="margin-top: 20px">
<el-col :span="24">
<el-card>
<template #header>
<div class="card-header">
<svg-icon style="font-size: 7px" :iconName="loadIconName(item.type)"></svg-icon>
<span style="font-size: 16px; font-weight: 500">
&nbsp;{{ loadBackupName(item.type) }}
</span>
<div style="float: right">
<el-button @click="onOpenDialog('edit', item)">
{{ $t('commons.button.edit') }}
</el-button>
<el-button v-if="item.type !== 'LOCAL'" @click="onBatchDelete(item)">
{{ $t('commons.button.delete') }}
</el-button>
</div>
<svg-icon style="font-size: 7px" iconName="p-file-folder"></svg-icon>
<span style="font-size: 16px; font-weight: 500">&nbsp;{{ $t('setting.serverDisk') }}</span>
<div style="float: right">
<el-button round @click="onOpenDialog('edit', 'local', localData)">
{{ $t('commons.button.edit') }}
</el-button>
</div>
</template>
<el-form label-position="left" label-width="130px">
<el-form-item v-if="item.type === 'LOCAL'" :label="$t('setting.currentPath')">
{{ item.varsJson['dir'] }}
</el-form-item>
<el-form-item v-if="item.type === 'S3'" label="Region">
{{ item.varsJson['region'] }}
</el-form-item>
<el-form-item v-if="hasBucket(item.type)" label="Endpoint">
{{ item.varsJson['endpoint'] }}
</el-form-item>
<el-form-item v-if="hasBucket(item.type)" label="Bucket">
{{ item.bucket }}
</el-form-item>
<el-form-item v-if="item.type === 'SFTP'" :label="$t('setting.address')">
{{ item.varsJson['address'] }}
</el-form-item>
<el-form-item v-if="item.type === 'SFTP'" :label="$t('setting.port')">
{{ item.varsJson['port'] }}
</el-form-item>
<el-form-item v-if="item.type === 'SFTP'" :label="$t('setting.path')">
{{ item.bucket }}
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFromat(0, 0, item.createdAt) }}
</el-form-item>
</el-form>
<el-form-item :label="$t('setting.currentPath')">
{{ localData.varsJson['dir'] }}
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFromat(0, 0, localData.createdAt) }}
</el-form-item>
</el-card>
</el-col>
</el-row>
</el-card>
<el-row :gutter="20" style="margin-top: 20px">
<el-col :span="12">
<el-card>
<template #header>
<svg-icon style="font-size: 7px" iconName="p-MINIO"></svg-icon>
<span style="font-size: 16px; font-weight: 500">&nbsp;MIMIO</span>
<div style="float: right">
<el-button :disabled="minioData.id === 0" round @click="onBatchDelete(minioData)">
{{ $t('commons.button.delete') }}
</el-button>
<el-button
round
:disabled="minioData.id === 0"
@click="onOpenDialog('edit', 'MINIO', minioData)"
>
{{ $t('commons.button.edit') }}
</el-button>
</div>
</template>
<div v-if="minioData.id !== 0">
<el-form-item label="Endpoint">
{{ minioData.varsJson['endpoint'] }}
</el-form-item>
<el-form-item label="Bucket">
{{ minioData.bucket }}
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFromat(0, 0, minioData.createdAt) }}
</el-form-item>
</div>
<el-alert v-else center style="height: 127px; background-color: #e2e4ec" :closable="false">
<el-button size="large" round plain type="primary" @click="onOpenDialog('create', 'MINIO')">
{{ $t('setting.createBackupAccount', ['MINIO']) }}
</el-button>
</el-alert>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<template #header>
<svg-icon style="font-size: 7px" iconName="p-OSS"></svg-icon>
<span style="font-size: 16px; font-weight: 500">&nbsp;OSS</span>
<div style="float: right">
<el-button round :disabled="ossData.id === 0" @click="onBatchDelete(ossData)">
{{ $t('commons.button.delete') }}
</el-button>
<el-button
round
:disabled="ossData.id === 0"
@click="onOpenDialog('edit', 'OSS', ossData)"
>
{{ $t('commons.button.edit') }}
</el-button>
</div>
</template>
<div v-if="ossData.id !== 0">
<el-form-item label="Endpoint">
{{ ossData.varsJson['endpoint'] }}
</el-form-item>
<el-form-item label="Bucket">
{{ ossData.bucket }}
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFromat(0, 0, ossData.createdAt) }}
</el-form-item>
</div>
<el-alert v-else center style="height: 127px; background-color: #e2e4ec" :closable="false">
<el-button size="large" round plain type="primary" @click="onOpenDialog('create', 'OSS')">
{{ $t('setting.createBackupAccount', ['OSS']) }}
</el-button>
</el-alert>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 20px">
<el-col :span="12">
<el-card>
<template #header>
<svg-icon style="font-size: 7px" iconName="p-aws"></svg-icon>
<span style="font-size: 16px; font-weight: 500">&nbsp;{{ $t('setting.S3') }}</span>
<div style="float: right">
<el-button round :disabled="s3Data.id === 0" @click="onBatchDelete(s3Data)">
{{ $t('commons.button.delete') }}
</el-button>
<el-button
round
:disabled="s3Data.id === 0"
@click="onOpenDialog('edit', 'S3', s3Data)"
>
{{ $t('commons.button.edit') }}
</el-button>
</div>
</template>
<div v-if="s3Data.id !== 0">
<el-form-item label="Region">
{{ s3Data.varsJson['region'] }}
</el-form-item>
<el-form-item label="Endpoint">
{{ s3Data.varsJson['endpoint'] }}
</el-form-item>
<el-form-item label="Bucket">
{{ s3Data.bucket }}
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFromat(0, 0, s3Data.createdAt) }}
</el-form-item>
</div>
<el-alert v-else center style="height: 167px; background-color: #e2e4ec" :closable="false">
<el-button size="large" round plain type="primary" @click="onOpenDialog('create', 'S3')">
{{ $t('setting.createBackupAccount', ['S3']) }}
</el-button>
</el-alert>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<template #header>
<svg-icon style="font-size: 7px" iconName="p-SFTP"></svg-icon>
<span style="font-size: 16px; font-weight: 500">&nbsp;SFTP</span>
<div style="float: right">
<el-button round :disabled="sftpData.id === 0" @click="onBatchDelete(sftpData)">
{{ $t('commons.button.delete') }}
</el-button>
<el-button
round
plain
:disabled="sftpData.id === 0"
@click="onOpenDialog('edit', 'SFTP', sftpData)"
>
{{ $t('commons.button.edit') }}
</el-button>
</div>
</template>
<div v-if="sftpData.id !== 0">
<el-form-item :label="$t('setting.address')">
{{ sftpData.varsJson['address'] }}
</el-form-item>
<el-form-item :label="$t('setting.port')">
{{ sftpData.varsJson['port'] }}
</el-form-item>
<el-form-item :label="$t('setting.path')">
{{ sftpData.bucket }}
</el-form-item>
<el-form-item :label="$t('commons.table.createdAt')">
{{ dateFromat(0, 0, sftpData.createdAt) }}
</el-form-item>
</div>
<el-alert v-else center style="height: 167px; background-color: #e2e4ec" :closable="false">
<el-button size="large" round plain type="primary" @click="onOpenDialog('create', 'SFTP')">
{{ $t('setting.createBackupAccount', ['SFTP']) }}
</el-button>
</el-alert>
</el-card>
</el-col>
</el-row>
</el-form>
<DialogOperate ref="dialogRef" @search="search" />
</div>
</template>
<script setup lang="ts">
import { dateFromat } from '@/utils/util';
import { onMounted, ref } from 'vue';
import { loadBackupName } from '@/views/setting/helper';
import { getBackupList, deleteBackup } from '@/api/modules/backup';
import DialogOperate from '@/views/setting/backup-account/operate/index.vue';
import Submenu from '@/views/setting/index.vue';
@ -74,12 +193,96 @@ import { ElForm } from 'element-plus';
import { useDeleteData } from '@/hooks/use-delete-data';
const data = ref();
const reflash = ref(false);
const localData = ref<Backup.BackupInfo>({
id: 0,
type: 'LOCAL',
accessKey: '',
bucket: '',
credential: '',
vars: '',
varsJson: {
dir: '',
},
createdAt: new Date(),
});
const ossData = ref<Backup.BackupInfo>({
id: 0,
type: 'OSS',
accessKey: '',
bucket: '',
credential: '',
vars: '',
varsJson: {
region: '',
endpoint: '',
},
createdAt: new Date(),
});
const minioData = ref<Backup.BackupInfo>({
id: 0,
type: 'MINIO',
accessKey: '',
bucket: '',
credential: '',
vars: '',
varsJson: {
region: '',
endpoint: '',
},
createdAt: new Date(),
});
const sftpData = ref<Backup.BackupInfo>({
id: 0,
type: 'SFTP',
accessKey: '',
bucket: '',
credential: '',
vars: '',
varsJson: {
address: '',
port: 0,
},
createdAt: new Date(),
});
const s3Data = ref<Backup.BackupInfo>({
id: 0,
type: 'S3',
accessKey: '',
bucket: '',
credential: '',
vars: '',
varsJson: {
region: '',
endpoint: '',
},
createdAt: new Date(),
});
const search = async () => {
const res = await getBackupList();
data.value = res.data;
data.value = res.data || [];
for (const bac of data.value) {
bac.varsJson = JSON.parse(bac.vars);
if (bac.id !== 0) {
bac.varsJson = JSON.parse(bac.vars);
}
switch (bac.type) {
case 'LOCAL':
localData.value = bac;
break;
case 'OSS':
ossData.value = bac;
break;
case 'S3':
s3Data.value = bac;
break;
case 'MINIO':
minioData.value = bac;
break;
case 'SFTP':
sftpData.value = bac;
break;
}
}
};
@ -93,46 +296,21 @@ const onBatchDelete = async (row: Backup.BackupInfo | null) => {
const dialogRef = ref();
const onOpenDialog = async (
title: string,
accountType: string,
rowData: Partial<Backup.BackupInfo> = {
id: 0,
type: accountType,
varsJson: {},
},
) => {
let types = [] as Array<string>;
for (const item of data.value) {
types.push(item.type);
}
console.log(rowData);
let params = {
title,
types,
rowData: { ...rowData },
};
dialogRef.value!.acceptParams(params);
};
function hasBucket(val: string) {
return val === 'OSS' || val === 'S3' || val === 'MINIO';
}
const loadIconName = (type: string) => {
switch (type) {
case 'OSS':
return 'p-oss';
break;
case 'S3':
return 'p-aws';
break;
case 'SFTP':
return 'p-SFTP';
break;
case 'MINIO':
return 'p-minio';
break;
case 'LOCAL':
return 'p-file-folder';
break;
}
};
onMounted(() => {
search();
});

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="dialogVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-drawer v-model="dialogVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<div class="card-header">
<span>{{ title }}{{ $t('setting.backupAccount') }}</span>
@ -7,14 +7,7 @@
</template>
<el-form ref="formRef" v-loading="loading" :model="dialogData.rowData" label-width="120px">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-select
style="width: 100%"
v-model="dialogData.rowData!.type"
@change="changeType"
:disabled="title === $t('commons.button.edit')"
>
<el-option v-for="item in typeOptions" :key="item.label" :value="item.value" :label="item.label" />
</el-select>
<span>{{ dialogData.rowData!.type }}</span>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'LOCAL'"
@ -116,7 +109,7 @@
</el-button>
</span>
</template>
</el-dialog>
</el-drawer>
</template>
<script lang="ts" setup>
@ -132,7 +125,6 @@ import { deepCopy } from '@/utils/util';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const typeOptions = ref();
const buckets = ref();
const endpoints = ref('http');
@ -141,7 +133,6 @@ const emit = defineEmits<{ (e: 'search'): void }>();
interface DialogProps {
title: string;
types: Array<string>;
rowData?: Backup.BackupInfo;
getTableList?: () => Promise<any>;
}
@ -149,7 +140,6 @@ const title = ref<string>('');
const dialogVisiable = ref(false);
const dialogData = ref<DialogProps>({
title: '',
types: [],
});
const acceptParams = (params: DialogProps): void => {
dialogData.value = params;
@ -161,36 +151,9 @@ const acceptParams = (params: DialogProps): void => {
}
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
loadOption(params.types);
dialogVisiable.value = true;
};
const loadOption = (existTypes: Array<string>) => {
let options = [
{ label: i18n.global.t('setting.serverDisk'), value: 'LOCAL' },
{ label: i18n.global.t('setting.OSS'), value: 'OSS' },
{ label: i18n.global.t('setting.S3'), value: 'S3' },
{ label: 'SFTP', value: 'SFTP' },
{ label: 'MinIO', value: 'MINIO' },
];
for (const item of existTypes) {
for (let i = 0; i < options.length; i++) {
if (item === options[i].value) {
options.splice(i, 1);
}
}
}
typeOptions.value = options;
};
const changeType = async (val: string) => {
let itemType = val;
buckets.value = [];
if (formRef.value) {
formRef.value.resetFields();
}
dialogData.value.rowData!.type = itemType;
};
const loadDir = async (path: string) => {
dialogData.value.rowData!.varsJson['dir'] = path;
};

View File

@ -3,205 +3,133 @@
<Submenu activeName="panel" />
<el-form :model="form" ref="panelFormRef" label-position="left" v-loading="loading" label-width="160px">
<el-card style="margin-top: 20px">
<template #header>
<div class="card-header">
<span>{{ $t('setting.panel') }}</span>
</div>
</template>
<el-row>
<el-col :span="1"><br /></el-col>
<el-col :span="10">
<el-form-item
:label="$t('commons.login.username')"
:rules="Rules.requiredInput"
prop="userName"
>
<el-input clearable v-model="form.userName">
<template #append>
<el-button
@click="onSave(panelFormRef, 'UserName', form.userName)"
icon="Collection"
>
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item
:label="$t('commons.login.password')"
:rules="Rules.requiredInput"
prop="password"
>
<el-input type="password" clearable disabled v-model="form.password">
<template #append>
<el-button icon="Setting" @click="onChangePassword">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.title')" :rules="Rules.requiredInput" prop="panelName">
<el-input clearable v-model="form.panelName">
<template #append>
<el-button
@click="onSave(panelFormRef, 'PanelName', form.panelName)"
icon="Collection"
>
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.theme')" :rules="Rules.requiredSelect" prop="theme">
<el-radio-group @change="onSave(panelFormRef, 'Theme', form.theme)" v-model="form.theme">
<el-radio-button label="dark">
<el-icon><Moon /></el-icon>
{{ $t('setting.dark') }}
</el-radio-button>
<el-radio-button label="light">
<el-icon><Sunny /></el-icon>
{{ $t('setting.light') }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('setting.language')" :rules="Rules.requiredSelect" prop="language">
<el-radio-group
style="width: 100%"
@change="onSave(panelFormRef, 'Language', form.language)"
v-model="form.language"
<LayoutContent :header="$t('setting.panel')">
<el-row>
<el-col :span="1"><br /></el-col>
<el-col :span="10">
<el-form-item
:label="$t('commons.login.username')"
:rules="Rules.requiredInput"
prop="userName"
>
<el-radio-button label="zh">中文</el-radio-button>
<el-radio-button label="en">English</el-radio-button>
</el-radio-group>
<div>
<span class="input-help">
{{ $t('setting.languageHelper') }}
</span>
</div>
</el-form-item>
<el-input clearable v-model="form.userName">
<template #append>
<el-button
@click="onSave(panelFormRef, 'UserName', form.userName)"
icon="Collection"
>
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.sessionTimeout')" :rules="Rules.number" prop="sessionTimeout">
<el-input v-model.number="form.sessionTimeout">
<template #append>
<el-button
@click="onSave(panelFormRef, 'SessionTimeout', form.sessionTimeout)"
icon="Collection"
>
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
<div>
<span class="input-help">
{{ $t('setting.sessionTimeoutHelper', [form.sessionTimeout]) }}
</span>
</div>
</el-form-item>
<el-form-item :label="$t('setting.title')" :rules="Rules.requiredInput" prop="panelName">
<el-input clearable v-model="form.panelName">
<template #append>
<el-button
@click="onSave(panelFormRef, 'PanelName', form.panelName)"
icon="Collection"
>
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.syncTime')">
<el-input disabled v-model="form.localTime">
<template #append>
<el-button v-show="!show" @click="onSyncTime" icon="Refresh">
{{ $t('commons.button.sync') }}
</el-button>
<span v-show="show">{{ count }} S</span>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('setting.theme')" :rules="Rules.requiredSelect" prop="theme">
<el-radio-group
@change="onSave(panelFormRef, 'Theme', form.theme)"
v-model="form.theme"
>
<el-radio-button label="dark">
<el-icon><Moon /></el-icon>
{{ $t('setting.dark') }}
</el-radio-button>
<el-radio-button label="light">
<el-icon><Sunny /></el-icon>
{{ $t('setting.light') }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('setting.language')" :rules="Rules.requiredSelect" prop="language">
<el-radio-group
style="width: 100%"
@change="onSave(panelFormRef, 'Language', form.language)"
v-model="form.language"
>
<el-radio label="zh">中文</el-radio>
<el-radio label="en">English</el-radio>
</el-radio-group>
<div>
<span class="input-help">
{{ $t('setting.languageHelper') }}
</span>
</div>
</el-form-item>
<el-form-item
:label="$t('setting.sessionTimeout')"
:rules="Rules.number"
prop="sessionTimeout"
>
<el-input v-model.number="form.sessionTimeout">
<template #append>
<el-button
@click="onSave(panelFormRef, 'SessionTimeout', form.sessionTimeout)"
icon="Collection"
>
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
<div>
<span class="input-help">
{{ $t('setting.sessionTimeoutHelper', [form.sessionTimeout]) }}
</span>
</div>
</el-form-item>
<el-form-item :label="$t('setting.syncTime')">
<el-input disabled v-model="form.localTime">
<template #append>
<el-button v-show="!show" @click="onSyncTime" icon="Refresh">
{{ $t('commons.button.sync') }}
</el-button>
<span v-show="show">{{ count }} S</span>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</LayoutContent>
</el-card>
</el-form>
<el-dialog
v-model="passwordVisiable"
:destroy-on-close="true"
:close-on-click-modal="false"
:title="$t('setting.changePassword')"
width="30%"
>
<el-form
v-loading="dialogLoading"
ref="passFormRef"
label-width="80px"
label-position="left"
:model="passForm"
:rules="passRules"
>
<el-form-item :label="$t('setting.oldPassword')" prop="oldPassword">
<el-input type="password" show-password clearable v-model="passForm.oldPassword" />
</el-form-item>
<el-form-item
v-if="form.complexityVerification === 'disable'"
:label="$t('setting.newPassword')"
prop="newPassword"
>
<el-input type="password" show-password clearable v-model="passForm.newPassword" />
</el-form-item>
<el-form-item
v-if="form.complexityVerification === 'enable'"
:label="$t('setting.newPassword')"
prop="newPasswordComplexity"
>
<el-input type="password" show-password clearable v-model="passForm.newPasswordComplexity" />
</el-form-item>
<el-form-item :label="$t('setting.retryPassword')" prop="retryPassword">
<el-input type="password" show-password clearable v-model="passForm.retryPassword" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="dialogLoading" @click="passwordVisiable = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="dialogLoading" @click="submitChangePassword(passFormRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { ElMessage, ElForm } from 'element-plus';
import { updatePassword, syncTime, getSettingInfo, updateSetting } from '@/api/modules/setting';
import LayoutContent from '@/layout/layout-content.vue';
import { syncTime, getSettingInfo, updateSetting } from '@/api/modules/setting';
import Submenu from '@/views/setting/index.vue';
import router from '@/routers/router';
import { Rules } from '@/global/form-rules';
import { GlobalStore } from '@/store';
import { useI18n } from 'vue-i18n';
import { useTheme } from '@/hooks/use-theme';
const loading = ref(false);
const dialogLoading = ref(false);
const i18n = useI18n();
const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig);
const { switchDark } = useTheme();
type FormInstance = InstanceType<typeof ElForm>;
const passFormRef = ref<FormInstance>();
const passRules = reactive({
oldPassword: [Rules.requiredInput],
newPassword: [Rules.requiredInput, { min: 6, message: i18n.t('commons.rule.commonPassword'), trigger: 'blur' }],
newPasswordComplexity: [Rules.requiredInput, Rules.password],
retryPassword: [Rules.requiredInput, { validator: checkPassword, trigger: 'blur' }],
});
const passwordVisiable = ref<boolean>(false);
const passForm = reactive({
oldPassword: '',
newPassword: '',
newPasswordComplexity: '',
retryPassword: '',
});
const form = reactive({
userName: '',
password: '',
email: '',
sessionTimeout: 0,
localTime: '',
@ -219,7 +147,6 @@ const show = ref();
const search = async () => {
const res = await getSettingInfo();
form.userName = res.data.userName;
form.password = '******';
form.sessionTimeout = Number(res.data.sessionTimeout);
form.localTime = res.data.localTime;
form.panelName = res.data.panelName;
@ -300,47 +227,6 @@ function callback(error: any) {
}
}
function checkPassword(rule: any, value: any, callback: any) {
let password = form.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity;
if (password !== passForm.retryPassword) {
return callback(new Error(i18n.t('commons.rule.rePassword')));
}
callback();
}
const onChangePassword = async () => {
passForm.oldPassword = '';
passForm.newPassword = '';
passForm.newPasswordComplexity = '';
passForm.retryPassword = '';
passwordVisiable.value = true;
};
const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
let password =
form.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity;
if (password === passForm.oldPassword) {
ElMessage.error(i18n.t('setting.duplicatePassword'));
return;
}
dialogLoading.value = true;
await updatePassword({ oldPassword: passForm.oldPassword, newPassword: password })
.then(() => {
dialogLoading.value = false;
passwordVisiable.value = false;
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
router.push({ name: 'login', params: { code: '' } });
globalStore.setLogStatus(false);
})
.catch(() => {
dialogLoading.value = false;
});
});
};
const onSyncTime = async () => {
loading.value = true;
await syncTime()

View File

@ -3,93 +3,110 @@
<Submenu activeName="safe" />
<el-form :model="form" ref="panelFormRef" v-loading="loading" label-position="left" label-width="160px">
<el-card style="margin-top: 20px">
<template #header>
<div class="card-header">
<span>{{ $t('setting.safe') }}</span>
</div>
</template>
<el-row>
<el-col :span="1"><br /></el-col>
<el-col :span="10">
<el-form-item
:label="$t('setting.expirationTime')"
prop="expirationTime"
:rules="Rules.requiredInput"
>
<el-input disabled v-model="form.expirationTime">
<template #append>
<el-button @click="onChangeExpirationTime" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
<div>
<span class="input-help" v-if="form.expirationTime !== $t('setting.unSetting')">
{{ $t('setting.timeoutHelper', [loadTimeOut()]) }}
</span>
<span class="input-help" v-else>
{{ $t('setting.noneSetting') }}
</span>
</div>
</el-form-item>
<el-form-item
:label="$t('setting.complexity')"
prop="complexityVerification"
:rules="Rules.requiredSelect"
>
<el-radio-group
style="width: 100%"
@change="onSave(panelFormRef, 'ComplexityVerification', form.complexityVerification)"
v-model="form.complexityVerification"
<LayoutContent :header="$t('setting.safe')">
<el-row>
<el-col :span="1"><br /></el-col>
<el-col :span="10">
<el-form-item
:label="$t('commons.login.password')"
:rules="Rules.requiredInput"
prop="password"
>
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
</el-radio-group>
<div>
<el-input type="password" clearable disabled v-model="form.password">
<template #append>
<el-button icon="Setting" @click="onChangePassword">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item
:label="$t('setting.expirationTime')"
prop="expirationTime"
:rules="Rules.requiredInput"
>
<el-input disabled v-model="form.expirationTime">
<template #append>
<el-button @click="onChangeExpirationTime" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
<div>
<span class="input-help" v-if="form.expirationTime !== $t('setting.unSetting')">
{{ $t('setting.timeoutHelper', [loadTimeOut()]) }}
</span>
<span class="input-help" v-else>
{{ $t('setting.noneSetting') }}
</span>
</div>
</el-form-item>
<el-form-item
:label="$t('setting.complexity')"
prop="complexityVerification"
:rules="Rules.requiredSelect"
>
<el-switch
@change="
onSave(panelFormRef, 'ComplexityVerification', form.complexityVerification)
"
v-model="form.complexityVerification"
active-value="enable"
inactive-value="disable"
/>
<span class="input-help">
{{ $t('setting.complexityHelper') }}
</span>
</div>
</el-form-item>
<el-form-item :label="$t('setting.mfa')" prop="securityEntrance" :rules="Rules.requiredSelect">
<el-radio-group @change="handleMFA()" v-model="form.mfaStatus">
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
</el-radio-group>
</el-form-item>
<div v-if="isMFAShow">
<el-card>
<ul style="margin-left: 120px; line-height: 24px">
<li>
{{ $t('setting.mfaHelper1') }}
<ul>
<li>Google Authenticator</li>
<li>Microsoft Authenticator</li>
<li>1Password</li>
<li>LastPass</li>
<li>Authenticator</li>
</ul>
</li>
<li>{{ $t('setting.mfaHelper2') }}</li>
<el-image
style="margin-left: 15px; width: 100px; height: 100px"
:src="otp.qrImage"
/>
<li>{{ $t('setting.mfaHelper3') }}</li>
<el-input v-model="mfaCode"></el-input>
<div style="margin-top: 10px; margin-bottom: 10px; float: right">
<el-button @click="onCancelMfaBind">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="onBind">
{{ $t('commons.button.saveAndEnable') }}
</el-button>
</div>
</ul>
</el-card>
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item
:label="$t('setting.mfa')"
prop="securityEntrance"
:rules="Rules.requiredSelect"
>
<el-switch
@change="handleMFA"
v-model="form.mfaStatus"
active-value="enable"
inactive-value="disable"
/>
<span class="input-help">
{{ $t('setting.mfaHelper') }}
</span>
</el-form-item>
<el-form-item v-if="isMFAShow">
<el-card style="width: 100%">
<ul style="line-height: 24px">
<li>
{{ $t('setting.mfaHelper1') }}
<ul>
<li>Google Authenticator</li>
<li>Microsoft Authenticator</li>
<li>1Password</li>
<li>LastPass</li>
<li>Authenticator</li>
</ul>
</li>
<li>{{ $t('setting.mfaHelper2') }}</li>
<el-image
style="margin-left: 15px; width: 100px; height: 100px"
:src="otp.qrImage"
/>
<li>{{ $t('setting.mfaHelper3') }}</li>
<el-input v-model="mfaCode"></el-input>
<div style="margin-top: 10px; margin-bottom: 10px; float: right">
<el-button @click="onCancelMfaBind">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="onBind">
{{ $t('commons.button.saveAndEnable') }}
</el-button>
</div>
</ul>
</el-card>
</el-form-item>
</el-col>
</el-row>
</LayoutContent>
</el-card>
</el-form>
<el-dialog
@ -114,6 +131,53 @@
</span>
</template>
</el-dialog>
<el-dialog
v-model="passwordVisiable"
:destroy-on-close="true"
:close-on-click-modal="false"
:title="$t('setting.changePassword')"
width="30%"
>
<el-form
v-loading="dialogLoading"
ref="passFormRef"
label-width="80px"
label-position="left"
:model="passForm"
:rules="passRules"
>
<el-form-item :label="$t('setting.oldPassword')" prop="oldPassword">
<el-input type="password" show-password clearable v-model="passForm.oldPassword" />
</el-form-item>
<el-form-item
v-if="form.complexityVerification === 'disable'"
:label="$t('setting.newPassword')"
prop="newPassword"
>
<el-input type="password" show-password clearable v-model="passForm.newPassword" />
</el-form-item>
<el-form-item
v-if="form.complexityVerification === 'enable'"
:label="$t('setting.newPassword')"
prop="newPasswordComplexity"
>
<el-input type="password" show-password clearable v-model="passForm.newPasswordComplexity" />
</el-form-item>
<el-form-item :label="$t('setting.retryPassword')" prop="retryPassword">
<el-input type="password" show-password clearable v-model="passForm.retryPassword" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="dialogLoading" @click="passwordVisiable = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="dialogLoading" type="primary" @click="submitChangePassword(passFormRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
@ -122,20 +186,44 @@ import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElForm } from 'element-plus';
import Submenu from '@/views/setting/index.vue';
import { Setting } from '@/api/interface/setting';
import { updateSetting, getMFA, bindMFA, getSettingInfo } from '@/api/modules/setting';
import LayoutContent from '@/layout/layout-content.vue';
import { updatePassword, updateSetting, getMFA, bindMFA, getSettingInfo } from '@/api/modules/setting';
import i18n from '@/lang';
import { Rules } from '@/global/form-rules';
import { dateFromat } from '@/utils/util';
import { GlobalStore } from '@/store';
import router from '@/routers';
const loading = ref(false);
const globalStore = GlobalStore();
const passFormRef = ref<FormInstance>();
const passRules = reactive({
oldPassword: [Rules.requiredInput],
newPassword: [
Rules.requiredInput,
{ min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' },
],
newPasswordComplexity: [Rules.requiredInput, Rules.password],
retryPassword: [Rules.requiredInput, { validator: checkPassword, trigger: 'blur' }],
});
const dialogLoading = ref(false);
const passwordVisiable = ref<boolean>(false);
const passForm = reactive({
oldPassword: '',
newPassword: '',
newPasswordComplexity: '',
retryPassword: '',
});
const form = reactive({
password: '',
serverPort: '',
securityEntrance: '',
expirationDays: 0,
expirationTime: '',
complexityVerification: '',
mfaStatus: '',
mfaSecret: '',
mfaStatus: 'disable',
mfaSecret: 'disable',
});
type FormInstance = InstanceType<typeof ElForm>;
const timeoutFormRef = ref<FormInstance>();
@ -146,6 +234,7 @@ const timeoutForm = reactive({
const search = async () => {
const res = await getSettingInfo();
form.password = '******';
form.securityEntrance = res.data.securityEntrance;
form.expirationDays = Number(res.data.expirationDays);
form.expirationTime = res.data.expirationTime;
@ -194,13 +283,56 @@ function callback(error: any) {
}
}
function checkPassword(rule: any, value: any, callback: any) {
let password = form.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity;
if (password !== passForm.retryPassword) {
return callback(new Error(i18n.global.t('commons.rule.rePassword')));
}
callback();
}
const onChangePassword = async () => {
passForm.oldPassword = '';
passForm.newPassword = '';
passForm.newPasswordComplexity = '';
passForm.retryPassword = '';
passwordVisiable.value = true;
};
const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
let password =
form.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity;
if (password === passForm.oldPassword) {
ElMessage.error(i18n.global.t('setting.duplicatePassword'));
return;
}
dialogLoading.value = true;
await updatePassword({ oldPassword: passForm.oldPassword, newPassword: password })
.then(() => {
dialogLoading.value = false;
passwordVisiable.value = false;
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
router.push({ name: 'login', params: { code: '' } });
globalStore.setLogStatus(false);
})
.catch(() => {
dialogLoading.value = false;
});
});
};
const handleMFA = async () => {
console.log('dawdwda');
if (form.mfaStatus === 'enable') {
const res = await getMFA();
otp.secret = res.data.secret;
otp.qrImage = res.data.qrImage;
isMFAShow.value = true;
} else {
isMFAShow.value = false;
loading.value = true;
await updateSetting({ key: 'MFAStatus', value: 'disable' })
.then(() => {