mirror of https://github.com/halo-dev/halo
refactor: improve code base of attachment-related (#5967)
#### What type of PR is this? /area ui /kind improvement /milestone 2.16.x #### What this PR does / why we need it: 优化附件管理的相关代码。 1. 优化附件管理相关的对话框组件,减少重复和不必要的请求。 2. 简化附件存储策略编辑组件的逻辑。 #### Special notes for your reviewer: 需要测试: 1. 附件上传的相关功能。 2. 附件存储策略的编辑和新建。 3. 附件筛选条件功能。 #### Does this PR introduce a user-facing change? ```release-note 优化附件管理相关代码,减少重复和不必要的请求。 ```pull/5975/head
parent
ce5757ae10
commit
89b20bf5a3
|
@ -2,185 +2,11 @@
|
|||
// types
|
||||
import { computed, watch, type ComputedRef, type Ref } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
|
||||
// libs
|
||||
import { cloneDeep, merge } from "lodash-es";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import type { ConfigMap, Setting, SettingForm } from "@halo-dev/api-client";
|
||||
import type { FormKitSchemaCondition, FormKitSchemaNode } from "@formkit/core";
|
||||
import { Toast } from "@halo-dev/components";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const initialConfigMap: ConfigMap = {
|
||||
apiVersion: "v1alpha1",
|
||||
kind: "ConfigMap",
|
||||
metadata: {
|
||||
name: "",
|
||||
},
|
||||
data: {},
|
||||
};
|
||||
|
||||
interface useSettingFormReturn {
|
||||
setting: Ref<Setting | undefined>;
|
||||
configMap: Ref<ConfigMap>;
|
||||
configMapFormData: Ref<Record<string, Record<string, string>> | undefined>;
|
||||
saving: Ref<boolean>;
|
||||
handleFetchSettings: () => void;
|
||||
handleFetchConfigMap: () => void;
|
||||
handleSaveConfigMap: () => void;
|
||||
handleReset: () => void;
|
||||
}
|
||||
|
||||
export function useSettingForm(
|
||||
settingName: Ref<string | undefined>,
|
||||
configMapName: Ref<string | undefined>
|
||||
): useSettingFormReturn {
|
||||
const { t } = useI18n();
|
||||
|
||||
const setting = ref<Setting>();
|
||||
const configMap = ref<ConfigMap>(cloneDeep(initialConfigMap));
|
||||
const configMapFormData = ref<
|
||||
Record<string, Record<string, string>> | undefined
|
||||
>();
|
||||
|
||||
const saving = ref(false);
|
||||
|
||||
const handleFetchSettings = async () => {
|
||||
if (!settingName.value) {
|
||||
setting.value = undefined;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { data } = await apiClient.extension.setting.getv1alpha1Setting({
|
||||
name: settingName.value,
|
||||
});
|
||||
setting.value = data;
|
||||
|
||||
// init configMapFormData
|
||||
if (!configMapFormData.value) {
|
||||
const { forms } = setting.value.spec;
|
||||
const initialConfigMapFormData: Record<
|
||||
string,
|
||||
Record<string, string>
|
||||
> = {};
|
||||
forms.forEach((form) => {
|
||||
initialConfigMapFormData[form.group] = {};
|
||||
const formSchema = form.formSchema as (
|
||||
| FormKitSchemaCondition
|
||||
| FormKitSchemaNode
|
||||
)[];
|
||||
formSchema.forEach((schema) => {
|
||||
// @ts-ignore
|
||||
if ("name" in schema && "$formkit" in schema) {
|
||||
initialConfigMapFormData[form.group][schema.name] =
|
||||
schema.value || undefined;
|
||||
}
|
||||
});
|
||||
});
|
||||
configMapFormData.value = cloneDeep(initialConfigMapFormData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch setting", e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFetchConfigMap = async () => {
|
||||
if (!configMapName.value) {
|
||||
configMap.value = cloneDeep(initialConfigMap);
|
||||
configMapFormData.value = undefined;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await apiClient.extension.configMap.getv1alpha1ConfigMap(
|
||||
{
|
||||
name: configMapName.value,
|
||||
},
|
||||
{
|
||||
mute: true,
|
||||
}
|
||||
);
|
||||
|
||||
configMap.value = response.data;
|
||||
|
||||
const { data } = configMap.value;
|
||||
|
||||
if (data) {
|
||||
// merge objects value
|
||||
const { forms } = setting.value?.spec || {};
|
||||
|
||||
forms?.forEach((form) => {
|
||||
if (!configMapFormData.value) {
|
||||
return;
|
||||
}
|
||||
configMapFormData.value[form.group] = merge(
|
||||
configMapFormData.value[form.group] || {},
|
||||
JSON.parse(data[form.group] || "{}")
|
||||
);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch configMap", e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveConfigMap = async () => {
|
||||
try {
|
||||
saving.value = true;
|
||||
|
||||
if (!configMap.value.metadata.name && configMapName.value) {
|
||||
configMap.value.metadata.name = configMapName.value;
|
||||
}
|
||||
|
||||
setting.value?.spec.forms.forEach((item: SettingForm) => {
|
||||
// @ts-ignore
|
||||
configMap.value.data[item.group] = JSON.stringify(
|
||||
configMapFormData?.value?.[item.group]
|
||||
);
|
||||
});
|
||||
|
||||
if (!configMap.value.metadata.creationTimestamp) {
|
||||
const { data } =
|
||||
await apiClient.extension.configMap.createv1alpha1ConfigMap({
|
||||
configMap: configMap.value,
|
||||
});
|
||||
|
||||
configMapName.value = data.metadata.name;
|
||||
} else {
|
||||
const { data } =
|
||||
await apiClient.extension.configMap.updatev1alpha1ConfigMap({
|
||||
configMap: configMap.value,
|
||||
name: configMap.value.metadata.name,
|
||||
});
|
||||
configMapName.value = data.metadata.name;
|
||||
}
|
||||
|
||||
Toast.success(t("core.common.toast.save_success"));
|
||||
} catch (e) {
|
||||
console.error("Failed to save configMap", e);
|
||||
} finally {
|
||||
await handleFetchSettings();
|
||||
await handleFetchConfigMap();
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setting.value = undefined;
|
||||
configMap.value = cloneDeep(initialConfigMap);
|
||||
configMapFormData.value = undefined;
|
||||
};
|
||||
|
||||
return {
|
||||
setting,
|
||||
configMap,
|
||||
configMapFormData,
|
||||
saving,
|
||||
handleFetchSettings,
|
||||
handleFetchConfigMap,
|
||||
handleSaveConfigMap,
|
||||
handleReset,
|
||||
};
|
||||
}
|
||||
|
||||
interface useSettingFormConvertReturn {
|
||||
formSchema: ComputedRef<
|
||||
|
|
|
@ -4,28 +4,29 @@ import {
|
|||
IconArrowRight,
|
||||
IconCheckboxFill,
|
||||
IconDatabase2Line,
|
||||
IconFolder,
|
||||
IconGrid,
|
||||
IconList,
|
||||
IconUpload,
|
||||
IconRefreshLine,
|
||||
IconUpload,
|
||||
Toast,
|
||||
VButton,
|
||||
VCard,
|
||||
VDropdown,
|
||||
VDropdownItem,
|
||||
VEmpty,
|
||||
VLoading,
|
||||
VPageHeader,
|
||||
VPagination,
|
||||
VSpace,
|
||||
VEmpty,
|
||||
IconFolder,
|
||||
VLoading,
|
||||
Toast,
|
||||
VDropdown,
|
||||
VDropdownItem,
|
||||
} from "@halo-dev/components";
|
||||
import LazyImage from "@/components/image/LazyImage.vue";
|
||||
import AttachmentDetailModal from "./components/AttachmentDetailModal.vue";
|
||||
import AttachmentUploadModal from "./components/AttachmentUploadModal.vue";
|
||||
import AttachmentPoliciesModal from "./components/AttachmentPoliciesModal.vue";
|
||||
import AttachmentGroupList from "./components/AttachmentGroupList.vue";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, onMounted, provide, ref, watch } from "vue";
|
||||
import type { Attachment, Group } from "@halo-dev/api-client";
|
||||
import { useFetchAttachmentPolicy } from "./composables/use-attachment-policy";
|
||||
import { useAttachmentControl } from "./composables/use-attachment";
|
||||
|
@ -37,8 +38,6 @@ import { useFetchAttachmentGroup } from "./composables/use-attachment-group";
|
|||
import { useI18n } from "vue-i18n";
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
import UserFilterDropdown from "@/components/filter/UserFilterDropdown.vue";
|
||||
import { provide } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import AttachmentListItem from "./components/AttachmentListItem.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -50,7 +49,7 @@ const detailVisible = ref(false);
|
|||
const { policies } = useFetchAttachmentPolicy();
|
||||
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
|
||||
|
||||
const selectedGroup = ref<Group>();
|
||||
const selectedGroup = useRouteQuery<string | undefined>("group");
|
||||
|
||||
// Filter
|
||||
const keyword = useRouteQuery<string>("keyword", "");
|
||||
|
@ -111,12 +110,8 @@ const {
|
|||
isChecked,
|
||||
handleReset,
|
||||
} = useAttachmentControl({
|
||||
group: selectedGroup,
|
||||
policy: computed(() => {
|
||||
return policies.value?.find(
|
||||
(policy) => policy.metadata.name === selectedPolicy.value
|
||||
);
|
||||
}),
|
||||
groupName: selectedGroup,
|
||||
policyName: selectedPolicy,
|
||||
user: selectedUser,
|
||||
accepts: computed(() => {
|
||||
if (!selectedAccepts.value) {
|
||||
|
@ -187,6 +182,7 @@ const onDetailModalClose = () => {
|
|||
const onUploadModalClose = () => {
|
||||
routeQueryAction.value = undefined;
|
||||
handleFetchAttachments();
|
||||
uploadVisible.value = false;
|
||||
};
|
||||
|
||||
// View type
|
||||
|
@ -258,11 +254,11 @@ onMounted(() => {
|
|||
</span>
|
||||
</template>
|
||||
</AttachmentDetailModal>
|
||||
<AttachmentUploadModal
|
||||
v-model:visible="uploadVisible"
|
||||
@close="onUploadModalClose"
|
||||
<AttachmentUploadModal v-if="uploadVisible" @close="onUploadModalClose" />
|
||||
<AttachmentPoliciesModal
|
||||
v-if="policyVisible"
|
||||
@close="policyVisible = false"
|
||||
/>
|
||||
<AttachmentPoliciesModal v-model:visible="policyVisible" />
|
||||
<VPageHeader :title="$t('core.attachment.title')">
|
||||
<template #icon>
|
||||
<IconFolder class="mr-2 self-center" />
|
||||
|
@ -470,7 +466,6 @@ onMounted(() => {
|
|||
|
||||
<div :style="`${viewType === 'list' ? 'padding:12px 16px 0' : ''}`">
|
||||
<AttachmentGroupList
|
||||
v-model:selected-group="selectedGroup"
|
||||
@select="handleReset"
|
||||
@update="handleFetchGroups"
|
||||
@reload-attachments="handleFetchAttachments"
|
||||
|
|
|
@ -2,32 +2,29 @@
|
|||
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
|
||||
import SubmitButton from "@/components/button/SubmitButton.vue";
|
||||
import type { Group } from "@halo-dev/api-client";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { reset } from "@formkit/core";
|
||||
import { setFocus } from "@/formkit/utils/focus";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean;
|
||||
group: Group | null;
|
||||
group?: Group;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
group: null,
|
||||
group: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:visible", visible: boolean): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const initialFormState: Group = {
|
||||
const modal = ref();
|
||||
const formState = ref<Group>({
|
||||
spec: {
|
||||
displayName: "",
|
||||
},
|
||||
|
@ -37,25 +34,17 @@ const initialFormState: Group = {
|
|||
name: "",
|
||||
generateName: "attachment-group-",
|
||||
},
|
||||
};
|
||||
|
||||
const formState = ref<Group>(cloneDeep(initialFormState));
|
||||
});
|
||||
const saving = ref(false);
|
||||
|
||||
const isUpdateMode = computed(() => {
|
||||
return !!formState.value.metadata.creationTimestamp;
|
||||
});
|
||||
|
||||
const modalTitle = computed(() => {
|
||||
return isUpdateMode.value
|
||||
? t("core.attachment.group_editing_modal.titles.update")
|
||||
: t("core.attachment.group_editing_modal.titles.create");
|
||||
});
|
||||
const modalTitle = props.group
|
||||
? t("core.attachment.group_editing_modal.titles.update")
|
||||
: t("core.attachment.group_editing_modal.titles.create");
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
saving.value = true;
|
||||
if (isUpdateMode.value) {
|
||||
if (props.group) {
|
||||
await apiClient.extension.storage.group.updatestorageHaloRunV1alpha1Group(
|
||||
{
|
||||
name: formState.value.metadata.name,
|
||||
|
@ -71,7 +60,7 @@ const handleSave = async () => {
|
|||
}
|
||||
|
||||
Toast.success(t("core.common.toast.save_success"));
|
||||
onVisibleChange(false);
|
||||
modal.value.close();
|
||||
} catch (e) {
|
||||
console.error("Failed to save attachment group", e);
|
||||
} finally {
|
||||
|
@ -79,47 +68,16 @@ const handleSave = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
emit("update:visible", visible);
|
||||
if (!visible) {
|
||||
emit("close");
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
setFocus("displayNameInput");
|
||||
|
||||
const handleResetForm = () => {
|
||||
formState.value = cloneDeep(initialFormState);
|
||||
reset("attachment-group-form");
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
setFocus("displayNameInput");
|
||||
} else {
|
||||
handleResetForm();
|
||||
}
|
||||
if (props.group) {
|
||||
formState.value = cloneDeep(props.group);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.group,
|
||||
(group) => {
|
||||
if (group) {
|
||||
formState.value = cloneDeep(group);
|
||||
} else {
|
||||
handleResetForm();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VModal
|
||||
:title="modalTitle"
|
||||
:visible="visible"
|
||||
:width="500"
|
||||
@update:visible="onVisibleChange"
|
||||
>
|
||||
<VModal ref="modal" :title="modalTitle" :width="500" @close="emit('close')">
|
||||
<FormKit
|
||||
id="attachment-group-form"
|
||||
name="attachment-group-form"
|
||||
|
@ -142,14 +100,13 @@ watch(
|
|||
<template #footer>
|
||||
<VSpace>
|
||||
<SubmitButton
|
||||
v-if="visible"
|
||||
:loading="saving"
|
||||
type="secondary"
|
||||
:text="$t('core.common.buttons.submit')"
|
||||
@submit="$formkit.submit('attachment-group-form')"
|
||||
>
|
||||
</SubmitButton>
|
||||
<VButton @click="onVisibleChange(false)">
|
||||
<VButton @click="modal.close()">
|
||||
{{ $t("core.common.buttons.cancel_and_shortcut") }}
|
||||
</VButton>
|
||||
</VSpace>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
// core libs
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
// components
|
||||
import {
|
||||
|
@ -26,17 +26,14 @@ const { t } = useI18n();
|
|||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selectedGroup: Group | undefined;
|
||||
readonly?: boolean;
|
||||
}>(),
|
||||
{
|
||||
selectedGroup: undefined,
|
||||
readonly: false,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:selectedGroup", group: Group): void;
|
||||
(event: "select", group: Group): void;
|
||||
(event: "update"): void;
|
||||
(event: "reload-attachments"): void;
|
||||
|
@ -67,19 +64,17 @@ const defaultGroups: Group[] = [
|
|||
|
||||
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
|
||||
|
||||
const groupToUpdate = ref<Group | null>(null);
|
||||
const groupToUpdate = ref<Group>();
|
||||
const loading = ref<boolean>(false);
|
||||
const editingModal = ref(false);
|
||||
|
||||
const routeQuery = useRouteQuery<string>("group");
|
||||
const selectedGroup = props.readonly
|
||||
? ref("")
|
||||
: useRouteQuery<string>("group", "");
|
||||
|
||||
const handleSelectGroup = (group: Group) => {
|
||||
emit("update:selectedGroup", group);
|
||||
emit("select", group);
|
||||
|
||||
if (!props.readonly) {
|
||||
routeQuery.value = group.metadata.name;
|
||||
}
|
||||
selectedGroup.value = group.metadata.name;
|
||||
};
|
||||
|
||||
const handleOpenEditingModal = (group: Group) => {
|
||||
|
@ -90,6 +85,7 @@ const handleOpenEditingModal = (group: Group) => {
|
|||
const onEditingModalClose = () => {
|
||||
emit("update");
|
||||
handleFetchGroups();
|
||||
editingModal.value = false;
|
||||
};
|
||||
|
||||
const handleDelete = (group: Group) => {
|
||||
|
@ -181,51 +177,20 @@ const handleDeleteWithAttachments = (group: Group) => {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => groups.value?.length,
|
||||
() => {
|
||||
const allGroups = [...defaultGroups, ...(groups.value || [])];
|
||||
const groupIndex = allGroups.findIndex(
|
||||
(group) => group.metadata.name === routeQuery.value
|
||||
);
|
||||
|
||||
if (groupIndex < 0) {
|
||||
handleSelectGroup(defaultGroups[0]);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await handleFetchGroups();
|
||||
if (routeQuery.value && !props.readonly) {
|
||||
const allGroups = [...defaultGroups, ...(groups.value || [])];
|
||||
const group = allGroups.find(
|
||||
(group) => group.metadata.name === routeQuery.value
|
||||
);
|
||||
if (group) {
|
||||
handleSelectGroup(group);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectGroup(defaultGroups[0]);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<AttachmentGroupEditingModal
|
||||
v-if="!readonly"
|
||||
v-model:visible="editingModal"
|
||||
v-if="!readonly && editingModal"
|
||||
:group="groupToUpdate"
|
||||
@close="onEditingModalClose"
|
||||
/>
|
||||
<div class="mb-5 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-6">
|
||||
<div
|
||||
v-for="(defaultGroup, index) in defaultGroups"
|
||||
:key="index"
|
||||
v-for="defaultGroup in defaultGroups"
|
||||
:key="defaultGroup.metadata.name"
|
||||
:class="{
|
||||
'!bg-gray-200 !text-gray-900':
|
||||
defaultGroup.metadata.name === selectedGroup?.metadata.name,
|
||||
defaultGroup.metadata.name === selectedGroup,
|
||||
}"
|
||||
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="handleSelectGroup(defaultGroup)"
|
||||
|
@ -235,11 +200,10 @@ onMounted(async () => {
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(group, index) in groups"
|
||||
:key="index"
|
||||
v-for="group in groups"
|
||||
:key="group.metadata.name"
|
||||
:class="{
|
||||
'!bg-gray-200 !text-gray-900':
|
||||
group.metadata.name === selectedGroup?.metadata.name,
|
||||
'!bg-gray-200 !text-gray-900': group.metadata.name === selectedGroup,
|
||||
}"
|
||||
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="handleSelectGroup(group)"
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<script lang="ts" setup>
|
||||
import {
|
||||
VSpace,
|
||||
VStatusDot,
|
||||
Dialog,
|
||||
Toast,
|
||||
VDropdownDivider,
|
||||
VDropdownItem,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
Toast,
|
||||
VDropdownItem,
|
||||
Dialog,
|
||||
VDropdownDivider,
|
||||
VSpace,
|
||||
VStatusDot,
|
||||
} from "@halo-dev/components";
|
||||
import { computed, ref } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, inject, markRaw, ref, toRefs } from "vue";
|
||||
import type { Attachment } from "@halo-dev/api-client";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
|
@ -17,14 +18,10 @@ import { useFetchAttachmentPolicy } from "../composables/use-attachment-policy";
|
|||
import { apiClient } from "@/utils/api-client";
|
||||
import { usePermission } from "@/utils/permission";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { inject } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import { useQueryClient } from "@tanstack/vue-query";
|
||||
import { useOperationItemExtensionPoint } from "@console/composables/use-operation-extension-points";
|
||||
import { toRefs } from "vue";
|
||||
import type { OperationItem } from "@halo-dev/console-shared";
|
||||
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";
|
||||
import { markRaw } from "vue";
|
||||
|
||||
const { currentUserHasPermission } = usePermission();
|
||||
const { t } = useI18n();
|
||||
|
@ -52,7 +49,7 @@ const selectedAttachments = inject<Ref<Set<Attachment>>>(
|
|||
ref<Set<Attachment>>(new Set())
|
||||
);
|
||||
|
||||
const policyName = computed(() => {
|
||||
const policyDisplayName = computed(() => {
|
||||
const policy = policies.value?.find(
|
||||
(p) => p.metadata.name === props.attachment.spec.policyName
|
||||
);
|
||||
|
@ -180,7 +177,7 @@ const { operationItems } = useOperationItemExtensionPoint<Attachment>(
|
|||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<VEntityField :description="policyName" />
|
||||
<VEntityField :description="policyDisplayName" />
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<RouterLink
|
||||
|
|
|
@ -2,16 +2,14 @@
|
|||
import { VButton } from "@halo-dev/components";
|
||||
import type { Attachment } from "@halo-dev/api-client";
|
||||
import { useAttachmentPermalinkCopy } from "../composables/use-attachment";
|
||||
import { toRefs, computed } from "vue";
|
||||
import { computed, toRefs } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean;
|
||||
attachment?: Attachment;
|
||||
mountToBody?: boolean;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
attachment: undefined,
|
||||
mountToBody: false,
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<script lang="ts" setup>
|
||||
import {
|
||||
IconAddCircle,
|
||||
VButton,
|
||||
VModal,
|
||||
VSpace,
|
||||
VEmpty,
|
||||
Dialog,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VStatusDot,
|
||||
IconAddCircle,
|
||||
Toast,
|
||||
VButton,
|
||||
VDropdown,
|
||||
VDropdownItem,
|
||||
Toast,
|
||||
VEmpty,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VModal,
|
||||
VSpace,
|
||||
VStatusDot,
|
||||
} from "@halo-dev/components";
|
||||
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
||||
import { ref } from "vue";
|
||||
|
@ -24,17 +24,7 @@ import {
|
|||
import { apiClient } from "@/utils/api-client";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:visible", visible: boolean): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
|
@ -43,36 +33,19 @@ const { t } = useI18n();
|
|||
const { policies, isLoading, handleFetchPolicies } = useFetchAttachmentPolicy();
|
||||
const { policyTemplates } = useFetchAttachmentPolicyTemplate();
|
||||
|
||||
const modal = ref();
|
||||
const selectedPolicy = ref<Policy>();
|
||||
const selectedTemplateName = ref();
|
||||
|
||||
const policyEditingModal = ref(false);
|
||||
|
||||
function onVisibleChange(visible: boolean) {
|
||||
emit("update:visible", visible);
|
||||
if (!visible) {
|
||||
emit("close");
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpenEditingModal = (policy: Policy) => {
|
||||
selectedPolicy.value = policy;
|
||||
policyEditingModal.value = true;
|
||||
};
|
||||
|
||||
const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
|
||||
selectedPolicy.value = {
|
||||
spec: {
|
||||
displayName: "",
|
||||
templateName: policyTemplate.metadata.name,
|
||||
configMapName: "",
|
||||
},
|
||||
apiVersion: "storage.halo.run/v1alpha1",
|
||||
kind: "Policy",
|
||||
metadata: {
|
||||
name: "",
|
||||
generateName: "attachment-policy-",
|
||||
},
|
||||
};
|
||||
selectedTemplateName.value = policyTemplate.metadata.name;
|
||||
policyEditingModal.value = true;
|
||||
};
|
||||
|
||||
|
@ -116,16 +89,17 @@ const handleDelete = async (policy: Policy) => {
|
|||
const onEditingModalClose = () => {
|
||||
selectedPolicy.value = undefined;
|
||||
handleFetchPolicies();
|
||||
policyEditingModal.value = false;
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<VModal
|
||||
:visible="visible"
|
||||
ref="modal"
|
||||
:width="750"
|
||||
:title="$t('core.attachment.policies_modal.title')"
|
||||
:body-class="['!p-0']"
|
||||
:layer-closable="true"
|
||||
@update:visible="onVisibleChange"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<template #actions>
|
||||
<VDropdown>
|
||||
|
@ -216,16 +190,16 @@ const onEditingModalClose = () => {
|
|||
</li>
|
||||
</ul>
|
||||
<template #footer>
|
||||
<VButton @click="onVisibleChange(false)">
|
||||
<VButton @click="modal.close()">
|
||||
{{ $t("core.common.buttons.close_and_shortcut") }}
|
||||
</VButton>
|
||||
</template>
|
||||
</VModal>
|
||||
|
||||
<AttachmentPolicyEditingModal
|
||||
v-if="visible"
|
||||
v-model:visible="policyEditingModal"
|
||||
v-if="policyEditingModal"
|
||||
:policy="selectedPolicy"
|
||||
:template-name="selectedTemplateName"
|
||||
@close="onEditingModalClose"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -1,38 +1,37 @@
|
|||
<script lang="ts" setup>
|
||||
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
|
||||
import { Toast, VButton, VLoading, VModal, VSpace } from "@halo-dev/components";
|
||||
import SubmitButton from "@/components/button/SubmitButton.vue";
|
||||
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
||||
import type { Policy } from "@halo-dev/api-client";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { computed, ref, toRaw, watch, watchEffect } from "vue";
|
||||
import { useSettingForm } from "@console/composables/use-setting-form";
|
||||
import { computed, onMounted, ref, toRaw, toRefs } from "vue";
|
||||
import { useSettingFormConvert } from "@console/composables/use-setting-form";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import {
|
||||
reset,
|
||||
type FormKitSchemaCondition,
|
||||
type FormKitSchemaNode,
|
||||
} from "@formkit/core";
|
||||
import { setFocus } from "@/formkit/utils/focus";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean;
|
||||
policy?: Policy;
|
||||
templateName?: string;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
policy: undefined,
|
||||
templateName: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
const { policy } = toRefs(props);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:visible", visible: boolean): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const initialFormState: Policy = {
|
||||
const modal = ref();
|
||||
|
||||
const formState = ref<Policy>({
|
||||
spec: {
|
||||
displayName: "",
|
||||
templateName: "",
|
||||
|
@ -44,82 +43,106 @@ const initialFormState: Policy = {
|
|||
name: "",
|
||||
generateName: "attachment-policy-",
|
||||
},
|
||||
};
|
||||
|
||||
const formState = ref<Policy>(cloneDeep(initialFormState));
|
||||
const policyTemplate = ref<PolicyTemplate | undefined>();
|
||||
|
||||
const settingName = computed(() => policyTemplate.value?.spec?.settingName);
|
||||
|
||||
const configMapName = computed({
|
||||
get() {
|
||||
return formState.value.spec.configMapName;
|
||||
},
|
||||
set(value) {
|
||||
formState.value.spec.configMapName = value;
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
const isUpdateMode = !!props.policy;
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.policy) {
|
||||
formState.value = cloneDeep(props.policy);
|
||||
}
|
||||
if (props.templateName) {
|
||||
formState.value.spec.templateName = props.templateName;
|
||||
}
|
||||
|
||||
setFocus("displayNameInput");
|
||||
});
|
||||
|
||||
const { data: policyTemplate } = useQuery({
|
||||
queryKey: [
|
||||
"core:attachment:policy-template",
|
||||
formState.value.spec.templateName,
|
||||
],
|
||||
queryFn: async () => {
|
||||
const { data } =
|
||||
await apiClient.extension.storage.policyTemplate.getstorageHaloRunV1alpha1PolicyTemplate(
|
||||
{
|
||||
name: formState.value.spec.templateName,
|
||||
}
|
||||
);
|
||||
return data;
|
||||
},
|
||||
retry: 0,
|
||||
enabled: computed(() => !!formState.value.spec.templateName),
|
||||
});
|
||||
|
||||
const { data: setting, isLoading } = useQuery({
|
||||
queryKey: [
|
||||
"core:attachment:policy-template:setting",
|
||||
policyTemplate.value?.spec?.settingName,
|
||||
],
|
||||
queryFn: async () => {
|
||||
if (!policyTemplate.value?.spec?.settingName) {
|
||||
throw new Error("No setting found");
|
||||
}
|
||||
|
||||
const { data } = await apiClient.extension.setting.getv1alpha1Setting({
|
||||
name: policyTemplate.value.spec.settingName,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
retry: 0,
|
||||
enabled: computed(() => !!policyTemplate.value?.spec?.settingName),
|
||||
});
|
||||
|
||||
const { data: configMap } = useQuery({
|
||||
queryKey: [
|
||||
"core:attachment:policy-template:configMap",
|
||||
policy.value?.spec.configMapName,
|
||||
],
|
||||
initialData: {
|
||||
data: {},
|
||||
apiVersion: "v1alpha1",
|
||||
kind: "ConfigMap",
|
||||
metadata: {
|
||||
generateName: "configMap-",
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
retry: 0,
|
||||
queryFn: async () => {
|
||||
if (!policy.value?.spec.configMapName) {
|
||||
throw new Error("No configMap found");
|
||||
}
|
||||
const { data } = await apiClient.extension.configMap.getv1alpha1ConfigMap({
|
||||
name: policy.value?.spec.configMapName,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
enabled: computed(() => !!policy.value?.spec.configMapName),
|
||||
});
|
||||
|
||||
const { configMapFormData, formSchema, convertToSave } = useSettingFormConvert(
|
||||
setting,
|
||||
configMapFormData,
|
||||
configMap,
|
||||
saving,
|
||||
handleFetchConfigMap,
|
||||
handleFetchSettings,
|
||||
handleSaveConfigMap,
|
||||
handleReset: handleResetSettingForm,
|
||||
} = useSettingForm(settingName, configMapName);
|
||||
ref("default")
|
||||
);
|
||||
|
||||
const formSchema = computed(() => {
|
||||
if (!setting.value) {
|
||||
return undefined;
|
||||
}
|
||||
const { forms } = setting.value.spec;
|
||||
return forms.find((item) => item.group === "default")?.formSchema as (
|
||||
| FormKitSchemaCondition
|
||||
| FormKitSchemaNode
|
||||
)[];
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (settingName.value) {
|
||||
handleFetchSettings();
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (configMapName.value && setting.value) {
|
||||
handleFetchConfigMap();
|
||||
}
|
||||
});
|
||||
|
||||
const isUpdateMode = computed(() => {
|
||||
return !!formState.value.metadata.creationTimestamp;
|
||||
});
|
||||
|
||||
const modalTitle = computed(() => {
|
||||
return isUpdateMode.value
|
||||
? t("core.attachment.policy_editing_modal.titles.update", {
|
||||
policy: props.policy?.spec.displayName,
|
||||
})
|
||||
: t("core.attachment.policy_editing_modal.titles.create", {
|
||||
policy_template: policyTemplate.value?.spec?.displayName,
|
||||
});
|
||||
});
|
||||
const submitting = ref(false);
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
saving.value = true;
|
||||
submitting.value = true;
|
||||
|
||||
if (!isUpdateMode.value) {
|
||||
configMap.value.metadata.name = "";
|
||||
configMap.value.metadata.generateName = "configMap-";
|
||||
}
|
||||
const configMapToUpdate = convertToSave();
|
||||
|
||||
await handleSaveConfigMap();
|
||||
if (isUpdateMode) {
|
||||
await apiClient.extension.configMap.updatev1alpha1ConfigMap({
|
||||
name: configMap.value.metadata.name,
|
||||
configMap: configMapToUpdate,
|
||||
});
|
||||
|
||||
if (isUpdateMode.value) {
|
||||
await apiClient.extension.storage.policy.updatestorageHaloRunV1alpha1Policy(
|
||||
{
|
||||
name: formState.value.metadata.name,
|
||||
|
@ -127,7 +150,12 @@ const handleSave = async () => {
|
|||
}
|
||||
);
|
||||
} else {
|
||||
formState.value.spec.configMapName = configMap.value.metadata.name;
|
||||
const { data: newConfigMap } =
|
||||
await apiClient.extension.configMap.createv1alpha1ConfigMap({
|
||||
configMap: configMapToUpdate,
|
||||
});
|
||||
|
||||
formState.value.spec.configMapName = newConfigMap.metadata.name;
|
||||
await apiClient.extension.storage.policy.createstorageHaloRunV1alpha1Policy(
|
||||
{
|
||||
policy: formState.value,
|
||||
|
@ -136,117 +164,68 @@ const handleSave = async () => {
|
|||
}
|
||||
|
||||
Toast.success(t("core.common.toast.save_success"));
|
||||
onVisibleChange(false);
|
||||
modal.value.close();
|
||||
} catch (e) {
|
||||
console.error("Failed to save attachment policy", e);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
submitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
formState.value = cloneDeep(initialFormState);
|
||||
reset("attachment-policy-form");
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
setFocus("displayNameInput");
|
||||
} else {
|
||||
const timer = setTimeout(() => {
|
||||
policyTemplate.value = undefined;
|
||||
handleResetForm();
|
||||
handleResetSettingForm();
|
||||
clearTimeout(timer);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.policy,
|
||||
async (policy) => {
|
||||
if (policy) {
|
||||
formState.value = cloneDeep(policy);
|
||||
|
||||
const { templateName } = formState.value.spec;
|
||||
|
||||
// Get policy template
|
||||
if (templateName) {
|
||||
const { data } =
|
||||
await apiClient.extension.storage.policyTemplate.getstorageHaloRunV1alpha1PolicyTemplate(
|
||||
{
|
||||
name: templateName,
|
||||
}
|
||||
);
|
||||
policyTemplate.value = data;
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
policyTemplate.value = undefined;
|
||||
handleResetForm();
|
||||
handleResetSettingForm();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
emit("update:visible", visible);
|
||||
if (!visible) {
|
||||
emit("close");
|
||||
}
|
||||
};
|
||||
const modalTitle = props.policy
|
||||
? t("core.attachment.policy_editing_modal.titles.update", {
|
||||
policy: props.policy?.spec.displayName,
|
||||
})
|
||||
: t("core.attachment.policy_editing_modal.titles.create", {
|
||||
policy_template: policyTemplate.value?.spec?.displayName,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VModal
|
||||
:title="modalTitle"
|
||||
:visible="visible"
|
||||
:width="600"
|
||||
@update:visible="onVisibleChange"
|
||||
>
|
||||
<VModal ref="modal" :title="modalTitle" :width="600" @close="emit('close')">
|
||||
<div>
|
||||
<FormKit
|
||||
v-if="formSchema && configMapFormData"
|
||||
id="attachment-policy-form"
|
||||
v-model="configMapFormData['default']"
|
||||
name="attachment-policy-form"
|
||||
:actions="false"
|
||||
:preserve="true"
|
||||
type="form"
|
||||
:config="{ validationVisibility: 'submit' }"
|
||||
@submit="handleSave"
|
||||
>
|
||||
<VLoading v-if="isLoading" />
|
||||
<template v-else>
|
||||
<FormKit
|
||||
id="displayNameInput"
|
||||
v-model="formState.spec.displayName"
|
||||
:label="
|
||||
$t('core.attachment.policy_editing_modal.fields.display_name.label')
|
||||
"
|
||||
type="text"
|
||||
name="displayName"
|
||||
validation="required|length:0,50"
|
||||
></FormKit>
|
||||
<FormKitSchema
|
||||
:schema="toRaw(formSchema)"
|
||||
:data="configMapFormData['default']"
|
||||
/>
|
||||
</FormKit>
|
||||
v-if="formSchema && configMapFormData"
|
||||
id="attachment-policy-form"
|
||||
v-model="configMapFormData['default']"
|
||||
name="attachment-policy-form"
|
||||
:actions="false"
|
||||
:preserve="true"
|
||||
type="form"
|
||||
:config="{ validationVisibility: 'submit' }"
|
||||
@submit="handleSave"
|
||||
>
|
||||
<FormKit
|
||||
id="displayNameInput"
|
||||
v-model="formState.spec.displayName"
|
||||
:label="
|
||||
$t(
|
||||
'core.attachment.policy_editing_modal.fields.display_name.label'
|
||||
)
|
||||
"
|
||||
type="text"
|
||||
name="displayName"
|
||||
validation="required|length:0,50"
|
||||
></FormKit>
|
||||
<FormKitSchema
|
||||
:schema="toRaw(formSchema)"
|
||||
:data="configMapFormData['default']"
|
||||
/>
|
||||
</FormKit>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<VSpace>
|
||||
<SubmitButton
|
||||
v-if="visible"
|
||||
:loading="saving"
|
||||
:loading="submitting"
|
||||
type="secondary"
|
||||
:text="$t('core.common.buttons.submit')"
|
||||
@submit="$formkit.submit('attachment-policy-form')"
|
||||
>
|
||||
</SubmitButton>
|
||||
<VButton @click="onVisibleChange(false)">
|
||||
<VButton @click="modal.close()">
|
||||
{{ $t("core.common.buttons.cancel_and_shortcut") }}
|
||||
</VButton>
|
||||
</VSpace>
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
VModal,
|
||||
} from "@halo-dev/components";
|
||||
import { ref, watch } from "vue";
|
||||
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
||||
import type { PolicyTemplate } from "@halo-dev/api-client";
|
||||
import {
|
||||
useFetchAttachmentPolicy,
|
||||
useFetchAttachmentPolicyTemplate,
|
||||
|
@ -16,17 +16,7 @@ import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
|
|||
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:visible", visible: boolean): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
|
@ -34,11 +24,11 @@ const { groups } = useFetchAttachmentGroup();
|
|||
const { policies, handleFetchPolicies } = useFetchAttachmentPolicy();
|
||||
const { policyTemplates } = useFetchAttachmentPolicyTemplate();
|
||||
|
||||
const modal = ref();
|
||||
const selectedGroupName = useLocalStorage("attachment-upload-group", "");
|
||||
const selectedPolicyName = useLocalStorage("attachment-upload-policy", "");
|
||||
const policyToCreate = ref<Policy>();
|
||||
const uploadVisible = ref(false);
|
||||
const policyEditingModal = ref(false);
|
||||
const policyTemplateNameToCreate = ref();
|
||||
|
||||
watch(
|
||||
() => groups.value,
|
||||
|
@ -71,57 +61,24 @@ watch(
|
|||
);
|
||||
|
||||
const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
|
||||
policyToCreate.value = {
|
||||
spec: {
|
||||
displayName: "",
|
||||
templateName: policyTemplate.metadata.name,
|
||||
configMapName: "",
|
||||
},
|
||||
apiVersion: "storage.halo.run/v1alpha1",
|
||||
kind: "Policy",
|
||||
metadata: {
|
||||
name: "",
|
||||
generateName: "attachment-policy-",
|
||||
},
|
||||
};
|
||||
policyTemplateNameToCreate.value = policyTemplate.metadata.name;
|
||||
policyEditingModal.value = true;
|
||||
};
|
||||
|
||||
const onEditingModalClose = async () => {
|
||||
await handleFetchPolicies();
|
||||
policyToCreate.value = policies.value?.[0];
|
||||
selectedPolicyName.value = policies.value?.[0].metadata.name;
|
||||
policyEditingModal.value = false;
|
||||
};
|
||||
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
emit("update:visible", visible);
|
||||
if (!visible) {
|
||||
emit("close");
|
||||
policyEditingModal.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
uploadVisible.value = true;
|
||||
} else {
|
||||
const uploadVisibleTimer = setTimeout(() => {
|
||||
uploadVisible.value = false;
|
||||
clearTimeout(uploadVisibleTimer);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<VModal
|
||||
ref="modal"
|
||||
:body-class="['!p-0']"
|
||||
:visible="visible"
|
||||
:width="650"
|
||||
:centered="false"
|
||||
:title="$t('core.attachment.upload_modal.title')"
|
||||
@update:visible="onVisibleChange"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<div class="w-full p-4">
|
||||
<div class="mb-2">
|
||||
|
@ -213,7 +170,6 @@ watch(
|
|||
/>
|
||||
</div>
|
||||
<UppyUpload
|
||||
v-if="uploadVisible"
|
||||
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
||||
:disabled="!selectedPolicyName"
|
||||
:meta="{
|
||||
|
@ -226,15 +182,14 @@ watch(
|
|||
? ''
|
||||
: $t('core.attachment.upload_modal.filters.policy.not_select')
|
||||
"
|
||||
:done-button-handler="() => onVisibleChange(false)"
|
||||
:done-button-handler="() => modal.close()"
|
||||
/>
|
||||
</div>
|
||||
</VModal>
|
||||
|
||||
<AttachmentPolicyEditingModal
|
||||
v-if="visible"
|
||||
v-model:visible="policyEditingModal"
|
||||
:policy="policyToCreate"
|
||||
v-if="policyEditingModal"
|
||||
:template-name="policyTemplateNameToCreate"
|
||||
@close="onEditingModalClose"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<script lang="ts" setup>
|
||||
import {
|
||||
IconCheckboxFill,
|
||||
IconArrowLeft,
|
||||
IconArrowRight,
|
||||
IconCheckboxCircle,
|
||||
IconCheckboxFill,
|
||||
IconEye,
|
||||
IconUpload,
|
||||
VButton,
|
||||
VCard,
|
||||
VEmpty,
|
||||
VSpace,
|
||||
VButton,
|
||||
IconUpload,
|
||||
VPagination,
|
||||
IconEye,
|
||||
IconCheckboxCircle,
|
||||
VSpace,
|
||||
} from "@halo-dev/components";
|
||||
import { watchEffect, ref } from "vue";
|
||||
import { computed, ref, watchEffect } from "vue";
|
||||
import { isImage } from "@/utils/image";
|
||||
import { useAttachmentControl } from "../../composables/use-attachment";
|
||||
import LazyImage from "@/components/image/LazyImage.vue";
|
||||
|
@ -22,7 +22,6 @@ import AttachmentUploadModal from "../AttachmentUploadModal.vue";
|
|||
import AttachmentDetailModal from "../AttachmentDetailModal.vue";
|
||||
import AttachmentGroupList from "../AttachmentGroupList.vue";
|
||||
import { matchMediaTypes } from "@/utils/media-type";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -44,7 +43,7 @@ const emit = defineEmits<{
|
|||
(event: "change-provider", providerId: string): void;
|
||||
}>();
|
||||
|
||||
const selectedGroup = ref<Group>();
|
||||
const selectedGroup = ref();
|
||||
const page = ref(1);
|
||||
const size = ref(60);
|
||||
|
||||
|
@ -61,7 +60,7 @@ const {
|
|||
handleReset,
|
||||
isChecked,
|
||||
} = useAttachmentControl({
|
||||
group: selectedGroup,
|
||||
groupName: selectedGroup,
|
||||
accepts: computed(() => {
|
||||
return props.accepts;
|
||||
}),
|
||||
|
@ -97,13 +96,19 @@ const isDisabled = (attachment: Attachment) => {
|
|||
|
||||
return !isMatchMediaType;
|
||||
};
|
||||
|
||||
function onUploadModalClose() {
|
||||
handleFetchAttachments();
|
||||
uploadVisible.value = false;
|
||||
}
|
||||
|
||||
function onGroupSelect(group: Group) {
|
||||
selectedGroup.value = group.metadata.name;
|
||||
handleReset();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<AttachmentGroupList
|
||||
v-model:selected-group="selectedGroup"
|
||||
readonly
|
||||
@select="handleReset"
|
||||
/>
|
||||
<AttachmentGroupList readonly @select="onGroupSelect" />
|
||||
<div v-if="attachments?.length" class="mb-5">
|
||||
<VButton @click="uploadVisible = true">
|
||||
<template #icon>
|
||||
|
@ -216,10 +221,7 @@ const isDisabled = (attachment: Attachment) => {
|
|||
:size-options="[60, 120, 200]"
|
||||
/>
|
||||
</div>
|
||||
<AttachmentUploadModal
|
||||
v-model:visible="uploadVisible"
|
||||
@close="handleFetchAttachments"
|
||||
/>
|
||||
<AttachmentUploadModal v-if="uploadVisible" @close="onUploadModalClose" />
|
||||
<AttachmentDetailModal
|
||||
v-model:visible="detailVisible"
|
||||
:mount-to-body="true"
|
||||
|
|
|
@ -20,14 +20,15 @@ export function useFetchAttachmentGroup(): useFetchAttachmentGroupReturn {
|
|||
sort: ["metadata.creationTimestamp,asc"],
|
||||
}
|
||||
);
|
||||
|
||||
return data.items;
|
||||
},
|
||||
refetchInterval(data) {
|
||||
const deletingGroups = data?.filter(
|
||||
const hasDeletingGroup = data?.some(
|
||||
(group) => !!group.metadata.deletionTimestamp
|
||||
);
|
||||
|
||||
return deletingGroups?.length ? 1000 : false;
|
||||
return hasDeletingGroup ? 1000 : false;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ export function useFetchAttachmentPolicy(): useFetchAttachmentPolicyReturn {
|
|||
return data.items;
|
||||
},
|
||||
refetchInterval(data) {
|
||||
const deletingPolicies = data?.filter(
|
||||
const hasDeletingPolicy = data?.some(
|
||||
(policy) => !!policy.metadata.deletionTimestamp
|
||||
);
|
||||
return deletingPolicies?.length ? 1000 : false;
|
||||
return hasDeletingPolicy ? 1000 : false;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { Attachment, Group, Policy } from "@halo-dev/api-client";
|
||||
import { computed, nextTick, type Ref } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
import type { Attachment } from "@halo-dev/api-client";
|
||||
import { computed, nextTick, type Ref, ref, watch } from "vue";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { Dialog, Toast } from "@halo-dev/components";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
|
@ -27,8 +26,8 @@ interface useAttachmentControlReturn {
|
|||
}
|
||||
|
||||
export function useAttachmentControl(filterOptions: {
|
||||
policy?: Ref<Policy | undefined>;
|
||||
group?: Ref<Group | undefined>;
|
||||
policyName?: Ref<string | undefined>;
|
||||
groupName?: Ref<string | undefined>;
|
||||
user?: Ref<string | undefined>;
|
||||
accepts?: Ref<string[]>;
|
||||
keyword?: Ref<string | undefined>;
|
||||
|
@ -38,7 +37,7 @@ export function useAttachmentControl(filterOptions: {
|
|||
}): useAttachmentControlReturn {
|
||||
const { t } = useI18n();
|
||||
|
||||
const { user, policy, group, keyword, sort, page, size, accepts } =
|
||||
const { user, policyName, groupName, keyword, sort, page, size, accepts } =
|
||||
filterOptions;
|
||||
|
||||
const selectedAttachment = ref<Attachment>();
|
||||
|
@ -52,9 +51,9 @@ export function useAttachmentControl(filterOptions: {
|
|||
const { data, isLoading, isFetching, refetch } = useQuery<Attachment[]>({
|
||||
queryKey: [
|
||||
"attachments",
|
||||
policy,
|
||||
policyName,
|
||||
keyword,
|
||||
group,
|
||||
groupName,
|
||||
user,
|
||||
accepts,
|
||||
page,
|
||||
|
@ -62,12 +61,12 @@ export function useAttachmentControl(filterOptions: {
|
|||
sort,
|
||||
],
|
||||
queryFn: async () => {
|
||||
const isUnGrouped = group?.value?.metadata.name === "ungrouped";
|
||||
const isUnGrouped = groupName?.value === "ungrouped";
|
||||
|
||||
const fieldSelectorMap: Record<string, string | undefined> = {
|
||||
"spec.policyName": policy?.value?.metadata.name,
|
||||
"spec.policyName": policyName?.value,
|
||||
"spec.ownerName": user?.value,
|
||||
"spec.groupName": isUnGrouped ? undefined : group?.value?.metadata.name,
|
||||
"spec.groupName": isUnGrouped ? undefined : groupName?.value,
|
||||
};
|
||||
|
||||
const fieldSelector = Object.entries(fieldSelectorMap)
|
||||
|
@ -95,10 +94,10 @@ export function useAttachmentControl(filterOptions: {
|
|||
return data.items;
|
||||
},
|
||||
refetchInterval(data) {
|
||||
const deletingAttachments = data?.filter(
|
||||
const hasDeletingAttachment = data?.some(
|
||||
(attachment) => !!attachment.metadata.deletionTimestamp
|
||||
);
|
||||
return deletingAttachments?.length ? 1000 : false;
|
||||
return hasDeletingAttachment ? 1000 : false;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue