79 lines
2.1 KiB
Vue
79 lines
2.1 KiB
Vue
<!-- app/components/CopyCom.vue - 通用复制按钮 -->
|
||
<script lang="ts" setup>
|
||
import { Check, Copy } from "lucide-vue-next";
|
||
import type { HTMLAttributes } from "vue";
|
||
|
||
import type { ButtonVariants } from "@/components/ui/button";
|
||
import { Button } from "@/components/ui/button";
|
||
import { cn } from "@/lib/utils";
|
||
import { copy } from "@/utils";
|
||
|
||
const {
|
||
text = "",
|
||
class: className,
|
||
variant = "outline",
|
||
size = "sm",
|
||
timeout = 2000,
|
||
disableDefaultCopy = false,
|
||
disabled = false
|
||
} = defineProps<{
|
||
/** 要复制的文本 */
|
||
text?: string;
|
||
// eslint-disable-next-line vue/require-default-prop
|
||
class?: HTMLAttributes["class"] | undefined;
|
||
variant?: ButtonVariants["variant"];
|
||
size?: ButtonVariants["size"];
|
||
/** 复制成功后恢复的延迟毫秒数,默认 2000 */
|
||
timeout?: number;
|
||
/** 禁用内置复制行为,只触发状态变化(外部自行处理复制) */
|
||
disableDefaultCopy?: boolean;
|
||
/** 禁用按鈕,不可点击且展示禁用样式 */
|
||
disabled?: boolean;
|
||
}>();
|
||
|
||
const emit = defineEmits<{
|
||
copy: [text: string];
|
||
}>();
|
||
|
||
const isCopied = ref(false);
|
||
|
||
const handleCopy = () => {
|
||
if (disabled) return;
|
||
if (!disableDefaultCopy) {
|
||
copy(text);
|
||
}
|
||
emit("copy", text);
|
||
isCopied.value = true;
|
||
setTimeout(() => {
|
||
isCopied.value = false;
|
||
}, timeout);
|
||
};
|
||
|
||
defineExpose({ isCopied });
|
||
</script>
|
||
|
||
<template>
|
||
<Button
|
||
type="button"
|
||
:variant="variant"
|
||
:size="size"
|
||
:disabled="disabled"
|
||
:class="cn('relative overflow-hidden pl-7', className)"
|
||
@click.prevent.stop="handleCopy"
|
||
>
|
||
<!-- 复制成功图标:未复制时向上移出,复制后滑入 -->
|
||
<Check
|
||
class="absolute size-3.5 transition-transform duration-200 left-2.5"
|
||
:class="isCopied ? 'translate-y-0' : '-translate-y-6'"
|
||
/>
|
||
<!-- 默认复制图标:复制后向下移出 -->
|
||
<Copy
|
||
class="absolute size-3.5 transition-transform duration-200 left-2.5"
|
||
:class="isCopied ? 'translate-y-6' : 'translate-y-0'"
|
||
/>
|
||
<slot :is-copied="isCopied">
|
||
<span>复制</span>
|
||
</slot>
|
||
</Button>
|
||
</template>
|