1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +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) { func (u *BackupService) List() ([]dto.BackupInfo, error) {
ops, err := backupRepo.List(commonRepo.WithOrderBy("created_at desc")) ops, err := backupRepo.List(commonRepo.WithOrderBy("created_at desc"))
var dtobas []dto.BackupInfo var dtobas []dto.BackupInfo
ossExist, s3Exist, sftpExist, minioExist := false, false, false, false
for _, group := range ops { 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 var item dto.BackupInfo
if err := copier.Copy(&item, &group); err != nil { if err := copier.Copy(&item, &group); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
} }
dtobas = append(dtobas, item) 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 return dtobas, err
} }

View File

@ -9,6 +9,7 @@ export namespace Backup {
credential: string; credential: string;
vars: string; vars: string;
varsJson: object; varsJson: object;
createdAt: Date;
} }
export interface BackupOperate { export interface BackupOperate {
id: number; 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!', duplicatePassword: 'The new password cannot be the same as the original password, please re-enter!',
backup: 'Backup', backup: 'Backup',
createBackupAccount: 'Create {0} backup account',
noTypeForCreate: 'No backup type is currently created', noTypeForCreate: 'No backup type is currently created',
serverDisk: 'Server disks', serverDisk: 'Server disks',
currentPath: 'Current path', currentPath: 'Current path',
@ -734,6 +735,7 @@ export default {
complexityHelper: complexityHelper:
'The password must contain at least eight characters and contain at least three uppercase letters, lowercase letters, digits, and special characters', 'The password must contain at least eight characters and contain at least three uppercase letters, lowercase letters, digits, and special characters',
mfa: 'MFA', 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:', 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', mfaHelper2: 'Scan the following QR code using the mobile app to obtain the 6-digit verification code',
mfaHelper3: 'Enter six digits from the app', mfaHelper3: 'Enter six digits from the app',

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ $t('container.containerCreate') }}</span> <span>{{ $t('container.containerCreate') }}</span>
@ -188,7 +188,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ $t('container.exportImage') }}</span> <span>{{ $t('container.exportImage') }}</span>
@ -40,7 +40,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>Tag {{ $t('container.image') }}</span> <span>Tag {{ $t('container.image') }}</span>
@ -35,7 +35,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ $t('container.createNetwork') }}</span> <span>{{ $t('container.createNetwork') }}</span>
@ -53,7 +53,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ title }}{{ $t('container.repo') }}</span> <span>{{ title }}{{ $t('container.repo') }}</span>
@ -60,7 +60,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

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

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ $t('container.createVolume') }}</span> <span>{{ $t('container.createVolume') }}</span>
@ -41,7 +41,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ title }}{{ $t('cronjob.cronTask') }}</span> <span>{{ title }}{{ $t('cronjob.cronTask') }}</span>
@ -138,7 +138,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ $t('database.backup') }} - {{ dbName }}</span> <span>{{ $t('database.backup') }} - {{ dbName }}</span>
@ -27,7 +27,7 @@
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix /> <fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix />
</ComplexTable> </ComplexTable>
</el-dialog> </el-drawer>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ $t('database.create') }}</span> <span>{{ $t('database.create') }}</span>
@ -9,7 +9,7 @@
<el-form-item :label="$t('commons.table.name')" prop="name"> <el-form-item :label="$t('commons.table.name')" prop="name">
<el-input clearable v-model.trim="form.name"> <el-input clearable v-model.trim="form.name">
<template #append> <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="utf8mb4" value="utf8mb4" />
<el-option label="utf-8" value="utf8" /> <el-option label="utf-8" value="utf8" />
<el-option label="gbk" value="gbk" /> <el-option label="gbk" value="gbk" />
@ -26,7 +26,7 @@
</el-form-item> </el-form-item>
<el-form-item :label="$t('database.permission')" prop="permission"> <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="localhost" :label="$t('database.permissionLocal')" />
<el-option value="%" :label="$t('database.permissionAll')" /> <el-option value="%" :label="$t('database.permissionAll')" />
<el-option value="ip" :label="$t('database.permissionForIP')" /> <el-option value="ip" :label="$t('database.permissionForIP')" />
@ -47,7 +47,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

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

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ $t('commons.button.import') }}</span> <span>{{ $t('commons.button.import') }}</span>
@ -59,7 +59,7 @@
fix fix
/> />
</ComplexTable> </ComplexTable>
</el-dialog> </el-drawer>
</div> </div>
</template> </template>

