halo/ui/console-src/modules/contents/attachments/components/AttachmentSelectorModal.vue

172 lines
4.2 KiB
Vue

<script lang="ts" setup>
import { usePluginModuleStore } from "@/stores/plugin";
import { VButton, VModal, VSpace, VTabbar } from "@halo-dev/components";
import type {
AttachmentLike,
AttachmentSelectProvider,
} from "@halo-dev/console-shared";
import { computed, markRaw, onMounted, ref } from "vue";
import { useI18n } from "vue-i18n";
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
const { t } = useI18n();
const props = withDefaults(
defineProps<{
visible: boolean;
accepts?: string[];
min?: number;
max?: number;
}>(),
{
visible: false,
accepts: () => ["*/*"],
min: undefined,
max: undefined,
}
);
const emit = defineEmits<{
(event: "update:visible", visible: boolean): void;
(event: "close"): void;
(event: "select", attachments: AttachmentLike[]): void;
}>();
const selected = ref<AttachmentLike[]>([] as AttachmentLike[]);
const attachmentSelectProviders = ref<AttachmentSelectProvider[]>([
{
id: "core",
label: t("core.attachment.select_modal.providers.default.label"),
component: markRaw(CoreSelectorProvider),
},
]);
// resolve plugin extension points
const { pluginModules } = usePluginModuleStore();
onMounted(async () => {
for (const pluginModule of pluginModules) {
try {
const callbackFunction =
pluginModule?.extensionPoints?.["attachment:selector:create"];
if (typeof callbackFunction !== "function") {
continue;
}
const providers = await callbackFunction();
attachmentSelectProviders.value.push(...providers);
} catch (error) {
console.error(`Error processing plugin module:`, pluginModule, error);
}
}
});
const activeId = ref(attachmentSelectProviders.value[0].id);
const onVisibleChange = (visible: boolean) => {
emit("update:visible", visible);
if (!visible) {
emit("close");
}
};
const onChangeProvider = (providerId: string) => {
const provider = attachmentSelectProviders.value.find(
(provider) => provider.id === providerId
);
if (!provider) {
return;
}
activeId.value = providerId;
};
const handleConfirm = () => {
emit("select", Array.from(selected.value));
onVisibleChange(false);
};
const confirmDisabled = computed(() => {
if (props.min === undefined) {
return false;
}
return selected.value.length < props.min;
});
const confirmCountMessage = computed(() => {
if (!props.min && !props.max) {
return selected.value.length;
}
return `${selected.value.length} / ${props.max || props.min}`;
});
</script>
<template>
<VModal
:visible="visible"
:width="1240"
:mount-to-body="true"
:layer-closable="true"
:title="$t('core.attachment.select_modal.title')"
height="calc(100vh - 20px)"
@update:visible="onVisibleChange"
>
<VTabbar
v-model:active-id="activeId"
:items="
attachmentSelectProviders.map((provider) => ({
id: provider.id,
label: provider.label,
}))
"
class="w-full"
type="outline"
></VTabbar>
<div v-if="visible" class="mt-2">
<template
v-for="(provider, index) in attachmentSelectProviders"
:key="index"
>
<Suspense>
<component
:is="provider.component"
v-if="activeId === provider.id"
v-model:selected="selected"
:accepts="accepts"
:min="min"
:max="max"
@change-provider="onChangeProvider"
></component>
<template #fallback>
{{ $t("core.common.status.loading") }}
</template>
</Suspense>
</template>
</div>
<template #footer>
<VSpace>
<VButton
type="secondary"
:disabled="confirmDisabled"
@click="handleConfirm"
>
{{ $t("core.common.buttons.confirm") }}
<span v-if="selected.length || props.min || props.max">
{{
$t("core.attachment.select_modal.operations.select.result", {
count: confirmCountMessage,
})
}}
</span>
</VButton>
<VButton @click="onVisibleChange(false)">
{{ $t("core.common.buttons.cancel") }}
</VButton>
</VSpace>
</template>
</VModal>
</template>