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>
|
<script lang="ts" setup>
|
||||||
|
import { useGlobalInfoStore } from "@/stores/global-info";
|
||||||
import { matchMediaType } from "@/utils/media-type";
|
import { matchMediaType } from "@/utils/media-type";
|
||||||
import type { Attachment } from "@halo-dev/api-client";
|
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 { useClipboard } from "@vueuse/core";
|
||||||
import { computed, toRefs } from "vue";
|
import { computed, ref, toRefs } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
attachment?: Attachment;
|
attachment?: Attachment;
|
||||||
mountToBody?: boolean;
|
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
attachment: undefined,
|
attachment: undefined,
|
||||||
mountToBody: false,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -21,6 +20,9 @@ const { attachment } = toRefs(props);
|
||||||
|
|
||||||
const { copy } = useClipboard({ legacy: true });
|
const { copy } = useClipboard({ legacy: true });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { globalInfo } = useGlobalInfoStore();
|
||||||
|
|
||||||
|
const activeId = ref<"relative" | "absolute">("relative");
|
||||||
|
|
||||||
const mediaType = computed(() => {
|
const mediaType = computed(() => {
|
||||||
return attachment.value?.spec.mediaType;
|
return attachment.value?.spec.mediaType;
|
||||||
|
@ -38,36 +40,48 @@ const isAudio = computed(() => {
|
||||||
return mediaType.value && matchMediaType(mediaType.value, "audio/*");
|
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 htmlText = computed(() => {
|
||||||
const { permalink } = attachment.value?.status || {};
|
|
||||||
const { displayName } = attachment.value?.spec || {};
|
const { displayName } = attachment.value?.spec || {};
|
||||||
|
|
||||||
if (isImage.value) {
|
if (isImage.value) {
|
||||||
return `<img src="${permalink}" alt="${displayName}" />`;
|
return `<img src="${permalink.value}" alt="${displayName}" />`;
|
||||||
} else if (isVideo.value) {
|
} else if (isVideo.value) {
|
||||||
return `<video src="${permalink}"></video>`;
|
return `<video src="${permalink.value}"></video>`;
|
||||||
} else if (isAudio.value) {
|
} 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 markdownText = computed(() => {
|
||||||
const { permalink } = attachment.value?.status || {};
|
|
||||||
const { displayName } = attachment.value?.spec || {};
|
const { displayName } = attachment.value?.spec || {};
|
||||||
if (isImage.value) {
|
if (isImage.value) {
|
||||||
return ``;
|
return ``;
|
||||||
}
|
}
|
||||||
return `[${displayName}](${permalink})`;
|
return `[${displayName}](${permalink.value})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCopy = (format: "markdown" | "html" | "url") => {
|
const handleCopy = (format: "markdown" | "html" | "url") => {
|
||||||
const { permalink } = attachment.value?.status || {};
|
|
||||||
|
|
||||||
if (!permalink) return;
|
|
||||||
|
|
||||||
if (format === "url") {
|
if (format === "url") {
|
||||||
copy(permalink);
|
copy(permalink.value || "");
|
||||||
} else if (format === "markdown") {
|
} else if (format === "markdown") {
|
||||||
copy(markdownText.value);
|
copy(markdownText.value);
|
||||||
} else if (format === "html") {
|
} else if (format === "html") {
|
||||||
|
@ -87,7 +101,7 @@ const formats = computed(
|
||||||
{
|
{
|
||||||
label: "URL",
|
label: "URL",
|
||||||
key: "url",
|
key: "url",
|
||||||
value: attachment?.value?.status?.permalink,
|
value: permalink.value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "HTML",
|
label: "HTML",
|
||||||
|
@ -105,7 +119,22 @@ const formats = computed(
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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">
|
<li v-for="format in formats" :key="format.key">
|
||||||
<div
|
<div
|
||||||
class="flex w-full cursor-pointer items-center justify-between space-x-3 rounded border p-3 hover:border-primary"
|
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:
|
items:
|
||||||
display_name_asc: Ascending order by display name
|
display_name_asc: Ascending order by display name
|
||||||
display_name_desc: Descending order by display name
|
display_name_desc: Descending order by display name
|
||||||
|
permalink_list:
|
||||||
|
relative: Relative path
|
||||||
|
absolute: Absolute path
|
||||||
group_editing_modal:
|
group_editing_modal:
|
||||||
toast:
|
toast:
|
||||||
group_name_exists: Group name already exists
|
group_name_exists: Group name already exists
|
||||||
|
|
|
@ -684,6 +684,9 @@ core:
|
||||||
video_not_support: The current browser does not support video playback.
|
video_not_support: The current browser does not support video playback.
|
||||||
audio_not_support: The current browser does not support audio playback.
|
audio_not_support: The current browser does not support audio playback.
|
||||||
not_support: This file does not support preview.
|
not_support: This file does not support preview.
|
||||||
|
permalink_list:
|
||||||
|
relative: Relative path
|
||||||
|
absolute: Absolute path
|
||||||
group_editing_modal:
|
group_editing_modal:
|
||||||
titles:
|
titles:
|
||||||
create: Create attachment group
|
create: Create attachment group
|
||||||
|
|
|
@ -719,6 +719,9 @@ core:
|
||||||
operations:
|
operations:
|
||||||
select:
|
select:
|
||||||
result: (已选择 {count} 项)
|
result: (已选择 {count} 项)
|
||||||
|
permalink_list:
|
||||||
|
relative: 相对路径
|
||||||
|
absolute: 完整路径
|
||||||
uc_attachment:
|
uc_attachment:
|
||||||
empty:
|
empty:
|
||||||
title: 当前没有附件
|
title: 当前没有附件
|
||||||
|
|
|
@ -704,6 +704,9 @@ core:
|
||||||
operations:
|
operations:
|
||||||
select:
|
select:
|
||||||
result: (已選擇 {count} 項)
|
result: (已選擇 {count} 項)
|
||||||
|
permalink_list:
|
||||||
|
relative: 相對路徑
|
||||||
|
absolute: 完整路徑
|
||||||
uc_attachment:
|
uc_attachment:
|
||||||
empty:
|
empty:
|
||||||
title: 當前沒有附件
|
title: 當前沒有附件
|
||||||
|
|
Loading…
Reference in New Issue