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

feat: 拆分备份账号代码 (#3454)

This commit is contained in:
ssongliu 2023-12-26 22:48:07 +08:00 committed by GitHub
parent b547955965
commit ee2fa70bb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1611 additions and 470 deletions

View File

@ -1239,6 +1239,28 @@ const message = {
'Please click on the "Acquire" button, then login to OneDrive and copy the content after "code" in the redirected link. Paste it into this input box. For specific instructions, please refer to the official documentation.',
loadCode: 'Acquire',
COS: 'Tencent COS',
ap_beijing_1: 'Beijing Zone 1',
ap_beijing: 'Beijing',
ap_nanjing: 'Nanjing',
ap_shanghai: 'Shanghai',
ap_guangzhou: 'Guangzhou',
ap_chengdu: 'Chengdu',
ap_chongqing: 'Chongqing',
ap_shenzhen_fsi: 'Shenzhen Financial',
ap_shanghai_fsi: 'Shanghai Financial',
ap_beijing_fsi: 'Beijing Financial',
ap_hongkong: 'Hong Kong, China',
ap_singapore: 'Singapore',
ap_mumbai: 'Mumbai',
ap_jakarta: 'Jakarta',
ap_seoul: 'Seoul',
ap_bangkok: 'Bangkok',
ap_tokyo: 'Tokyo',
na_siliconvalley: 'Silicon Valley (US West)',
na_ashburn: 'Ashburn (US East)',
na_toronto: 'Toronto',
sa_saopaulo: 'Sao Paulo',
eu_frankfurt: 'Frankfurt',
KODO: 'Qiniu Kodo',
scType: ' Storage type',
typeStandard: 'Standard',
@ -1253,7 +1275,6 @@ const message = {
scDeep_Archive: 'Durable cold storage is suitable for business scenarios with extremely low access frequency.',
archiveHelper:
'Archival storage files cannot be downloaded directly and must first be restored through the corresponding cloud service provider`s website. Please use with caution!',
domainHelper: 'The accelerated domain name must contain http:// or https://',
backupAlert:
"In theory, as long as the cloud provider is compatible with the S3 protocol, existing Amazon S3 cloud storage can be used for backup. For specific configurations, please refer to the <a target=“_blank” href='https://1panel.cn/docs/user_manual/settings/#3'>official documentation.</a> ",
domain: 'Accelerate domain',

View File

@ -1165,6 +1165,28 @@ const message = {
'請點擊獲取按鈕然後登錄 OneDrive 復製跳轉鏈接中 code 後面的內容粘貼到該輸入框中具體操作可參考官方文檔',
loadCode: '獲取',
COS: '騰訊雲 COS',
ap_beijing_1: '北京一區',
ap_beijing: '北京',
ap_nanjing: '南京',
ap_shanghai: '上海',
ap_guangzhou: '廣州',
ap_chengdu: '成都',
ap_chongqing: '重慶',
ap_shenzhen_fsi: '深圳金融',
ap_shanghai_fsi: '上海金融',
ap_beijing_fsi: '北京金融',
ap_hongkong: '中國香港',
ap_singapore: '新加坡',
ap_mumbai: '孟買',
ap_jakarta: '雅加達',
ap_seoul: '首爾',
ap_bangkok: '曼谷',
ap_tokyo: '東京',
na_siliconvalley: '硅谷美西',
na_ashburn: '弗吉尼亞美東',
na_toronto: '多倫多',
sa_saopaulo: '聖保羅',
eu_frankfurt: '法蘭克福',
KODO: '七牛雲 Kodo',
scType: '存儲類型',
typeStandard: '標準存儲',
@ -1176,7 +1198,6 @@ const message = {
scArchive: '歸檔存儲適用於極低訪問頻率例如半年訪問1次的業務場景',
scDeep_Archive: '深度歸檔存儲適用於極低訪問頻率例如1年訪問1~2的業務場景',
archiveHelper: '歸檔存儲的文件無法直接下載需要先在對應的雲服務商網站進行恢復操作請謹慎使用',
domainHelper: '加速域名必須包含 http:// 或者 https://',
backupAlert:
"理論上只要雲廠商兼容 S3 協議,就可以用現有的亞馬遜 S3 雲存儲來備份,具體配置參考 <a target=「_blank」 href='https://1panel.cn/docs/user_manual/settings/#3'>官方文檔</a> ",
domain: '加速域名',

View File

@ -1166,6 +1166,28 @@ const message = {
'请点击获取按钮然后登录 OneDrive 复制跳转链接中 code 后面的内容粘贴到该输入框中具体操作可参考官方文档',
loadCode: '获取',
COS: '腾讯云 COS',
ap_beijing_1: '北京一区',
ap_beijing: '北京',
ap_nanjing: '南京',
ap_shanghai: '上海',
ap_guangzhou: '广州',
ap_chengdu: '成都',
ap_chongqing: '重庆',
ap_shenzhen_fsi: '深圳金融',
ap_shanghai_fsi: '上海金融',
ap_beijing_fsi: '北京金融',
ap_hongkong: '中国香港',
ap_singapore: '新加坡',
ap_mumbai: '孟买',
ap_jakarta: '雅加达',
ap_seoul: '首尔',
ap_bangkok: '曼谷',
ap_tokyo: '东京',
na_siliconvalley: '硅谷美西',
na_ashburn: '弗吉尼亚美东',
na_toronto: '多伦多',
sa_saopaulo: '圣保罗',
eu_frankfurt: '法兰克福',
KODO: '七牛云 Kodo',
scType: '存储类型',
typeStandard: '标准存储',
@ -1177,7 +1199,6 @@ const message = {
scArchive: '归档存储适用于极低访问频率例如半年访问1次的业务场景',
scDeep_Archive: '深度归档存储适用于极低访问频率例如1年访问1~2的业务场景',
archiveHelper: '归档存储的文件无法直接下载需要先在对应的云服务商网站进行恢复操作请谨慎使用',
domainHelper: '加速域名必须包含 http:// 或者 https://',
backupAlert:
"理论上只要云厂商兼容 S3 协议,就可以用现有的亚马逊 S3 云存储来备份,具体配置参考 <a target=“_blank” href='https://1panel.cn/docs/user_manual/settings/#3'>官方文档</a> ",
domain: '加速域名',

View File

@ -370,6 +370,19 @@ export function transTimeUnit(val: string): any {
return val + i18n.global.t('commons.units.second');
}
export function splitHttp(url: string) {
if (url.indexOf('https://') != -1) {
return { proto: 'https', url: url.replaceAll('https://', '') };
}
if (url.indexOf('http://') != -1) {
return { proto: 'http', url: url.replaceAll('http://', '') };
}
return { proto: '', url: url };
}
export function spliceHttp(proto: string, url: string) {
return proto + '://' + url.replaceAll('https://', '').replaceAll('http://', '');
}
export function getAge(d1: string): string {
const dateBegin = new Date(d1);
const dateEnd = new Date();

View File

@ -0,0 +1,255 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form @submit.prevent ref="formRef" v-loading="loading" label-position="top" :model="cosData.rowData">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + cosData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item label="Access Key ID" prop="accessKey" :rules="Rules.requiredInput">
<el-input v-model.trim="cosData.rowData!.accessKey" />
</el-form-item>
<el-form-item label="Secret Key" prop="credential" :rules="Rules.requiredInput">
<el-input show-password clearable v-model.trim="cosData.rowData!.credential" />
</el-form-item>
<el-form-item label="Region" prop="varsJson.region" :rules="Rules.requiredInput">
<el-checkbox v-model="regionInput" :label="$t('container.input')" />
<el-select
v-if="!regionInput"
v-model="cosData.rowData!.varsJson['region']"
filterable
clearable
>
<el-option
v-for="item in cities"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="float: left">{{ item.label }}</span>
<span class="option-help">
{{ item.value }}
</span>
</el-option>
</el-select>
<el-input v-else v-model.trim="cosData.rowData!.varsJson['region']" />
</el-form-item>
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
<el-input v-model.trim="cosData.rowData!.varsJson['endpointItem']">
<template #prepend>
<el-select v-model.trim="endpointProto" style="width: 100px">
<el-option label="http" value="http" />
<el-option label="https" value="https" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item label="Bucket" prop="bucket">
<el-select
@change="errBuckets = false"
style="width: 80%"
v-model="cosData.rowData!.bucket"
>
<el-option v-for="item in buckets" :key="item" :value="item" />
</el-select>
<el-button style="width: 20%" plain @click="getBuckets(formRef)">
{{ $t('setting.loadBucket') }}
</el-button>
<span v-if="errBuckets" class="input-error">{{ $t('commons.rule.requiredSelect') }}</span>
</el-form-item>
<el-form-item
:label="$t('setting.scType')"
prop="varsJson.scType"
:rules="[Rules.requiredSelect]"
>
<el-select v-model="cosData.rowData!.varsJson['scType']">
<el-option value="Standard" :label="$t('setting.scStandard')" />
<el-option value="Standard_IA" :label="$t('setting.scStandard_IA')" />
<el-option value="Archive" :label="$t('setting.scArchive')" />
<el-option value="Deep_Archive" :label="$t('setting.scDeep_Archive')" />
</el-select>
<el-alert
v-if="cosData.rowData!.varsJson['scType'] === 'Archive' || cosData.rowData!.varsJson['scType'] === 'Deep_Archive'"
style="margin-top: 10px"
:closable="false"
type="warning"
:title="$t('setting.archiveHelper')"
/>
</el-form-item>
<el-form-item :label="$t('setting.backupDir')" prop="backupPath">
<el-input clearable v-model.trim="cosData.rowData!.backupPath" placeholder="/1panel" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup, listBucket } from '@/api/modules/setting';
import { deepCopy, spliceHttp, splitHttp } from '@/utils/util';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const buckets = ref();
const regionInput = ref();
const errBuckets = ref();
const endpointProto = ref('http');
const emit = defineEmits(['search']);
const cities = [
{ value: 'ap-beijing-1', label: i18n.global.t('setting.ap_beijing_1') },
{ value: 'ap-beijing', label: i18n.global.t('setting.ap_beijing') },
{ value: 'ap-nanjing', label: i18n.global.t('setting.ap_nanjing') },
{ value: 'ap-shanghai', label: i18n.global.t('setting.ap_shanghai') },
{ value: 'ap-guangzhou', label: i18n.global.t('setting.ap_guangzhou') },
{ value: 'ap-chengdu', label: i18n.global.t('setting.ap_chengdu') },
{ value: 'ap-chongqing', label: i18n.global.t('setting.ap_chongqing') },
{ value: 'ap-shenzhen_fsi', label: i18n.global.t('setting.ap_shenzhen_fsi') },
{ value: 'ap-shanghai_fsi', label: i18n.global.t('setting.ap_shanghai_fsi') },
{ value: 'ap-beijing_fsi', label: i18n.global.t('setting.ap_beijing_fsi') },
{ value: 'ap-hongkong', label: i18n.global.t('setting.ap_hongkong') },
{ value: 'ap-singapore', label: i18n.global.t('setting.ap_singapore') },
{ value: 'ap-mumbai', label: i18n.global.t('setting.ap_mumbai') },
{ value: 'ap-jakarta', label: i18n.global.t('setting.ap_jakarta') },
{ value: 'ap-seoul', label: i18n.global.t('setting.ap_seoul') },
{ value: 'ap-bangkok', label: i18n.global.t('setting.ap_bangkok') },
{ value: 'ap-tokyo', label: i18n.global.t('setting.ap_tokyo') },
{ value: 'na-siliconvalley', label: i18n.global.t('setting.na_siliconvalley') },
{ value: 'na-ashburn', label: i18n.global.t('setting.na_ashburn') },
{ value: 'na-toronto', label: i18n.global.t('setting.na_toronto') },
{ value: 'sa-saopaulo', label: i18n.global.t('setting.sa_saopaulo') },
{ value: 'eu-frankfurt', label: i18n.global.t('setting.eu_frankfurt') },
];
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const cosData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
buckets.value = [];
cosData.value = params;
if (params.title === 'create' || (params.title === 'edit' && !cosData.value.rowData.varsJson['scType'])) {
cosData.value.rowData.varsJson['scType'] = 'Standard';
}
if (cosData.value.title === 'edit') {
let httpItem = splitHttp(cosData.value.rowData!.varsJson['endpoint']);
cosData.value.rowData!.varsJson['endpointItem'] = httpItem.url;
endpointProto.value = httpItem.proto;
}
title.value = i18n.global.t('commons.button.' + cosData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const getBuckets = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
let item = deepCopy(cosData.value.rowData!.varsJson);
item['endpoint'] = spliceHttp(endpointProto.value, cosData.value.rowData!.varsJson['endpointItem']);
listBucket({
type: cosData.value.rowData!.type,
vars: JSON.stringify(item),
accessKey: cosData.value.rowData!.accessKey,
credential: cosData.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 (!cosData.value.rowData.bucket) {
errBuckets.value = true;
return;
}
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!cosData.value.rowData) return;
cosData.value.rowData!.varsJson['endpoint'] = spliceHttp(
endpointProto.value,
cosData.value.rowData!.varsJson['endpointItem'],
);
cosData.value.rowData.vars = JSON.stringify(cosData.value.rowData!.varsJson);
loading.value = true;
if (cosData.value.title === 'create') {
await addBackup(cosData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(cosData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>
<style scoped lang="scss">
.option-help {
float: right;
font-size: 12px;
word-break: break-all;
color: #8f959e;
}
</style>

View File

@ -2,7 +2,7 @@
<div>
<LayoutContent :title="$t('commons.button.backup')">
<template #main>
<el-form label-position="left" label-width="130px" :v-key="reflash">
<el-form label-width="130px" :v-key="refresh">
<el-row :gutter="20">
<el-col :span="24">
<div>
@ -414,8 +414,15 @@
</template>
</LayoutContent>
<s3Dialog ref="s3Ref" @search="search" />
<ossDialog ref="ossRef" @search="search" />
<cosDialog ref="cosRef" @search="search" />
<oneDriveDialog ref="oneDriveRef" @search="search" />
<kodoDialog ref="kodoRef" @search="search" />
<minioDialog ref="minioRef" @search="search" />
<sftpDialog ref="sftpRef" @search="search" />
<webDavDialog ref="webDavRef" @search="search" />
<OpDialog ref="opRef" @search="search" />
<DialogOperate ref="dialogRef" @search="search" />
</div>
</template>
<script setup lang="ts">
@ -423,14 +430,31 @@ import { dateFormat } from '@/utils/util';
import { onMounted, ref } from 'vue';
import OpDialog from '@/components/del-dialog/index.vue';
import { getBackupList, deleteBackup } from '@/api/modules/setting';
import DialogOperate from '@/views/setting/backup-account/operate/index.vue';
import s3Dialog from '@/views/setting/backup-account/s3/index.vue';
import ossDialog from '@/views/setting/backup-account/oss/index.vue';
import cosDialog from '@/views/setting/backup-account/cos/index.vue';
import oneDriveDialog from '@/views/setting/backup-account/onedrive/index.vue';
import kodoDialog from '@/views/setting/backup-account/kodo/index.vue';
import minioDialog from '@/views/setting/backup-account/minio/index.vue';
import sftpDialog from '@/views/setting/backup-account/sftp/index.vue';
import webDavDialog from '@/views/setting/backup-account/webdav/index.vue';
import { Backup } from '@/api/interface/backup';
import { ElForm } from 'element-plus';
import i18n from '@/lang';
const data = ref();
const opRef = ref();
const reflash = ref(false);
const refresh = ref(false);
const s3Ref = ref();
const ossRef = ref();
const cosRef = ref();
const oneDriveRef = ref();
const kodoRef = ref();
const minioRef = ref();
const sftpRef = ref();
const webDavRef = ref();
const localData = ref<Backup.BackupInfo>({
id: 0,
type: 'LOCAL',
@ -453,7 +477,6 @@ const ossData = ref<Backup.BackupInfo>({
backupPath: '',
vars: '',
varsJson: {
region: '',
endpoint: '',
scType: 'Standard',
},
@ -540,6 +563,7 @@ const cosData = ref<Backup.BackupInfo>({
varsJson: {
region: '',
scType: 'Standard',
endpoint: '',
},
createdAt: new Date(),
});
@ -609,7 +633,6 @@ const onDelete = async (row: Backup.BackupInfo) => {
});
};
const dialogRef = ref();
const onOpenDialog = async (
title: string,
accountType: string,
@ -623,7 +646,32 @@ const onOpenDialog = async (
title,
rowData: { ...rowData },
};
dialogRef.value!.acceptParams(params);
switch (accountType) {
case 'S3':
s3Ref.value.acceptParams(params);
return;
case 'OSS':
ossRef.value.acceptParams(params);
return;
case 'COS':
cosRef.value.acceptParams(params);
return;
case 'OneDrive':
oneDriveRef.value.acceptParams(params);
return;
case 'KODO':
kodoRef.value.acceptParams(params);
return;
case 'MINIO':
minioRef.value.acceptParams(params);
return;
case 'SFTP':
sftpRef.value.acceptParams(params);
return;
case 'WebDAV':
webDavRef.value.acceptParams(params);
return;
}
};
onMounted(() => {

View File

@ -0,0 +1,180 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form @submit.prevent ref="formRef" v-loading="loading" label-position="top" :model="kodoData.rowData">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + kodoData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item label="Access Key ID" prop="accessKey" :rules="Rules.requiredInput">
<el-input v-model.trim="kodoData.rowData!.accessKey" />
</el-form-item>
<el-form-item label="Secret Key" prop="credential" :rules="Rules.requiredInput">
<el-input show-password clearable v-model.trim="kodoData.rowData!.credential" />
</el-form-item>
<el-form-item
:label="$t('setting.domain')"
prop="varsJson.domainItem"
:rules="Rules.requiredInput"
>
<el-input v-model="kodoData.rowData!.varsJson['domainItem']">
<template #prepend>
<el-select v-model.trim="domainProto" style="width: 100px">
<el-option label="http" value="http" />
<el-option label="https" value="https" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item label="Bucket" prop="bucket">
<el-select
@change="errBuckets = false"
style="width: 80%"
v-model="kodoData.rowData!.bucket"
>
<el-option v-for="item in buckets" :key="item" :value="item" />
</el-select>
<el-button style="width: 20%" plain @click="getBuckets(formRef)">
{{ $t('setting.loadBucket') }}
</el-button>
<span v-if="errBuckets" class="input-error">{{ $t('commons.rule.requiredSelect') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.backupDir')" prop="backupPath">
<el-input clearable v-model.trim="kodoData.rowData!.backupPath" placeholder="/1panel" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup, listBucket } from '@/api/modules/setting';
import { deepCopy, spliceHttp, splitHttp } from '@/utils/util';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const buckets = ref();
const errBuckets = ref();
const domainProto = ref('http');
const emit = defineEmits(['search']);
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const kodoData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
buckets.value = [];
kodoData.value = params;
if (kodoData.value.title === 'edit') {
let httpItem = splitHttp(kodoData.value.rowData!.varsJson['domain']);
kodoData.value.rowData!.varsJson['domainItem'] = httpItem.url;
domainProto.value = httpItem.proto;
}
title.value = i18n.global.t('commons.button.' + kodoData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const getBuckets = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
let item = deepCopy(kodoData.value.rowData!.varsJson);
item['domain'] = spliceHttp(domainProto.value, kodoData.value.rowData!.varsJson['domainItem']);
listBucket({
type: kodoData.value.rowData!.type,
vars: JSON.stringify(item),
accessKey: kodoData.value.rowData!.accessKey,
credential: kodoData.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 (!kodoData.value.rowData.bucket) {
errBuckets.value = true;
return;
}
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!kodoData.value.rowData) return;
kodoData.value.rowData!.varsJson['domain'] = spliceHttp(
domainProto.value,
kodoData.value.rowData!.varsJson['domainItem'],
);
kodoData.value.rowData.vars = JSON.stringify(kodoData.value.rowData!.varsJson);
loading.value = true;
if (kodoData.value.title === 'create') {
await addBackup(kodoData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(kodoData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -0,0 +1,178 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form @submit.prevent ref="formRef" v-loading="loading" label-position="top" :model="minioData.rowData">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + minioData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item label="Access Key ID" prop="accessKey" :rules="Rules.requiredInput">
<el-input v-model.trim="minioData.rowData!.accessKey" />
</el-form-item>
<el-form-item label="Secret Key" prop="credential" :rules="Rules.requiredInput">
<el-input show-password clearable v-model.trim="minioData.rowData!.credential" />
</el-form-item>
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
<el-input v-model="minioData.rowData!.varsJson['endpointItem']">
<template #prepend>
<el-select v-model.trim="endpointProto" style="width: 100px">
<el-option label="http" value="http" />
<el-option label="https" value="https" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item label="Bucket" prop="bucket">
<el-select
style="width: 80%"
@change="errBuckets = false"
v-model="minioData.rowData!.bucket"
>
<el-option v-for="item in buckets" :key="item" :value="item" />
</el-select>
<el-button style="width: 20%" plain @click="getBuckets(formRef)">
{{ $t('setting.loadBucket') }}
</el-button>
<span v-if="errBuckets" class="input-error">{{ $t('commons.rule.requiredSelect') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.backupDir')" prop="backupPath">
<el-input clearable v-model.trim="minioData.rowData!.backupPath" placeholder="/1panel" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup, listBucket } from '@/api/modules/setting';
import { deepCopy, splitHttp, spliceHttp } from '@/utils/util';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const buckets = ref();
const errBuckets = ref();
const endpointProto = ref('http');
const emit = defineEmits(['search']);
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const minioData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
buckets.value = [];
minioData.value = params;
if (minioData.value.title === 'edit') {
let httpItem = splitHttp(minioData.value.rowData!.varsJson['endpoint']);
minioData.value.rowData!.varsJson['endpointItem'] = httpItem.url;
endpointProto.value = httpItem.proto;
}
title.value = i18n.global.t('commons.button.' + minioData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const getBuckets = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
let item = deepCopy(minioData.value.rowData!.varsJson);
item['endpoint'] = spliceHttp(endpointProto.value, minioData.value.rowData!.varsJson['endpointItem']);
item['endpointItem'] = undefined;
listBucket({
type: minioData.value.rowData!.type,
vars: JSON.stringify(item),
accessKey: minioData.value.rowData!.accessKey,
credential: minioData.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 (!minioData.value.rowData.bucket) {
errBuckets.value = true;
return;
}
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!minioData.value.rowData) return;
minioData.value.rowData!.varsJson['endpoint'] = spliceHttp(
endpointProto.value,
minioData.value.rowData!.varsJson['endpointItem'],
);
minioData.value.rowData!.varsJson['endpointItem'] = undefined;
minioData.value.rowData.vars = JSON.stringify(minioData.value.rowData!.varsJson);
loading.value = true;
if (minioData.value.title === 'create') {
await addBackup(minioData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(minioData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -0,0 +1,175 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form
@submit.prevent
ref="formRef"
v-loading="loading"
label-position="top"
:model="onedriveData.rowData"
>
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + onedriveData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item>
<el-checkbox
disabled
v-model="onedriveData.rowData!.varsJson['isCN']"
:label="$t('setting.isCN')"
/>
</el-form-item>
<el-form-item :label="$t('setting.code')" prop="varsJson.code" :rules="rules.driveCode">
<div style="width: 100%">
<el-input
style="width: calc(100% - 80px)"
:autosize="{ minRows: 3, maxRows: 15 }"
type="textarea"
clearable
v-model.trim="onedriveData.rowData!.varsJson['code']"
/>
<el-button class="append-button" @click="jumpAzure">
{{ $t('setting.loadCode') }}
</el-button>
</div>
<span class="input-help">
{{ $t('setting.codeHelper') }}
<el-link
style="font-size: 12px; margin-left: 5px"
icon="Position"
@click="toDoc()"
type="primary"
>
{{ $t('firewall.quickJump') }}
</el-link>
</span>
</el-form-item>
<el-form-item :label="$t('setting.backupDir')" prop="backupPath">
<el-input clearable v-model.trim="onedriveData.rowData!.backupPath" placeholder="/1panel" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup, getOneDriveInfo } from '@/api/modules/setting';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const rules = reactive({
driveCode: [{ validator: checkDriveCode, required: true, trigger: 'blur' }],
});
function checkDriveCode(rule: any, value: any, callback: any) {
const reg = /^[A-Za-z0-9_.-]+$/;
if (!reg.test(value)) {
return callback(new Error(i18n.global.t('setting.codeWarning')));
}
callback();
}
const emit = defineEmits(['search']);
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const onedriveData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
onedriveData.value = params;
title.value = i18n.global.t('commons.button.' + onedriveData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const jumpAzure = async () => {
const res = await getOneDriveInfo();
let commonUrl = `response_type=code&client_id=${res.data}&redirect_uri=http://localhost/login/authorized&scope=offline_access+Files.ReadWrite.All+User.Read`;
if (!onedriveData.value.rowData!.varsJson['isCN']) {
window.open('https://login.microsoftonline.com/common/oauth2/v2.0/authorize?' + commonUrl, '_blank');
} else {
window.open('https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize?' + commonUrl, '_blank');
}
};
const toDoc = () => {
window.open('https://1panel.cn/docs/user_manual/settings/', '_blank', 'noopener,noreferrer');
};
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!onedriveData.value.rowData) return;
onedriveData.value.rowData.vars = JSON.stringify(onedriveData.value.rowData!.varsJson);
loading.value = true;
if (onedriveData.value.title === 'create') {
await addBackup(onedriveData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(onedriveData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>
<style lang="scss" scoped>
.append-button {
width: 80px;
background-color: var(--el-fill-color-light);
color: var(--el-color-info);
}
</style>

View File

@ -1,460 +0,0 @@
<template>
<div v-loading="loading">
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form
@submit.prevent
ref="formRef"
v-loading="loading"
label-position="top"
:model="dialogData.rowData"
label-width="120px"
>
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + dialogData.rowData!.type) }}</el-tag>
</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 #prepend>
<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.trim="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 clearable v-model.trim="dialogData.rowData!.credential" />
</el-form-item>
<el-form-item v-if="dialogData.rowData!.type === 'OneDrive'">
<el-checkbox
disabled
v-model="dialogData.rowData!.varsJson['isCN']"
:label="$t('setting.isCN')"
/>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'OneDrive'"
:label="$t('setting.code')"
prop="varsJson.code"
:rules="rules.driveCode"
>
<div style="width: 100%">
<el-input
style="width: calc(100% - 80px)"
:autosize="{ minRows: 3, maxRows: 15 }"
type="textarea"
clearable
v-model.trim="dialogData.rowData!.varsJson['code']"
/>
<el-button class="append-button" @click="jumpAzure">
{{ $t('setting.loadCode') }}
</el-button>
</div>
<span class="input-help">
{{ $t('setting.codeHelper') }}
<el-link
style="font-size: 12px; margin-left: 5px"
icon="Position"
@click="toDoc()"
type="primary"
>
{{ $t('firewall.quickJump') }}
</el-link>
</span>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'S3' || dialogData.rowData!.type === 'COS'"
label="Region"
prop="varsJson.region"
:rules="Rules.requiredInput"
>
<el-input v-model.trim="dialogData.rowData!.varsJson['region']" />
</el-form-item>
<el-form-item
v-if="hasEndpoint(dialogData.rowData!.type)"
label="Endpoint"
prop="varsJson.endpoint"
:rules="Rules.requiredInput"
>
<el-input v-model.trim="dialogData.rowData!.varsJson['endpoint']" />
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'KODO'"
:label="$t('setting.domain')"
prop="varsJson.domain"
:rules="Rules.requiredInput"
>
<el-input v-model.trim="dialogData.rowData!.varsJson['domain']" />
<span class="input-help">{{ $t('setting.domainHelper') }}</span>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'MINIO'"
label="Endpoint"
prop="varsJson.endpointItem"
:rules="Rules.requiredInput"
>
<el-input v-model="dialogData.rowData!.varsJson['endpointItem']">
<template #prepend>
<el-select v-model.trim="endpoints" style="width: 80px">
<el-option label="http" value="http" />
<el-option label="https" value="https" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type !== '' && hasBucket(dialogData.rowData!.type)"
label="Bucket"
prop="bucket"
>
<el-select
style="width: 80%"
@change="errBuckets = false"
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(formRef)">
{{ $t('setting.loadBucket') }}
</el-button>
<span v-if="errBuckets" class="input-error">{{ $t('commons.rule.requiredSelect') }}</span>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'COS'"
:label="$t('setting.scType')"
prop="varsJson.scType"
:rules="[Rules.requiredSelect]"
>
<el-select v-model="dialogData.rowData!.varsJson['scType']">
<el-option value="Standard" :label="$t('setting.scStandard')" />
<el-option value="Standard_IA" :label="$t('setting.scStandard_IA')" />
<el-option value="Archive" :label="$t('setting.scArchive')" />
<el-option value="Deep_Archive" :label="$t('setting.scDeep_Archive')" />
</el-select>
<el-alert
v-if="dialogData.rowData!.varsJson['scType'] === 'Archive' || dialogData.rowData!.varsJson['scType'] === 'Deep_Archive'"
style="margin-top: 10px"
:closable="false"
type="warning"
:title="$t('setting.archiveHelper')"
/>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'OSS'"
:label="$t('setting.scType')"
prop="varsJson.scType"
:rules="[Rules.requiredSelect]"
>
<el-select v-model="dialogData.rowData!.varsJson['scType']">
<el-option value="Standard" :label="$t('setting.scStandard')" />
<el-option value="IA" :label="$t('setting.scStandard_IA')" />
<el-option value="Archive" :label="$t('setting.scArchive')" />
<el-option value="ColdArchive" :label="$t('setting.scDeep_Archive')" />
</el-select>
<el-alert
v-if="dialogData.rowData!.varsJson['scType'] === 'Archive' || dialogData.rowData!.varsJson['scType'] === 'ColdArchive'"
style="margin-top: 10px"
:closable="false"
type="warning"
:title="$t('setting.archiveHelper')"
/>
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'S3'"
:label="$t('setting.scType')"
prop="varsJson.scType"
:rules="[Rules.requiredSelect]"
>
<el-select v-model="dialogData.rowData!.varsJson['scType']">
<el-option value="STANDARD" :label="$t('setting.scStandard')" />
<el-option value="STANDARD_IA" :label="$t('setting.scStandard_IA')" />
<el-option value="GLACIER" :label="$t('setting.scArchive')" />
<el-option value="DEEP_ARCHIVE" :label="$t('setting.scDeep_Archive')" />
</el-select>
<el-alert
v-if="dialogData.rowData!.varsJson['scType'] === 'Archive' || dialogData.rowData!.varsJson['scType'] === 'ColdArchive'"
style="margin-top: 10px"
:closable="false"
type="warning"
:title="$t('setting.archiveHelper')"
/>
</el-form-item>
<div v-if="isSftpOrWebDAV(dialogData.rowData!.type)">
<el-form-item
v-if="dialogData.rowData!.type === 'SFTP'"
:label="$t('setting.address')"
prop="varsJson.address"
:rules="Rules.host"
>
<el-input v-model.trim="dialogData.rowData!.varsJson['address']" />
</el-form-item>
<el-form-item
v-if="dialogData.rowData!.type === 'WebDAV'"
:label="$t('setting.address')"
prop="varsJson.address"
:rules="Rules.requiredInput"
>
<el-input v-model.trim="dialogData.rowData!.varsJson['address']" />
<span class="input-help">http://172.16.10.111</span>
</el-form-item>
<el-form-item :label="$t('commons.table.port')" prop="varsJson.port" :rules="[Rules.port]">
<el-input-number
:min="0"
:max="65535"
v-model.number="dialogData.rowData!.varsJson['port']"
/>
</el-form-item>
<el-form-item
:label="$t('commons.login.username')"
prop="accessKey"
:rules="[Rules.requiredInput]"
>
<el-input v-model.trim="dialogData.rowData!.accessKey" />
</el-form-item>
<el-form-item
:label="$t('commons.login.password')"
prop="credential"
:rules="[Rules.requiredInput]"
>
<el-input
type="password"
clearable
show-password
v-model.trim="dialogData.rowData!.credential"
/>
</el-form-item>
<el-form-item :label="$t('setting.path')" prop="bucket" :rules="[Rules.requiredInput]">
<el-input v-model.trim="dialogData.rowData!.bucket" />
</el-form-item>
</div>
<el-form-item
v-if="dialogData.rowData!.type !== 'LOCAL' && !isSftpOrWebDAV(dialogData.rowData!.type)"
:label="$t('setting.backupDir')"
prop="backupPath"
>
<el-input clearable v-model.trim="dialogData.rowData!.backupPath" placeholder="/1panel" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { Rules } from '@/global/form-rules';
import FileList from '@/components/file-list/index.vue';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup, getOneDriveInfo, listBucket } from '@/api/modules/setting';
import { deepCopy } from '@/utils/util';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const buckets = ref();
const errBuckets = ref();
const endpoints = ref('http');
const rules = reactive({
driveCode: [{ validator: checkDriveCode, required: true, trigger: 'blur' }],
});
function checkDriveCode(rule: any, value: any, callback: any) {
const reg = /^[A-Za-z0-9_.-]+$/;
if (!reg.test(value)) {
return callback(new Error(i18n.global.t('setting.codeWarning')));
}
callback();
}
const emit = defineEmits<{ (e: 'search'): void }>();
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
getTableList?: () => Promise<any>;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const dialogData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
buckets.value = [];
dialogData.value = params;
if (dialogData.value.title === 'edit' && dialogData.value.rowData!.type === 'MINIO') {
if (dialogData.value.rowData!.varsJson['endpoint'].indexOf('://') !== 0) {
endpoints.value = dialogData.value.rowData!.varsJson['endpoint'].split('://')[0];
dialogData.value.rowData!.varsJson['endpointItem'] =
dialogData.value.rowData!.varsJson['endpoint'].split('://')[1];
}
}
if (dialogData.value.title === 'create' && dialogData.value.rowData!.type === 'SFTP') {
dialogData.value.rowData.varsJson['port'] = 22;
}
if (dialogData.value.rowData!.type === 'COS' || dialogData.value.rowData!.type === 'OSS') {
if (params.title === 'create' || (params.title === 'edit' && !dialogData.value.rowData.varsJson['scType'])) {
dialogData.value.rowData.varsJson['scType'] = 'Standard';
}
}
if (dialogData.value.rowData!.type === 'S3') {
if (params.title === 'create' || (params.title === 'edit' && !dialogData.value.rowData.varsJson['scType'])) {
dialogData.value.rowData.varsJson['scType'] = 'STANDARD';
}
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const jumpAzure = async () => {
const res = await getOneDriveInfo();
let commonUrl = `response_type=code&client_id=${res.data}&redirect_uri=http://localhost/login/authorized&scope=offline_access+Files.ReadWrite.All+User.Read`;
if (!dialogData.value.rowData!.varsJson['isCN']) {
window.open('https://login.microsoftonline.com/common/oauth2/v2.0/authorize?' + commonUrl, '_blank');
} else {
window.open('https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize?' + commonUrl, '_blank');
}
};
const loadDir = async (path: string) => {
dialogData.value.rowData!.varsJson['dir'] = path;
};
function hasBucket(val: string) {
return val === 'OSS' || val === 'S3' || val === 'MINIO' || val === 'COS' || val === 'KODO';
}
function hasEndpoint(val: string) {
return val === 'OSS' || val === 'S3';
}
function isSftpOrWebDAV(val: string) {
return val === 'SFTP' || val === 'WebDAV';
}
const toDoc = () => {
window.open('https://1panel.cn/docs/user_manual/settings/', '_blank', 'noopener,noreferrer');
};
const getBuckets = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
let item = deepCopy(dialogData.value.rowData!.varsJson);
if (dialogData.value.rowData!.type === 'MINIO') {
dialogData.value.rowData!.varsJson['endpointItem'] = dialogData.value
.rowData!.varsJson['endpointItem'].replace('https://', '')
.replace('http://', '');
item['endpoint'] = endpoints.value + '://' + dialogData.value.rowData!.varsJson['endpointItem'];
item['endpointItem'] = undefined;
}
listBucket({
type: dialogData.value.rowData!.type,
vars: JSON.stringify(item),
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 (hasBucket(dialogData.value.rowData.type) && !dialogData.value.rowData.bucket) {
errBuckets.value = true;
return;
}
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!dialogData.value.rowData) return;
if (dialogData.value.rowData!.type === 'MINIO') {
dialogData.value.rowData!.varsJson['endpointItem'].replace('https://', '').replace('http://', '');
dialogData.value.rowData!.varsJson['endpoint'] =
endpoints.value + '://' + dialogData.value.rowData!.varsJson['endpointItem'];
dialogData.value.rowData!.varsJson['endpointItem'] = undefined;
}
dialogData.value.rowData.vars = JSON.stringify(dialogData.value.rowData!.varsJson);
loading.value = true;
if (dialogData.value.title === 'create') {
await addBackup(dialogData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(dialogData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>
<style lang="scss" scoped>
.append-button {
width: 80px;
background-color: var(--el-fill-color-light);
color: var(--el-color-info);
}
</style>

View File

@ -0,0 +1,198 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form @submit.prevent ref="formRef" v-loading="loading" label-position="top" :model="ossData.rowData">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + ossData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item label="Access Key ID" prop="accessKey" :rules="Rules.requiredInput">
<el-input v-model.trim="ossData.rowData!.accessKey" />
</el-form-item>
<el-form-item label="Secret Key" prop="credential" :rules="Rules.requiredInput">
<el-input show-password clearable v-model.trim="ossData.rowData!.credential" />
</el-form-item>
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
<el-input v-model="ossData.rowData!.varsJson['endpointItem']">
<template #prepend>
<el-select v-model.trim="endpointProto" style="width: 100px">
<el-option label="http" value="http" />
<el-option label="https" value="https" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item label="Bucket" prop="bucket">
<el-select
@change="errBuckets = false"
style="width: 80%"
v-model="ossData.rowData!.bucket"
>
<el-option v-for="item in buckets" :key="item" :value="item" />
</el-select>
<el-button style="width: 20%" plain @click="getBuckets(formRef)">
{{ $t('setting.loadBucket') }}
</el-button>
<span v-if="errBuckets" class="input-error">{{ $t('commons.rule.requiredSelect') }}</span>
</el-form-item>
<el-form-item
:label="$t('setting.scType')"
prop="varsJson.scType"
:rules="[Rules.requiredSelect]"
>
<el-select v-model="ossData.rowData!.varsJson['scType']">
<el-option value="Standard" :label="$t('setting.scStandard')" />
<el-option value="IA" :label="$t('setting.scStandard_IA')" />
<el-option value="Archive" :label="$t('setting.scArchive')" />
<el-option value="ColdArchive" :label="$t('setting.scDeep_Archive')" />
</el-select>
<el-alert
v-if="ossData.rowData!.varsJson['scType'] === 'Archive' || ossData.rowData!.varsJson['scType'] === 'ColdArchive'"
style="margin-top: 10px"
:closable="false"
type="warning"
:title="$t('setting.archiveHelper')"
/>
</el-form-item>
<el-form-item :label="$t('setting.backupDir')" prop="backupPath">
<el-input clearable v-model.trim="ossData.rowData!.backupPath" placeholder="/1panel" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup, listBucket } from '@/api/modules/setting';
import { deepCopy, spliceHttp, splitHttp } from '@/utils/util';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const buckets = ref();
const errBuckets = ref();
const endpointProto = ref('http');
const emit = defineEmits(['search']);
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const ossData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
buckets.value = [];
ossData.value = params;
if (params.title === 'create' || (params.title === 'edit' && !ossData.value.rowData.varsJson['scType'])) {
ossData.value.rowData.varsJson['scType'] = 'Standard';
}
if (ossData.value.title === 'edit') {
let httpItem = splitHttp(ossData.value.rowData!.varsJson['endpoint']);
ossData.value.rowData!.varsJson['endpointItem'] = httpItem.url;
endpointProto.value = httpItem.proto;
}
title.value = i18n.global.t('commons.button.' + ossData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const getBuckets = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
let item = deepCopy(ossData.value.rowData!.varsJson);
item['endpoint'] = spliceHttp(endpointProto.value, ossData.value.rowData!.varsJson['endpointItem']);
listBucket({
type: ossData.value.rowData!.type,
vars: JSON.stringify(item),
accessKey: ossData.value.rowData!.accessKey,
credential: ossData.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 (!ossData.value.rowData.bucket) {
errBuckets.value = true;
return;
}
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!ossData.value.rowData) return;
ossData.value.rowData!.varsJson['endpoint'] = spliceHttp(
endpointProto.value,
ossData.value.rowData!.varsJson['endpointItem'],
);
ossData.value.rowData.vars = JSON.stringify(ossData.value.rowData!.varsJson);
loading.value = true;
if (ossData.value.title === 'create') {
await addBackup(ossData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(ossData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -0,0 +1,197 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form @submit.prevent ref="formRef" v-loading="loading" label-position="top" :model="s3Data.rowData">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + s3Data.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item label="Access Key ID" prop="accessKey" :rules="Rules.requiredInput">
<el-input v-model.trim="s3Data.rowData!.accessKey" />
</el-form-item>
<el-form-item label="Secret Key" prop="credential" :rules="Rules.requiredInput">
<el-input show-password clearable v-model.trim="s3Data.rowData!.credential" />
</el-form-item>
<el-form-item label="Region" prop="varsJson.region" :rules="Rules.requiredInput">
<el-input pro v-model.trim="s3Data.rowData!.varsJson['region']" />
</el-form-item>
<el-form-item label="Endpoint" prop="varsJson.endpointItem" :rules="Rules.requiredInput">
<el-input v-model="s3Data.rowData!.varsJson['endpointItem']">
<template #prepend>
<el-select v-model.trim="endpointProto" style="width: 100px">
<el-option label="http" value="http" />
<el-option label="https" value="https" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item label="Bucket" prop="bucket">
<el-select @change="errBuckets = false" style="width: 80%" v-model="s3Data.rowData!.bucket">
<el-option v-for="item in buckets" :key="item" :value="item" />
</el-select>
<el-button style="width: 20%" plain @click="getBuckets(formRef)">
{{ $t('setting.loadBucket') }}
</el-button>
<span v-if="errBuckets" class="input-error">{{ $t('commons.rule.requiredSelect') }}</span>
</el-form-item>
<el-form-item
:label="$t('setting.scType')"
prop="varsJson.scType"
:rules="[Rules.requiredSelect]"
>
<el-select v-model="s3Data.rowData!.varsJson['scType']">
<el-option value="STANDARD" :label="$t('setting.scStandard')" />
<el-option value="STANDARD_IA" :label="$t('setting.scStandard_IA')" />
<el-option value="GLACIER" :label="$t('setting.scArchive')" />
<el-option value="DEEP_ARCHIVE" :label="$t('setting.scDeep_Archive')" />
</el-select>
<el-alert
v-if="s3Data.rowData!.varsJson['scType'] === 'Archive' || s3Data.rowData!.varsJson['scType'] === 'ColdArchive'"
style="margin-top: 10px"
:closable="false"
type="warning"
:title="$t('setting.archiveHelper')"
/>
</el-form-item>
<el-form-item :label="$t('setting.backupDir')" prop="backupPath">
<el-input clearable v-model.trim="s3Data.rowData!.backupPath" placeholder="/1panel" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup, listBucket } from '@/api/modules/setting';
import { deepCopy, spliceHttp, splitHttp } from '@/utils/util';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const buckets = ref();
const errBuckets = ref();
const endpointProto = ref('http');
const emit = defineEmits(['search']);
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const s3Data = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
buckets.value = [];
s3Data.value = params;
if (params.title === 'create' || (params.title === 'edit' && !s3Data.value.rowData.varsJson['scType'])) {
s3Data.value.rowData.varsJson['scType'] = 'STANDARD';
}
if (s3Data.value.title === 'edit') {
let httpItem = splitHttp(s3Data.value.rowData!.varsJson['endpoint']);
s3Data.value.rowData!.varsJson['endpointItem'] = httpItem.url;
endpointProto.value = httpItem.proto;
}
title.value = i18n.global.t('commons.button.' + s3Data.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const getBuckets = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
let item = deepCopy(s3Data.value.rowData!.varsJson);
item['endpoint'] = spliceHttp(endpointProto.value, s3Data.value.rowData!.varsJson['endpointItem']);
listBucket({
type: s3Data.value.rowData!.type,
vars: JSON.stringify(item),
accessKey: s3Data.value.rowData!.accessKey,
credential: s3Data.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 (!s3Data.value.rowData.bucket) {
errBuckets.value = true;
return;
}
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!s3Data.value.rowData) return;
s3Data.value.rowData!.varsJson['endpoint'] = spliceHttp(
endpointProto.value,
s3Data.value.rowData!.varsJson['endpointItem'],
);
s3Data.value.rowData.vars = JSON.stringify(s3Data.value.rowData!.varsJson);
loading.value = true;
if (s3Data.value.title === 'create') {
await addBackup(s3Data.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(s3Data.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -0,0 +1,138 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form @submit.prevent ref="formRef" v-loading="loading" label-position="top" :model="sftpData.rowData">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + sftpData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item :label="$t('setting.address')" prop="varsJson.address" :rules="Rules.host">
<el-input v-model.trim="sftpData.rowData!.varsJson['address']" />
</el-form-item>
<el-form-item :label="$t('commons.table.port')" prop="varsJson.port" :rules="[Rules.port]">
<el-input-number
:min="0"
:max="65535"
v-model.number="sftpData.rowData!.varsJson['port']"
/>
</el-form-item>
<el-form-item
:label="$t('commons.login.username')"
prop="accessKey"
:rules="[Rules.requiredInput]"
>
<el-input v-model.trim="sftpData.rowData!.accessKey" />
</el-form-item>
<el-form-item
:label="$t('commons.login.password')"
prop="credential"
:rules="[Rules.requiredInput]"
>
<el-input
type="password"
clearable
show-password
v-model.trim="sftpData.rowData!.credential"
/>
</el-form-item>
<el-form-item :label="$t('setting.path')" prop="bucket" :rules="[Rules.requiredInput]">
<el-input v-model.trim="sftpData.rowData!.bucket" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup } from '@/api/modules/setting';
import { MsgSuccess } from '@/utils/message';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const emit = defineEmits(['search']);
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
getTableList?: () => Promise<any>;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const sftpData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
sftpData.value = params;
if (sftpData.value.title === 'create') {
sftpData.value.rowData.varsJson['port'] = 22;
}
title.value = i18n.global.t('commons.button.' + sftpData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!sftpData.value.rowData) return;
sftpData.value.rowData.vars = JSON.stringify(sftpData.value.rowData!.varsJson);
loading.value = true;
if (sftpData.value.title === 'create') {
await addBackup(sftpData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(sftpData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -0,0 +1,156 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template>
<el-form @submit.prevent ref="formRef" v-loading="loading" label-position="top" :model="webdavData.rowData">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
<el-tag>{{ $t('setting.' + webdavData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item
:label="$t('setting.address')"
prop="varsJson.address"
:rules="Rules.requiredInput"
>
<el-input v-model="webdavData.rowData!.varsJson['addressItem']">
<template #prepend>
<el-select v-model.trim="addressProto" style="width: 100px">
<el-option label="http" value="http" />
<el-option label="https" value="https" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('commons.table.port')" prop="varsJson.port" :rules="[Rules.port]">
<el-input-number
:min="0"
:max="65535"
v-model.number="webdavData.rowData!.varsJson['port']"
/>
</el-form-item>
<el-form-item
:label="$t('commons.login.username')"
prop="accessKey"
:rules="[Rules.requiredInput]"
>
<el-input v-model.trim="webdavData.rowData!.accessKey" />
</el-form-item>
<el-form-item
:label="$t('commons.login.password')"
prop="credential"
:rules="[Rules.requiredInput]"
>
<el-input
type="password"
clearable
show-password
v-model.trim="webdavData.rowData!.credential"
/>
</el-form-item>
<el-form-item :label="$t('setting.path')" prop="bucket" :rules="[Rules.requiredInput]">
<el-input v-model.trim="webdavData.rowData!.bucket" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="handleClose">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { Backup } from '@/api/interface/backup';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { addBackup, editBackup } from '@/api/modules/setting';
import { MsgSuccess } from '@/utils/message';
import { spliceHttp, splitHttp } from '@/utils/util';
const loading = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
const addressProto = ref('http');
const emit = defineEmits(['search']);
interface DialogProps {
title: string;
rowData?: Backup.BackupInfo;
}
const title = ref<string>('');
const drawerVisible = ref(false);
const webdavData = ref<DialogProps>({
title: '',
});
const acceptParams = (params: DialogProps): void => {
webdavData.value = params;
if (webdavData.value.title === 'edit') {
let httpItem = splitHttp(webdavData.value.rowData!.varsJson['address']);
webdavData.value.rowData!.varsJson['addressItem'] = httpItem.url;
addressProto.value = httpItem.proto;
}
title.value = i18n.global.t('commons.button.' + webdavData.value.title);
drawerVisible.value = true;
};
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!webdavData.value.rowData) return;
webdavData.value.rowData!.varsJson['address'] = spliceHttp(
addressProto.value,
webdavData.value.rowData!.varsJson['addressItem'],
);
webdavData.value.rowData.vars = JSON.stringify(webdavData.value.rowData!.varsJson);
loading.value = true;
if (webdavData.value.title === 'create') {
await addBackup(webdavData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
return;
}
await editBackup(webdavData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>