mirror of
https://github.com/rustfs/console.git
synced 2026-09-18 08:46:23 +08:00
feat : API
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
<template>
|
||||
<n-modal v-model:show="visible" :mask-closable="false" preset="card" :title="t('Add Bucket Replication Rule', { bucket: bucketName })" class="max-w-screen-md" :segmented="{
|
||||
content: true,
|
||||
action: true,
|
||||
}">
|
||||
<n-modal
|
||||
v-model:show="visible"
|
||||
:mask-closable="false"
|
||||
preset="card"
|
||||
:title="t('Add Bucket Replication Rule', { bucket: bucketName })"
|
||||
class="max-w-screen-md"
|
||||
:segmented="{
|
||||
content: true,
|
||||
action: true,
|
||||
}">
|
||||
<n-card>
|
||||
<n-form label-placement="left" :label-width='100' ref="formRef" :model="formData">
|
||||
<n-form label-placement="left" :label-width="100" ref="formRef" :model="formData">
|
||||
<n-form-item :label="t('Priority')" path="level">
|
||||
<n-input v-model:value="formData.level" :placeholder="t('Please enter priority')" />
|
||||
</n-form-item>
|
||||
@@ -27,13 +33,21 @@
|
||||
<n-input v-model:value="formData.region" :placeholder="t('Please enter region')" />
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('Replication Mode')" path="modeType">
|
||||
<n-select v-model:value="formData.modeType" :placeholder="t('Please select replication mode')" filterable :options="modes" />
|
||||
<n-select
|
||||
v-model:value="formData.modeType"
|
||||
:placeholder="t('Please select replication mode')"
|
||||
filterable
|
||||
:options="modes" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item :label="t('Bandwidth')" path="type">
|
||||
<n-form-item v-if="formData.modeType === 'async'" :label="t('Bandwidth')" path="type">
|
||||
<n-input-group>
|
||||
<n-input v-model="formData.daikuan" :placeholder="t('Please enter bandwidth')" />
|
||||
<n-select v-model:value="formData.unit" :placeholder="t('Please select unit')" filterable :options="units" />
|
||||
<n-select
|
||||
v-model:value="formData.unit"
|
||||
:placeholder="t('Please select unit')"
|
||||
filterable
|
||||
:options="units" />
|
||||
</n-input-group>
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('Health Check Duration')" path="timecheck">
|
||||
@@ -49,49 +63,51 @@
|
||||
<n-input v-model="formData.prefix" :placeholder="t('Please enter prefix')" />
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('Tags')">
|
||||
<n-dynamic-input v-model:value="formData.tags" preset="pair" :key-placeholder="t('Tag Name')" :value-placeholder="t('Tag Value')" />
|
||||
<n-dynamic-input
|
||||
v-model:value="formData.tags"
|
||||
preset="pair"
|
||||
:key-placeholder="t('Tag Name')"
|
||||
:value-placeholder="t('Tag Value')" />
|
||||
</n-form-item>
|
||||
</n-card>
|
||||
|
||||
|
||||
<!-- 复制选项 -->
|
||||
<n-card class="my-4">
|
||||
<n-collapse>
|
||||
<n-collapse-item :title="t('Replication Options')" name="advanced">
|
||||
<n-form-item :label="t('Existing Objects')">
|
||||
<n-space>
|
||||
<n-switch v-model:value="formData.expiredDeleteMark" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t('Replicate existing objects') }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item :label="t('Metadata Sync')">
|
||||
<n-space>
|
||||
<n-switch v-model:value="formData.deleteAllExpired" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t('Sync metadata') }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item :label="t('Delete Marker')">
|
||||
<n-space>
|
||||
<n-switch v-model:value="formData.delete" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t('Replicate soft delete') }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item :label="t('Delete')">
|
||||
<n-space>
|
||||
<n-switch v-model:value="formData.deleteforever" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t('Replicate version delete') }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
<div style="text-align: right">
|
||||
<n-form-item :label="t('Existing Objects')">
|
||||
<n-space align="center" justify="end">
|
||||
<n-switch v-model:value="formData.expiredDeleteMark" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t("Replicate existing objects") }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('Metadata Sync')">
|
||||
<n-space align="center" justify="end">
|
||||
<n-switch v-model:value="formData.deleteAllExpired" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t("Sync metadata") }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('Delete Marker')">
|
||||
<n-space align="center" justify="end">
|
||||
<n-switch v-model:value="formData.delete" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t("Replicate soft delete") }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
<n-form-item :label="t('Delete')">
|
||||
<n-space align="center" justify="end">
|
||||
<n-switch v-model:value="formData.deleteforever" :round="false" />
|
||||
<span class="ml-4 text-gray-500">{{ t("Replicate version delete") }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
</div>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
</n-card>
|
||||
|
||||
<n-space justify="center">
|
||||
<n-button @click="handleCancel">{{ t('Cancel') }}</n-button>
|
||||
<n-button type="primary" @click="handleSave">{{ t('Save') }}</n-button>
|
||||
<n-button @click="handleCancel">{{ t("Cancel") }}</n-button>
|
||||
<n-button type="primary" @click="handleSave">{{ t("Save") }}</n-button>
|
||||
</n-space>
|
||||
</n-form>
|
||||
</n-card>
|
||||
@@ -99,97 +115,161 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NDynamicInput,
|
||||
NCollapse,
|
||||
NCollapseItem,
|
||||
NSwitch,
|
||||
NButton,
|
||||
} from 'naive-ui'
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { NForm, NFormItem, NInput, NDynamicInput, NCollapse, NCollapseItem, NSwitch, NButton } from "naive-ui";
|
||||
import { useBucket } from "@/composables/useBucket";
|
||||
|
||||
const { t } = useI18n()
|
||||
const { t } = useI18n();
|
||||
|
||||
const modes = [
|
||||
{
|
||||
label: t('Sync'),
|
||||
value: 'sync',
|
||||
label: t("Sync"),
|
||||
value: "sync",
|
||||
},
|
||||
{
|
||||
label: t('Async'),
|
||||
value: 'async',
|
||||
label: t("Async"),
|
||||
value: "async",
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
const units = [
|
||||
{
|
||||
label: 'Mi',
|
||||
value: 'Mi',
|
||||
label: "Mi",
|
||||
value: "Mi",
|
||||
},
|
||||
{
|
||||
label: 'Gi',
|
||||
value: 'Gi',
|
||||
label: "Gi",
|
||||
value: "Gi",
|
||||
},
|
||||
{
|
||||
label: 'Ti',
|
||||
value: 'Ti',
|
||||
label: "Ti",
|
||||
value: "Ti",
|
||||
},
|
||||
{
|
||||
label: 'Pi',
|
||||
value: 'Pi',
|
||||
label: "Pi",
|
||||
value: "Pi",
|
||||
},
|
||||
{
|
||||
label: 'Ei',
|
||||
value: 'Ei',
|
||||
label: "Ei",
|
||||
value: "Ei",
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
const formRef = ref(null)
|
||||
const formRef = ref(null);
|
||||
const formData = ref({
|
||||
level: '1',
|
||||
timecheck: '60',
|
||||
modeType: 'sync',
|
||||
level: "1",
|
||||
timecheck: "60",
|
||||
modeType: "sync",
|
||||
type: null,
|
||||
unit: 'Mi',
|
||||
tags: [{
|
||||
key: '',
|
||||
value: ''
|
||||
}],
|
||||
unit: "Mi",
|
||||
tags: [
|
||||
{
|
||||
key: "",
|
||||
value: "",
|
||||
},
|
||||
],
|
||||
expiredDeleteMark: false,
|
||||
deleteAllExpired: false,
|
||||
delete: false,
|
||||
deleteforever: false,
|
||||
})
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
bucketName: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const visible = ref(false)
|
||||
const visible = ref(false);
|
||||
const open = () => {
|
||||
visible.value = true
|
||||
}
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
const handleSave = () => {
|
||||
formRef.value?.validate((errors) => {
|
||||
if (!errors) {
|
||||
console.log(t('Submit data'), formData.value)
|
||||
// 调用保存接口
|
||||
}
|
||||
})
|
||||
open,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["success"]);
|
||||
|
||||
function genRuleId() {
|
||||
return "rule-" + Math.random().toString(36).substr(2, 9) + Date.now();
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
const { putBucketReplication } = useBucket({});
|
||||
const config = {
|
||||
Role: formData.value.role || "arn:aws:iam::xxxx:role/xxx",
|
||||
Rules: [
|
||||
{
|
||||
ID: genRuleId(),
|
||||
Status: "Enabled",
|
||||
Priority: Number(formData.value.level),
|
||||
DeleteMarkerReplication: {
|
||||
Status: formData.value.delete ? "Enabled" : "Disabled",
|
||||
},
|
||||
Filter: {
|
||||
And: {
|
||||
Prefix: formData.value.prefix,
|
||||
Tag: (formData.value.tags || [])
|
||||
.filter((tag) => tag.key && tag.value)
|
||||
.map((tag) => ({
|
||||
Key: tag.key,
|
||||
Value: tag.value,
|
||||
})),
|
||||
},
|
||||
},
|
||||
Destination: {
|
||||
Bucket: `arn:aws:s3:::${formData.value.bucket}`,
|
||||
...(formData.value.metricsStatus && formData.value.metricsMinutes
|
||||
? {
|
||||
Metrics: {
|
||||
Status: formData.value.metricsStatus,
|
||||
EventThreshold: {
|
||||
Minutes: Number(formData.value.metricsMinutes),
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(formData.value.replicationTimeStatus && formData.value.replicationTimeMinutes
|
||||
? {
|
||||
ReplicationTime: {
|
||||
Status: formData.value.replicationTimeStatus,
|
||||
Time: {
|
||||
Minutes: Number(formData.value.replicationTimeMinutes),
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(formData.value.modeType === "async" && formData.value.daikuan
|
||||
? {
|
||||
Bandwidth: {
|
||||
Value: formData.value.daikuan,
|
||||
Unit: formData.value.unit,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
console.log("🚀 ~ formRef.value?.validate ~ config:", config);
|
||||
|
||||
await putBucketReplication(props.bucketName, config);
|
||||
emit("success");
|
||||
visible.value = false;
|
||||
} catch (e) {
|
||||
window.$message?.error?.(t("Save failed"));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
// 取消逻辑
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -23,8 +23,13 @@ import {
|
||||
GetBucketLifecycleConfigurationCommand,
|
||||
PutBucketLifecycleConfigurationCommand,
|
||||
DeleteBucketLifecycleCommand,
|
||||
GetBucketReplicationCommand,
|
||||
PutBucketReplicationCommand,
|
||||
DeleteBucketReplicationCommand,
|
||||
} from "@aws-sdk/client-s3";
|
||||
|
||||
const { $api } = useNuxtApp();
|
||||
|
||||
export function useBucket({ region }: { region?: string }) {
|
||||
const $client = useNuxtApp().$s3Client;
|
||||
|
||||
@@ -195,6 +200,40 @@ export function useBucket({ region }: { region?: string }) {
|
||||
return await $client.send(new DeleteBucketEncryptionCommand(params));
|
||||
};
|
||||
|
||||
/********************S3 Replication********************/
|
||||
const getBucketReplication = async (bucket: string) => {
|
||||
const params = {
|
||||
Bucket: bucket,
|
||||
};
|
||||
return await $client.send(new GetBucketReplicationCommand(params));
|
||||
};
|
||||
const putBucketReplication = async (bucket: string, replication: any) => {
|
||||
const params = {
|
||||
Bucket: bucket,
|
||||
ReplicationConfiguration: replication,
|
||||
};
|
||||
return await $client.send(new PutBucketReplicationCommand(params));
|
||||
};
|
||||
const deleteBucketReplication = async (bucket: string) => {
|
||||
const params = {
|
||||
Bucket: bucket,
|
||||
};
|
||||
return await $client.send(new DeleteBucketReplicationCommand(params));
|
||||
};
|
||||
|
||||
/********************S3 Replication********************/
|
||||
|
||||
const setRemoteReplicationTarget = async (bucket: string, data: any) => {
|
||||
console.log("🚀 ~ setRemoteReplicationTarget ~ bucket:", bucket);
|
||||
return await $api.put(`/set-remote-target?bucket=${bucket}}`, data);
|
||||
};
|
||||
|
||||
const listRemoteReplicationTarget = async (bucket: string) => {
|
||||
return await $api.get(`/list-remote-target?bucket=${bucket}`);
|
||||
};
|
||||
|
||||
/********************rustfs Replication********************/
|
||||
|
||||
return {
|
||||
listBuckets,
|
||||
createBucket,
|
||||
@@ -218,5 +257,10 @@ export function useBucket({ region }: { region?: string }) {
|
||||
getBucketEncryption,
|
||||
putBucketEncryption,
|
||||
deleteBucketEncryption,
|
||||
getBucketReplication,
|
||||
putBucketReplication,
|
||||
deleteBucketReplication,
|
||||
setRemoteReplicationTarget,
|
||||
listRemoteReplicationTarget,
|
||||
};
|
||||
}
|
||||
|
||||
+10
-1
@@ -343,5 +343,14 @@
|
||||
"Days After": "Days After",
|
||||
"Expire": "Expire",
|
||||
"On": "On",
|
||||
"Off": "Off"
|
||||
"Off": "Off",
|
||||
"Replicate existing objects": "Replicate existing objects",
|
||||
"Sync metadata": "Sync metadata",
|
||||
"Replicate soft delete": "Replicate soft delete",
|
||||
"Replicate version delete": "Replicate version delete",
|
||||
"Health Check Duration": "Health Check Duration",
|
||||
"Please enter health check duration": "Please enter health check duration",
|
||||
"Please enter bandwidth": "Please enter bandwidth",
|
||||
"Please enter storage type": "Please enter storage type",
|
||||
"Add Replication Rule": "Add Replication Rule"
|
||||
}
|
||||
|
||||
+10
-1
@@ -534,5 +534,14 @@
|
||||
"Please enter valid days": "请输入天数",
|
||||
"Expire": "过期",
|
||||
"On": "开启",
|
||||
"Off": "关闭"
|
||||
"Off": "关闭",
|
||||
"Replicate existing objects": "复制现有对象",
|
||||
"Sync metadata": "同步元数据",
|
||||
"Replicate soft delete": "复制软删除",
|
||||
"Replicate version delete": "复制版本删除",
|
||||
"Health Check Duration": "健康检查时长",
|
||||
"Please enter health check duration": "请输入健康检查时长",
|
||||
"Please enter bandwidth": "请输入带宽",
|
||||
"Please enter storage type": "请输入存储类型",
|
||||
"Add Replication Rule": "添加复制规则"
|
||||
}
|
||||
|
||||
+126
-63
@@ -2,107 +2,127 @@
|
||||
<div>
|
||||
<page-header>
|
||||
<template #title>
|
||||
<h1 class="text-2xl font-bold">{{ t('Bucket Replication') }}</h1>
|
||||
<h1 class="text-2xl font-bold">{{ t("Bucket Replication") }}</h1>
|
||||
</template>
|
||||
</page-header>
|
||||
<page-content class="flex flex-col gap-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div style="width:300px">
|
||||
<div style="width: 300px">
|
||||
<n-form-item :label="t('Bucket')" path="" class="flex-auto" label-placement="left">
|
||||
<n-select filterable v-model:value="bucketName" :placeholder="t('Please select bucket')" :options="bucketList" />
|
||||
<n-select
|
||||
filterable
|
||||
v-model:value="bucketName"
|
||||
:placeholder="t('Please select bucket')"
|
||||
:options="bucketList" />
|
||||
</n-form-item>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<n-button @click="() => openForm()">
|
||||
<Icon name="ri:add-line" class="mr-2" />
|
||||
<span>{{ t('Add Replication Rule') }}</span>
|
||||
<span>{{ t("Add Replication Rule") }}</span>
|
||||
</n-button>
|
||||
<n-button @click="">
|
||||
<n-button @click="loadReplication">
|
||||
<Icon name="ri:refresh-line" class="mr-2" />
|
||||
<span>{{ t('Refresh') }}</span>
|
||||
<span>{{ t("Refresh") }}</span>
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-card class="flex flex-center" style="height:400px">
|
||||
<n-empty :description="t('No Data')" ></n-empty>
|
||||
<n-data-table
|
||||
class="border dark:border-neutral-700 rounded overflow-hidden"
|
||||
:columns="columns"
|
||||
:data="pageData"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
v-if="pageData.length > 0" />
|
||||
<n-card class="flex flex-center" style="height: 400px" v-else>
|
||||
<n-empty :description="t('No Data')"></n-empty>
|
||||
</n-card>
|
||||
<!-- <n-data-table class="border dark:border-neutral-700 rounded overflow-hidden" :columns="columns" :data="pageData" :pagination="false" :bordered="false" /> -->
|
||||
<replication-new-form :bucketName="bucketName" ref="addFromRef"></replication-new-form>
|
||||
<replication-new-form :bucketName="bucketName" ref="addFromRef" @success="onAddSuccess"></replication-new-form>
|
||||
</page-content>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Icon } from '#components'
|
||||
import { NButton, NSpace, type DataTableColumns } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Icon } from "#components";
|
||||
import { NButton, NSpace, type DataTableColumns } from "naive-ui";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useBucket } from "@/composables/useBucket";
|
||||
import { h, ref, computed, watch } from "vue";
|
||||
import { useMessage } from "naive-ui";
|
||||
|
||||
const { t } = useI18n()
|
||||
const { listBuckets } = useBucket({});
|
||||
const { t } = useI18n();
|
||||
const { listBuckets, listRemoteReplicationTarget, deleteBucketReplication } = useBucket({});
|
||||
const formVisible = ref(false);
|
||||
const searchTerm = ref('');
|
||||
const searchTerm = ref("");
|
||||
const message = useMessage();
|
||||
|
||||
interface RowData {
|
||||
Name: string;
|
||||
CreationDate: string;
|
||||
}
|
||||
|
||||
const columns: DataTableColumns<RowData> = [
|
||||
const columns: DataTableColumns<ReplicationRule> = [
|
||||
{
|
||||
title: t('Type'),
|
||||
key: '',
|
||||
|
||||
title: t("Rule ID"),
|
||||
key: "ID",
|
||||
align: "center",
|
||||
},
|
||||
{
|
||||
title: t('Version'),
|
||||
key: '',
|
||||
},
|
||||
{
|
||||
title: t('Expiration Delete Mark'),
|
||||
key: '',
|
||||
},
|
||||
{
|
||||
title: t('Tier'),
|
||||
key: '',
|
||||
},
|
||||
{
|
||||
title: t('Prefix'),
|
||||
key: '',
|
||||
},
|
||||
{
|
||||
title: t('Time'),
|
||||
key: '',
|
||||
},
|
||||
{
|
||||
title: t('Status'),
|
||||
key: '',
|
||||
title: t("Status"),
|
||||
key: "Status",
|
||||
align: "center",
|
||||
render: (row) => (row.Status === "Enabled" ? t("Enabled") : t("Disabled")),
|
||||
},
|
||||
{
|
||||
title: t('Actions'),
|
||||
key: 'actions',
|
||||
align: 'center',
|
||||
title: t("Priority"),
|
||||
key: "Priority",
|
||||
align: "center",
|
||||
},
|
||||
{
|
||||
title: t("Prefix"),
|
||||
key: "Filter",
|
||||
align: "center",
|
||||
render: (row) => row.Filter?.Prefix || "-",
|
||||
},
|
||||
{
|
||||
title: t("Destination Bucket"),
|
||||
key: "Destination",
|
||||
align: "center",
|
||||
render: (row) => {
|
||||
// MinIO/标准S3结构:arn:aws:s3:::bucketname
|
||||
const bucketArn = row.Destination?.Bucket || "";
|
||||
return bucketArn.replace(/^arn:aws:s3:::/, "");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("Storage Class"),
|
||||
key: "Destination",
|
||||
align: "center",
|
||||
render: (row) => row.Destination?.StorageClass || "-",
|
||||
},
|
||||
{
|
||||
title: t("Actions"),
|
||||
key: "actions",
|
||||
align: "center",
|
||||
width: 100,
|
||||
render: (row: RowData) => {
|
||||
render: (row) => {
|
||||
return h(
|
||||
NSpace,
|
||||
{
|
||||
justify: 'center',
|
||||
},
|
||||
{ justify: "center" },
|
||||
{
|
||||
default: () => [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
size: "small",
|
||||
secondary: true,
|
||||
onClick: (e) => handleRowDelete(row,e),
|
||||
onClick: (e) => handleRowDelete(row, e),
|
||||
},
|
||||
{
|
||||
default: () => '',
|
||||
icon: () => h(Icon, { name: 'ri:delete-bin-7-line' }),
|
||||
default: () => "",
|
||||
icon: () => h(Icon, { name: "ri:delete-bin-7-line" }),
|
||||
}
|
||||
),
|
||||
],
|
||||
@@ -114,10 +134,14 @@ const columns: DataTableColumns<RowData> = [
|
||||
|
||||
// 获取桶列表
|
||||
const { data } = await useAsyncData(
|
||||
'buckets',
|
||||
"buckets",
|
||||
async () => {
|
||||
const response = await listBuckets();
|
||||
return response.Buckets?.sort((a:any, b:any) => {return a.Name.localeCompare(b.Name) }) || [];
|
||||
return (
|
||||
response.Buckets?.sort((a: any, b: any) => {
|
||||
return a.Name.localeCompare(b.Name);
|
||||
}) || []
|
||||
);
|
||||
},
|
||||
{ default: () => [] }
|
||||
);
|
||||
@@ -129,19 +153,58 @@ const bucketList = computed(() => {
|
||||
}));
|
||||
});
|
||||
|
||||
const bucketName = ref<string>(
|
||||
bucketList.value.length > 0 ? bucketList.value[0]?.value ?? '' : ''
|
||||
const bucketName = ref<string>(bucketList.value.length > 0 ? bucketList.value[0]?.value ?? "" : "");
|
||||
|
||||
// 复制规则类型
|
||||
interface ReplicationRule {
|
||||
[key: string]: any;
|
||||
}
|
||||
const pageData = ref<ReplicationRule[]>([]);
|
||||
|
||||
// 加载复制规则
|
||||
const loadReplication = async () => {
|
||||
if (!bucketName.value) {
|
||||
pageData.value = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
console.log(11111);
|
||||
const res = await listRemoteReplicationTarget(bucketName.value);
|
||||
// 兼容无规则时返回空对象
|
||||
pageData.value = res?.ReplicationConfiguration?.Rules || [];
|
||||
} catch (e) {
|
||||
pageData.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 监听 bucketName 变化自动加载
|
||||
watch(
|
||||
bucketName,
|
||||
() => {
|
||||
loadReplication();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const pageData = ref([])
|
||||
|
||||
const handleRowDelete = (row: RowData, e: Event) => {
|
||||
// 删除规则
|
||||
const handleRowDelete = async (row: any, e: Event) => {
|
||||
e.stopPropagation();
|
||||
console.log(row);
|
||||
try {
|
||||
await deleteBucketReplication(bucketName.value);
|
||||
message.success(t("Delete success"));
|
||||
loadReplication();
|
||||
} catch (err) {
|
||||
message.error(t("Delete failed"));
|
||||
}
|
||||
};
|
||||
|
||||
const addFromRef = ref();
|
||||
const openForm = () => {
|
||||
addFromRef.value.open();
|
||||
}
|
||||
};
|
||||
|
||||
// 新增成功后刷新
|
||||
const onAddSuccess = () => {
|
||||
loadReplication();
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user