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

fix: 面板设置界面改为路由实现

This commit is contained in:
ssongliu 2022-12-22 15:03:43 +08:00 committed by ssongliu
parent b4254e3403
commit 26eacf81b7
20 changed files with 976 additions and 1074 deletions

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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 = () => {

View File

@ -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',

View File

@ -689,7 +689,7 @@ export default {
sessionTimeout: '超时时间',
sessionTimeoutError: '最小超时时间为 300 ',
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板面板将自动退出登录',
syncTime: '同步时间',
syncTime: '服务器时间',
changePassword: '密码修改',
oldPassword: '原密码',
newPassword: '新密码',

View File

@ -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',

View 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>

View 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">
&nbsp;{{ loadBackupName(item.type) }}
</span>
<div style="float: right">
<el-button @click="onOpenDialog('edit', item)">
{{ $t('commons.button.edit') }}
</el-button>
<el-button v-if="item.type !== 'LOCAL'" @click="onBatchDelete(item)">
{{ $t('commons.button.delete') }}
</el-button>
</div>
</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>

View 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>

View File

@ -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>

View 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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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">&nbsp;{{ 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>