mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 16:29:17 +08:00
fix: 面板设置界面改为路由实现
This commit is contained in:
parent
b4254e3403
commit
26eacf81b7
@ -6,6 +6,7 @@ type BackupOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Bucket string `json:"bucket"`
|
||||
AccessKey string `json:"accessKey"`
|
||||
Credential string `json:"credential"`
|
||||
Vars string `json:"vars" validate:"required"`
|
||||
}
|
||||
@ -49,6 +50,7 @@ type DownloadRecord struct {
|
||||
|
||||
type ForBuckets struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
AccessKey string `json:"accessKey"`
|
||||
Credential string `json:"credential" validate:"required"`
|
||||
Vars string `json:"vars" validate:"required"`
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ type BackupAccount struct {
|
||||
BaseModel
|
||||
Type string `gorm:"type:varchar(64);unique;not null" json:"type"`
|
||||
Bucket string `gorm:"type:varchar(256)" json:"bucket"`
|
||||
AccessKey string `gorm:"type:varchar(256)" json:"accessKey"`
|
||||
Credential string `gorm:"type:varchar(256)" json:"credential"`
|
||||
Vars string `gorm:"type:longText" json:"vars"`
|
||||
}
|
||||
|
@ -81,8 +81,10 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp:
|
||||
varMap["username"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
}
|
||||
backClient, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||
@ -131,8 +133,10 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
varMap["type"] = backupDto.Type
|
||||
switch backupDto.Type {
|
||||
case constant.Sftp:
|
||||
varMap["username"] = backupDto.AccessKey
|
||||
varMap["password"] = backupDto.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
varMap["accessKey"] = backupDto.AccessKey
|
||||
varMap["secretKey"] = backupDto.Credential
|
||||
}
|
||||
client, err := cloud_storage.NewCloudStorageClient(varMap)
|
||||
@ -189,8 +193,10 @@ func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.Cl
|
||||
varMap["bucket"] = backup.Bucket
|
||||
switch backup.Type {
|
||||
case constant.Sftp:
|
||||
varMap["username"] = backup.AccessKey
|
||||
varMap["password"] = backup.Credential
|
||||
case constant.OSS, constant.S3, constant.MinIo:
|
||||
varMap["accessKey"] = backup.AccessKey
|
||||
varMap["secretKey"] = backup.Credential
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,16 @@ export namespace Backup {
|
||||
export interface BackupInfo {
|
||||
id: number;
|
||||
type: string;
|
||||
accessKey: string;
|
||||
bucket: string;
|
||||
credential: string;
|
||||
vars: string;
|
||||
varsJson: object;
|
||||
}
|
||||
export interface BackupOperate {
|
||||
id: number;
|
||||
type: string;
|
||||
accessKey: string;
|
||||
bucket: string;
|
||||
credential: string;
|
||||
vars: string;
|
||||
@ -30,6 +33,7 @@ export namespace Backup {
|
||||
}
|
||||
export interface ForBucket {
|
||||
type: string;
|
||||
accessKey: string;
|
||||
credential: string;
|
||||
vars: string;
|
||||
}
|
||||
|
@ -6,11 +6,11 @@ export const getSettingInfo = () => {
|
||||
};
|
||||
|
||||
export const updateSetting = (param: Setting.SettingUpdate) => {
|
||||
return http.post(`/settings`, param);
|
||||
return http.post(`/settings/update`, param);
|
||||
};
|
||||
|
||||
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) => {
|
||||
@ -18,7 +18,7 @@ export const handleExpired = (param: Setting.PasswordUpdate) => {
|
||||
};
|
||||
|
||||
export const syncTime = () => {
|
||||
return http.post(`/settings/time/sync`, {});
|
||||
return http.post<string>(`/settings/time/sync`, {});
|
||||
};
|
||||
|
||||
export const cleanMonitors = () => {
|
||||
|
@ -676,7 +676,7 @@ export default {
|
||||
sessionTimeoutError: 'The minimum timeout is 300 seconds',
|
||||
sessionTimeoutHelper:
|
||||
'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',
|
||||
oldPassword: 'Original password',
|
||||
newPassword: 'New password',
|
||||
|
@ -689,7 +689,7 @@ export default {
|
||||
sessionTimeout: '超时时间',
|
||||
sessionTimeoutError: '最小超时时间为 300 秒',
|
||||
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板,面板将自动退出登录',
|
||||
syncTime: '同步时间',
|
||||
syncTime: '服务器时间',
|
||||
changePassword: '密码修改',
|
||||
oldPassword: '原密码',
|
||||
newPassword: '新密码',
|
||||
|
@ -19,6 +19,51 @@ const settingRouter = {
|
||||
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',
|
||||
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>
|
||||
<div>
|
||||
<el-card class="topCard">
|
||||
<el-radio-group v-model="activeNames">
|
||||
<el-radio-button class="topButton" size="large" label="all">{{ $t('setting.all') }}</el-radio-button>
|
||||
<el-radio-button class="topButton" size="large" label="panel">
|
||||
<el-radio-group v-model="active">
|
||||
<el-radio-button class="topButton" size="large" @click="routerTo('/setting/panel')" label="panel">
|
||||
{{ $t('setting.panel') }}
|
||||
</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" label="backup">
|
||||
{{ $t('setting.backup') }}
|
||||
<el-radio-button class="topButton" size="large" @click="routerTo('/setting/safe')" label="safe">
|
||||
{{ $t('setting.safe') }}
|
||||
</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') }}
|
||||
</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') }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { getSettingInfo, updateSetting } from '@/api/modules/setting';
|
||||
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';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import router from '@/routers';
|
||||
|
||||
const i18n = useI18n();
|
||||
const globalStore = GlobalStore();
|
||||
const themeConfig = computed(() => globalStore.themeConfig);
|
||||
|
||||
const activeNames = ref('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: '',
|
||||
interface MenuProps {
|
||||
activeName: string;
|
||||
}
|
||||
const props = withDefaults(defineProps<MenuProps>(), {
|
||||
activeName: 'all',
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
const res = await getSettingInfo();
|
||||
form.value = res.data;
|
||||
form.value.password = '******';
|
||||
form.value.expirationTime = form.value.expirationDays === 0 ? '-' : form.value.expirationTime;
|
||||
const active = ref('all');
|
||||
const routerTo = (path: string) => {
|
||||
router.push({ path: path });
|
||||
};
|
||||
|
||||
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(() => {
|
||||
search();
|
||||
if (props.activeName) {
|
||||
active.value = props.activeName;
|
||||
}
|
||||
});
|
||||
</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>
|
||||
<div>
|
||||
<Submenu activeName="safe" />
|
||||
<el-form :model="form" ref="panelFormRef" label-position="left" label-width="160px">
|
||||
<el-card style="margin-top: 20px">
|
||||
<template #header>
|
||||
@ -12,21 +13,18 @@
|
||||
<el-col :span="10">
|
||||
<el-form-item
|
||||
:label="$t('setting.expirationTime')"
|
||||
prop="settingInfo.expirationTime"
|
||||
prop="expirationTime"
|
||||
:rules="Rules.requiredInput"
|
||||
>
|
||||
<el-input disabled v-model="form.settingInfo.expirationTime">
|
||||
<el-input disabled v-model="form.expirationTime">
|
||||
<template #append>
|
||||
<el-button @click="onChangeExoirationTime" icon="Setting">
|
||||
<el-button @click="onChangeExpirationTime" icon="Setting">
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<div>
|
||||
<span
|
||||
class="input-help"
|
||||
v-if="form.settingInfo.expirationTime !== $t('setting.unSetting')"
|
||||
>
|
||||
<span class="input-help" v-if="form.expirationTime !== $t('setting.unSetting')">
|
||||
{{ $t('setting.timeoutHelper', [loadTimeOut()]) }}
|
||||
</span>
|
||||
<span class="input-help" v-else>
|
||||
@ -36,19 +34,13 @@
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('setting.complexity')"
|
||||
prop="settingInfo.complexityVerification"
|
||||
prop="complexityVerification"
|
||||
:rules="Rules.requiredSelect"
|
||||
>
|
||||
<el-radio-group
|
||||
style="width: 100%"
|
||||
@change="
|
||||
onSave(
|
||||
panelFormRef,
|
||||
'ComplexityVerification',
|
||||
form.settingInfo.complexityVerification,
|
||||
)
|
||||
"
|
||||
v-model="form.settingInfo.complexityVerification"
|
||||
@change="onSave(panelFormRef, 'ComplexityVerification', form.complexityVerification)"
|
||||
v-model="form.complexityVerification"
|
||||
>
|
||||
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
|
||||
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
|
||||
@ -59,12 +51,8 @@
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('setting.mfa')"
|
||||
prop="settingInfo.securityEntrance"
|
||||
:rules="Rules.requiredSelect"
|
||||
>
|
||||
<el-radio-group @change="handleMFA()" v-model="form.settingInfo.mfaStatus">
|
||||
<el-form-item :label="$t('setting.mfa')" prop="securityEntrance" :rules="Rules.requiredSelect">
|
||||
<el-radio-group @change="handleMFA()" v-model="form.mfaStatus">
|
||||
<el-radio-button label="enable">{{ $t('commons.button.enable') }}</el-radio-button>
|
||||
<el-radio-button label="disable">{{ $t('commons.button.disable') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
@ -130,21 +118,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessage, ElForm } from 'element-plus';
|
||||
import Submenu from '@/views/setting/index.vue';
|
||||
import { Setting } from '@/api/interface/setting';
|
||||
import { updateSetting, getMFA, bindMFA } from '@/api/modules/setting';
|
||||
import { updateSetting, getMFA, bindMFA, getSettingInfo } from '@/api/modules/setting';
|
||||
import i18n from '@/lang';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { dateFromat } from '@/utils/util';
|
||||
|
||||
const emit = defineEmits(['on-save', 'search']);
|
||||
|
||||
interface Props {
|
||||
settingInfo: any;
|
||||
}
|
||||
const form = withDefaults(defineProps<Props>(), {
|
||||
settingInfo: {
|
||||
const form = reactive({
|
||||
serverPort: '',
|
||||
securityEntrance: '',
|
||||
expirationDays: 0,
|
||||
@ -152,7 +137,6 @@ const form = withDefaults(defineProps<Props>(), {
|
||||
complexityVerification: '',
|
||||
mfaStatus: '',
|
||||
mfaSecret: '',
|
||||
},
|
||||
});
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const timeoutFormRef = ref<FormInstance>();
|
||||
@ -161,6 +145,16 @@ const timeoutForm = reactive({
|
||||
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 otp = reactive<Setting.MFAInfo>({
|
||||
secret: '',
|
||||
@ -169,12 +163,33 @@ const otp = reactive<Setting.MFAInfo>({
|
||||
const mfaCode = ref();
|
||||
const panelFormRef = ref<FormInstance>();
|
||||
|
||||
function onSave(formEl: FormInstance | undefined, key: string, val: any) {
|
||||
emit('on-save', formEl, key, val);
|
||||
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;
|
||||
}
|
||||
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 () => {
|
||||
if (form.settingInfo.mfaStatus === 'enable') {
|
||||
if (form.mfaStatus === 'enable') {
|
||||
const res = await getMFA();
|
||||
otp.secret = res.data.secret;
|
||||
otp.qrImage = res.data.qrImage;
|
||||
@ -194,12 +209,12 @@ const onBind = async () => {
|
||||
};
|
||||
|
||||
const onCancelMfaBind = async () => {
|
||||
form.settingInfo.mfaStatus = 'disable';
|
||||
form.mfaStatus = 'disable';
|
||||
isMFAShow.value = false;
|
||||
};
|
||||
|
||||
const onChangeExoirationTime = async () => {
|
||||
timeoutForm.days = form.settingInfo.expirationDays;
|
||||
const onChangeExpirationTime = async () => {
|
||||
timeoutForm.days = form.expirationDays;
|
||||
timeoutVisiable.value = true;
|
||||
};
|
||||
|
||||
@ -211,21 +226,25 @@ const submitTimeout = async (formEl: FormInstance | undefined) => {
|
||||
await updateSetting({ key: 'ExpirationDays', value: timeoutForm.days + '' });
|
||||
emit('search');
|
||||
loadTimeOut();
|
||||
form.settingInfo.expirationTime = dateFromat(0, 0, time);
|
||||
form.expirationTime = dateFromat(0, 0, time);
|
||||
timeoutVisiable.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
function loadTimeOut() {
|
||||
if (form.settingInfo.expirationDays === 0) {
|
||||
form.settingInfo.expirationTime = i18n.global.t('setting.unSetting');
|
||||
if (form.expirationDays === 0) {
|
||||
form.expirationTime = 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) {
|
||||
form.settingInfo.expirationTime = i18n.global.t('setting.unSetting');
|
||||
form.expirationTime = i18n.global.t('setting.unSetting');
|
||||
return i18n.global.t('setting.unSetting');
|
||||
}
|
||||
return Math.floor(staytimeGap / (3600 * 1000 * 24));
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</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