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
Ryan Wang 2022-11-04 16:30:10 +08:00 committed by GitHub
parent b0359c4e17
commit d3d28dd3da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 69 deletions

View File

@ -95,7 +95,7 @@ const handleClose = () => {
mr-3
flex-1
font-medium
text-base;
text-sm;
}
.alert-close {
@ -113,7 +113,7 @@ const handleClose = () => {
}
.alert-description {
@apply text-sm
@apply text-xs
mt-2;
}

View File

@ -10,10 +10,12 @@ import {
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
import { ref, watch } from "vue";
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
import { apiClient } from "@/utils/api-client";
import { v4 as uuid } from "uuid";
import { formatDatetime } from "@/utils/date";
import { useFetchAttachmentPolicy } from "../composables/use-attachment-policy";
import {
useFetchAttachmentPolicy,
useFetchAttachmentPolicyTemplate,
} from "../composables/use-attachment-policy";
const props = withDefaults(
defineProps<{
@ -30,22 +32,15 @@ const emit = defineEmits<{
}>();
const { policies, loading, handleFetchPolicies } = useFetchAttachmentPolicy();
const { policyTemplates, handleFetchPolicyTemplates } =
useFetchAttachmentPolicyTemplate({
fetchOnMounted: false,
});
const selectedPolicy = ref<Policy | null>(null);
const policyTemplates = ref<PolicyTemplate[]>([] as PolicyTemplate[]);
const selectedPolicy = ref<Policy>();
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) {
emit("update:visible", visible);
if (!visible) {
@ -79,14 +74,14 @@ const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
};
const onEditingModalClose = () => {
selectedPolicy.value = null;
selectedPolicy.value = undefined;
handleFetchPolicies();
};
watch(
() => props.visible,
(newValue) => {
if (newValue) {
(visible) => {
if (visible) {
handleFetchPolicyTemplates();
handleFetchPolicies();
}

View File

@ -17,11 +17,11 @@ import { setFocus } from "@/formkit/utils/focus";
const props = withDefaults(
defineProps<{
visible: boolean;
policy: Policy | null;
policy?: Policy;
}>(),
{
visible: false,
policy: null,
policy: undefined,
}
);

View File

@ -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 = () => {
emit("select", Array.from(selected.value));
onVisibleChange(false);
@ -69,7 +81,7 @@ const handleConfirm = () => {
<VTabbar
v-model:active-id="activeId"
:items="attachmentSelectorPublicState.providers"
class="w-full !rounded-none"
class="w-full"
type="outline"
></VTabbar>
@ -83,6 +95,7 @@ const handleConfirm = () => {
:is="provider.component"
v-if="activeId === provider.id"
v-model:selected="selected"
@change-provider="onChangeProvider"
></component>
<template #fallback> 加载中 </template>
</Suspense>

View File

@ -1,16 +1,14 @@
<script lang="ts" setup>
import {
VModal,
VEmpty,
IconAddCircle,
VButton,
VSpace,
} from "@halo-dev/components";
import { VModal, IconAddCircle, VAlert } from "@halo-dev/components";
import UppyUpload from "@/components/upload/UppyUpload.vue";
import { computed, ref, watch, watchEffect } from "vue";
import type { Policy, Group } from "@halo-dev/api-client";
import { useFetchAttachmentPolicy } from "../composables/use-attachment-policy";
import AttachmentPoliciesModal from "./AttachmentPoliciesModal.vue";
import type { Policy, Group, PolicyTemplate } from "@halo-dev/api-client";
import {
useFetchAttachmentPolicy,
useFetchAttachmentPolicyTemplate,
} from "../composables/use-attachment-policy";
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
import { v4 as uuid } from "uuid";
const props = withDefaults(
defineProps<{
@ -28,15 +26,19 @@ const emit = defineEmits<{
(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 policyVisible = ref(false);
const selectedPolicy = ref<Policy>();
const uploadVisible = ref(false);
const policyEditingModal = ref(false);
const modalTitle = computed(() => {
if (props.group && props.group.metadata.name) {
return `上传附件${props.group.spec.displayName}`;
return `上传附件到分组${props.group.spec.displayName}`;
}
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) => {
emit("update:visible", visible);
if (!visible) {
emit("close");
policyVisible.value = false;
policyEditingModal.value = false;
}
};
@ -60,6 +87,7 @@ watch(
(newValue) => {
if (newValue) {
handleFetchPolicies();
handleFetchPolicyTemplates();
uploadVisible.value = true;
} else {
const uploadVisibleTimer = setTimeout(() => {
@ -74,28 +102,11 @@ watch(
<VModal
:body-class="['!p-0']"
:visible="visible"
:width="600"
:width="650"
:title="modalTitle"
@update:visible="onVisibleChange"
>
<VEmpty
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="w-full p-4">
<div class="mb-2">
<span class="text-sm text-gray-900">选择存储策略</span>
</div>
@ -119,15 +130,41 @@ watch(
</span>
</div>
</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"
@click="policyVisible = true"
>
<div class="flex flex-1 items-center truncate">
<span class="truncate text-sm">新建策略</span>
<FloatingDropdown>
<div
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"
>
<div class="flex flex-1 items-center truncate">
<span class="truncate text-sm">新建策略</span>
</div>
<IconAddCircle />
</div>
<IconAddCircle />
</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>
<UppyUpload
v-if="uploadVisible"
@ -143,8 +180,9 @@ watch(
</div>
</VModal>
<AttachmentPoliciesModal
v-model:visible="policyVisible"
@close="handleFetchPolicies"
<AttachmentPolicyEditingModal
v-model:visible="policyEditingModal"
:policy="selectedPolicy"
@close="onEditingModalClose"
/>
</template>

View File

@ -34,6 +34,7 @@ withDefaults(
const emit = defineEmits<{
(event: "update:selected", attachments: AttachmentLike[]): void;
(event: "change-provider", providerId: string): void;
}>();
const selectedGroup = ref<Group>();
@ -85,7 +86,7 @@ await handleFetchAttachments();
<template #actions>
<VSpace>
<VButton @click="handleFetchAttachments"></VButton>
<VButton type="secondary" @click="uploadVisible = true">
<VButton type="secondary" @click="emit('change-provider', 'upload')">
<template #icon>
<IconUpload class="h-full w-full" />
</template>

View File

@ -1,6 +1,6 @@
import { onMounted, 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";
interface useFetchAttachmentPolicyReturn {
@ -9,6 +9,12 @@ interface useFetchAttachmentPolicyReturn {
handleFetchPolicies: () => void;
}
interface useFetchAttachmentPolicyTemplatesReturn {
policyTemplates: Ref<PolicyTemplate[]>;
loading: Ref<boolean>;
handleFetchPolicyTemplates: () => void;
}
export function useFetchAttachmentPolicy(options?: {
fetchOnMounted: boolean;
}): useFetchAttachmentPolicyReturn {
@ -40,3 +46,35 @@ export function useFetchAttachmentPolicy(options?: {
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,
};
}