mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-16 18:54:43 +08:00
fix: 面板设置界面改为路由实现
This commit is contained in:
parent
b4254e3403
commit
26eacf81b7
@ -6,6 +6,7 @@ type BackupOperate struct {
|
|||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Type string `json:"type" validate:"required"`
|
Type string `json:"type" validate:"required"`
|
||||||
Bucket string `json:"bucket"`
|
Bucket string `json:"bucket"`
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
Credential string `json:"credential"`
|
Credential string `json:"credential"`
|
||||||
Vars string `json:"vars" validate:"required"`
|
Vars string `json:"vars" validate:"required"`
|
||||||
}
|
}
|
||||||
@ -49,6 +50,7 @@ type DownloadRecord struct {
|
|||||||
|
|
||||||
type ForBuckets struct {
|
type ForBuckets struct {
|
||||||
Type string `json:"type" validate:"required"`
|
Type string `json:"type" validate:"required"`
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
Credential string `json:"credential" validate:"required"`
|
Credential string `json:"credential" validate:"required"`
|
||||||
Vars string `json:"vars" validate:"required"`
|
Vars string `json:"vars" validate:"required"`
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ type BackupAccount struct {
|
|||||||
BaseModel
|
BaseModel
|
||||||
Type string `gorm:"type:varchar(64);unique;not null" json:"type"`
|
Type string `gorm:"type:varchar(64);unique;not null" json:"type"`
|
||||||
Bucket string `gorm:"type:varchar(256)" json:"bucket"`
|
Bucket string `gorm:"type:varchar(256)" json:"bucket"`
|
||||||
|
AccessKey string `gorm:"type:varchar(256)" json:"accessKey"`
|
||||||
Credential string `gorm:"type:varchar(256)" json:"credential"`
|
Credential string `gorm:"type:varchar(256)" json:"credential"`
|
||||||
Vars string `gorm:"type:longText" json:"vars"`
|
Vars string `gorm:"type:longText" json:"vars"`
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,10 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
|||||||
varMap["bucket"] = backup.Bucket
|
varMap["bucket"] = backup.Bucket
|
||||||
switch backup.Type {
|
switch backup.Type {
|
||||||
case constant.Sftp:
|
case constant.Sftp:
|
||||||
|
varMap["username"] = backup.AccessKey
|
||||||
varMap["password"] = backup.Credential
|
varMap["password"] = backup.Credential
|
||||||
case constant.OSS, constant.S3, constant.MinIo:
|
case constant.OSS, constant.S3, constant.MinIo:
|
||||||
|
varMap["accessKey"] = backup.AccessKey
|
||||||
varMap["secretKey"] = backup.Credential
|
varMap["secretKey"] = backup.Credential
|
||||||
}
|
}
|
||||||
backClient, err := cloud_storage.NewCloudStorageClient(varMap)
|
backClient, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||||
@ -131,8 +133,10 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
|||||||
varMap["type"] = backupDto.Type
|
varMap["type"] = backupDto.Type
|
||||||
switch backupDto.Type {
|
switch backupDto.Type {
|
||||||
case constant.Sftp:
|
case constant.Sftp:
|
||||||
|
varMap["username"] = backupDto.AccessKey
|
||||||
varMap["password"] = backupDto.Credential
|
varMap["password"] = backupDto.Credential
|
||||||
case constant.OSS, constant.S3, constant.MinIo:
|
case constant.OSS, constant.S3, constant.MinIo:
|
||||||
|
varMap["accessKey"] = backupDto.AccessKey
|
||||||
varMap["secretKey"] = backupDto.Credential
|
varMap["secretKey"] = backupDto.Credential
|
||||||
}
|
}
|
||||||
client, err := cloud_storage.NewCloudStorageClient(varMap)
|
client, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||||
@ -189,8 +193,10 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
|||||||
varMap["bucket"] = backup.Bucket
|
varMap["bucket"] = backup.Bucket
|
||||||
switch backup.Type {
|
switch backup.Type {
|
||||||
case constant.Sftp:
|
case constant.Sftp:
|
||||||
|
varMap["username"] = backup.AccessKey
|
||||||
varMap["password"] = backup.Credential
|
varMap["password"] = backup.Credential
|
||||||
case constant.OSS, constant.S3, constant.MinIo:
|
case constant.OSS, constant.S3, constant.MinIo:
|
||||||
|
varMap["accessKey"] = backup.AccessKey
|
||||||
varMap["secretKey"] = backup.Credential
|
varMap["secretKey"] = backup.Credential
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,16 @@ export namespace Backup {
|
|||||||
export interface BackupInfo {
|
export interface BackupInfo {
|
||||||
id: number;
|
id: number;
|
||||||
type: string;
|
type: string;
|
||||||
|
accessKey: string;
|
||||||
bucket: string;
|
bucket: string;
|
||||||
|
credential: string;
|
||||||
vars: string;
|
vars: string;
|
||||||
varsJson: object;
|
varsJson: object;
|
||||||
}
|
}
|
||||||
export interface BackupOperate {
|
export interface BackupOperate {
|
||||||
id: number;
|
id: number;
|
||||||
type: string;
|
type: string;
|
||||||
|
accessKey: string;
|
||||||
bucket: string;
|
bucket: string;
|
||||||
credential: string;
|
credential: string;
|
||||||
vars: string;
|
vars: string;
|
||||||
@ -30,6 +33,7 @@ export namespace Backup {
|
|||||||
}
|
}
|
||||||
export interface ForBucket {
|
export interface ForBucket {
|
||||||
type: string;
|
type: string;
|
||||||
|
accessKey: string;
|
||||||
credential: string;
|
credential: string;
|
||||||
vars: string;
|
vars: string;
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ export const getSettingInfo = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const updateSetting = (param: Setting.SettingUpdate) => {
|
export const updateSetting = (param: Setting.SettingUpdate) => {
|
||||||
return http.post(`/settings`, param);
|
return http.post(`/settings/update`, param);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updatePassword = (param: Setting.PasswordUpdate) => {
|
export const updatePassword = (param: Setting.PasswordUpdate) => {
|
||||||
return http.post(`/settings/password`, param);
|
return http.post(`/settings/password/update`, param);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleExpired = (param: Setting.PasswordUpdate) => {
|
export const handleExpired = (param: Setting.PasswordUpdate) => {
|
||||||
@ -18,7 +18,7 @@ export const handleExpired = (param: Setting.PasswordUpdate) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const syncTime = () => {
|
export const syncTime = () => {
|
||||||
return http.post(`/settings/time/sync`, {});
|
return http.post<string>(`/settings/time/sync`, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cleanMonitors = () => {
|
export const cleanMonitors = () => {
|
||||||
|
@ -676,7 +676,7 @@ export default {
|
|||||||
sessionTimeoutError: 'The minimum timeout is 300 seconds',
|
sessionTimeoutError: 'The minimum timeout is 300 seconds',
|
||||||
sessionTimeoutHelper:
|
sessionTimeoutHelper:
|
||||||
'If you do not operate the panel for more than {0} seconds, the panel automatically logs out',
|
'If you do not operate the panel for more than {0} seconds, the panel automatically logs out',
|
||||||
syncTime: 'Synchronization time',
|
syncTime: 'Server time',
|
||||||
changePassword: 'Password change',
|
changePassword: 'Password change',
|
||||||
oldPassword: 'Original password',
|
oldPassword: 'Original password',
|
||||||
newPassword: 'New password',
|
newPassword: 'New password',
|
||||||
|
@ -689,7 +689,7 @@ export default {
|
|||||||
sessionTimeout: '超时时间',
|
sessionTimeout: '超时时间',
|
||||||
sessionTimeoutError: '最小超时时间为 300 秒',
|
sessionTimeoutError: '最小超时时间为 300 秒',
|
||||||
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板,面板将自动退出登录',
|
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板,面板将自动退出登录',
|
||||||
syncTime: '同步时间',
|
syncTime: '服务器时间',
|
||||||
changePassword: '密码修改',
|
changePassword: '密码修改',
|
||||||
oldPassword: '原密码',
|
oldPassword: '原密码',
|
||||||
newPassword: '新密码',
|
newPassword: '新密码',
|
||||||
|
@ -19,6 +19,51 @@ const settingRouter = {
|
|||||||
key: 'Setting',
|
key: 'Setting',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/setting/backupaccount',
|
||||||
|
name: 'BackupAccount',
|
||||||
|
component: () => import('@/views/setting/backup-account/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
key: 'Setting',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/setting/about',
|
||||||
|
name: 'About',
|
||||||
|
component: () => import('@/views/setting/about/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
key: 'Setting',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/setting/monitor',
|
||||||
|
name: 'Monitor',
|
||||||
|
component: () => import('@/views/setting/monitor/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
key: 'Setting',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/setting/panel',
|
||||||
|
name: 'Panel',
|
||||||
|
component: () => import('@/views/setting/panel/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
key: 'Setting',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/setting/safe',
|
||||||
|
name: 'Safe',
|
||||||
|
component: () => import('@/views/setting/safe/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
key: 'Setting',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/expired',
|
path: '/expired',
|
||||||
name: 'Expired',
|
name: 'Expired',
|
||||||
|
54
frontend/src/views/setting/about/index.vue
Normal file
54
frontend/src/views/setting/about/index.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="about" />
|
||||||
|
<el-card style="margin-top: 20px">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ $t('setting.about') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div style="text-align: center">
|
||||||
|
<div style="justify-self: center">
|
||||||
|
<img style="width: 80px" src="@/assets/images/ko_image.png" />
|
||||||
|
</div>
|
||||||
|
<h3>{{ $t('setting.description') }}</h3>
|
||||||
|
<h3>v1.0.0</h3>
|
||||||
|
<div style="margin-top: 10px">
|
||||||
|
<el-link @click="toGithub">
|
||||||
|
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-huaban88"></svg-icon>
|
||||||
|
<span style="line-height: 20px">{{ $t('setting.project') }}</span>
|
||||||
|
</el-link>
|
||||||
|
<el-link @click="toIssue" style="margin-left: 15px">
|
||||||
|
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-bug"></svg-icon>
|
||||||
|
<span>{{ $t('setting.issue') }}</span>
|
||||||
|
</el-link>
|
||||||
|
<el-link @click="toTalk" style="margin-left: 15px">
|
||||||
|
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-taolun"></svg-icon>
|
||||||
|
<span>{{ $t('setting.chat') }}</span>
|
||||||
|
</el-link>
|
||||||
|
<el-link @click="toGithubStar" style="margin-left: 15px">
|
||||||
|
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-star"></svg-icon>
|
||||||
|
<span>{{ $t('setting.star') }}</span>
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import Submenu from '@/views/setting/index.vue';
|
||||||
|
|
||||||
|
const toGithub = () => {
|
||||||
|
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
|
||||||
|
};
|
||||||
|
const toIssue = () => {
|
||||||
|
window.open('https://github.com/1Panel-dev/1Panel/issues', '_blank');
|
||||||
|
};
|
||||||
|
const toTalk = () => {
|
||||||
|
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
|
||||||
|
};
|
||||||
|
const toGithubStar = () => {
|
||||||
|
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
|
||||||
|
};
|
||||||
|
</script>
|
139
frontend/src/views/setting/backup-account/index.vue
Normal file
139
frontend/src/views/setting/backup-account/index.vue
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<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">
|
||||||
|
<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">
|
||||||
|
{{ 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>
|
||||||
|
</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.username')">
|
||||||
|
{{ item.accessKey }}
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.createdAt')">
|
||||||
|
{{ dateFromat(0, 0, item.createdAt) }}
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
<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';
|
||||||
|
import { Backup } from '@/api/interface/backup';
|
||||||
|
import { ElForm } from 'element-plus';
|
||||||
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
const res = await getBackupList();
|
||||||
|
data.value = res.data;
|
||||||
|
for (const bac of data.value) {
|
||||||
|
bac.varsJson = JSON.parse(bac.vars);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBatchDelete = async (row: Backup.BackupInfo | null) => {
|
||||||
|
let ids: Array<number> = [];
|
||||||
|
ids.push(row.id);
|
||||||
|
await useDeleteData(deleteBackup, { ids: ids }, 'commons.msg.delete');
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const dialogRef = ref();
|
||||||
|
const onOpenDialog = async (
|
||||||
|
title: string,
|
||||||
|
rowData: Partial<Backup.BackupInfo> = {
|
||||||
|
id: 0,
|
||||||
|
varsJson: {},
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
let types = [] as Array<string>;
|
||||||
|
for (const item of data.value) {
|
||||||
|
types.push(item.type);
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
</script>
|
213
frontend/src/views/setting/backup-account/operate/index.vue
Normal file
213
frontend/src/views/setting/backup-account/operate/index.vue
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ title }}{{ $t('setting.backupAccount') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form ref="formRef" :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>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dialogData.rowData!.type === 'LOCAL'"
|
||||||
|
:label="$t('setting.currentPath')"
|
||||||
|
prop="varsJson['dir']"
|
||||||
|
:rules="Rules.requiredInput"
|
||||||
|
>
|
||||||
|
<el-input v-model="dialogData.rowData!.varsJson['dir']">
|
||||||
|
<template #append>
|
||||||
|
<FileList @choose="loadDir" :dir="true"></FileList>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="hasBucket(dialogData.rowData!.type)"
|
||||||
|
label="Access Key ID"
|
||||||
|
prop="accessKey"
|
||||||
|
:rules="Rules.requiredInput"
|
||||||
|
>
|
||||||
|
<el-input v-model="dialogData.rowData!.accessKey" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="hasBucket(dialogData.rowData!.type)"
|
||||||
|
label="Secret Key"
|
||||||
|
prop="credential"
|
||||||
|
:rules="Rules.requiredInput"
|
||||||
|
>
|
||||||
|
<el-input show-password v-model="dialogData.rowData!.credential" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dialogData.rowData!.type === 'S3'"
|
||||||
|
label="Region"
|
||||||
|
prop="varsJson.region"
|
||||||
|
:rules="Rules.requiredInput"
|
||||||
|
>
|
||||||
|
<el-input v-model="dialogData.rowData!.varsJson['region']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="hasBucket(dialogData.rowData!.type)"
|
||||||
|
label="Endpoint"
|
||||||
|
prop="varsJson.endpoint"
|
||||||
|
:rules="Rules.requiredInput"
|
||||||
|
>
|
||||||
|
<el-input v-model="dialogData.rowData!.varsJson['endpoint']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="dialogData.rowData!.type !== '' && hasBucket(dialogData.rowData!.type)"
|
||||||
|
label="Bucket"
|
||||||
|
prop="bucket"
|
||||||
|
:rules="Rules.requiredSelect"
|
||||||
|
>
|
||||||
|
<el-select style="width: 80%" v-model="dialogData.rowData!.bucket">
|
||||||
|
<el-option v-for="item in buckets" :key="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
<el-button style="width: 20%" plain @click="getBuckets">
|
||||||
|
{{ $t('setting.loadBucket') }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="dialogData.rowData!.type === 'SFTP'">
|
||||||
|
<el-form-item :label="$t('setting.address')" prop="varsJson.address" :rules="Rules.requiredInput">
|
||||||
|
<el-input v-model="dialogData.rowData!.varsJson['address']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('setting.port')" prop="varsJson.port" :rules="[Rules.number]">
|
||||||
|
<el-input-number :min="0" :max="65535" v-model.number="dialogData.rowData!.varsJson['port']" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('setting.username')" prop="accessKey" :rules="[Rules.requiredInput]">
|
||||||
|
<el-input v-model="dialogData.rowData!.accessKey" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('setting.password')" prop="credential" :rules="[Rules.requiredInput]">
|
||||||
|
<el-input type="password" show-password v-model="dialogData.rowData!.credential" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('setting.path')" prop="bucket">
|
||||||
|
<el-input v-model="dialogData.rowData!.bucket" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit(formRef)">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import FileList from '@/components/file-list/index.vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
|
import { Backup } from '@/api/interface/backup';
|
||||||
|
import { addBackup, editBackup, listBucket } from '@/api/modules/backup';
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const typeOptions = ref();
|
||||||
|
const buckets = ref();
|
||||||
|
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
title: string;
|
||||||
|
types: Array<string>;
|
||||||
|
rowData?: Backup.BackupInfo;
|
||||||
|
getTableList?: () => Promise<any>;
|
||||||
|
}
|
||||||
|
const title = ref<string>('');
|
||||||
|
const dialogVisiable = ref(false);
|
||||||
|
const dialogData = ref<DialogProps>({
|
||||||
|
title: '',
|
||||||
|
types: [],
|
||||||
|
});
|
||||||
|
const acceptParams = (params: DialogProps): void => {
|
||||||
|
dialogData.value = params;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
function hasBucket(val: string) {
|
||||||
|
return val === 'OSS' || val === 'S3' || val === 'MINIO';
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBuckets = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
listBucket({
|
||||||
|
type: dialogData.value.rowData!.type,
|
||||||
|
vars: JSON.stringify(dialogData.value.rowData!.varsJson),
|
||||||
|
accessKey: dialogData.value.rowData!.accessKey,
|
||||||
|
credential: dialogData.value.rowData!.credential,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
buckets.value = res.data;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
buckets.value = [];
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
if (!dialogData.value.rowData) return;
|
||||||
|
if (dialogData.value.title === 'create') {
|
||||||
|
await addBackup(dialogData.value.rowData);
|
||||||
|
}
|
||||||
|
if (dialogData.value.title === 'edit') {
|
||||||
|
await editBackup(dialogData.value.rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
dialogVisiable.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,144 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-card class="topCard">
|
<el-card class="topCard">
|
||||||
<el-radio-group v-model="activeNames">
|
<el-radio-group v-model="active">
|
||||||
<el-radio-button class="topButton" size="large" label="all">{{ $t('setting.all') }}</el-radio-button>
|
<el-radio-button class="topButton" size="large" @click="routerTo('/setting/panel')" label="panel">
|
||||||
<el-radio-button class="topButton" size="large" label="panel">
|
|
||||||
{{ $t('setting.panel') }}
|
{{ $t('setting.panel') }}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
<el-radio-button class="topButton" size="large" label="safe">{{ $t('setting.safe') }}</el-radio-button>
|
<el-radio-button class="topButton" size="large" @click="routerTo('/setting/safe')" label="safe">
|
||||||
<el-radio-button class="topButton" size="large" label="backup">
|
{{ $t('setting.safe') }}
|
||||||
{{ $t('setting.backup') }}
|
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
<el-radio-button class="topButton" size="large" label="monitor">
|
<el-radio-button
|
||||||
|
class="topButton"
|
||||||
|
@click="routerTo('/setting/backupaccount')"
|
||||||
|
size="large"
|
||||||
|
label="backupaccount"
|
||||||
|
>
|
||||||
|
{{ $t('setting.backupAccount') }}
|
||||||
|
</el-radio-button>
|
||||||
|
<el-radio-button class="topButton" size="large" @click="routerTo('/setting/monitor')" label="monitor">
|
||||||
{{ $t('menu.monitor') }}
|
{{ $t('menu.monitor') }}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
<el-radio-button class="topButton" size="large" label="about">
|
<el-radio-button class="topButton" size="large" @click="routerTo('/setting/about')" label="about">
|
||||||
{{ $t('setting.about') }}
|
{{ $t('setting.about') }}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-card>
|
</el-card>
|
||||||
<Panel
|
|
||||||
v-if="activeNames === 'all' || activeNames === 'panel'"
|
|
||||||
:settingInfo="form"
|
|
||||||
@on-save="SaveSetting"
|
|
||||||
@search="search"
|
|
||||||
/>
|
|
||||||
<Safe
|
|
||||||
v-if="activeNames === 'all' || activeNames === 'safe'"
|
|
||||||
:settingInfo="form"
|
|
||||||
@on-save="SaveSetting"
|
|
||||||
@search="search"
|
|
||||||
/>
|
|
||||||
<Backup v-if="activeNames === 'all' || activeNames === 'backup'" :settingInfo="form" @on-save="SaveSetting" />
|
|
||||||
<Monitor v-if="activeNames === 'all' || activeNames === 'monitor'" :settingInfo="form" @on-save="SaveSetting" />
|
|
||||||
<About v-if="activeNames === 'all' || activeNames === 'about'" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { getSettingInfo, updateSetting } from '@/api/modules/setting';
|
import router from '@/routers';
|
||||||
import { Setting } from '@/api/interface/setting';
|
|
||||||
import Panel from '@/views/setting/tabs/panel.vue';
|
|
||||||
import Safe from '@/views/setting/tabs/safe.vue';
|
|
||||||
import Backup from '@/views/setting/tabs/backup.vue';
|
|
||||||
import Monitor from '@/views/setting/tabs/monitor.vue';
|
|
||||||
import About from '@/views/setting/tabs/about.vue';
|
|
||||||
import { GlobalStore } from '@/store';
|
|
||||||
import { useTheme } from '@/hooks/use-theme';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { ElMessage, FormInstance } from 'element-plus';
|
|
||||||
|
|
||||||
const i18n = useI18n();
|
interface MenuProps {
|
||||||
const globalStore = GlobalStore();
|
activeName: string;
|
||||||
const themeConfig = computed(() => globalStore.themeConfig);
|
}
|
||||||
|
const props = withDefaults(defineProps<MenuProps>(), {
|
||||||
const activeNames = ref('all');
|
activeName: 'all',
|
||||||
let form = ref<Setting.SettingInfo>({
|
|
||||||
userName: '',
|
|
||||||
password: '',
|
|
||||||
email: '',
|
|
||||||
sessionTimeout: 86400,
|
|
||||||
localTime: '',
|
|
||||||
panelName: '',
|
|
||||||
theme: '',
|
|
||||||
language: '',
|
|
||||||
serverPort: 8888,
|
|
||||||
securityEntrance: '',
|
|
||||||
expirationDays: 0,
|
|
||||||
expirationTime: '',
|
|
||||||
complexityVerification: '',
|
|
||||||
mfaStatus: '',
|
|
||||||
mfaSecret: '',
|
|
||||||
monitorStatus: '',
|
|
||||||
monitorStoreDays: 30,
|
|
||||||
messageType: '',
|
|
||||||
emailVars: '',
|
|
||||||
weChatVars: '',
|
|
||||||
dingVars: '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const search = async () => {
|
const active = ref('all');
|
||||||
const res = await getSettingInfo();
|
const routerTo = (path: string) => {
|
||||||
form.value = res.data;
|
router.push({ path: path });
|
||||||
form.value.password = '******';
|
|
||||||
form.value.expirationTime = form.value.expirationDays === 0 ? '-' : form.value.expirationTime;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const { switchDark } = useTheme();
|
|
||||||
|
|
||||||
const SaveSetting = async (formEl: FormInstance | undefined, key: string, val: any) => {
|
|
||||||
if (!formEl) return;
|
|
||||||
const result = await formEl.validateField('settingInfo.' + key.replace(key[0], key[0].toLowerCase()), callback);
|
|
||||||
if (!result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (val === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (key) {
|
|
||||||
case 'Language':
|
|
||||||
i18n.locale.value = val;
|
|
||||||
globalStore.updateLanguage(val);
|
|
||||||
break;
|
|
||||||
case 'Theme':
|
|
||||||
globalStore.setThemeConfig({ ...themeConfig.value, theme: val });
|
|
||||||
switchDark();
|
|
||||||
break;
|
|
||||||
case 'SessionTimeout':
|
|
||||||
if (Number(val) < 300) {
|
|
||||||
ElMessage.error(i18n.t('setting.sessionTimeoutError'));
|
|
||||||
search();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'PanelName':
|
|
||||||
globalStore.setThemeConfig({ ...themeConfig.value, panelName: val });
|
|
||||||
break;
|
|
||||||
case 'MonitorStoreDays':
|
|
||||||
case 'ServerPort':
|
|
||||||
val = val + '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let param = {
|
|
||||||
key: key,
|
|
||||||
value: val + '',
|
|
||||||
};
|
|
||||||
await updateSetting(param);
|
|
||||||
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
|
|
||||||
search();
|
|
||||||
};
|
|
||||||
|
|
||||||
function callback(error: any) {
|
|
||||||
if (error) {
|
|
||||||
return error.message;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
search();
|
if (props.activeName) {
|
||||||
|
active.value = props.activeName;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
107
frontend/src/views/setting/monitor/index.vue
Normal file
107
frontend/src/views/setting/monitor/index.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="monitor" />
|
||||||
|
<el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
|
||||||
|
<el-card style="margin-top: 20px">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ $t('menu.monitor') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="1"><br /></el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('setting.enableMonitor')"
|
||||||
|
:rules="Rules.requiredInput"
|
||||||
|
prop="monitorStatus"
|
||||||
|
>
|
||||||
|
<el-radio-group
|
||||||
|
@change="onSave(panelFormRef, 'MonitorStatus', form.monitorStatus)"
|
||||||
|
v-model="form.monitorStatus"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<el-form-item :label="$t('setting.storeDays')" :rules="Rules.number" prop="monitorStoreDays">
|
||||||
|
<el-input clearable v-model.number="form.monitorStoreDays">
|
||||||
|
<template #append>
|
||||||
|
<el-button
|
||||||
|
@click="onSave(panelFormRef, 'MonitorStoreDays', form.monitorStoreDays)"
|
||||||
|
icon="Collection"
|
||||||
|
>
|
||||||
|
{{ $t('commons.button.save') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="onClean()" icon="Delete">{{ $t('setting.cleanMonitor') }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import { ElMessage, FormInstance } from 'element-plus';
|
||||||
|
import { cleanMonitors, getSettingInfo, updateSetting } from '@/api/modules/setting';
|
||||||
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
import Submenu from '@/views/setting/index.vue';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
monitorStatus: '',
|
||||||
|
monitorStoreDays: 30,
|
||||||
|
});
|
||||||
|
const panelFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
const res = await getSettingInfo();
|
||||||
|
form.monitorStatus = res.data.monitorStatus;
|
||||||
|
form.monitorStoreDays = res.data.monitorStoreDays;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = async (formEl: FormInstance | undefined, key: string, val: any) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
const result = await formEl.validateField(key.replace(key[0], key[0].toLowerCase()), callback);
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (val === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (key) {
|
||||||
|
case 'MonitorStoreDays':
|
||||||
|
case 'ServerPort':
|
||||||
|
val = val + '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let param = {
|
||||||
|
key: key,
|
||||||
|
value: val + '',
|
||||||
|
};
|
||||||
|
await updateSetting(param);
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
};
|
||||||
|
function callback(error: any) {
|
||||||
|
if (error) {
|
||||||
|
return error.message;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClean = async () => {
|
||||||
|
await useDeleteData(cleanMonitors, {}, 'commons.msg.delete');
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
306
frontend/src/views/setting/panel/index.vue
Normal file
306
frontend/src/views/setting/panel/index.vue
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="panel" />
|
||||||
|
<el-form :model="form" ref="panelFormRef" label-position="left" 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"
|
||||||
|
>
|
||||||
|
<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-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 @click="onSyncTime" icon="Refresh">
|
||||||
|
{{ $t('commons.button.sync') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</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 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 @click="passwordVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button @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 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 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: '',
|
||||||
|
panelName: '',
|
||||||
|
theme: '',
|
||||||
|
language: '',
|
||||||
|
complexityVerification: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
const res = await getSettingInfo();
|
||||||
|
form.userName = res.data.userName;
|
||||||
|
form.password = '******';
|
||||||
|
form.sessionTimeout = res.data.sessionTimeout;
|
||||||
|
form.localTime = res.data.localTime;
|
||||||
|
form.panelName = res.data.panelName;
|
||||||
|
form.theme = res.data.theme;
|
||||||
|
form.language = res.data.language;
|
||||||
|
form.complexityVerification = res.data.complexityVerification;
|
||||||
|
};
|
||||||
|
const panelFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const onSave = async (formEl: FormInstance | undefined, key: string, val: any) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
const result = await formEl.validateField(key.replace(key[0], key[0].toLowerCase()), callback);
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (val === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (key) {
|
||||||
|
case 'Language':
|
||||||
|
i18n.locale.value = val;
|
||||||
|
globalStore.updateLanguage(val);
|
||||||
|
break;
|
||||||
|
case 'Theme':
|
||||||
|
globalStore.setThemeConfig({ ...themeConfig.value, theme: val });
|
||||||
|
switchDark();
|
||||||
|
break;
|
||||||
|
case 'SessionTimeout':
|
||||||
|
if (Number(val) < 300) {
|
||||||
|
ElMessage.error(i18n.t('setting.sessionTimeoutError'));
|
||||||
|
search();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'PanelName':
|
||||||
|
globalStore.setThemeConfig({ ...themeConfig.value, panelName: val });
|
||||||
|
break;
|
||||||
|
case 'MonitorStoreDays':
|
||||||
|
case 'ServerPort':
|
||||||
|
val = val + '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let param = {
|
||||||
|
key: key,
|
||||||
|
value: val + '',
|
||||||
|
};
|
||||||
|
await updateSetting(param);
|
||||||
|
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
function callback(error: any) {
|
||||||
|
if (error) {
|
||||||
|
return error.message;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
await updatePassword({ oldPassword: passForm.oldPassword, newPassword: password });
|
||||||
|
passwordVisiable.value = false;
|
||||||
|
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
|
||||||
|
router.push({ name: 'login', params: { code: '' } });
|
||||||
|
globalStore.setLogStatus(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSyncTime = async () => {
|
||||||
|
const res = await syncTime();
|
||||||
|
form.localTime = res.data;
|
||||||
|
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<Submenu activeName="safe" />
|
||||||
<el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
|
<el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
|
||||||
<el-card style="margin-top: 20px">
|
<el-card style="margin-top: 20px">
|
||||||
<template #header>
|
<template #header>
|
||||||
@ -12,21 +13,18 @@
|
|||||||
<el-col :span="10">
|
<el-col :span="10">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
:label="$t('setting.expirationTime')"
|
:label="$t('setting.expirationTime')"
|
||||||
prop="settingInfo.expirationTime"
|
prop="expirationTime"
|
||||||
:rules="Rules.requiredInput"
|
:rules="Rules.requiredInput"
|
||||||
>
|
>
|
||||||
<el-input disabled v-model="form.settingInfo.expirationTime">
|
<el-input disabled v-model="form.expirationTime">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button @click="onChangeExoirationTime" icon="Setting">
|
<el-button @click="onChangeExpirationTime" icon="Setting">
|
||||||
{{ $t('commons.button.set') }}
|
{{ $t('commons.button.set') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span class="input-help" v-if="form.expirationTime !== $t('setting.unSetting')">
|
||||||
class="input-help"
|
|
||||||
v-if="form.settingInfo.expirationTime !== $t('setting.unSetting')"
|
|
||||||
>
|
|
||||||
{{ $t('setting.timeoutHelper', [loadTimeOut()]) }}
|
{{ $t('setting.timeoutHelper', [loadTimeOut()]) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="input-help" v-else>
|
<span class="input-help" v-else>
|
||||||
@ -36,19 +34,13 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
:label="$t('setting.complexity')"
|
:label="$t('setting.complexity')"
|
||||||
prop="settingInfo.complexityVerification"
|
prop="complexityVerification"
|
||||||
:rules="Rules.requiredSelect"
|
:rules="Rules.requiredSelect"
|
||||||
>
|
>
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@change="
|
@change="onSave(panelFormRef, 'ComplexityVerification', form.complexityVerification)"
|
||||||
onSave(
|
v-model="form.complexityVerification"
|
||||||
panelFormRef,
|
|
||||||
'ComplexityVerification',
|
|
||||||
form.settingInfo.complexityVerification,
|
|
||||||
)
|
|
||||||
"
|
|
||||||
v-model="form.settingInfo.complexityVerification"
|
|
||||||
>
|
>
|
||||||
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
|
<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-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
|
||||||
@ -59,12 +51,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</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="settingInfo.securityEntrance"
|
|
||||||
:rules="Rules.requiredSelect"
|
|
||||||
>
|
|
||||||
<el-radio-group @change="handleMFA()" v-model="form.settingInfo.mfaStatus">
|
|
||||||
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
|
<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-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
@ -130,29 +118,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive } from 'vue';
|
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 { Setting } from '@/api/interface/setting';
|
import { Setting } from '@/api/interface/setting';
|
||||||
import { updateSetting, getMFA, bindMFA } from '@/api/modules/setting';
|
import { 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';
|
||||||
|
|
||||||
const emit = defineEmits(['on-save', 'search']);
|
const emit = defineEmits(['on-save', 'search']);
|
||||||
|
|
||||||
interface Props {
|
const form = reactive({
|
||||||
settingInfo: any;
|
serverPort: '',
|
||||||
}
|
securityEntrance: '',
|
||||||
const form = withDefaults(defineProps<Props>(), {
|
expirationDays: 0,
|
||||||
settingInfo: {
|
expirationTime: '',
|
||||||
serverPort: '',
|
complexityVerification: '',
|
||||||
securityEntrance: '',
|
mfaStatus: '',
|
||||||
expirationDays: 0,
|
mfaSecret: '',
|
||||||
expirationTime: '',
|
|
||||||
complexityVerification: '',
|
|
||||||
mfaStatus: '',
|
|
||||||
mfaSecret: '',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const timeoutFormRef = ref<FormInstance>();
|
const timeoutFormRef = ref<FormInstance>();
|
||||||
@ -161,6 +145,16 @@ const timeoutForm = reactive({
|
|||||||
days: 0,
|
days: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
const res = await getSettingInfo();
|
||||||
|
form.securityEntrance = res.data.securityEntrance;
|
||||||
|
form.expirationDays = res.data.expirationDays;
|
||||||
|
form.expirationTime = res.data.expirationTime;
|
||||||
|
form.complexityVerification = res.data.complexityVerification;
|
||||||
|
form.mfaStatus = res.data.mfaStatus;
|
||||||
|
form.mfaSecret = res.data.mfaSecret;
|
||||||
|
};
|
||||||
|
|
||||||
const isMFAShow = ref<boolean>(false);
|
const isMFAShow = ref<boolean>(false);
|
||||||
const otp = reactive<Setting.MFAInfo>({
|
const otp = reactive<Setting.MFAInfo>({
|
||||||
secret: '',
|
secret: '',
|
||||||
@ -169,12 +163,33 @@ const otp = reactive<Setting.MFAInfo>({
|
|||||||
const mfaCode = ref();
|
const mfaCode = ref();
|
||||||
const panelFormRef = ref<FormInstance>();
|
const panelFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
function onSave(formEl: FormInstance | undefined, key: string, val: any) {
|
const onSave = async (formEl: FormInstance | undefined, key: string, val: any) => {
|
||||||
emit('on-save', formEl, key, val);
|
if (!formEl) return;
|
||||||
|
const result = await formEl.validateField(key.replace(key[0], key[0].toLowerCase()), callback);
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (val === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let param = {
|
||||||
|
key: key,
|
||||||
|
value: val + '',
|
||||||
|
};
|
||||||
|
await updateSetting(param);
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
function callback(error: any) {
|
||||||
|
if (error) {
|
||||||
|
return error.message;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMFA = async () => {
|
const handleMFA = async () => {
|
||||||
if (form.settingInfo.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;
|
||||||
@ -194,12 +209,12 @@ const onBind = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onCancelMfaBind = async () => {
|
const onCancelMfaBind = async () => {
|
||||||
form.settingInfo.mfaStatus = 'disable';
|
form.mfaStatus = 'disable';
|
||||||
isMFAShow.value = false;
|
isMFAShow.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangeExoirationTime = async () => {
|
const onChangeExpirationTime = async () => {
|
||||||
timeoutForm.days = form.settingInfo.expirationDays;
|
timeoutForm.days = form.expirationDays;
|
||||||
timeoutVisiable.value = true;
|
timeoutVisiable.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -211,21 +226,25 @@ const submitTimeout = async (formEl: FormInstance | undefined) => {
|
|||||||
await updateSetting({ key: 'ExpirationDays', value: timeoutForm.days + '' });
|
await updateSetting({ key: 'ExpirationDays', value: timeoutForm.days + '' });
|
||||||
emit('search');
|
emit('search');
|
||||||
loadTimeOut();
|
loadTimeOut();
|
||||||
form.settingInfo.expirationTime = dateFromat(0, 0, time);
|
form.expirationTime = dateFromat(0, 0, time);
|
||||||
timeoutVisiable.value = false;
|
timeoutVisiable.value = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadTimeOut() {
|
function loadTimeOut() {
|
||||||
if (form.settingInfo.expirationDays === 0) {
|
if (form.expirationDays === 0) {
|
||||||
form.settingInfo.expirationTime = i18n.global.t('setting.unSetting');
|
form.expirationTime = i18n.global.t('setting.unSetting');
|
||||||
return i18n.global.t('setting.unSetting');
|
return i18n.global.t('setting.unSetting');
|
||||||
}
|
}
|
||||||
let staytimeGap = new Date(form.settingInfo.expirationTime).getTime() - new Date().getTime();
|
let staytimeGap = new Date(form.expirationTime).getTime() - new Date().getTime();
|
||||||
if (staytimeGap < 0) {
|
if (staytimeGap < 0) {
|
||||||
form.settingInfo.expirationTime = i18n.global.t('setting.unSetting');
|
form.expirationTime = i18n.global.t('setting.unSetting');
|
||||||
return i18n.global.t('setting.unSetting');
|
return i18n.global.t('setting.unSetting');
|
||||||
}
|
}
|
||||||
return Math.floor(staytimeGap / (3600 * 1000 * 24));
|
return Math.floor(staytimeGap / (3600 * 1000 * 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
@ -1,49 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-card style="margin-top: 20px">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>{{ $t('setting.about') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div style="text-align: center">
|
|
||||||
<div style="justify-self: center">
|
|
||||||
<img style="width: 80px" src="@/assets/images/ko_image.png" />
|
|
||||||
</div>
|
|
||||||
<h3>{{ $t('setting.description') }}</h3>
|
|
||||||
<h3>v1.0.0</h3>
|
|
||||||
<div style="margin-top: 10px">
|
|
||||||
<el-link @click="toGithub">
|
|
||||||
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-huaban88"></svg-icon>
|
|
||||||
<span style="line-height: 20px">{{ $t('setting.project') }}</span>
|
|
||||||
</el-link>
|
|
||||||
<el-link @click="toIssue" style="margin-left: 15px">
|
|
||||||
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-bug"></svg-icon>
|
|
||||||
<span>{{ $t('setting.issue') }}</span>
|
|
||||||
</el-link>
|
|
||||||
<el-link @click="toTalk" style="margin-left: 15px">
|
|
||||||
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-taolun"></svg-icon>
|
|
||||||
<span>{{ $t('setting.chat') }}</span>
|
|
||||||
</el-link>
|
|
||||||
<el-link @click="toGithubStar" style="margin-left: 15px">
|
|
||||||
<svg-icon style="font-size: 7px; margin-bottom: 3px" iconName="p-star"></svg-icon>
|
|
||||||
<span>{{ $t('setting.star') }}</span>
|
|
||||||
</el-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const toGithub = () => {
|
|
||||||
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
|
|
||||||
};
|
|
||||||
const toIssue = () => {
|
|
||||||
window.open('https://github.com/1Panel-dev/1Panel/issues', '_blank');
|
|
||||||
};
|
|
||||||
const toTalk = () => {
|
|
||||||
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
|
|
||||||
};
|
|
||||||
const toGithubStar = () => {
|
|
||||||
window.open('https://github.com/1Panel-dev/1Panel', '_blank');
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,355 +0,0 @@
|
|||||||
<template>
|
|
||||||
<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="onCreate">
|
|
||||||
{{ $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>
|
|
||||||
<div class="card-header">
|
|
||||||
<svg-icon style="font-size: 7px" :iconName="loadIconName(item.type)"></svg-icon>
|
|
||||||
<span style="font-size: 16px; font-weight: 500"> {{ loadBackupName(item.type) }}</span>
|
|
||||||
<div style="float: right">
|
|
||||||
<el-button @click="onEdit(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>
|
|
||||||
</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.username')">
|
|
||||||
{{ item.varsJson['username'] }}
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('commons.table.createdAt')">
|
|
||||||
{{ dateFromat(0, 0, item.createdAt) }}
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
@close="search"
|
|
||||||
v-model="backupVisiable"
|
|
||||||
:destroy-on-close="true"
|
|
||||||
:title="$t('setting.backupAccount')"
|
|
||||||
width="30%"
|
|
||||||
>
|
|
||||||
<div v-loading="loading">
|
|
||||||
<el-form ref="formRef" label-position="left" :model="form" label-width="160px">
|
|
||||||
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
|
|
||||||
<el-select
|
|
||||||
style="width: 100%"
|
|
||||||
v-model="form.type"
|
|
||||||
@change="changeType"
|
|
||||||
:disabled="operation === '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
|
|
||||||
v-if="form.type === 'LOCAL'"
|
|
||||||
:label="$t('setting.currentPath')"
|
|
||||||
prop="varsJson['dir']"
|
|
||||||
:rules="Rules.requiredInput"
|
|
||||||
>
|
|
||||||
<el-input v-model="form.varsJson['dir']">
|
|
||||||
<template #append>
|
|
||||||
<FileList @choose="loadDir" :dir="true"></FileList>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="hasBucket(form.type) && operation !== 'edit'"
|
|
||||||
label="Access Key ID"
|
|
||||||
prop="varsJson.accessKey"
|
|
||||||
:rules="Rules.requiredInput"
|
|
||||||
>
|
|
||||||
<el-input v-model="form.varsJson['accessKey']" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="hasBucket(form.type)"
|
|
||||||
label="Access Key Secret"
|
|
||||||
prop="credential"
|
|
||||||
:rules="Rules.requiredInput"
|
|
||||||
>
|
|
||||||
<el-input show-password v-model="form.credential" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="form.type === 'S3'"
|
|
||||||
label="Region"
|
|
||||||
prop="varsJson.region"
|
|
||||||
:rules="Rules.requiredInput"
|
|
||||||
>
|
|
||||||
<el-input v-model="form.varsJson['region']" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="hasBucket(form.type)"
|
|
||||||
label="Endpoint"
|
|
||||||
prop="varsJson.endpoint"
|
|
||||||
:rules="Rules.requiredInput"
|
|
||||||
>
|
|
||||||
<el-input v-model="form.varsJson['endpoint']" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="form.type !== '' && hasBucket(form.type)"
|
|
||||||
label="Bucket"
|
|
||||||
prop="bucket"
|
|
||||||
:rules="Rules.requiredSelect"
|
|
||||||
>
|
|
||||||
<el-select style="width: 80%" v-model="form.bucket">
|
|
||||||
<el-option v-for="item in buckets" :key="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
<el-button style="width: 20%" plain @click="getBuckets">
|
|
||||||
{{ $t('setting.loadBucket') }}
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
<div v-if="form.type === 'SFTP'">
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('setting.address')"
|
|
||||||
prop="varsJson.address"
|
|
||||||
:rules="Rules.requiredInput"
|
|
||||||
>
|
|
||||||
<el-input v-model="form.varsJson['address']" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('setting.port')" prop="varsJson.port" :rules="[Rules.number]">
|
|
||||||
<el-input-number :min="0" :max="65535" v-model.number="form.varsJson['port']" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('setting.username')"
|
|
||||||
prop="varsJson.username"
|
|
||||||
:rules="[Rules.requiredInput]"
|
|
||||||
>
|
|
||||||
<el-input v-model="form.varsJson['username']" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('setting.password')" prop="credential" :rules="[Rules.requiredInput]">
|
|
||||||
<el-input type="password" show-password v-model="form.credential" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('setting.path')" prop="bucket">
|
|
||||||
<el-input v-model="form.bucket" />
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<span class="dialog-footer">
|
|
||||||
<el-button @click="backupVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
|
||||||
<el-button type="primary" @click="onSubmit(formRef)">
|
|
||||||
{{ $t('commons.button.confirm') }}
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</el-card>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { dateFromat } from '@/utils/util';
|
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
|
||||||
import { loadBackupName } from '@/views/setting/helper';
|
|
||||||
import { getBackupList, addBackup, editBackup, listBucket, deleteBackup } from '@/api/modules/backup';
|
|
||||||
import { Backup } from '@/api/interface/backup';
|
|
||||||
import i18n from '@/lang';
|
|
||||||
import { Rules } from '@/global/form-rules';
|
|
||||||
import { ElForm, ElMessage } from 'element-plus';
|
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
|
||||||
import FileList from '@/components/file-list/index.vue';
|
|
||||||
|
|
||||||
const data = ref();
|
|
||||||
const selects = ref<any>([]);
|
|
||||||
const backupVisiable = ref<boolean>(false);
|
|
||||||
const operation = ref<string>('create');
|
|
||||||
const loading = ref(false);
|
|
||||||
|
|
||||||
const form = reactive({
|
|
||||||
id: 0,
|
|
||||||
type: 'LOCAL',
|
|
||||||
bucket: '',
|
|
||||||
credential: '',
|
|
||||||
vars: '',
|
|
||||||
varsJson: {},
|
|
||||||
});
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
|
||||||
const formRef = ref<FormInstance>();
|
|
||||||
const typeOptions = ref();
|
|
||||||
const buckets = ref();
|
|
||||||
|
|
||||||
const search = async () => {
|
|
||||||
const res = await getBackupList();
|
|
||||||
data.value = res.data;
|
|
||||||
for (const bac of data.value) {
|
|
||||||
bac.varsJson = JSON.parse(bac.vars);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCreate = () => {
|
|
||||||
loadOption();
|
|
||||||
if (!typeOptions.value || typeOptions.value.length === 0) {
|
|
||||||
ElMessage.info(i18n.global.t('setting.noTypeForCreate'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
operation.value = 'create';
|
|
||||||
form.id = 0;
|
|
||||||
form.type = typeOptions.value[0].value;
|
|
||||||
form.bucket = '';
|
|
||||||
form.credential = '';
|
|
||||||
form.vars = '';
|
|
||||||
form.varsJson = {};
|
|
||||||
backupVisiable.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBatchDelete = async (row: Backup.BackupInfo | null) => {
|
|
||||||
let ids: Array<number> = [];
|
|
||||||
if (row === null) {
|
|
||||||
selects.value.forEach((item: Backup.BackupInfo) => {
|
|
||||||
ids.push(item.id);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ids.push(row.id);
|
|
||||||
}
|
|
||||||
await useDeleteData(deleteBackup, { ids: ids }, 'commons.msg.delete');
|
|
||||||
search();
|
|
||||||
restForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEdit = (row: Backup.BackupInfo) => {
|
|
||||||
typeOptions.value = [
|
|
||||||
{ 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' },
|
|
||||||
];
|
|
||||||
restForm();
|
|
||||||
form.id = row.id;
|
|
||||||
form.type = row.type;
|
|
||||||
form.bucket = row.bucket;
|
|
||||||
form.varsJson = JSON.parse(row.vars);
|
|
||||||
operation.value = 'edit';
|
|
||||||
backupVisiable.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeType = async (val: string) => {
|
|
||||||
let itemType = val;
|
|
||||||
restForm();
|
|
||||||
buckets.value = [];
|
|
||||||
form.type = itemType;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.validate(async (valid) => {
|
|
||||||
if (!valid) return;
|
|
||||||
form.vars = JSON.stringify(form.varsJson);
|
|
||||||
if (form.id !== 0 && operation.value === 'edit') {
|
|
||||||
await editBackup(form);
|
|
||||||
} else if (form.id === 0 && operation.value === 'create') {
|
|
||||||
await addBackup(form);
|
|
||||||
} else {
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.notSupportOperation'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
|
||||||
restForm();
|
|
||||||
search();
|
|
||||||
backupVisiable.value = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
function hasBucket(val: string) {
|
|
||||||
return val === 'OSS' || val === 'S3' || val === 'MINIO';
|
|
||||||
}
|
|
||||||
function restForm() {
|
|
||||||
if (formRef.value) {
|
|
||||||
formRef.value.resetFields();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getBuckets = async () => {
|
|
||||||
loading.value = true;
|
|
||||||
listBucket({
|
|
||||||
type: form.type,
|
|
||||||
vars: JSON.stringify(form.varsJson),
|
|
||||||
credential: form.credential,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
loading.value = false;
|
|
||||||
buckets.value = res.data;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
buckets.value = [];
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const loadDir = async (path: string) => {
|
|
||||||
form.varsJson['dir'] = path;
|
|
||||||
};
|
|
||||||
const loadOption = () => {
|
|
||||||
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 data.value) {
|
|
||||||
for (let i = 0; i < options.length; i++) {
|
|
||||||
if (item.type === options[i].value) {
|
|
||||||
options.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
typeOptions.value = options;
|
|
||||||
};
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,153 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-form :model="mesForm" label-position="left" label-width="160px">
|
|
||||||
<el-card style="margin-top: 20px">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>{{ $t('setting.message') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="1"><br /></el-col>
|
|
||||||
<el-col :span="10">
|
|
||||||
<el-form-item :label="$t('setting.messageType')">
|
|
||||||
<el-radio-group v-model="mesForm.messageType">
|
|
||||||
<el-radio-button label="none">{{ $t('commons.button.close') }}</el-radio-button>
|
|
||||||
<el-radio-button label="email">{{ $t('setting.email') }}</el-radio-button>
|
|
||||||
<el-radio-button label="wechat">{{ $t('setting.wechat') }}</el-radio-button>
|
|
||||||
<el-radio-button label="dingding">{{ $t('setting.dingding') }}</el-radio-button>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<div v-if="mesForm.messageType === 'none'">
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="SaveSetting()">{{ $t('setting.closeMessage') }}</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<div v-if="mesForm.messageType === 'email'">
|
|
||||||
<el-form-item :label="$t('setting.emailServer')">
|
|
||||||
<el-input clearable v-model="mesForm.emailVars.serverName" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('setting.emailAddr')">
|
|
||||||
<el-input clearable v-model="mesForm.emailVars.serverAddr" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('setting.emailSMTP')">
|
|
||||||
<el-input clearable v-model="mesForm.emailVars.serverSMTP" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="SaveSetting()">{{ $t('commons.button.saveAndEnable') }}</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<div v-if="mesForm.messageType === 'wechat'">
|
|
||||||
<el-form-item label="orpid">
|
|
||||||
<el-input clearable v-model="mesForm.weChatVars.orpid" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="corpsecret">
|
|
||||||
<el-input clearable v-model="mesForm.weChatVars.corpsecret" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="touser">
|
|
||||||
<el-input clearable v-model="mesForm.weChatVars.touser" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="agentid">
|
|
||||||
<el-input clearable v-model="mesForm.weChatVars.agentid" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="SaveSetting()">{{ $t('commons.button.saveAndEnable') }}</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<div v-if="mesForm.messageType === 'dingding'">
|
|
||||||
<el-form-item label="webhook token">
|
|
||||||
<el-input clearable v-model="mesForm.dingVars.webhookToken" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('setting.secret')">
|
|
||||||
<el-input clearable v-model="mesForm.dingVars.secret" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="SaveSetting()">{{ $t('commons.button.saveAndEnable') }}</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { reactive, watch } from 'vue';
|
|
||||||
import { ElMessage } from 'element-plus';
|
|
||||||
import { updateSetting } from '@/api/modules/setting';
|
|
||||||
import i18n from '@/lang';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
settingInfo: any;
|
|
||||||
}
|
|
||||||
const form = withDefaults(defineProps<Props>(), {
|
|
||||||
settingInfo: {
|
|
||||||
messageType: '',
|
|
||||||
emailVars: '',
|
|
||||||
weChatVars: '',
|
|
||||||
dingVars: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const mesForm = reactive({
|
|
||||||
messageType: '',
|
|
||||||
emailVars: {
|
|
||||||
serverName: '',
|
|
||||||
serverAddr: '',
|
|
||||||
serverSMTP: '',
|
|
||||||
},
|
|
||||||
weChatVars: {
|
|
||||||
orpid: '',
|
|
||||||
corpsecret: '',
|
|
||||||
touser: '',
|
|
||||||
agentid: '',
|
|
||||||
},
|
|
||||||
dingVars: {
|
|
||||||
webhookToken: '',
|
|
||||||
secret: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(form, (val: any) => {
|
|
||||||
if (val.settingInfo.messageType) {
|
|
||||||
mesForm.messageType = form.settingInfo.messageType;
|
|
||||||
mesForm.emailVars = val.settingInfo.emailVars
|
|
||||||
? JSON.parse(val.settingInfo.emailVars)
|
|
||||||
: { serverName: '', serverAddr: '', serverSMTP: '' };
|
|
||||||
mesForm.weChatVars = val.settingInfo.weChatVars
|
|
||||||
? JSON.parse(val.settingInfo.weChatVars)
|
|
||||||
: { orpid: '', corpsecret: '', touser: '', agentid: '' };
|
|
||||||
mesForm.dingVars = val.settingInfo.dingVars
|
|
||||||
? JSON.parse(val.settingInfo.dingVars)
|
|
||||||
: { webhookToken: '', secret: '' };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const SaveSetting = async () => {
|
|
||||||
let settingKey = '';
|
|
||||||
let settingVal = '';
|
|
||||||
switch (mesForm.messageType) {
|
|
||||||
case 'none':
|
|
||||||
settingVal = '';
|
|
||||||
break;
|
|
||||||
case 'email':
|
|
||||||
settingVal = JSON.stringify(mesForm.emailVars);
|
|
||||||
settingKey = 'EmailVars';
|
|
||||||
break;
|
|
||||||
case 'wechat':
|
|
||||||
settingVal = JSON.stringify(mesForm.weChatVars);
|
|
||||||
settingKey = 'WeChatVars';
|
|
||||||
break;
|
|
||||||
case 'dingding':
|
|
||||||
settingVal = JSON.stringify(mesForm.dingVars);
|
|
||||||
settingKey = 'DingVars';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let param = {
|
|
||||||
key: settingKey,
|
|
||||||
value: settingVal,
|
|
||||||
};
|
|
||||||
await updateSetting({ key: 'MessageType', value: mesForm.messageType });
|
|
||||||
await updateSetting(param);
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,77 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
|
|
||||||
<el-card style="margin-top: 20px">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>{{ $t('menu.monitor') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="1"><br /></el-col>
|
|
||||||
<el-col :span="10">
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('setting.enableMonitor')"
|
|
||||||
:rules="Rules.requiredInput"
|
|
||||||
prop="settingInfo.monitorStatus"
|
|
||||||
>
|
|
||||||
<el-radio-group
|
|
||||||
@change="onSave(panelFormRef, 'MonitorStatus', form.settingInfo.monitorStatus)"
|
|
||||||
v-model="form.settingInfo.monitorStatus"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('setting.storeDays')"
|
|
||||||
:rules="Rules.number"
|
|
||||||
prop="settingInfo.monitorStoreDays"
|
|
||||||
>
|
|
||||||
<el-input clearable v-model.number="form.settingInfo.monitorStoreDays">
|
|
||||||
<template #append>
|
|
||||||
<el-button
|
|
||||||
@click="onSave(panelFormRef, 'MonitorStoreDays', form.settingInfo.monitorStoreDays)"
|
|
||||||
icon="Collection"
|
|
||||||
>
|
|
||||||
{{ $t('commons.button.save') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="onClean()" icon="Delete">{{ $t('setting.cleanMonitor') }}</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { FormInstance } from 'element-plus';
|
|
||||||
import { cleanMonitors } from '@/api/modules/setting';
|
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
|
||||||
import { Rules } from '@/global/form-rules';
|
|
||||||
|
|
||||||
const emit = defineEmits<{ (e: 'on-save', formEl: FormInstance | undefined, key: string, val: any): void }>();
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
settingInfo: any;
|
|
||||||
}
|
|
||||||
const form = withDefaults(defineProps<Props>(), {
|
|
||||||
settingInfo: {
|
|
||||||
monitorStatus: '',
|
|
||||||
monitorStoreDays: 30,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const panelFormRef = ref<FormInstance>();
|
|
||||||
|
|
||||||
function onSave(formEl: FormInstance | undefined, key: string, val: any) {
|
|
||||||
emit('on-save', formEl, key, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClean = async () => {
|
|
||||||
await useDeleteData(cleanMonitors, {}, 'commons.msg.delete');
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,268 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-form :model="form" ref="panelFormRef" label-position="left" 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="settingInfo.userName"
|
|
||||||
>
|
|
||||||
<el-input clearable v-model="form.settingInfo.userName">
|
|
||||||
<template #append>
|
|
||||||
<el-button
|
|
||||||
@click="onSave(panelFormRef, 'UserName', form.settingInfo.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="settingInfo.password"
|
|
||||||
>
|
|
||||||
<el-input type="password" clearable disabled v-model="form.settingInfo.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="settingInfo.panelName"
|
|
||||||
>
|
|
||||||
<el-input clearable v-model="form.settingInfo.panelName">
|
|
||||||
<template #append>
|
|
||||||
<el-button
|
|
||||||
@click="onSave(panelFormRef, 'PanelName', form.settingInfo.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="settingInfo.theme">
|
|
||||||
<el-radio-group
|
|
||||||
@change="onSave(panelFormRef, 'Theme', form.settingInfo.theme)"
|
|
||||||
v-model="form.settingInfo.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="settingInfo.language"
|
|
||||||
>
|
|
||||||
<el-radio-group
|
|
||||||
style="width: 100%"
|
|
||||||
@change="onSave(panelFormRef, 'Language', form.settingInfo.language)"
|
|
||||||
v-model="form.settingInfo.language"
|
|
||||||
>
|
|
||||||
<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-form-item
|
|
||||||
:label="$t('setting.sessionTimeout')"
|
|
||||||
:rules="Rules.number"
|
|
||||||
prop="settingInfo.sessionTimeout"
|
|
||||||
>
|
|
||||||
<el-input v-model.number="form.settingInfo.sessionTimeout">
|
|
||||||
<template #append>
|
|
||||||
<el-button
|
|
||||||
@click="onSave(panelFormRef, 'SessionTimeout', form.settingInfo.sessionTimeout)"
|
|
||||||
icon="Collection"
|
|
||||||
>
|
|
||||||
{{ $t('commons.button.save') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<div>
|
|
||||||
<span class="input-help">
|
|
||||||
{{ $t('setting.sessionTimeoutHelper', [form.settingInfo.sessionTimeout]) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item :label="$t('setting.syncTime')">
|
|
||||||
<el-input disabled v-model="form.settingInfo.localTime">
|
|
||||||
<template #append>
|
|
||||||
<el-button @click="onSyncTime" icon="Refresh">
|
|
||||||
{{ $t('commons.button.sync') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</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 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.settingInfo.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.settingInfo.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 @click="passwordVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
|
||||||
<el-button @click="submitChangePassword(passFormRef)">
|
|
||||||
{{ $t('commons.button.confirm') }}
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, reactive } from 'vue';
|
|
||||||
import { ElMessage, ElForm } from 'element-plus';
|
|
||||||
import { updatePassword, syncTime } from '@/api/modules/setting';
|
|
||||||
import router from '@/routers/router';
|
|
||||||
import { Rules } from '@/global/form-rules';
|
|
||||||
import i18n from '@/lang';
|
|
||||||
import { GlobalStore } from '@/store';
|
|
||||||
|
|
||||||
const globalStore = GlobalStore();
|
|
||||||
const emit = defineEmits(['on-save', 'search']);
|
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
|
||||||
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 passwordVisiable = ref<boolean>(false);
|
|
||||||
const passForm = reactive({
|
|
||||||
oldPassword: '',
|
|
||||||
newPassword: '',
|
|
||||||
newPasswordComplexity: '',
|
|
||||||
retryPassword: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
settingInfo: any;
|
|
||||||
}
|
|
||||||
const form = withDefaults(defineProps<Props>(), {
|
|
||||||
settingInfo: {
|
|
||||||
userName: '',
|
|
||||||
password: '',
|
|
||||||
email: '',
|
|
||||||
sessionTimeout: 0,
|
|
||||||
localTime: '',
|
|
||||||
panelName: '',
|
|
||||||
theme: '',
|
|
||||||
language: '',
|
|
||||||
complexityVerification: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const panelFormRef = ref<FormInstance>();
|
|
||||||
|
|
||||||
function onSave(formEl: FormInstance | undefined, key: string, val: any) {
|
|
||||||
emit('on-save', formEl, key, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkPassword(rule: any, value: any, callback: any) {
|
|
||||||
let password =
|
|
||||||
form.settingInfo.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.settingInfo.complexityVerification === 'disable'
|
|
||||||
? passForm.newPassword
|
|
||||||
: passForm.newPasswordComplexity;
|
|
||||||
if (password === passForm.oldPassword) {
|
|
||||||
ElMessage.error(i18n.global.t('setting.duplicatePassword'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await updatePassword({ oldPassword: passForm.oldPassword, newPassword: password });
|
|
||||||
passwordVisiable.value = false;
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
|
||||||
router.push({ name: 'login', params: { code: '' } });
|
|
||||||
globalStore.setLogStatus(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSyncTime = async () => {
|
|
||||||
const res = await syncTime();
|
|
||||||
emit('search');
|
|
||||||
form.settingInfo.localTime = res.data;
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
|
||||||
};
|
|
||||||
</script>
|
|
Loading…
x
Reference in New Issue
Block a user