mirror of https://github.com/halo-dev/halo
feat: add support for copying full path in attachment details (#7550)
#### What type of PR is this? /area ui /kind feature /milestone 2.21.x #### What this PR does / why we need it: Add support for copying full path ina attachment details modal. <img width="1023" alt="image" src="https://github.com/user-attachments/assets/1b337655-e774-4c8a-8a32-3dac83cb77b2" /> #### Does this PR introduce a user-facing change? ```release-note 支持在附件详情弹窗中复制完整的附件地址。 ```pull/7555/head
parent
21e115165f
commit
15b029af56
|
@ -1,19 +1,18 @@
|
|||
<script lang="ts" setup>
|
||||
import { useGlobalInfoStore } from "@/stores/global-info";
|
||||
import { matchMediaType } from "@/utils/media-type";
|
||||
import type { Attachment } from "@halo-dev/api-client";
|
||||
import { Toast, VButton } from "@halo-dev/components";
|
||||
import { Toast, VButton, VTabbar } from "@halo-dev/components";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
import { computed, toRefs } from "vue";
|
||||
import { computed, ref, toRefs } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
attachment?: Attachment;
|
||||
mountToBody?: boolean;
|
||||
}>(),
|
||||
{
|
||||
attachment: undefined,
|
||||
mountToBody: false,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -21,6 +20,9 @@ const { attachment } = toRefs(props);
|
|||
|
||||
const { copy } = useClipboard({ legacy: true });
|
||||
const { t } = useI18n();
|
||||
const { globalInfo } = useGlobalInfoStore();
|
||||
|
||||
const activeId = ref<"relative" | "absolute">("relative");
|
||||
|
||||
const mediaType = computed(() => {
|
||||
return attachment.value?.spec.mediaType;
|
||||
|
@ -38,36 +40,48 @@ const isAudio = computed(() => {
|
|||
return mediaType.value && matchMediaType(mediaType.value, "audio/*");
|
||||
});
|
||||
|
||||
const isLocalAttachment = computed(() => {
|
||||
return props.attachment?.status?.permalink?.startsWith("/");
|
||||
});
|
||||
|
||||
const permalink = computed(() => {
|
||||
const { permalink: value } = props.attachment?.status || {};
|
||||
|
||||
if (!isLocalAttachment.value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (activeId.value === "relative") {
|
||||
return value;
|
||||
}
|
||||
|
||||
return `${globalInfo?.externalUrl}${value}`;
|
||||
});
|
||||
|
||||
const htmlText = computed(() => {
|
||||
const { permalink } = attachment.value?.status || {};
|
||||
const { displayName } = attachment.value?.spec || {};
|
||||
|
||||
if (isImage.value) {
|
||||
return `<img src="${permalink}" alt="${displayName}" />`;
|
||||
return `<img src="${permalink.value}" alt="${displayName}" />`;
|
||||
} else if (isVideo.value) {
|
||||
return `<video src="${permalink}"></video>`;
|
||||
return `<video src="${permalink.value}"></video>`;
|
||||
} else if (isAudio.value) {
|
||||
return `<audio src="${permalink}"></audio>`;
|
||||
return `<audio src="${permalink.value}"></audio>`;
|
||||
}
|
||||
return `<a href="${permalink}">${displayName}</a>`;
|
||||
return `<a href="${permalink.value}">${displayName}</a>`;
|
||||
});
|
||||
|
||||
const markdownText = computed(() => {
|
||||
const { permalink } = attachment.value?.status || {};
|
||||
const { displayName } = attachment.value?.spec || {};
|
||||
if (isImage.value) {
|
||||
return ``;
|
||||
return ``;
|
||||
}
|
||||
return `[${displayName}](${permalink})`;
|
||||
return `[${displayName}](${permalink.value})`;
|
||||
});
|
||||
|
||||
const handleCopy = (format: "markdown" | "html" | "url") => {
|
||||
const { permalink } = attachment.value?.status || {};
|
||||
|
||||
if (!permalink) return;
|
||||
|
||||
if (format === "url") {
|
||||
copy(permalink);
|
||||
copy(permalink.value || "");
|
||||
} else if (format === "markdown") {
|
||||
copy(markdownText.value);
|
||||
} else if (format === "html") {
|
||||
|
@ -87,7 +101,7 @@ const formats = computed(
|
|||
{
|
||||
label: "URL",
|
||||
key: "url",
|
||||
value: attachment?.value?.status?.permalink,
|
||||
value: permalink.value,
|
||||
},
|
||||
{
|
||||
label: "HTML",
|
||||
|
@ -105,7 +119,22 @@ const formats = computed(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<ul class="flex flex-col space-y-2">
|
||||
<VTabbar
|
||||
v-if="isLocalAttachment"
|
||||
v-model:active-id="activeId"
|
||||
:items="[
|
||||
{
|
||||
label: $t('core.attachment.permalink_list.relative'),
|
||||
id: 'relative',
|
||||
},
|
||||
{
|
||||
label: $t('core.attachment.permalink_list.absolute'),
|
||||
id: 'absolute',
|
||||
},
|
||||
]"
|
||||
type="outline"
|
||||
></VTabbar>
|
||||
<ul class="flex flex-col space-y-2 mt-3">
|
||||
<li v-for="format in formats" :key="format.key">
|
||||
<div
|
||||
class="flex w-full cursor-pointer items-center justify-between space-x-3 rounded border p-3 hover:border-primary"
|
||||
|
|
|
@ -213,6 +213,9 @@ core:
|
|||
items:
|
||||
display_name_asc: Ascending order by display name
|
||||
display_name_desc: Descending order by display name
|
||||
permalink_list:
|
||||
relative: Relative path
|
||||
absolute: Absolute path
|
||||
group_editing_modal:
|
||||
toast:
|
||||
group_name_exists: Group name already exists
|
||||
|
|
|
@ -684,6 +684,9 @@ core:
|
|||
video_not_support: The current browser does not support video playback.
|
||||
audio_not_support: The current browser does not support audio playback.
|
||||
not_support: This file does not support preview.
|
||||
permalink_list:
|
||||
relative: Relative path
|
||||
absolute: Absolute path
|
||||
group_editing_modal:
|
||||
titles:
|
||||
create: Create attachment group
|
||||
|
|
|
@ -719,6 +719,9 @@ core:
|
|||
operations:
|
||||
select:
|
||||
result: (已选择 {count} 项)
|
||||
permalink_list:
|
||||
relative: 相对路径
|
||||
absolute: 完整路径
|
||||
uc_attachment:
|
||||
empty:
|
||||
title: 当前没有附件
|
||||
|
|
|
@ -704,6 +704,9 @@ core:
|
|||
operations:
|
||||
select:
|
||||
result: (已選擇 {count} 項)
|
||||
permalink_list:
|
||||
relative: 相對路徑
|
||||
absolute: 完整路徑
|
||||
uc_attachment:
|
||||
empty:
|
||||
title: 當前沒有附件
|
||||
|
|
Loading…
Reference in New Issue