mirror of https://github.com/halo-dev/halo
refactor: improve ui/ux of attachment group and policy selector (#5996)
#### What type of PR is this? /area ui /kind improvement /milestone 2.16.x #### What this PR does / why we need it: 优化附件分组、存储策略选择组件的 UI。 <img width="1649" alt="image" src="https://github.com/halo-dev/halo/assets/21301288/194d6334-e600-4235-85f7-affb1d44c003"> <img width="953" alt="image" src="https://github.com/halo-dev/halo/assets/21301288/d8cb2470-2500-4b2c-a251-5fdf45a9cccb"> #### Does this PR introduce a user-facing change? ```release-note 优化附件分组、存储策略选择组件的 UI。 ```pull/5994/head^2
parent
19fb1c2311
commit
94826d44c0
|
@ -1,4 +1,9 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import UserFilterDropdown from "@/components/filter/UserFilterDropdown.vue";
|
||||||
|
import LazyImage from "@/components/image/LazyImage.vue";
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import { isImage } from "@/utils/image";
|
||||||
|
import type { Attachment, Group } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
IconArrowLeft,
|
IconArrowLeft,
|
||||||
IconArrowRight,
|
IconArrowRight,
|
||||||
|
@ -20,25 +25,20 @@ import {
|
||||||
VPagination,
|
VPagination,
|
||||||
VSpace,
|
VSpace,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import LazyImage from "@/components/image/LazyImage.vue";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import AttachmentDetailModal from "./components/AttachmentDetailModal.vue";
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import AttachmentUploadModal from "./components/AttachmentUploadModal.vue";
|
import { cloneDeep } from "lodash-es";
|
||||||
import AttachmentPoliciesModal from "./components/AttachmentPoliciesModal.vue";
|
|
||||||
import AttachmentGroupList from "./components/AttachmentGroupList.vue";
|
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import { computed, onMounted, provide, ref, watch } 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";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
|
||||||
import { cloneDeep } from "lodash-es";
|
|
||||||
import { isImage } from "@/utils/image";
|
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import { useFetchAttachmentGroup } from "./composables/use-attachment-group";
|
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
import AttachmentDetailModal from "./components/AttachmentDetailModal.vue";
|
||||||
import UserFilterDropdown from "@/components/filter/UserFilterDropdown.vue";
|
import AttachmentGroupList from "./components/AttachmentGroupList.vue";
|
||||||
import AttachmentListItem from "./components/AttachmentListItem.vue";
|
import AttachmentListItem from "./components/AttachmentListItem.vue";
|
||||||
|
import AttachmentPoliciesModal from "./components/AttachmentPoliciesModal.vue";
|
||||||
|
import AttachmentUploadModal from "./components/AttachmentUploadModal.vue";
|
||||||
|
import { useAttachmentControl } from "./composables/use-attachment";
|
||||||
|
import { useFetchAttachmentGroup } from "./composables/use-attachment-group";
|
||||||
|
import { useFetchAttachmentPolicy } from "./composables/use-attachment-policy";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ const uploadVisible = ref(false);
|
||||||
const detailVisible = ref(false);
|
const detailVisible = ref(false);
|
||||||
|
|
||||||
const { policies } = useFetchAttachmentPolicy();
|
const { policies } = useFetchAttachmentPolicy();
|
||||||
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
|
const { groups } = useFetchAttachmentGroup();
|
||||||
|
|
||||||
const selectedGroup = useRouteQuery<string | undefined>("group");
|
const selectedGroup = useRouteQuery<string | undefined>("group");
|
||||||
|
|
||||||
|
@ -465,11 +465,7 @@ onMounted(() => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div :style="`${viewType === 'list' ? 'padding:12px 16px 0' : ''}`">
|
<div :style="`${viewType === 'list' ? 'padding:12px 16px 0' : ''}`">
|
||||||
<AttachmentGroupList
|
<AttachmentGroupList @select="handleReset" />
|
||||||
@select="handleReset"
|
|
||||||
@update="handleFetchGroups"
|
|
||||||
@reload-attachments="handleFetchAttachments"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VLoading v-if="isLoading" />
|
<VLoading v-if="isLoading" />
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { Group } from "@halo-dev/api-client";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
IconCheckboxCircle,
|
||||||
|
IconMore,
|
||||||
|
Toast,
|
||||||
|
VDropdown,
|
||||||
|
VDropdownItem,
|
||||||
|
VStatusDot,
|
||||||
|
} from "@halo-dev/components";
|
||||||
|
import { useQueryClient } from "@tanstack/vue-query";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import AttachmentGroupEditingModal from "./AttachmentGroupEditingModal.vue";
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
group?: Group;
|
||||||
|
isSelected?: boolean;
|
||||||
|
features?: { actions: boolean; checkIcon?: boolean };
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
group: undefined,
|
||||||
|
isSelected: false,
|
||||||
|
features: () => {
|
||||||
|
return {
|
||||||
|
actions: true,
|
||||||
|
checkIcon: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
Dialog.warning({
|
||||||
|
title: t("core.attachment.group_list.operations.delete.title"),
|
||||||
|
description: t("core.attachment.group_list.operations.delete.description"),
|
||||||
|
confirmType: "danger",
|
||||||
|
confirmText: t("core.common.buttons.confirm"),
|
||||||
|
cancelText: t("core.common.buttons.cancel"),
|
||||||
|
onConfirm: async () => {
|
||||||
|
if (!props.group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 后续将修改为在后端进行批量操作处理
|
||||||
|
const { data } = await apiClient.attachment.searchAttachments({
|
||||||
|
fieldSelector: [`spec.groupName=${props.group.metadata.name}`],
|
||||||
|
page: 0,
|
||||||
|
size: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
await apiClient.extension.storage.group.deleteStorageHaloRunV1alpha1Group(
|
||||||
|
{ name: props.group.metadata.name }
|
||||||
|
);
|
||||||
|
|
||||||
|
// move attachments to none group
|
||||||
|
const moveToUnGroupRequests = data.items.map((attachment) => {
|
||||||
|
attachment.spec.groupName = undefined;
|
||||||
|
return apiClient.extension.storage.attachment.updateStorageHaloRunV1alpha1Attachment(
|
||||||
|
{
|
||||||
|
name: attachment.metadata.name,
|
||||||
|
attachment: attachment,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(moveToUnGroupRequests);
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["attachment-groups"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["attachments"] });
|
||||||
|
|
||||||
|
Toast.success(
|
||||||
|
t("core.attachment.group_list.operations.delete.toast_success", {
|
||||||
|
total: data.total,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteWithAttachments = () => {
|
||||||
|
Dialog.warning({
|
||||||
|
title: t(
|
||||||
|
"core.attachment.group_list.operations.delete_with_attachments.title"
|
||||||
|
),
|
||||||
|
description: t(
|
||||||
|
"core.attachment.group_list.operations.delete_with_attachments.description"
|
||||||
|
),
|
||||||
|
confirmType: "danger",
|
||||||
|
confirmText: t("core.common.buttons.confirm"),
|
||||||
|
cancelText: t("core.common.buttons.cancel"),
|
||||||
|
onConfirm: async () => {
|
||||||
|
if (!props.group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 后续将修改为在后端进行批量操作处理
|
||||||
|
const { data } = await apiClient.attachment.searchAttachments({
|
||||||
|
fieldSelector: [`spec.groupName=${props.group.metadata.name}`],
|
||||||
|
page: 0,
|
||||||
|
size: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
await apiClient.extension.storage.group.deleteStorageHaloRunV1alpha1Group(
|
||||||
|
{ name: props.group.metadata.name }
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteAttachmentRequests = data.items.map((attachment) => {
|
||||||
|
return apiClient.extension.storage.attachment.deleteStorageHaloRunV1alpha1Attachment(
|
||||||
|
{ name: attachment.metadata.name }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(deleteAttachmentRequests);
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["attachment-groups"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["attachments"] });
|
||||||
|
|
||||||
|
Toast.success(
|
||||||
|
t(
|
||||||
|
"core.attachment.group_list.operations.delete_with_attachments.toast_success",
|
||||||
|
{ total: data.total }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Editing
|
||||||
|
const editingModalVisible = ref(false);
|
||||||
|
|
||||||
|
const onEditingModalClose = () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["attachment-groups"] });
|
||||||
|
editingModalVisible.value = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="inline-flex h-full w-full items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-800 hover:bg-gray-50 hover:shadow-sm"
|
||||||
|
:class="{ '!bg-gray-100 shadow-sm': isSelected }"
|
||||||
|
>
|
||||||
|
<div class="inline-flex w-full flex-1 gap-x-2 break-all text-left">
|
||||||
|
<slot name="text">
|
||||||
|
{{ group?.spec.displayName }}
|
||||||
|
</slot>
|
||||||
|
<VStatusDot
|
||||||
|
v-if="group?.metadata.deletionTimestamp"
|
||||||
|
v-tooltip="$t('core.common.status.deleting')"
|
||||||
|
state="warning"
|
||||||
|
animate
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex-none">
|
||||||
|
<HasPermission
|
||||||
|
v-if="features.actions"
|
||||||
|
:permissions="['system:attachments:manage']"
|
||||||
|
>
|
||||||
|
<VDropdown>
|
||||||
|
<IconMore @click.stop />
|
||||||
|
<template #popper>
|
||||||
|
<VDropdownItem @click="editingModalVisible = true">
|
||||||
|
{{ $t("core.attachment.group_list.operations.rename.button") }}
|
||||||
|
</VDropdownItem>
|
||||||
|
<VDropdown placement="right" :triggers="['click']">
|
||||||
|
<VDropdownItem type="danger">
|
||||||
|
{{ $t("core.common.buttons.delete") }}
|
||||||
|
</VDropdownItem>
|
||||||
|
<template #popper>
|
||||||
|
<VDropdownItem type="danger" @click="handleDelete()">
|
||||||
|
{{
|
||||||
|
$t("core.attachment.group_list.operations.delete.button")
|
||||||
|
}}
|
||||||
|
</VDropdownItem>
|
||||||
|
<VDropdownItem
|
||||||
|
type="danger"
|
||||||
|
@click="handleDeleteWithAttachments()"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
"core.attachment.group_list.operations.delete_with_attachments.button"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</VDropdownItem>
|
||||||
|
</template>
|
||||||
|
</VDropdown>
|
||||||
|
</template>
|
||||||
|
</VDropdown>
|
||||||
|
</HasPermission>
|
||||||
|
|
||||||
|
<IconCheckboxCircle
|
||||||
|
v-if="isSelected && features.checkIcon"
|
||||||
|
class="text-primary"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<slot name="actions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AttachmentGroupEditingModal
|
||||||
|
v-if="editingModalVisible"
|
||||||
|
:group="group"
|
||||||
|
@close="onEditingModalClose"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</template>
|
|
@ -1,12 +1,12 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
|
|
||||||
import SubmitButton from "@/components/button/SubmitButton.vue";
|
import SubmitButton from "@/components/button/SubmitButton.vue";
|
||||||
import type { Group } from "@halo-dev/api-client";
|
|
||||||
import { onMounted, ref } from "vue";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
|
||||||
import { setFocus } from "@/formkit/utils/focus";
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
import { useI18n } from "vue-i18n";
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { Group } from "@halo-dev/api-client";
|
||||||
|
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -35,7 +35,7 @@ const formState = ref<Group>({
|
||||||
generateName: "attachment-group-",
|
generateName: "attachment-group-",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const saving = ref(false);
|
const isSubmitting = ref(false);
|
||||||
|
|
||||||
const modalTitle = props.group
|
const modalTitle = props.group
|
||||||
? t("core.attachment.group_editing_modal.titles.update")
|
? t("core.attachment.group_editing_modal.titles.update")
|
||||||
|
@ -43,7 +43,7 @@ const modalTitle = props.group
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
isSubmitting.value = true;
|
||||||
if (props.group) {
|
if (props.group) {
|
||||||
await apiClient.extension.storage.group.updateStorageHaloRunV1alpha1Group(
|
await apiClient.extension.storage.group.updateStorageHaloRunV1alpha1Group(
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ const handleSave = async () => {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to save attachment group", e);
|
console.error("Failed to save attachment group", e);
|
||||||
} finally {
|
} finally {
|
||||||
saving.value = false;
|
isSubmitting.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,7 +77,13 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VModal ref="modal" :title="modalTitle" :width="500" @close="emit('close')">
|
<VModal
|
||||||
|
ref="modal"
|
||||||
|
mount-to-body
|
||||||
|
:title="modalTitle"
|
||||||
|
:width="500"
|
||||||
|
@close="emit('close')"
|
||||||
|
>
|
||||||
<FormKit
|
<FormKit
|
||||||
id="attachment-group-form"
|
id="attachment-group-form"
|
||||||
name="attachment-group-form"
|
name="attachment-group-form"
|
||||||
|
@ -100,7 +106,7 @@ onMounted(() => {
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
:loading="saving"
|
:loading="isSubmitting"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
:text="$t('core.common.buttons.submit')"
|
:text="$t('core.common.buttons.submit')"
|
||||||
@submit="$formkit.submit('attachment-group-form')"
|
@submit="$formkit.submit('attachment-group-form')"
|
||||||
|
|
|
@ -1,26 +1,13 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// core libs
|
|
||||||
import { ref } from "vue";
|
|
||||||
|
|
||||||
// components
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
IconAddCircle,
|
|
||||||
IconMore,
|
|
||||||
Toast,
|
|
||||||
VDropdown,
|
|
||||||
VDropdownItem,
|
|
||||||
VStatusDot,
|
|
||||||
} from "@halo-dev/components";
|
|
||||||
import AttachmentGroupEditingModal from "./AttachmentGroupEditingModal.vue";
|
|
||||||
|
|
||||||
// types
|
|
||||||
import type { Group } from "@halo-dev/api-client";
|
import type { Group } from "@halo-dev/api-client";
|
||||||
|
import { IconAddCircle } from "@halo-dev/components";
|
||||||
|
import { useQueryClient } from "@tanstack/vue-query";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
|
import { ref } from "vue";
|
||||||
import { apiClient } from "@/utils/api-client";
|
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
|
||||||
|
import AttachmentGroupBadge from "./AttachmentGroupBadge.vue";
|
||||||
|
import AttachmentGroupEditingModal from "./AttachmentGroupEditingModal.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -35,10 +22,10 @@ const props = withDefaults(
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "select", group: Group): void;
|
(event: "select", group: Group): void;
|
||||||
(event: "update"): void;
|
|
||||||
(event: "reload-attachments"): void;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const defaultGroups: Group[] = [
|
const defaultGroups: Group[] = [
|
||||||
{
|
{
|
||||||
spec: {
|
spec: {
|
||||||
|
@ -62,11 +49,10 @@ const defaultGroups: Group[] = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
|
const { groups } = useFetchAttachmentGroup();
|
||||||
|
|
||||||
const groupToUpdate = ref<Group>();
|
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const editingModal = ref(false);
|
const creationModalVisible = ref(false);
|
||||||
|
|
||||||
const selectedGroup = props.readonly
|
const selectedGroup = props.readonly
|
||||||
? ref("")
|
? ref("")
|
||||||
|
@ -77,189 +63,52 @@ const handleSelectGroup = (group: Group) => {
|
||||||
selectedGroup.value = group.metadata.name;
|
selectedGroup.value = group.metadata.name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenEditingModal = (group: Group) => {
|
const onCreationModalClose = () => {
|
||||||
groupToUpdate.value = group;
|
queryClient.invalidateQueries({ queryKey: ["attachment-groups"] });
|
||||||
editingModal.value = true;
|
creationModalVisible.value = false;
|
||||||
};
|
|
||||||
|
|
||||||
const onEditingModalClose = () => {
|
|
||||||
emit("update");
|
|
||||||
handleFetchGroups();
|
|
||||||
editingModal.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = (group: Group) => {
|
|
||||||
Dialog.warning({
|
|
||||||
title: t("core.attachment.group_list.operations.delete.title"),
|
|
||||||
description: t("core.attachment.group_list.operations.delete.description"),
|
|
||||||
confirmType: "danger",
|
|
||||||
confirmText: t("core.common.buttons.confirm"),
|
|
||||||
cancelText: t("core.common.buttons.cancel"),
|
|
||||||
onConfirm: async () => {
|
|
||||||
// TODO: 后续将修改为在后端进行批量操作处理
|
|
||||||
const { data } = await apiClient.attachment.searchAttachments({
|
|
||||||
fieldSelector: [`spec.groupName=${group.metadata.name}`],
|
|
||||||
page: 0,
|
|
||||||
size: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
await apiClient.extension.storage.group.deleteStorageHaloRunV1alpha1Group(
|
|
||||||
{ name: group.metadata.name }
|
|
||||||
);
|
|
||||||
|
|
||||||
// move attachments to none group
|
|
||||||
const moveToUnGroupRequests = data.items.map((attachment) => {
|
|
||||||
attachment.spec.groupName = undefined;
|
|
||||||
return apiClient.extension.storage.attachment.updateStorageHaloRunV1alpha1Attachment(
|
|
||||||
{
|
|
||||||
name: attachment.metadata.name,
|
|
||||||
attachment: attachment,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(moveToUnGroupRequests);
|
|
||||||
|
|
||||||
handleFetchGroups();
|
|
||||||
emit("reload-attachments");
|
|
||||||
emit("update");
|
|
||||||
|
|
||||||
Toast.success(
|
|
||||||
t("core.attachment.group_list.operations.delete.toast_success", {
|
|
||||||
total: data.total,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteWithAttachments = (group: Group) => {
|
|
||||||
Dialog.warning({
|
|
||||||
title: t(
|
|
||||||
"core.attachment.group_list.operations.delete_with_attachments.title"
|
|
||||||
),
|
|
||||||
description: t(
|
|
||||||
"core.attachment.group_list.operations.delete_with_attachments.description"
|
|
||||||
),
|
|
||||||
confirmType: "danger",
|
|
||||||
confirmText: t("core.common.buttons.confirm"),
|
|
||||||
cancelText: t("core.common.buttons.cancel"),
|
|
||||||
onConfirm: async () => {
|
|
||||||
// TODO: 后续将修改为在后端进行批量操作处理
|
|
||||||
const { data } = await apiClient.attachment.searchAttachments({
|
|
||||||
fieldSelector: [`spec.groupName=${group.metadata.name}`],
|
|
||||||
page: 0,
|
|
||||||
size: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
await apiClient.extension.storage.group.deleteStorageHaloRunV1alpha1Group(
|
|
||||||
{ name: group.metadata.name }
|
|
||||||
);
|
|
||||||
|
|
||||||
const deleteAttachmentRequests = data.items.map((attachment) => {
|
|
||||||
return apiClient.extension.storage.attachment.deleteStorageHaloRunV1alpha1Attachment(
|
|
||||||
{ name: attachment.metadata.name }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(deleteAttachmentRequests);
|
|
||||||
|
|
||||||
handleFetchGroups();
|
|
||||||
emit("reload-attachments");
|
|
||||||
emit("update");
|
|
||||||
|
|
||||||
Toast.success(
|
|
||||||
t(
|
|
||||||
"core.attachment.group_list.operations.delete_with_attachments.toast_success",
|
|
||||||
{ total: data.total }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<AttachmentGroupEditingModal
|
<AttachmentGroupEditingModal
|
||||||
v-if="!readonly && editingModal"
|
v-if="!readonly && creationModalVisible"
|
||||||
:group="groupToUpdate"
|
@close="onCreationModalClose"
|
||||||
@close="onEditingModalClose"
|
|
||||||
/>
|
/>
|
||||||
<div class="mb-5 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-6">
|
<div
|
||||||
<div
|
class="mb-5 grid grid-cols-2 gap-x-2 gap-y-3 md:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-6"
|
||||||
|
>
|
||||||
|
<AttachmentGroupBadge
|
||||||
v-for="defaultGroup in defaultGroups"
|
v-for="defaultGroup in defaultGroups"
|
||||||
:key="defaultGroup.metadata.name"
|
:key="defaultGroup.metadata.name"
|
||||||
:class="{
|
:group="defaultGroup"
|
||||||
'!bg-gray-200 !text-gray-900':
|
:is-selected="defaultGroup.metadata.name === selectedGroup"
|
||||||
defaultGroup.metadata.name === selectedGroup,
|
:features="{ actions: false, checkIcon: readonly }"
|
||||||
}"
|
|
||||||
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)"
|
@click="handleSelectGroup(defaultGroup)"
|
||||||
>
|
/>
|
||||||
<div class="flex flex-1 items-center">
|
|
||||||
<span class="text-sm">{{ defaultGroup.spec.displayName }}</span>
|
<AttachmentGroupBadge
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-for="group in groups"
|
v-for="group in groups"
|
||||||
:key="group.metadata.name"
|
:key="group.metadata.name"
|
||||||
:class="{
|
:group="group"
|
||||||
'!bg-gray-200 !text-gray-900': group.metadata.name === selectedGroup,
|
:is-selected="group.metadata.name === selectedGroup"
|
||||||
}"
|
:features="{ actions: !readonly, checkIcon: readonly }"
|
||||||
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)"
|
@click="handleSelectGroup(group)"
|
||||||
>
|
/>
|
||||||
<div class="flex flex-1 items-center gap-2 truncate">
|
|
||||||
<span class="truncate text-sm">
|
<HasPermission
|
||||||
{{ group.spec.displayName }}
|
|
||||||
</span>
|
|
||||||
<VStatusDot
|
|
||||||
v-if="group.metadata.deletionTimestamp"
|
|
||||||
v-tooltip="$t('core.common.status.deleting')"
|
|
||||||
state="warning"
|
|
||||||
animate
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<VDropdown v-if="!readonly" v-permission="['system:attachments:manage']">
|
|
||||||
<IconMore @click.stop />
|
|
||||||
<template #popper>
|
|
||||||
<VDropdownItem @click="handleOpenEditingModal(group)">
|
|
||||||
{{ $t("core.attachment.group_list.operations.rename.button") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
<VDropdown placement="right" :triggers="['click']">
|
|
||||||
<VDropdownItem type="danger">
|
|
||||||
{{ $t("core.common.buttons.delete") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
<template #popper>
|
|
||||||
<VDropdownItem type="danger" @click="handleDelete(group)">
|
|
||||||
{{ $t("core.attachment.group_list.operations.delete.button") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
<VDropdownItem
|
|
||||||
type="danger"
|
|
||||||
@click="handleDeleteWithAttachments(group)"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
"core.attachment.group_list.operations.delete_with_attachments.button"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</VDropdownItem>
|
|
||||||
</template>
|
|
||||||
</VDropdown>
|
|
||||||
</template>
|
|
||||||
</VDropdown>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="!loading && !readonly"
|
v-if="!loading && !readonly"
|
||||||
v-permission="['system:attachments:manage']"
|
:permissions="['system:attachments:manage']"
|
||||||
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="editingModal = true"
|
|
||||||
>
|
>
|
||||||
<div class="flex flex-1 items-center truncate">
|
<AttachmentGroupBadge
|
||||||
<span class="truncate text-sm">
|
:features="{ actions: false }"
|
||||||
{{ $t("core.common.buttons.new") }}
|
@click="creationModalVisible = true"
|
||||||
</span>
|
>
|
||||||
</div>
|
<template #text>
|
||||||
<IconAddCircle />
|
<span>{{ $t("core.common.buttons.new") }}</span>
|
||||||
</div>
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<IconAddCircle />
|
||||||
|
</template>
|
||||||
|
</AttachmentGroupBadge>
|
||||||
|
</HasPermission>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import { formatDatetime } from "@/utils/date";
|
||||||
|
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
IconAddCircle,
|
IconAddCircle,
|
||||||
|
@ -13,16 +16,13 @@ import {
|
||||||
VSpace,
|
VSpace,
|
||||||
VStatusDot,
|
VStatusDot,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
import { useI18n } from "vue-i18n";
|
||||||
import { formatDatetime } from "@/utils/date";
|
|
||||||
import {
|
import {
|
||||||
useFetchAttachmentPolicy,
|
useFetchAttachmentPolicy,
|
||||||
useFetchAttachmentPolicyTemplate,
|
useFetchAttachmentPolicyTemplate,
|
||||||
} from "../composables/use-attachment-policy";
|
} from "../composables/use-attachment-policy";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "close"): void;
|
(event: "close"): void;
|
||||||
|
@ -63,7 +63,7 @@ const handleDelete = async (policy: Policy) => {
|
||||||
"core.attachment.policies_modal.operations.can_not_delete.description"
|
"core.attachment.policies_modal.operations.can_not_delete.description"
|
||||||
),
|
),
|
||||||
confirmText: t("core.common.buttons.confirm"),
|
confirmText: t("core.common.buttons.confirm"),
|
||||||
cancelText: t("core.common.buttons.cancel"),
|
showCancel: false,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -88,9 +88,17 @@ const handleDelete = async (policy: Policy) => {
|
||||||
|
|
||||||
const onEditingModalClose = () => {
|
const onEditingModalClose = () => {
|
||||||
selectedPolicy.value = undefined;
|
selectedPolicy.value = undefined;
|
||||||
|
selectedTemplateName.value = undefined;
|
||||||
handleFetchPolicies();
|
handleFetchPolicies();
|
||||||
policyEditingModal.value = false;
|
policyEditingModal.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getPolicyTemplateDisplayName(templateName: string) {
|
||||||
|
const policyTemplate = policyTemplates.value?.find(
|
||||||
|
(template) => template.metadata.name === templateName
|
||||||
|
);
|
||||||
|
return policyTemplate?.spec?.displayName || "--";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VModal
|
<VModal
|
||||||
|
@ -108,8 +116,8 @@ const onEditingModalClose = () => {
|
||||||
</span>
|
</span>
|
||||||
<template #popper>
|
<template #popper>
|
||||||
<VDropdownItem
|
<VDropdownItem
|
||||||
v-for="(policyTemplate, index) in policyTemplates"
|
v-for="policyTemplate in policyTemplates"
|
||||||
:key="index"
|
:key="policyTemplate.metadata.name"
|
||||||
@click="handleOpenCreateNewPolicyModal(policyTemplate)"
|
@click="handleOpenCreateNewPolicyModal(policyTemplate)"
|
||||||
>
|
>
|
||||||
{{ policyTemplate.spec?.displayName }}
|
{{ policyTemplate.spec?.displayName }}
|
||||||
|
@ -157,7 +165,9 @@ const onEditingModalClose = () => {
|
||||||
<template #start>
|
<template #start>
|
||||||
<VEntityField
|
<VEntityField
|
||||||
:title="policy.spec.displayName"
|
:title="policy.spec.displayName"
|
||||||
:description="policy.spec.templateName"
|
:description="
|
||||||
|
getPolicyTemplateDisplayName(policy.spec.templateName)
|
||||||
|
"
|
||||||
></VEntityField>
|
></VEntityField>
|
||||||
</template>
|
</template>
|
||||||
<template #end>
|
<template #end>
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { Policy } from "@halo-dev/api-client";
|
||||||
|
import { IconCheckboxCircle } from "@halo-dev/components";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useFetchAttachmentPolicyTemplate } from "../composables/use-attachment-policy";
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
policy?: Policy;
|
||||||
|
isSelected?: boolean;
|
||||||
|
features?: { checkIcon?: boolean };
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
policy: undefined,
|
||||||
|
isSelected: false,
|
||||||
|
features: () => {
|
||||||
|
return {
|
||||||
|
checkIcon: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { policyTemplates } = useFetchAttachmentPolicyTemplate();
|
||||||
|
|
||||||
|
const policyTemplate = computed(() => {
|
||||||
|
return policyTemplates.value?.find(
|
||||||
|
(template) => template.metadata.name === props.policy?.spec.templateName
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="inline-flex h-full w-full items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-800 hover:bg-gray-50 hover:shadow-sm"
|
||||||
|
:class="{ '!bg-gray-100 shadow-sm': isSelected }"
|
||||||
|
>
|
||||||
|
<div class="inline-flex w-full flex-1 flex-col space-y-0.5 text-left">
|
||||||
|
<slot name="text">
|
||||||
|
<div>
|
||||||
|
{{ policy?.spec.displayName }}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs font-normal text-gray-600">
|
||||||
|
{{ policyTemplate?.spec?.displayName || "--" }}
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-none">
|
||||||
|
<IconCheckboxCircle
|
||||||
|
v-if="isSelected && features.checkIcon"
|
||||||
|
class="text-primary"
|
||||||
|
/>
|
||||||
|
<slot name="actions" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</template>
|
|
@ -1,14 +1,14 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Toast, VButton, VLoading, VModal, VSpace } from "@halo-dev/components";
|
|
||||||
import SubmitButton from "@/components/button/SubmitButton.vue";
|
import SubmitButton from "@/components/button/SubmitButton.vue";
|
||||||
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import { useSettingFormConvert } from "@console/composables/use-setting-form";
|
||||||
import type { Policy } from "@halo-dev/api-client";
|
import type { Policy } from "@halo-dev/api-client";
|
||||||
|
import { Toast, VButton, VLoading, VModal, VSpace } from "@halo-dev/components";
|
||||||
|
import { useQuery } from "@tanstack/vue-query";
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
import { computed, onMounted, ref, toRaw, toRefs } from "vue";
|
import { computed, onMounted, ref, toRaw, toRefs } from "vue";
|
||||||
import { useSettingFormConvert } from "@console/composables/use-setting-form";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
|
||||||
import { setFocus } from "@/formkit/utils/focus";
|
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useQuery } from "@tanstack/vue-query";
|
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -63,6 +63,7 @@ const { data: policyTemplate } = useQuery({
|
||||||
"core:attachment:policy-template",
|
"core:attachment:policy-template",
|
||||||
formState.value.spec.templateName,
|
formState.value.spec.templateName,
|
||||||
],
|
],
|
||||||
|
cacheTime: 0,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } =
|
const { data } =
|
||||||
await apiClient.extension.storage.policyTemplate.getStorageHaloRunV1alpha1PolicyTemplate(
|
await apiClient.extension.storage.policyTemplate.getStorageHaloRunV1alpha1PolicyTemplate(
|
||||||
|
@ -81,6 +82,7 @@ const { data: setting, isLoading } = useQuery({
|
||||||
"core:attachment:policy-template:setting",
|
"core:attachment:policy-template:setting",
|
||||||
policyTemplate.value?.spec?.settingName,
|
policyTemplate.value?.spec?.settingName,
|
||||||
],
|
],
|
||||||
|
cacheTime: 0,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!policyTemplate.value?.spec?.settingName) {
|
if (!policyTemplate.value?.spec?.settingName) {
|
||||||
throw new Error("No setting found");
|
throw new Error("No setting found");
|
||||||
|
@ -101,6 +103,7 @@ const { data: configMap } = useQuery({
|
||||||
"core:attachment:policy-template:configMap",
|
"core:attachment:policy-template:configMap",
|
||||||
policy.value?.spec.configMapName,
|
policy.value?.spec.configMapName,
|
||||||
],
|
],
|
||||||
|
cacheTime: 0,
|
||||||
initialData: {
|
initialData: {
|
||||||
data: {},
|
data: {},
|
||||||
apiVersion: "v1alpha1",
|
apiVersion: "v1alpha1",
|
||||||
|
@ -181,7 +184,13 @@ const modalTitle = props.policy
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VModal ref="modal" :title="modalTitle" :width="600" @close="emit('close')">
|
<VModal
|
||||||
|
ref="modal"
|
||||||
|
mount-to-body
|
||||||
|
:title="modalTitle"
|
||||||
|
:width="600"
|
||||||
|
@close="emit('close')"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<VLoading v-if="isLoading" />
|
<VLoading v-if="isLoading" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { PolicyTemplate } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
IconAddCircle,
|
IconAddCircle,
|
||||||
VAlert,
|
VAlert,
|
||||||
|
@ -6,15 +7,16 @@ import {
|
||||||
VDropdownItem,
|
VDropdownItem,
|
||||||
VModal,
|
VModal,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import type { PolicyTemplate } from "@halo-dev/api-client";
|
import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
|
||||||
import {
|
import {
|
||||||
useFetchAttachmentPolicy,
|
useFetchAttachmentPolicy,
|
||||||
useFetchAttachmentPolicyTemplate,
|
useFetchAttachmentPolicyTemplate,
|
||||||
} from "../composables/use-attachment-policy";
|
} from "../composables/use-attachment-policy";
|
||||||
import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
|
import AttachmentGroupBadge from "./AttachmentGroupBadge.vue";
|
||||||
|
import AttachmentPolicyBadge from "./AttachmentPolicyBadge.vue";
|
||||||
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "close"): void;
|
(event: "close"): void;
|
||||||
|
@ -67,6 +69,7 @@ const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
|
||||||
|
|
||||||
const onEditingModalClose = async () => {
|
const onEditingModalClose = async () => {
|
||||||
await handleFetchPolicies();
|
await handleFetchPolicies();
|
||||||
|
policyTemplateNameToCreate.value = undefined;
|
||||||
selectedPolicyName.value = policies.value?.[0].metadata.name;
|
selectedPolicyName.value = policies.value?.[0].metadata.name;
|
||||||
policyEditingModal.value = false;
|
policyEditingModal.value = false;
|
||||||
};
|
};
|
||||||
|
@ -75,80 +78,37 @@ const onEditingModalClose = async () => {
|
||||||
<VModal
|
<VModal
|
||||||
ref="modal"
|
ref="modal"
|
||||||
:body-class="['!p-0']"
|
:body-class="['!p-0']"
|
||||||
:width="650"
|
:width="920"
|
||||||
:centered="false"
|
height="calc(100vh - 20px)"
|
||||||
:title="$t('core.attachment.upload_modal.title')"
|
:title="$t('core.attachment.upload_modal.title')"
|
||||||
|
mount-to-body
|
||||||
@close="emit('close')"
|
@close="emit('close')"
|
||||||
>
|
>
|
||||||
<div class="w-full p-4">
|
<div class="w-full p-4">
|
||||||
<div class="mb-2">
|
|
||||||
<span class="text-sm text-gray-800">
|
|
||||||
{{ $t("core.attachment.upload_modal.filters.group.label") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-4">
|
|
||||||
<div
|
|
||||||
v-for="(group, index) in [
|
|
||||||
{
|
|
||||||
metadata: { name: '' },
|
|
||||||
spec: {
|
|
||||||
displayName: $t('core.attachment.common.text.ungrouped'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...(groups || []),
|
|
||||||
]"
|
|
||||||
:key="index"
|
|
||||||
:class="{
|
|
||||||
'!bg-gray-200 !text-gray-900':
|
|
||||||
group.metadata.name === selectedGroupName,
|
|
||||||
}"
|
|
||||||
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="selectedGroupName = group.metadata.name"
|
|
||||||
>
|
|
||||||
<div class="flex flex-1 items-center gap-2 truncate">
|
|
||||||
<span class="truncate text-sm">
|
|
||||||
{{ group.spec.displayName }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<span class="text-sm text-gray-800">
|
<span class="text-sm text-gray-800">
|
||||||
{{ $t("core.attachment.upload_modal.filters.policy.label") }}
|
{{ $t("core.attachment.upload_modal.filters.policy.label") }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-4">
|
<div class="mb-3 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-4">
|
||||||
<div
|
<AttachmentPolicyBadge
|
||||||
v-for="(policy, index) in policies"
|
v-for="policy in policies"
|
||||||
:key="index"
|
:key="policy.metadata.name"
|
||||||
:class="{
|
:policy="policy"
|
||||||
'!bg-gray-200 !text-gray-900':
|
:is-selected="selectedPolicyName === policy.metadata.name"
|
||||||
selectedPolicyName === policy.metadata.name,
|
:features="{ checkIcon: true }"
|
||||||
}"
|
|
||||||
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="selectedPolicyName = policy.metadata.name"
|
@click="selectedPolicyName = policy.metadata.name"
|
||||||
>
|
/>
|
||||||
<div class="flex flex-1 flex-col items-start truncate">
|
|
||||||
<span class="truncate text-sm">
|
|
||||||
{{ policy.spec.displayName }}
|
|
||||||
</span>
|
|
||||||
<span class="text-xs">
|
|
||||||
{{ policy.spec.templateName }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<VDropdown>
|
<VDropdown>
|
||||||
<div
|
<AttachmentPolicyBadge>
|
||||||
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"
|
<template #text>
|
||||||
>
|
<span>{{ $t("core.common.buttons.new") }}</span>
|
||||||
<div class="flex flex-1 items-center truncate">
|
</template>
|
||||||
<span class="truncate text-sm">
|
<template #actions>
|
||||||
{{ $t("core.common.buttons.new") }}
|
<IconAddCircle />
|
||||||
</span>
|
</template>
|
||||||
</div>
|
</AttachmentPolicyBadge>
|
||||||
<IconAddCircle />
|
|
||||||
</div>
|
|
||||||
<template #popper>
|
<template #popper>
|
||||||
<VDropdownItem
|
<VDropdownItem
|
||||||
v-for="(policyTemplate, index) in policyTemplates"
|
v-for="(policyTemplate, index) in policyTemplates"
|
||||||
|
@ -169,6 +129,32 @@ const onEditingModalClose = async () => {
|
||||||
:closable="false"
|
:closable="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="text-sm text-gray-800">
|
||||||
|
{{ $t("core.attachment.upload_modal.filters.group.label") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-4">
|
||||||
|
<AttachmentGroupBadge
|
||||||
|
v-for="group in [
|
||||||
|
{
|
||||||
|
metadata: { name: '' },
|
||||||
|
apiVersion: '',
|
||||||
|
kind: '',
|
||||||
|
spec: {
|
||||||
|
displayName: $t('core.attachment.common.text.ungrouped'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(groups || []),
|
||||||
|
]"
|
||||||
|
:key="group.metadata.name"
|
||||||
|
:group="group"
|
||||||
|
:is-selected="group.metadata.name === selectedGroupName"
|
||||||
|
:features="{ actions: false, checkIcon: true }"
|
||||||
|
@click="selectedGroupName = group.metadata.name"
|
||||||
|
>
|
||||||
|
</AttachmentGroupBadge>
|
||||||
|
</div>
|
||||||
<UppyUpload
|
<UppyUpload
|
||||||
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
||||||
:disabled="!selectedPolicyName"
|
:disabled="!selectedPolicyName"
|
||||||
|
@ -176,6 +162,7 @@ const onEditingModalClose = async () => {
|
||||||
policyName: selectedPolicyName,
|
policyName: selectedPolicyName,
|
||||||
groupName: selectedGroupName,
|
groupName: selectedGroupName,
|
||||||
}"
|
}"
|
||||||
|
width="100%"
|
||||||
:allowed-meta-fields="['policyName', 'groupName']"
|
:allowed-meta-fields="['policyName', 'groupName']"
|
||||||
:note="
|
:note="
|
||||||
selectedPolicyName
|
selectedPolicyName
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import LazyImage from "@/components/image/LazyImage.vue";
|
||||||
|
import { isImage } from "@/utils/image";
|
||||||
|
import { matchMediaTypes } from "@/utils/media-type";
|
||||||
|
import type { Attachment, Group } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
IconArrowLeft,
|
IconArrowLeft,
|
||||||
IconArrowRight,
|
IconArrowRight,
|
||||||
|
@ -12,16 +16,12 @@ import {
|
||||||
VPagination,
|
VPagination,
|
||||||
VSpace,
|
VSpace,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import { computed, ref, watchEffect } from "vue";
|
|
||||||
import { isImage } from "@/utils/image";
|
|
||||||
import { useAttachmentControl } from "../../composables/use-attachment";
|
|
||||||
import LazyImage from "@/components/image/LazyImage.vue";
|
|
||||||
import type { AttachmentLike } from "@halo-dev/console-shared";
|
import type { AttachmentLike } from "@halo-dev/console-shared";
|
||||||
import type { Attachment, Group } from "@halo-dev/api-client";
|
import { computed, ref, watchEffect } from "vue";
|
||||||
import AttachmentUploadModal from "../AttachmentUploadModal.vue";
|
import { useAttachmentControl } from "../../composables/use-attachment";
|
||||||
import AttachmentDetailModal from "../AttachmentDetailModal.vue";
|
import AttachmentDetailModal from "../AttachmentDetailModal.vue";
|
||||||
import AttachmentGroupList from "../AttachmentGroupList.vue";
|
import AttachmentGroupList from "../AttachmentGroupList.vue";
|
||||||
import { matchMediaTypes } from "@/utils/media-type";
|
import AttachmentUploadModal from "../AttachmentUploadModal.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
Loading…
Reference in New Issue