344 lines
13 KiB
Vue
344 lines
13 KiB
Vue
<template>
|
|
<el-card style="margin-top: 20px">
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span>{{ $t('setting.backup') }}</span>
|
|
</div>
|
|
</template>
|
|
<el-button type="primary" icon="Plus" @click="onCreate">
|
|
{{ $t('commons.button.create') }}
|
|
</el-button>
|
|
<el-row :gutter="20" class="row-box">
|
|
<el-col v-for="item in data" :key="item.id" :span="8" style="margin-top: 20px">
|
|
<el-card class="el-card">
|
|
<template #header>
|
|
<div class="card-header">
|
|
<svg-icon style="font-size: 7px" :iconName="loadIconName(item.type)"></svg-icon>
|
|
<span style="font-size: 16px; font-weight: 500"> {{ loadBackupName(item.type) }}</span>
|
|
<div style="float: right">
|
|
<el-button @click="onEdit(item)">{{ $t('commons.button.edit') }}</el-button>
|
|
<el-button v-if="item.type !== 'LOCAL'" @click="onBatchDelete(item)">
|
|
{{ $t('commons.button.delete') }}
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<el-form label-position="left" label-width="130px">
|
|
<el-form-item v-if="item.type === 'LOCAL'" :label="$t('setting.currentPath')">
|
|
{{ item.varsJson['dir'] }}
|
|
</el-form-item>
|
|
<el-form-item v-if="item.type === 'S3'" label="Region">
|
|
{{ item.varsJson['region'] }}
|
|
</el-form-item>
|
|
<el-form-item v-if="hasBucket(item.type)" label="Endpoint">
|
|
{{ item.varsJson['endpoint'] }}
|
|
</el-form-item>
|
|
<el-form-item v-if="hasBucket(item.type)" label="Bucket">
|
|
{{ item.bucket }}
|
|
</el-form-item>
|
|
<el-form-item v-if="item.type === 'SFTP'" :label="$t('setting.address')">
|
|
{{ item.varsJson['address'] }}
|
|
</el-form-item>
|
|
<el-form-item v-if="item.type === 'SFTP'" :label="$t('setting.port')">
|
|
{{ item.varsJson['port'] }}
|
|
</el-form-item>
|
|
<el-form-item v-if="item.type === 'SFTP'" :label="$t('setting.username')">
|
|
{{ item.varsJson['username'] }}
|
|
</el-form-item>
|
|
<el-form-item :label="$t('commons.table.createdAt')">
|
|
{{ dateFromat(0, 0, item.createdAt) }}
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-dialog
|
|
@close="search"
|
|
v-model="backupVisiable"
|
|
:destroy-on-close="true"
|
|
:title="$t('setting.backupAccount')"
|
|
width="30%"
|
|
>
|
|
<div v-loading="loading">
|
|
<el-form ref="formRef" label-position="left" :model="form" label-width="160px">
|
|
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
|
|
<el-select style="width: 100%" v-model="form.type" :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 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 = true;
|
|
buckets.value = res.data;
|
|
})
|
|
.finally(() => {
|
|
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>
|