mirror of https://github.com/halo-dev/halo-admin
perf: optimize the creation of attachment storage policies (#681)
#### What type of PR is this? /kind improvement /milestone 2.0 #### What this PR does / why we need it: 优化首次上传附件时,创建存储策略的流程,现在可以直接打开新建策略的表单。 #### Screenshots: before: <img width="898" alt="image" src="https://user-images.githubusercontent.com/21301288/199915792-92547e21-ffbb-4c9f-9614-b1f89f7d6f75.png"> after: <img width="1087" alt="image" src="https://user-images.githubusercontent.com/21301288/199915494-00447427-060a-4744-83b0-d1067e745517.png"> #### Special notes for your reviewer: /cc @halo-dev/sig-halo-console 测试方式: 1. 测试在上传弹窗中新建存储策略。 #### Does this PR introduce a user-facing change? ```release-note 优化首次上传附件时,创建存储策略的流程。 ```pull/684/head
parent
b0359c4e17
commit
d3d28dd3da
|
@ -95,7 +95,7 @@ const handleClose = () => {
|
||||||
mr-3
|
mr-3
|
||||||
flex-1
|
flex-1
|
||||||
font-medium
|
font-medium
|
||||||
text-base;
|
text-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-close {
|
.alert-close {
|
||||||
|
@ -113,7 +113,7 @@ const handleClose = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-description {
|
.alert-description {
|
||||||
@apply text-sm
|
@apply text-xs
|
||||||
mt-2;
|
mt-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,12 @@ import {
|
||||||
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
||||||
import { apiClient } from "@/utils/api-client";
|
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import { formatDatetime } from "@/utils/date";
|
import { formatDatetime } from "@/utils/date";
|
||||||
import { useFetchAttachmentPolicy } from "../composables/use-attachment-policy";
|
import {
|
||||||
|
useFetchAttachmentPolicy,
|
||||||
|
useFetchAttachmentPolicyTemplate,
|
||||||
|
} from "../composables/use-attachment-policy";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -30,22 +32,15 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { policies, loading, handleFetchPolicies } = useFetchAttachmentPolicy();
|
const { policies, loading, handleFetchPolicies } = useFetchAttachmentPolicy();
|
||||||
|
const { policyTemplates, handleFetchPolicyTemplates } =
|
||||||
|
useFetchAttachmentPolicyTemplate({
|
||||||
|
fetchOnMounted: false,
|
||||||
|
});
|
||||||
|
|
||||||
const selectedPolicy = ref<Policy | null>(null);
|
const selectedPolicy = ref<Policy>();
|
||||||
const policyTemplates = ref<PolicyTemplate[]>([] as PolicyTemplate[]);
|
|
||||||
|
|
||||||
const policyEditingModal = ref(false);
|
const policyEditingModal = ref(false);
|
||||||
|
|
||||||
const handleFetchPolicyTemplates = async () => {
|
|
||||||
try {
|
|
||||||
const { data } =
|
|
||||||
await apiClient.extension.storage.policyTemplate.liststorageHaloRunV1alpha1PolicyTemplate();
|
|
||||||
policyTemplates.value = data.items;
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Failed to fetch attachment policy templates", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function onVisibleChange(visible: boolean) {
|
function onVisibleChange(visible: boolean) {
|
||||||
emit("update:visible", visible);
|
emit("update:visible", visible);
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
|
@ -79,14 +74,14 @@ const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onEditingModalClose = () => {
|
const onEditingModalClose = () => {
|
||||||
selectedPolicy.value = null;
|
selectedPolicy.value = undefined;
|
||||||
handleFetchPolicies();
|
handleFetchPolicies();
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(newValue) => {
|
(visible) => {
|
||||||
if (newValue) {
|
if (visible) {
|
||||||
handleFetchPolicyTemplates();
|
handleFetchPolicyTemplates();
|
||||||
handleFetchPolicies();
|
handleFetchPolicies();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@ import { setFocus } from "@/formkit/utils/focus";
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
policy: Policy | null;
|
policy?: Policy;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
visible: false,
|
visible: false,
|
||||||
policy: null,
|
policy: undefined,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,18 @@ const onVisibleChange = (visible: boolean) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChangeProvider = (providerId: string) => {
|
||||||
|
const provider = attachmentSelectorPublicState.value.providers.find(
|
||||||
|
(provider) => provider.id === providerId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeId.value = providerId;
|
||||||
|
};
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
emit("select", Array.from(selected.value));
|
emit("select", Array.from(selected.value));
|
||||||
onVisibleChange(false);
|
onVisibleChange(false);
|
||||||
|
@ -69,7 +81,7 @@ const handleConfirm = () => {
|
||||||
<VTabbar
|
<VTabbar
|
||||||
v-model:active-id="activeId"
|
v-model:active-id="activeId"
|
||||||
:items="attachmentSelectorPublicState.providers"
|
:items="attachmentSelectorPublicState.providers"
|
||||||
class="w-full !rounded-none"
|
class="w-full"
|
||||||
type="outline"
|
type="outline"
|
||||||
></VTabbar>
|
></VTabbar>
|
||||||
|
|
||||||
|
@ -83,6 +95,7 @@ const handleConfirm = () => {
|
||||||
:is="provider.component"
|
:is="provider.component"
|
||||||
v-if="activeId === provider.id"
|
v-if="activeId === provider.id"
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
|
@change-provider="onChangeProvider"
|
||||||
></component>
|
></component>
|
||||||
<template #fallback> 加载中 </template>
|
<template #fallback> 加载中 </template>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { VModal, IconAddCircle, VAlert } from "@halo-dev/components";
|
||||||
VModal,
|
|
||||||
VEmpty,
|
|
||||||
IconAddCircle,
|
|
||||||
VButton,
|
|
||||||
VSpace,
|
|
||||||
} from "@halo-dev/components";
|
|
||||||
import UppyUpload from "@/components/upload/UppyUpload.vue";
|
import UppyUpload from "@/components/upload/UppyUpload.vue";
|
||||||
import { computed, ref, watch, watchEffect } from "vue";
|
import { computed, ref, watch, watchEffect } from "vue";
|
||||||
import type { Policy, Group } from "@halo-dev/api-client";
|
import type { Policy, Group, PolicyTemplate } from "@halo-dev/api-client";
|
||||||
import { useFetchAttachmentPolicy } from "../composables/use-attachment-policy";
|
import {
|
||||||
import AttachmentPoliciesModal from "./AttachmentPoliciesModal.vue";
|
useFetchAttachmentPolicy,
|
||||||
|
useFetchAttachmentPolicyTemplate,
|
||||||
|
} from "../composables/use-attachment-policy";
|
||||||
|
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
||||||
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -28,15 +26,19 @@ const emit = defineEmits<{
|
||||||
(event: "close"): void;
|
(event: "close"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { policies, loading, handleFetchPolicies } = useFetchAttachmentPolicy();
|
const { policies, handleFetchPolicies } = useFetchAttachmentPolicy({
|
||||||
|
fetchOnMounted: false,
|
||||||
|
});
|
||||||
|
const { policyTemplates, handleFetchPolicyTemplates } =
|
||||||
|
useFetchAttachmentPolicyTemplate();
|
||||||
|
|
||||||
const selectedPolicy = ref<Policy | null>(null);
|
const selectedPolicy = ref<Policy>();
|
||||||
const policyVisible = ref(false);
|
|
||||||
const uploadVisible = ref(false);
|
const uploadVisible = ref(false);
|
||||||
|
const policyEditingModal = ref(false);
|
||||||
|
|
||||||
const modalTitle = computed(() => {
|
const modalTitle = computed(() => {
|
||||||
if (props.group && props.group.metadata.name) {
|
if (props.group && props.group.metadata.name) {
|
||||||
return `上传附件:${props.group.spec.displayName}`;
|
return `上传附件到分组:${props.group.spec.displayName}`;
|
||||||
}
|
}
|
||||||
return "上传附件";
|
return "上传附件";
|
||||||
});
|
});
|
||||||
|
@ -47,11 +49,36 @@ watchEffect(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
|
||||||
|
selectedPolicy.value = {
|
||||||
|
spec: {
|
||||||
|
displayName: "",
|
||||||
|
templateRef: {
|
||||||
|
name: policyTemplate.metadata.name,
|
||||||
|
},
|
||||||
|
configMapRef: {
|
||||||
|
name: uuid(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apiVersion: "storage.halo.run/v1alpha1",
|
||||||
|
kind: "Policy",
|
||||||
|
metadata: {
|
||||||
|
name: uuid(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
policyEditingModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEditingModalClose = async () => {
|
||||||
|
await handleFetchPolicies();
|
||||||
|
selectedPolicy.value = policies.value[0];
|
||||||
|
};
|
||||||
|
|
||||||
const onVisibleChange = (visible: boolean) => {
|
const onVisibleChange = (visible: boolean) => {
|
||||||
emit("update:visible", visible);
|
emit("update:visible", visible);
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
emit("close");
|
emit("close");
|
||||||
policyVisible.value = false;
|
policyEditingModal.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,6 +87,7 @@ watch(
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
handleFetchPolicies();
|
handleFetchPolicies();
|
||||||
|
handleFetchPolicyTemplates();
|
||||||
uploadVisible.value = true;
|
uploadVisible.value = true;
|
||||||
} else {
|
} else {
|
||||||
const uploadVisibleTimer = setTimeout(() => {
|
const uploadVisibleTimer = setTimeout(() => {
|
||||||
|
@ -74,28 +102,11 @@ watch(
|
||||||
<VModal
|
<VModal
|
||||||
:body-class="['!p-0']"
|
:body-class="['!p-0']"
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="600"
|
:width="650"
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
@update:visible="onVisibleChange"
|
@update:visible="onVisibleChange"
|
||||||
>
|
>
|
||||||
<VEmpty
|
<div class="w-full p-4">
|
||||||
v-if="!policies.length && !loading"
|
|
||||||
message="当前没有上传附件的存储策略,请先创建存储策略"
|
|
||||||
title="无存储策略"
|
|
||||||
>
|
|
||||||
<template #actions>
|
|
||||||
<VSpace>
|
|
||||||
<VButton @click="handleFetchPolicies">刷新</VButton>
|
|
||||||
<VButton type="secondary" @click="policyVisible = true">
|
|
||||||
<template #icon>
|
|
||||||
<IconAddCircle class="h-full w-full" />
|
|
||||||
</template>
|
|
||||||
新建策略
|
|
||||||
</VButton>
|
|
||||||
</VSpace>
|
|
||||||
</template>
|
|
||||||
</VEmpty>
|
|
||||||
<div v-else class="w-full p-4">
|
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<span class="text-sm text-gray-900">选择存储策略:</span>
|
<span class="text-sm text-gray-900">选择存储策略:</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,15 +130,41 @@ watch(
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<FloatingDropdown>
|
||||||
<div
|
<div
|
||||||
class="flex cursor-pointer items-center rounded-base bg-gray-100 p-2 text-gray-500 transition-all hover:bg-gray-200 hover:text-gray-900 hover:shadow-sm"
|
class="flex h-full cursor-pointer items-center rounded-base bg-gray-100 p-2 text-gray-500 transition-all hover:bg-gray-200 hover:text-gray-900 hover:shadow-sm"
|
||||||
@click="policyVisible = true"
|
|
||||||
>
|
>
|
||||||
<div class="flex flex-1 items-center truncate">
|
<div class="flex flex-1 items-center truncate">
|
||||||
<span class="truncate text-sm">新建策略</span>
|
<span class="truncate text-sm">新建策略</span>
|
||||||
</div>
|
</div>
|
||||||
<IconAddCircle />
|
<IconAddCircle />
|
||||||
</div>
|
</div>
|
||||||
|
<template #popper>
|
||||||
|
<div class="w-72 p-4">
|
||||||
|
<ul class="space-y-1">
|
||||||
|
<li
|
||||||
|
v-for="(policyTemplate, index) in policyTemplates"
|
||||||
|
:key="index"
|
||||||
|
v-close-popper
|
||||||
|
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
|
||||||
|
@click="handleOpenCreateNewPolicyModal(policyTemplate)"
|
||||||
|
>
|
||||||
|
<span class="truncate">
|
||||||
|
{{ policyTemplate.spec?.displayName }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FloatingDropdown>
|
||||||
|
</div>
|
||||||
|
<div v-if="policies.length <= 0" class="mb-3">
|
||||||
|
<VAlert
|
||||||
|
title="没有存储策略"
|
||||||
|
description="在上传之前,需要新建一个存储策略"
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<UppyUpload
|
<UppyUpload
|
||||||
v-if="uploadVisible"
|
v-if="uploadVisible"
|
||||||
|
@ -143,8 +180,9 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
</VModal>
|
</VModal>
|
||||||
|
|
||||||
<AttachmentPoliciesModal
|
<AttachmentPolicyEditingModal
|
||||||
v-model:visible="policyVisible"
|
v-model:visible="policyEditingModal"
|
||||||
@close="handleFetchPolicies"
|
:policy="selectedPolicy"
|
||||||
|
@close="onEditingModalClose"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -34,6 +34,7 @@ withDefaults(
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "update:selected", attachments: AttachmentLike[]): void;
|
(event: "update:selected", attachments: AttachmentLike[]): void;
|
||||||
|
(event: "change-provider", providerId: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const selectedGroup = ref<Group>();
|
const selectedGroup = ref<Group>();
|
||||||
|
@ -85,7 +86,7 @@ await handleFetchAttachments();
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<VButton @click="handleFetchAttachments">刷新</VButton>
|
<VButton @click="handleFetchAttachments">刷新</VButton>
|
||||||
<VButton type="secondary" @click="uploadVisible = true">
|
<VButton type="secondary" @click="emit('change-provider', 'upload')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<IconUpload class="h-full w-full" />
|
<IconUpload class="h-full w-full" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import type { Policy } from "@halo-dev/api-client";
|
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
|
||||||
interface useFetchAttachmentPolicyReturn {
|
interface useFetchAttachmentPolicyReturn {
|
||||||
|
@ -9,6 +9,12 @@ interface useFetchAttachmentPolicyReturn {
|
||||||
handleFetchPolicies: () => void;
|
handleFetchPolicies: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface useFetchAttachmentPolicyTemplatesReturn {
|
||||||
|
policyTemplates: Ref<PolicyTemplate[]>;
|
||||||
|
loading: Ref<boolean>;
|
||||||
|
handleFetchPolicyTemplates: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
export function useFetchAttachmentPolicy(options?: {
|
export function useFetchAttachmentPolicy(options?: {
|
||||||
fetchOnMounted: boolean;
|
fetchOnMounted: boolean;
|
||||||
}): useFetchAttachmentPolicyReturn {
|
}): useFetchAttachmentPolicyReturn {
|
||||||
|
@ -40,3 +46,35 @@ export function useFetchAttachmentPolicy(options?: {
|
||||||
handleFetchPolicies,
|
handleFetchPolicies,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useFetchAttachmentPolicyTemplate(options?: {
|
||||||
|
fetchOnMounted: boolean;
|
||||||
|
}): useFetchAttachmentPolicyTemplatesReturn {
|
||||||
|
const { fetchOnMounted } = options || {};
|
||||||
|
|
||||||
|
const policyTemplates = ref<PolicyTemplate[]>([] as PolicyTemplate[]);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const handleFetchPolicyTemplates = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const { data } =
|
||||||
|
await apiClient.extension.storage.policyTemplate.liststorageHaloRunV1alpha1PolicyTemplate();
|
||||||
|
policyTemplates.value = data.items;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to fetch attachment policy templates", e);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchOnMounted && handleFetchPolicyTemplates();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
policyTemplates,
|
||||||
|
loading,
|
||||||
|
handleFetchPolicyTemplates,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue