🚧 developing

This commit is contained in:
2025-02-24 17:10:01 +08:00
parent 5befcddaf5
commit 0e1b7c32b0
10 changed files with 197 additions and 62 deletions

View File

@@ -1,7 +1,6 @@
import {service} from "@/utils/alova/service.ts";
import {uploadImageRequest} from "@/types/upscale";
export const uploadImage = (data: uploadImageRequest) => {
export const uploadImage = (data: any) => {
return service.Post('/api/auth/phone/upload', {
image: data.image,
access_token: data.access_token,
@@ -13,3 +12,17 @@ export const uploadImage = (data: uploadImageRequest) => {
}
});
};
/**
* 上传分享图片
* @param data
*/
export const sharePhoneUploadApi = (data: any) => {
return service.Post('/api/auth/phone/share/upload', {
...data,
}, {
meta: {
ignoreToken: false,
signature: false,
}
});
};

View File

@@ -74,3 +74,21 @@ export const queryShareOverviewApi = () => {
},
});
};
/**
* 删除分享记录
* @param id
* @param invite_code
* @param album_id
*/
export const deleteShareRecordApi = (id: number, invite_code: string, album_id: number) => {
return service.Post('/api/auth/share/record/delete', {
id: id,
invite_code: invite_code,
album_id: album_id,
}, {
meta: {
ignoreToken: false,
signature: false,
},
});
};

View File

