feat(front): refactor sharing logic by introducing useAppShare composable for file and text sharing

This commit is contained in:
keven1024
2025-06-01 17:58:03 +08:00
parent d3af363e16
commit 31a082eea2
4 changed files with 236 additions and 150 deletions

View File

@@ -1,75 +1,79 @@
<script setup lang="ts">
import { Button } from '@/components/ui/button';
import FilePreviewView from '@/components/FilePreviewView.vue';
import { Input } from '@/components/ui/input'
import { useClipboard } from '@vueuse/core'
import { toast } from 'vue-sonner'
import { useQuery } from '@tanstack/vue-query';
import { Button } from "@/components/ui/button";
import FilePreviewView from "@/components/FilePreviewView.vue";
import { Input } from "@/components/ui/input";
import { useClipboard } from "@vueuse/core";
import { toast } from "vue-sonner";
import { useQuery } from "@tanstack/vue-query";
import useAppShare from "@/composables/useShare";
const props = defineProps<{
data: { file: File, config: any, handle_type: string, file_id: string }
}>()
data: { file: File; config: any; handle_type: string; file_id: string };
}>();
const emit = defineEmits<{
(e: 'change', key: string): void
}>()
(e: "change", key: string): void;
}>();
const { createFileShare } = useAppShare();
const { data } = useQuery({
queryKey: ['create-share', props?.data?.file_id],
queryFn: async () => {
const { file_id, config, file } = props?.data || {}
const { name } = file || {}
const data = await $fetch<{
code: number
data: {
id?: string
}
}>(`/api/share`, {
method: 'POST',
body: {
type: 'file',
config,
data: file_id,
file_name: name
}
})
return data?.data
}
})
queryKey: ["create-share", props?.data?.file_id],
queryFn: async () => {
const { file_id, config, file } = props?.data || {};
const { name } = file || {};
const data = await createFileShare({
file_id,
config,
file_name: name,
});
return data?.data;
},
});
const appConfig = useAppConfig()
const appConfig = useAppConfig();
const url = computed(() => {
const { id } = data?.value || {}
return `${appConfig?.value?.site_url}/s/${id}`
})
const { id } = data?.value || {};
return `${appConfig?.value?.site_url}/s/${id}`;
});
const { copy } = useClipboard()
const { copy } = useClipboard();
</script>
<template>
<div class="flex flex-col gap-3">
<h2 class="text-lg">上传成功</h2>
<div class="flex flex-col gap-3 items-center">
<div class="flex flex-col h-30 items-center">
<FilePreviewView :value="props?.data?.file" />
</div>
<div class="flex flex-row gap-2">
<Input v-model="url" class="bg-white/70" />
<Button
variant="outline"
class="bg-white/70"
size="icon"
@click="
() => {
copy(url);
toast.success('复制成功');
}
"
>
<LucideCopy />
</Button>
<div class="flex flex-col gap-3">
<h2 class="text-lg">上传成功</h2>
<div class="flex flex-col gap-3 items-center">
<div class="flex flex-col h-30 items-center">
<FilePreviewView :value="props?.data?.file" />
</div>
<div class="flex flex-row gap-2">
<Input v-model="url" class="bg-white/70" />
<Button variant="outline" class="bg-white/70" size="icon" @click="() => {
copy(url)
toast.success('复制成功')
}">
<LucideCopy />
</Button>
<Button variant="outline" class="bg-white/70" size="icon">
<LucideQrCode />
</Button>
</div>
<Button variant="ghost" class="hover:bg-white/50 w-40" @click="() => {
emit('change', 'input')
}">
确定
</Button>
</div>
<Button variant="outline" class="bg-white/70" size="icon">
<LucideQrCode />
</Button>
</div>
<Button
variant="ghost"
class="hover:bg-white/50 w-40"
@click="
() => {
emit('change', 'input');
}
"
>
确定
</Button>
</div>
</template>
</div>
</template>

View File

@@ -1,44 +1,45 @@
<script lang="ts" setup>
import FileShareResult from '@/components/Result/FileShareResult.vue'
import TextShareResult from '@/components/Result/TextShareResult.vue'
import FileShareResult from "@/components/Result/FileShareResult.vue";
import TextShareResult from "@/components/Result/TextShareResult.vue";
import ImageCompressResult from "@/components/Result/ImageCompressResult.vue";
type basehandleData = { config: any, handle_type: string }
type basehandleData = { config: any; handle_type: string };
type filehandleData = { file: File, file_id: string } & basehandleData
type texthandleData = { text: string } & basehandleData
type filehandleData = { file: File; file_id: string } & basehandleData;
type texthandleData = { text: string } & basehandleData;
const props = defineProps<{
data: filehandleData | texthandleData
}>()
data: filehandleData | texthandleData;
}>();
const emit = defineEmits<{
(e: 'change', key: string): void
}>()
(e: "change", key: string): void;
}>();
const handleList = [
{ component: FileShareResult, key: 'file-share' },
{ component: TextShareResult, key: 'text-share' },
]
{ component: FileShareResult, key: "file-share" },
{ component: TextShareResult, key: "text-share" },
{ component: ImageCompressResult, key: "file-image-compress" },
];
const activeHandle = computed(() => {
return handleList.find((item) => item.key === props?.data?.handle_type)
})
return handleList.find((item) => item.key === props?.data?.handle_type);
});
// vue这个ts蠢的没边了本来想写component: FileShareResult | TextShareResult结果不行
</script>
<template>
<div>
<component
v-if="'file' in data"
:is="activeHandle?.component"
:data="data"
@change="(key: string) => emit('change', key)"
/>
<component
v-if="'text' in data"
:is="activeHandle?.component"
:data="data"
@change="(key: string) => emit('change', key)"
/>
</div>
</template>
<div>
<component
v-if="'file' in data"
:is="activeHandle?.component"
:data="data"
@change="(key: string) => emit('change', key)"
/>
<component
v-if="'text' in data"
:is="activeHandle?.component"
:data="data"
@change="(key: string) => emit('change', key)"
/>
</div>
</template>