View File

@ -1,71 +1,190 @@
<template> <template>
<div> <div>
<Submenu activeName="backupaccount" /> <Submenu activeName="backupaccount" />
<el-card style="margin-top: 20px"> <el-form label-position="left" label-width="130px" :v-key="reflash">
<template #header> <el-row :gutter="20" style="margin-top: 20px">
<div class="card-header"> <el-col :span="24">
<span>{{ $t('setting.backup') }}</span> <el-card>
</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">
<template #header> <template #header>
<div class="card-header"> <svg-icon style="font-size: 7px" iconName="p-file-folder"></svg-icon>
<svg-icon style="font-size: 7px" :iconName="loadIconName(item.type)"></svg-icon> <span style="font-size: 16px; font-weight: 500">&nbsp;{{ $t('setting.serverDisk') }}</span>
<span style="font-size: 16px; font-weight: 500"> <div style="float: right">
&nbsp;{{ loadBackupName(item.type) }} <el-button round @click="onOpenDialog('edit', 'local', localData)">
</span> {{ $t('commons.button.edit') }}
<div style="float: right"> </el-button>
<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>
</div> </div>
</template> </template>
<el-form label-position="left" label-width="130px"> <el-form-item :label="$t('setting.currentPath')">
<el-form-item v-if="item.type === 'LOCAL'" :label="$t('setting.currentPath')"> {{ localData.varsJson['dir'] }}
{{ item.varsJson['dir'] }} </el-form-item>
</el-form-item> <el-form-item :label="$t('commons.table.createdAt')">
<el-form-item v-if="item.type === 'S3'" label="Region"> {{ dateFromat(0, 0, localData.createdAt) }}
{{ item.varsJson['region'] }} </el-form-item>
</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-card> </el-card>
</el-col> </el-col>
</el-row> </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" /> <DialogOperate ref="dialogRef" @search="search" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { dateFromat } from '@/utils/util'; import { dateFromat } from '@/utils/util';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { loadBackupName } from '@/views/setting/helper';
import { getBackupList, deleteBackup } from '@/api/modules/backup'; import { getBackupList, deleteBackup } from '@/api/modules/backup';
import DialogOperate from '@/views/setting/backup-account/operate/index.vue'; import DialogOperate from '@/views/setting/backup-account/operate/index.vue';
import Submenu from '@/views/setting/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'; import { useDeleteData } from '@/hooks/use-delete-data';
const data = ref(); 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 search = async () => {
const res = await getBackupList(); const res = await getBackupList();
data.value = res.data; data.value = res.data || [];
for (const bac of data.value) { 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 dialogRef = ref();
const onOpenDialog = async ( const onOpenDialog = async (
title: string, title: string,
accountType: string,
rowData: Partial<Backup.BackupInfo> = { rowData: Partial<Backup.BackupInfo> = {
id: 0, id: 0,
type: accountType,
varsJson: {}, varsJson: {},
}, },
) => { ) => {
let types = [] as Array<string>; console.log(rowData);
for (const item of data.value) {
types.push(item.type);
}
let params = { let params = {
title, title,
types,
rowData: { ...rowData }, rowData: { ...rowData },
}; };
dialogRef.value!.acceptParams(params); 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(() => { onMounted(() => {
search(); search();
}); });

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<div class="card-header"> <div class="card-header">
<span>{{ title }}{{ $t('setting.backupAccount') }}</span> <span>{{ title }}{{ $t('setting.backupAccount') }}</span>
@ -7,14 +7,7 @@
</template> </template>
<el-form ref="formRef" v-loading="loading" :model="dialogData.rowData" label-width="120px"> <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-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-select <span>{{ dialogData.rowData!.type }}</span>
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>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="dialogData.rowData!.type === 'LOCAL'" v-if="dialogData.rowData!.type === 'LOCAL'"
@ -116,7 +109,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -132,7 +125,6 @@ import { deepCopy } from '@/utils/util';
const loading = ref(false); const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
const typeOptions = ref();
const buckets = ref(); const buckets = ref();
const endpoints = ref('http'); const endpoints = ref('http');
@ -141,7 +133,6 @@ const emit = defineEmits<{ (e: 'search'): void }>();
interface DialogProps { interface DialogProps {
title: string; title: string;
types: Array<string>;
rowData?: Backup.BackupInfo; rowData?: Backup.BackupInfo;
getTableList?: () => Promise<any>; getTableList?: () => Promise<any>;
} }
@ -149,7 +140,6 @@ const title = ref<string>('');
const dialogVisiable = ref(false); const dialogVisiable = ref(false);
const dialogData = ref<DialogProps>({ const dialogData = ref<DialogProps>({
title: '', title: '',
types: [],
}); });
const acceptParams = (params: DialogProps): void => { const acceptParams = (params: DialogProps): void => {
dialogData.value = params; dialogData.value = params;
@ -161,36 +151,9 @@ const acceptParams = (params: DialogProps): void => {
} }
} }
title.value = i18n.global.t('commons.button.' + dialogData.value.title); title.value = i18n.global.t('commons.button.' + dialogData.value.title);
loadOption(params.types);
dialogVisiable.value = true; 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) => { const loadDir = async (path: string) => {
dialogData.value.rowData!.varsJson['dir'] = path; dialogData.value.rowData!.varsJson['dir'] = path;
}; };

View File

@ -3,205 +3,133 @@
<Submenu activeName="panel" /> <Submenu activeName="panel" />
<el-form :model="form" ref="panelFormRef" label-position="left" v-loading="loading" label-width="160px"> <el-form :model="form" ref="panelFormRef" label-position="left" v-loading="loading" label-width="160px">
<el-card style="margin-top: 20px"> <el-card style="margin-top: 20px">
<template #header> <LayoutContent :header="$t('setting.panel')">
<div class="card-header"> <el-row>
<span>{{ $t('setting.panel') }}</span> <el-col :span="1"><br /></el-col>
</div> <el-col :span="10">
</template> <el-form-item
<el-row> :label="$t('commons.login.username')"
<el-col :span="1"><br /></el-col> :rules="Rules.requiredInput"
<el-col :span="10"> prop="userName"
<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"
> >
<el-radio-button label="zh">中文</el-radio-button> <el-input clearable v-model="form.userName">
<el-radio-button label="en">English</el-radio-button> <template #append>
</el-radio-group> <el-button
<div> @click="onSave(panelFormRef, 'UserName', form.userName)"
<span class="input-help"> icon="Collection"
{{ $t('setting.languageHelper') }} >
</span> {{ $t('commons.button.save') }}
</div> </el-button>
</el-form-item> </template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.sessionTimeout')" :rules="Rules.number" prop="sessionTimeout"> <el-form-item :label="$t('setting.title')" :rules="Rules.requiredInput" prop="panelName">
<el-input v-model.number="form.sessionTimeout"> <el-input clearable v-model="form.panelName">
<template #append> <template #append>
<el-button <el-button
@click="onSave(panelFormRef, 'SessionTimeout', form.sessionTimeout)" @click="onSave(panelFormRef, 'PanelName', form.panelName)"
icon="Collection" icon="Collection"
> >
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
</el-button> </el-button>
</template> </template>
</el-input> </el-input>
<div> </el-form-item>
<span class="input-help">
{{ $t('setting.sessionTimeoutHelper', [form.sessionTimeout]) }}
</span>
</div>
</el-form-item>
<el-form-item :label="$t('setting.syncTime')"> <el-form-item :label="$t('setting.theme')" :rules="Rules.requiredSelect" prop="theme">
<el-input disabled v-model="form.localTime"> <el-radio-group
<template #append> @change="onSave(panelFormRef, 'Theme', form.theme)"
<el-button v-show="!show" @click="onSyncTime" icon="Refresh"> v-model="form.theme"
{{ $t('commons.button.sync') }} >
</el-button> <el-radio-button label="dark">
<span v-show="show">{{ count }} S</span> <el-icon><Moon /></el-icon>
</template> {{ $t('setting.dark') }}
</el-input> </el-radio-button>
</el-form-item> <el-radio-button label="light">
</el-col> <el-icon><Sunny /></el-icon>
</el-row> {{ $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-card>
</el-form> </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> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, onMounted, computed } from 'vue'; import { ref, reactive, onMounted, computed } from 'vue';
import { ElMessage, ElForm } from 'element-plus'; 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 Submenu from '@/views/setting/index.vue';
import router from '@/routers/router';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useTheme } from '@/hooks/use-theme'; import { useTheme } from '@/hooks/use-theme';
const loading = ref(false); const loading = ref(false);
const dialogLoading = ref(false);
const i18n = useI18n(); const i18n = useI18n();
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig); const themeConfig = computed(() => globalStore.themeConfig);
const { switchDark } = useTheme(); const { switchDark } = useTheme();
type FormInstance = InstanceType<typeof ElForm>; 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({ const form = reactive({
userName: '', userName: '',
password: '',
email: '', email: '',
sessionTimeout: 0, sessionTimeout: 0,
localTime: '', localTime: '',
@ -219,7 +147,6 @@ const show = ref();
const search = async () => { const search = async () => {
const res = await getSettingInfo(); const res = await getSettingInfo();
form.userName = res.data.userName; form.userName = res.data.userName;
form.password = '******';
form.sessionTimeout = Number(res.data.sessionTimeout); form.sessionTimeout = Number(res.data.sessionTimeout);
form.localTime = res.data.localTime; form.localTime = res.data.localTime;
form.panelName = res.data.panelName; 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 () => { const onSyncTime = async () => {
loading.value = true; loading.value = true;
await syncTime() await syncTime()

View File

@ -3,93 +3,110 @@
<Submenu activeName="safe" /> <Submenu activeName="safe" />
<el-form :model="form" ref="panelFormRef" v-loading="loading" label-position="left" label-width="160px"> <el-form :model="form" ref="panelFormRef" v-loading="loading" label-position="left" label-width="160px">
<el-card style="margin-top: 20px"> <el-card style="margin-top: 20px">
<template #header> <LayoutContent :header="$t('setting.safe')">
<div class="card-header"> <el-row>
<span>{{ $t('setting.safe') }}</span> <el-col :span="1"><br /></el-col>
</div> <el-col :span="10">
</template> <el-form-item
<el-row> :label="$t('commons.login.password')"
<el-col :span="1"><br /></el-col> :rules="Rules.requiredInput"
<el-col :span="10"> prop="password"
<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"
> >
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button> <el-input type="password" clearable disabled v-model="form.password">
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button> <template #append>
</el-radio-group> <el-button icon="Setting" @click="onChangePassword">
<div> {{ $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"> <span class="input-help">
{{ $t('setting.complexityHelper') }} {{ $t('setting.complexityHelper') }}
</span> </span>
</div> </el-form-item>
</el-form-item> <el-form-item
<el-form-item :label="$t('setting.mfa')" prop="securityEntrance" :rules="Rules.requiredSelect"> :label="$t('setting.mfa')"
<el-radio-group @change="handleMFA()" v-model="form.mfaStatus"> prop="securityEntrance"
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button> :rules="Rules.requiredSelect"
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button> >
</el-radio-group> <el-switch
</el-form-item> @change="handleMFA"
<div v-if="isMFAShow"> v-model="form.mfaStatus"
<el-card> active-value="enable"
<ul style="margin-left: 120px; line-height: 24px"> inactive-value="disable"
<li> />
{{ $t('setting.mfaHelper1') }} <span class="input-help">
<ul> {{ $t('setting.mfaHelper') }}
<li>Google Authenticator</li> </span>
<li>Microsoft Authenticator</li> </el-form-item>
<li>1Password</li> <el-form-item v-if="isMFAShow">
<li>LastPass</li> <el-card style="width: 100%">
<li>Authenticator</li> <ul style="line-height: 24px">
</ul> <li>
</li> {{ $t('setting.mfaHelper1') }}
<li>{{ $t('setting.mfaHelper2') }}</li> <ul>
<el-image <li>Google Authenticator</li>
style="margin-left: 15px; width: 100px; height: 100px" <li>Microsoft Authenticator</li>
:src="otp.qrImage" <li>1Password</li>
/> <li>LastPass</li>
<li>{{ $t('setting.mfaHelper3') }}</li> <li>Authenticator</li>
<el-input v-model="mfaCode"></el-input> </ul>
<div style="margin-top: 10px; margin-bottom: 10px; float: right"> </li>
<el-button @click="onCancelMfaBind"> <li>{{ $t('setting.mfaHelper2') }}</li>
{{ $t('commons.button.cancel') }} <el-image
</el-button> style="margin-left: 15px; width: 100px; height: 100px"
<el-button type="primary" @click="onBind"> :src="otp.qrImage"
{{ $t('commons.button.saveAndEnable') }} />
</el-button> <li>{{ $t('setting.mfaHelper3') }}</li>
</div> <el-input v-model="mfaCode"></el-input>
</ul> <div style="margin-top: 10px; margin-bottom: 10px; float: right">
</el-card> <el-button @click="onCancelMfaBind">
</div> {{ $t('commons.button.cancel') }}
</el-col> </el-button>
</el-row> <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-card>
</el-form> </el-form>
<el-dialog <el-dialog
@ -114,6 +131,53 @@
</span> </span>
</template> </template>
</el-dialog> </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> </div>
</template> </template>
@ -122,20 +186,44 @@ import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElForm } from 'element-plus'; import { ElMessage, ElForm } from 'element-plus';
import Submenu from '@/views/setting/index.vue'; import Submenu from '@/views/setting/index.vue';
import { Setting } from '@/api/interface/setting'; 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 i18n from '@/lang';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import { dateFromat } from '@/utils/util'; import { dateFromat } from '@/utils/util';
import { GlobalStore } from '@/store';
import router from '@/routers';
const loading = ref(false); 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({ const form = reactive({
password: '',
serverPort: '', serverPort: '',
securityEntrance: '', securityEntrance: '',
expirationDays: 0, expirationDays: 0,
expirationTime: '', expirationTime: '',
complexityVerification: '', complexityVerification: '',
mfaStatus: '', mfaStatus: 'disable',
mfaSecret: '', mfaSecret: 'disable',
}); });
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
const timeoutFormRef = ref<FormInstance>(); const timeoutFormRef = ref<FormInstance>();
@ -146,6 +234,7 @@ const timeoutForm = reactive({
const search = async () => { const search = async () => {
const res = await getSettingInfo(); const res = await getSettingInfo();
form.password = '******';
form.securityEntrance = res.data.securityEntrance; form.securityEntrance = res.data.securityEntrance;
form.expirationDays = Number(res.data.expirationDays); form.expirationDays = Number(res.data.expirationDays);
form.expirationTime = res.data.expirationTime; 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 () => { const handleMFA = async () => {
console.log('dawdwda');
if (form.mfaStatus === 'enable') { if (form.mfaStatus === 'enable') {
const res = await getMFA(); const res = await getMFA();
otp.secret = res.data.secret; otp.secret = res.data.secret;
otp.qrImage = res.data.qrImage; otp.qrImage = res.data.qrImage;
isMFAShow.value = true; isMFAShow.value = true;
} else { } else {
isMFAShow.value = false;
loading.value = true; loading.value = true;
await updateSetting({ key: 'MFAStatus', value: 'disable' }) await updateSetting({ key: 'MFAStatus', value: 'disable' })
.then(() => { .then(() => {