@@ -1,6 +1,6 @@
export default [
{
path: '/main/photo/phone',
path: '/main/photo/upscale',
name: 'upscale',
component: () => import('@/views/Upscale/index.vue'),
meta: {

View File

@@ -1,5 +0,0 @@
export interface uploadImageRequest {
image: string;
access_token: string;
user_id: string;
}

View File

@@ -0,0 +1,26 @@
/**
* 将 File 对象转换为包含前缀的 Base64 字符串
* @param file - 要转换的 File 对象
* @returns 返回一个 Promise解析为包含前缀的 Base64 字符串
*/
export function fileToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
// 创建 FileReader 对象
const reader = new FileReader();
// 读取文件内容
reader.readAsDataURL(file);
// 读取成功时触发
reader.onload = () => {
// 获取 Base64 字符串(包含前缀)
const base64String = reader.result as string;
resolve(base64String);
};
// 读取失败时触发
reader.onerror = (error) => {
reject(error);
};
});
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="upscale-upload-content">
<div class="share-upload-content">
<AUploadDragger
name="file"
accept="image/*"
@@ -7,29 +7,30 @@
:directory="false"
:maxCount="1"
:beforeUpload="beforeUpload"
:fileList="fileList"
:custom-request="customRequest"
v-model:fileList="fileList"
:loading="uploading"
:showUploadList="true">
<div class="upscale-upload-content-main">
:showUploadList="false">
<div class="share-upload-content-main">
<ABadge :offset="[-10, 10]">
<template #count>
<AAvatar :size="25" :src="sueccess" v-if="fileList.length > 0"/>
<AAvatar :size="25" :src="succeed" v-if="fileList.length > 0"/>
<AAvatar :size="25" :src="warn" v-if="fileList.length === 0"/>
</template>
<AAvatar shape="square" :size="70" :src="file"/>
</ABadge>
<span class="upscale-upload-text">
<span class="share-upload-text">
点击上传图片
</span>
</div>
</AUploadDragger>
<ADivider orientation="center" :plain="true">
<span class="upscale-divider-title">图片预览</span>
<span class="share-divider-title">图片预览</span>
</ADivider>
<div class="upscale-upload-image" v-if="fileList.length > 0">
<ABadge v-for="(item, index) in fileList" :key="index">
<div class="share-upload-image" v-if="fileList.length > 0">
<ABadge>
<template #count>
<AButton type="text" size="small" class="upscale-file-btn">
<AButton type="text" size="small" class="share-file-btn" @click="fileList = []">
<template #icon>
<AAvatar shape="square" :size="25" :src="remove"/>
</template>
@@ -37,7 +38,7 @@
</template>
<AAvatar shape="square" :size="100">
<template #icon>
<AImage :src="convertFileToUrl(item.file)" width="100%" height="100%"/>
<AImage :src="convertFileToUrl(fileList?.[0].originFileObj)" width="100%" height="100%"/>
</template>
</AAvatar>
</ABadge>
@@ -46,7 +47,7 @@
<ADivider/>
<div>
<AButton type="primary" size="large" :disabled="fileList.length === 0" :loading="uploading"
class="upscale-upload-btn">
class="share-upload-btn" @click="sendImage()">
发送图片
</AButton>
</div>
@@ -55,44 +56,49 @@
</template>
<script setup lang="ts">
import file from "@/assets/svgs/file.svg";
import useStore from "@/store";
import sueccess from '@/assets/svgs/success.svg';
import succeed from '@/assets/svgs/success.svg';
import warn from '@/assets/svgs/warn.svg';
import remove from '@/assets/svgs/remove.svg';
import empty from '@/assets/svgs/empty.svg';
import {message} from "ant-design-vue";
import {useI18n} from "vue-i18n";
import i18n from "@/locales";
import {initNSFWJs, predictNSFW} from "@/utils/tfjs/nsfw.ts";
import {NSFWJS} from "nsfwjs";
import {sharePhoneUploadApi} from "@/api/phone";
import {useI18n} from "vue-i18n";
import {fileToBase64} from "@/utils/imageUtils/fileToBase64.ts";
const upscale = useStore().upscale;
const route = useRoute();
const {t} = useI18n();
const fileList = ref<any[]>([]);
const {t} = useI18n();
const uploading = ref(false);
// async function sendImage() {
// if (!upscale.imageData) {
// return;
// }
// const base64 = await blobToBase64(upscale.imageData);
// const data: uploadImageRequest = {
// image: base64,
// access_token: route.query.token as string,
// user_id: route.query.user_id as string,
// };
// const res: any = await uploadImage(data);
// if (res && res && res.code === 200) {
// message.success(t('upload.uploadSuccess'));
// } else {
// message.error(res.message);
// }
// }
async function sendImage() {
if (fileList.value.length === 0) {
return;
}
const base64 = await fileToBase64(fileList.value?.[0].originFileObj);
const data: any = {
origin_file_obj: base64,
name: fileList.value?.[0].name,
size: fileList.value?.[0].size,
access_token: route.query.token as string,
user_id: route.query.user_id as string,
type: fileList.value?.[0].type,
};
const res: any = await sharePhoneUploadApi(data);
if (res && res && res.code === 200) {
message.success(t('upload.uploadSuccess'));
fileList.value = [];
} else {
message.error(res.message);
}
}
/**
* 上传文件前置
@@ -117,6 +123,12 @@ async function beforeUpload(file: any) {
};
return true;
}
function customRequest() {
return;
}
/**
* 转换文件为 URL
* @param file
@@ -127,13 +139,13 @@ function convertFileToUrl(file: any) {
</script>
<style scoped lang="scss">
.upscale-upload-content {
.share-upload-content {
width: 90%;
height: 30vh;
height: 40vh;
padding: 15px;
margin: 0 auto;
.upscale-upload-content-main {
.share-upload-content-main {
width: 100%;
height: 100%;
display: flex;
@@ -143,33 +155,33 @@ function convertFileToUrl(file: any) {
overflow: scroll;
.upscale-upload-text {
.share-upload-text {
font-size: 20px;
font-weight: bold;
}
.upscale-upload-btn {
.share-upload-btn {
width: 60%;
}
.upscale-upload-tip {
.share-upload-tip {
font-size: 12px;
color: rgba(126, 126, 135, 0.99);
}
}
.upscale-divider-title {
.share-divider-title {
font-size: 13px;
color: rgba(126, 126, 135, 0.99);
}
.upscale-upload-image {
.share-upload-image {
display: flex;
justify-content: center;
align-items: center;
}
.upscale-upload-btn {
.share-upload-btn {
width: 100%;
display: flex;
justify-content: center;

View File

@@ -63,7 +63,6 @@ import remove from '@/assets/svgs/remove.svg';
import empty from '@/assets/svgs/empty.svg';
import {blobToBase64} from "@/utils/imageUtils/blobToBase64.ts";
import {uploadImage} from "src/api/phone";
import {uploadImageRequest} from "@/types/upscale";
import {message} from "ant-design-vue";
import {useI18n} from "vue-i18n";
@@ -78,7 +77,7 @@ async function sendImage() {
return;
}
const base64 = await blobToBase64(upscale.imageData);
const data: uploadImageRequest = {
const data: any = {
image: base64,
access_token: route.query.token as string,
user_id: route.query.user_id as string,

View File

@@ -12,7 +12,7 @@
style="background: linear-gradient(102.74deg, rgb(66, 230, 171) -7.03%, rgb(103, 235, 187) 97.7%);">
<div class="image-share-left-item-content">
<span style="font-weight: bolder;font-size: 2.3vh">浏览次数</span>
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData?overviewData.visit_count:0 }}</span>
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData ? overviewData.visit_count : 0 }}</span>
<p style="font-size: 2vh;color: hsla(0,0%,100%,.6);">今日浏览
<span
style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+{{
@@ -26,11 +26,11 @@
style="background: linear-gradient(101.63deg, rgb(82, 138, 250) -12.83%, rgb(122, 167, 255) 100%);">
<div class="image-share-left-item-content">
<span style="font-weight: bolder;font-size: 2.3vh">浏览人数</span>
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData?overviewData.viewer_count:0 }}</span>
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData ? overviewData.viewer_count : 0 }}</span>
<p style="font-size: 2vh;color: hsla(0,0%,100%,.6);">今日浏览人数
<span
style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+{{
overviewData?overviewData.viewer_count_today:0
overviewData ? overviewData.viewer_count_today : 0
}}</span>
</p>
</div>
@@ -40,7 +40,9 @@
style="background: linear-gradient(102.99deg, rgb(126, 92, 255) 3.18%, rgb(162, 139, 255) 102.52%);">
<div class="image-share-left-item-content">
<span style="font-weight: bolder;font-size: 2.3vh">发布次数</span>
<span style="font-weight: bolder;font-size: 5vh">{{ overviewData?overviewData.publish_count:0 }}</span>
<span style="font-weight: bolder;font-size: 5vh">{{
overviewData ? overviewData.publish_count : 0
}}</span>
<p style="font-size: 2vh;color: hsla(0,0%,100%,.6);">今日发布
<span
style="font-weight: bolder;font-size: 2.8vh;color: #fff;">+{{
@@ -109,6 +111,8 @@
title="确定删除该快传记录?"
ok-text="确定"
cancel-text="取消"
@confirm="async () => await deleteShareRecord(record.id, record.invite_code, record.album_id)"
@cancel="() => {}"
>
<AButton type="text" size="small" danger>
<DeleteOutlined/>
@@ -131,7 +135,7 @@
import {Dayjs} from 'dayjs';
import dayjs from 'dayjs';
import ShareUpload from "@/views/Share/ImageShare/ShareUpload.vue";
import {queryShareOverviewApi, queryShareRecordListApi} from "@/api/share";
import {deleteShareRecordApi, queryShareOverviewApi, queryShareRecordListApi} from "@/api/share";
import {message} from "ant-design-vue";
import 'dayjs/locale/zh-cn';
@@ -278,6 +282,24 @@ async function getShareOverview() {
overviewDataLoading.value = false;
}
/**
* 删除分享记录
* @param id
* @param invite_code
* @param album_id
*/
async function deleteShareRecord(id: number, invite_code: string, album_id: number) {
const res: any = await deleteShareRecordApi(id, invite_code, album_id);
if (res && res.code === 200) {
message.success('删除成功');
const endDate = dayjs().format('YYYY-MM-DD'); // 当前日期
const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD'); // 30 天前的日期
await getShareRecords([startDate, endDate]);
} else {
message.error('删除失败');
}
}
onMounted(async () => {
const endDate = dayjs().format('YYYY-MM-DD'); // 当前日期
const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD'); // 30 天前的日期

View File

@@ -33,10 +33,10 @@
<template #content>
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
:size="qrcodeSize"
:value="`git.landaiqing.cneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNjI1MTEyMjE3MzQyMDIxIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTczOTg3ODIyOCwibmJmIjoxNzM5ODcxMDI4LCJpYXQiOjE3Mzk4NzEwMjh9.EUiZsVjhGqHx1V5o90S3W5li6nIqucxy9eEY9LWgqXY`"
:value="generateQrCodeUrl()"
:icon="phone"
:iconSize="iconSize"
:status="`active`"
:status="qrStatus"
/>
</template>
<AButton type="default" size="large" shape="round" style="width: 70%"
@@ -430,6 +430,11 @@ const wsOptions = {
protocols: [user.token.accessToken],
};
function generateQrCodeUrl(): string {
console.log(import.meta.env.VITE_APP_WEB_URL + "/main/share/phone/app?user_id=" + user.user.uid + "&token=" + user.token.accessToken);
return import.meta.env.VITE_APP_WEB_URL + "/main/share/phone/app?user_id=" + user.user.uid + "&token=" + user.token.accessToken;
}
/**
* 初始化 WebSocket
*/
@@ -438,11 +443,56 @@ function initWebSocket() {
websocket.on("message", async (res: any) => {
if (res && res.code === 200) {
const {data} = res;
console.log(data);
const file: File = base64ToFile(data.origin_file_obj, data.name, data.type);
fileList.value.push({
originFileObj: file,
name: data.name,
type: data.type,
size: data.size,
});
}
});
}
/**
* 将 Base64 字符串转换为 File 对象
* @param base64
* @param filename
* @param mimeType
*/
function base64ToFile(base64, filename, mimeType): File {
// 检查并移除Base64前缀如果有
const base64Data = base64.split(',')[1] || base64;
// 将Base64字符串解码为二进制字符串
const binaryString = atob(base64Data);
// 将二进制字符串转换为ArrayBuffer
const arrayBuffer = new ArrayBuffer(binaryString.length);
const uint8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < binaryString.length; i++) {
uint8Array[i] = binaryString.charCodeAt(i);
}
// 创建Blob对象
const blob = new Blob([arrayBuffer], {type: mimeType});
// 创建File对象
return new File([blob], filename, {type: mimeType});
}
const qrStatus = ref<string>('loading');
watch(
() => websocket.readyState,
(newStatus) => {
if (newStatus === WebSocket.OPEN) {
qrStatus.value = 'active';
} else {
qrStatus.value = 'expired';
}
}
);
onMounted(() => {
window.addEventListener("resize", updateQrcodeSize);
});

View File

@@ -82,7 +82,7 @@ async function startTask() {
if (upscale.input === null) return;
upscale.isProcessing = true;
const start = Date.now();
const worker = new Worker(new URL("@/workers/phone/phone.worker.ts", import.meta.url), {
const worker = new Worker(new URL("@/workers/upscale/upscale.worker.ts", import.meta.url), {
type: "module",
});
worker.onmessage = (e: MessageEvent<any>) => {