View File

@@ -1,70 +1,79 @@
<script setup lang="ts">
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'
import { useClipboard } from '@vueuse/core'
import { toast } from 'vue-sonner'
import { useQuery } from '@tanstack/vue-query';
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useClipboard } from "@vueuse/core";
import { toast } from "vue-sonner";
import { useQuery } from "@tanstack/vue-query";
import useAppShare from "@/composables/useShare";
const props = defineProps<{
data: { text: string, config: any, handle_type: string }
}>()
data: { text: string; config: any; handle_type: string };
}>();
const emit = defineEmits<{
(e: 'change', key: string): void
}>()
(e: "change", key: string): void;
}>();
const { createTextShare } = useAppShare();
const { data } = useQuery({
queryKey: ['create-share', props?.data?.text],
queryFn: async () => {
const { config, text } = props?.data || {}
const data = await $fetch<{
code: number
data: {
id?: string
}
}>(`/api/share`, {
method: 'POST',
body: {
type: 'text',
config,
data: text,
}
})
return data?.data
}
})
const appConfig = useAppConfig()
queryKey: ["create-share", props?.data?.text],
queryFn: async () => {
const { config, text } = props?.data || {};
const data = await createTextShare({
text,
config,
});
return data?.data;
},
});
const appConfig = useAppConfig();
const url = computed(() => {
const { id } = data?.value || {}
return `${appConfig?.value?.site_url}/s/${id}`
})
const { id } = data?.value || {};
return `${appConfig?.value?.site_url}/s/${id}`;
});
const { copy } = useClipboard()
const { copy } = useClipboard();
</script>
<template>
<div class="flex flex-col gap-3">
<div class="flex flex-row justify-between">
<h2 class="text-lg">分享成功</h2>
<div class="flex flex-row gap-2 basis-1/2">
<Button variant="outline" class="bg-white/70" size="icon" @click="() => {
emit('change', 'input')
}">
<LucideHome />
</Button>
<Input v-model="url" class="bg-white/70" />
<Button variant="outline" class="bg-white/70" size="icon" @click="() => {
copy(url)
toast.success('复制成功')
}">
<LucideCopy />
</Button>
<Button variant="outline" class="bg-white/70" size="icon">
<LucideQrCode />
</Button>
</div>
</div>
<div class="prose rounded-md bg-white/70 p-3 w-full max-w-full" v-html="props?.data?.text" />
<div class="flex flex-col gap-3">
<div class="flex flex-row justify-between">
<h2 class="text-lg">分享成功</h2>
<div class="flex flex-row gap-2 basis-1/2">
<Button
variant="outline"
class="bg-white/70"
size="icon"
@click="
() => {
emit('change', 'input');
}
"
>
<LucideHome />
</Button>
<Input v-model="url" class="bg-white/70" />
<Button
variant="outline"
class="bg-white/70"
size="icon"
@click="
() => {
copy(url);
toast.success('复制成功');
}
"
>
<LucideCopy />
</Button>
<Button variant="outline" class="bg-white/70" size="icon">
<LucideQrCode />
</Button>
</div>
</div>
</template>
<div
class="prose rounded-md bg-white/70 p-3 w-full max-w-full"
v-html="props?.data?.text"
/>
</div>
</template>

View File

@@ -0,0 +1,72 @@
const downloadFile = async (share_id: string) => {
const data = await $fetch<{
code: number;
data: {
token?: string;
};
}>(`/api/download`, {
method: "POST",
body: {
share_id,
},
});
const { token } = data?.data || {};
if (!token) {
return;
}
(window as any)?.open(`/api/download?token=${token}`);
};
const createShare = async (data: any) => {
return await $fetch<{
code: number;
data: {
id?: string;
};
}>(`/api/share`, {
method: "POST",
body: data,
});
};
const createFileShare = async (data: {
file_id: string;
config: {
download_nums: number;
expire_time: number;
has_pickup_code?: boolean;
has_password?: boolean;
pickup_code?: string;
password?: string;
notify_email?: string;
};
file_name: string;
}) => {
const { file_id, config, file_name } = data || {};
return await createShare({
type: "file",
data: file_id,
config,
file_name,
});
};
const createTextShare = async (data: { text: string; config: any }) => {
const { text, config } = data || {};
return await createShare({
type: "text",
data: text,
config,
});
};
const useAppShare = () => {
return {
downloadFile,
createShare,
createFileShare,
createTextShare,
};
};
export default useAppShare;