🚧 developing
This commit is contained in:
@@ -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,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -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,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
export default [
|
||||
{
|
||||
path: '/main/photo/phone',
|
||||
path: '/main/photo/upscale',
|
||||
name: 'upscale',
|
||||
component: () => import('@/views/Upscale/index.vue'),
|
||||
meta: {
|
||||
|
5
src/types/upscale.d.ts
vendored
5
src/types/upscale.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
export interface uploadImageRequest {
|
||||
image: string;
|
||||
access_token: string;
|
||||
user_id: string;
|
||||
}
|
26
src/utils/imageUtils/fileToBase64.ts
Normal file
26
src/utils/imageUtils/fileToBase64.ts
Normal 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);
|
||||
};
|
||||
});
|
||||
}
|
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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 天前的日期
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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>) => {
|
||||
|
Reference in New Issue
Block a user