From 3c24f804c5ea04304f8ad78961fec021f3ad6353 Mon Sep 17 00:00:00 2001 From: IchliebedichZhu <54796446@qq.com> Date: Sun, 3 Mar 2024 21:34:16 +0000 Subject: [PATCH 1/2] feat: Translate imageCutout component to vue3 --- src/api/ai.ts | 21 +- .../business/image-cutout/ImageCutout.vue | 222 ------------------ .../image-cutout/ImageCutout/index.vue | 202 ++++++++++++++++ .../image-cutout/ImageCutout/method.ts | 68 ++++++ src/components/business/image-cutout/index.ts | 2 +- src/types/global.d.ts | 5 +- 6 files changed, 290 insertions(+), 230 deletions(-) delete mode 100644 src/components/business/image-cutout/ImageCutout.vue create mode 100644 src/components/business/image-cutout/ImageCutout/index.vue create mode 100644 src/components/business/image-cutout/ImageCutout/method.ts diff --git a/src/api/ai.ts b/src/api/ai.ts index db7d7b7..21ab492 100644 --- a/src/api/ai.ts +++ b/src/api/ai.ts @@ -2,23 +2,32 @@ * @Author: ShawnPhang * @Date: 2021-08-27 14:42:15 * @Description: AI相关接口 - * @LastEditors: ShawnPhang - * @LastEditTime: 2023-10-13 00:07:19 + * @LastEditors: ShawnPhang , Jeremy Yu + * @Date: 2024-03-03 19:00:00 */ import fetch from '@/utils/axios' +export type TCommonUploadCb = (up: number, dp: number) => void + +type TUploadProgressCbData = { + loaded: number + total: number +} + +export type TUploadErrorResult = {type: "application/json"} + // 上传接口 -export const upload = (file: File, cb: Function) => { +export const upload = (file: File, cb: TCommonUploadCb) => { const formData = new FormData() formData.append('file', file) const extra = { responseType: 'blob', - onUploadProgress: (progress: any) => { + onUploadProgress: (progress: TUploadProgressCbData) => { cb(Math.floor((progress.loaded / progress.total) * 100), 0) }, - onDownloadProgress: (progress: any) => { + onDownloadProgress: (progress: TUploadProgressCbData) => { cb(100, Math.floor((progress.loaded / progress.total) * 100)) }, } - return fetch('https://res.palxp.cn/ai/upload', formData, 'post', {}, extra) + return fetch('https://res.palxp.cn/ai/upload', formData, 'post', {}, extra) } diff --git a/src/components/business/image-cutout/ImageCutout.vue b/src/components/business/image-cutout/ImageCutout.vue deleted file mode 100644 index 6d183e8..0000000 --- a/src/components/business/image-cutout/ImageCutout.vue +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - diff --git a/src/components/business/image-cutout/ImageCutout/index.vue b/src/components/business/image-cutout/ImageCutout/index.vue new file mode 100644 index 0000000..e80a965 --- /dev/null +++ b/src/components/business/image-cutout/ImageCutout/index.vue @@ -0,0 +1,202 @@ + + + + + + + + diff --git a/src/components/business/image-cutout/ImageCutout/method.ts b/src/components/business/image-cutout/ImageCutout/method.ts new file mode 100644 index 0000000..c7bcb44 --- /dev/null +++ b/src/components/business/image-cutout/ImageCutout/method.ts @@ -0,0 +1,68 @@ +/* + * @Author: Jeremy Yu + * @Date: 2024-03-03 19:00:00 + * @Description: 裁剪组件公共方法 + * @LastEditors: Jeremy Yu + * @Date: 2024-03-03 19:00:00 + */ + +import Qiniu from '@/common/methods/QiNiu' +import { TCommonUploadCb, TUploadErrorResult } from "@/api/ai" +import { TImageCutoutState } from "./index.vue" +import api from "@/api" +import { getImage } from '@/common/methods/getImgDetail' +import _config from '@/config' +import { Ref } from 'vue' + +/** 选择图片 */ +export const selectImageFile = async ( + state: TImageCutoutState, + raw: Ref, + file: File, + successCb?: (result: MediaSource, fileName: string) => void, + uploadCb?: TCommonUploadCb, +) => { + if (file.size > 1024 * 1024 * 2) { + alert('上传图片超出限制') + return false + } + if (!raw.value) return + // 显示选择的图片 + raw.value.addEventListener('load', () => { + state.offsetWidth = (raw.value as HTMLElement).offsetWidth + }) + state.rawImage = URL.createObjectURL(file) + + // 返回抠图结果 + const result = await api.ai.upload(file, (up: number, dp: number) => { + uploadCb && uploadCb(up, dp) + if (dp) { + state.progressText = dp === 100 ? '' : '导入中..' + state.progress = dp + } else { + state.progressText = up < 100 ? '上传中..' : '正在处理,请稍候..' + state.progress = up < 100 ? up : 0 + } + }) + if (typeof result == 'object' && (result as TUploadErrorResult).type !== 'application/json') { + successCb && successCb(result as MediaSource, file.name) + } else alert('服务器繁忙,请稍等下重新尝试~') +} + +export async function uploadCutPhotoToCloud(cutImage: string) { + try { + const response = await fetch(cutImage) + const buffer = await response.arrayBuffer() + const file = new File([buffer], `cut_image_${Math.random()}.png`) + // upload + const qnOptions = { bucket: 'xp-design', prePath: 'user' } + const result = await Qiniu.upload(file, qnOptions) + const { width, height } = await getImage(file) + const url = _config.IMG_URL + result.key + await api.material.addMyPhoto({ width, height, url }) + return url + } catch(e) { + console.error(`upload cut file error: msg: ${e}`) + return '' + } +} diff --git a/src/components/business/image-cutout/index.ts b/src/components/business/image-cutout/index.ts index 7c83cc4..61379c2 100644 --- a/src/components/business/image-cutout/index.ts +++ b/src/components/business/image-cutout/index.ts @@ -5,5 +5,5 @@ * @LastEditors: ShawnPhang * @LastEditTime: 2023-07-12 00:05:48 */ -import index from './ImageCutout.vue' +import index from './ImageCutout/index.vue' export default index diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 2306063..08e1e47 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -31,7 +31,10 @@ interface HTMLElementEventMap { } interface IQiniuSubscribeCb { - (result: { total: { percent: number }}): void + (result: { + total: { percent: number } + key: string + }): void } interface Window { From 8544e343484431ffe02184a09b9dcaeab4528c1e Mon Sep 17 00:00:00 2001 From: IchliebedichZhu <54796446@qq.com> Date: Sun, 3 Mar 2024 21:34:16 +0000 Subject: [PATCH 2/2] feat: Convert ImageCutout component to vue3 --- src/api/ai.ts | 21 +- .../business/image-cutout/ImageCutout.vue | 222 ------------------ .../image-cutout/ImageCutout/index.vue | 202 ++++++++++++++++ .../image-cutout/ImageCutout/method.ts | 68 ++++++ src/components/business/image-cutout/index.ts | 2 +- src/types/global.d.ts | 5 +- 6 files changed, 290 insertions(+), 230 deletions(-) delete mode 100644 src/components/business/image-cutout/ImageCutout.vue create mode 100644 src/components/business/image-cutout/ImageCutout/index.vue create mode 100644 src/components/business/image-cutout/ImageCutout/method.ts diff --git a/src/api/ai.ts b/src/api/ai.ts index db7d7b7..21ab492 100644 --- a/src/api/ai.ts +++ b/src/api/ai.ts @@ -2,23 +2,32 @@ * @Author: ShawnPhang * @Date: 2021-08-27 14:42:15 * @Description: AI相关接口 - * @LastEditors: ShawnPhang - * @LastEditTime: 2023-10-13 00:07:19 + * @LastEditors: ShawnPhang , Jeremy Yu + * @Date: 2024-03-03 19:00:00 */ import fetch from '@/utils/axios' +export type TCommonUploadCb = (up: number, dp: number) => void + +type TUploadProgressCbData = { + loaded: number + total: number +} + +export type TUploadErrorResult = {type: "application/json"} + // 上传接口 -export const upload = (file: File, cb: Function) => { +export const upload = (file: File, cb: TCommonUploadCb) => { const formData = new FormData() formData.append('file', file) const extra = { responseType: 'blob', - onUploadProgress: (progress: any) => { + onUploadProgress: (progress: TUploadProgressCbData) => { cb(Math.floor((progress.loaded / progress.total) * 100), 0) }, - onDownloadProgress: (progress: any) => { + onDownloadProgress: (progress: TUploadProgressCbData) => { cb(100, Math.floor((progress.loaded / progress.total) * 100)) }, } - return fetch('https://res.palxp.cn/ai/upload', formData, 'post', {}, extra) + return fetch('https://res.palxp.cn/ai/upload', formData, 'post', {}, extra) } diff --git a/src/components/business/image-cutout/ImageCutout.vue b/src/components/business/image-cutout/ImageCutout.vue deleted file mode 100644 index 6d183e8..0000000 --- a/src/components/business/image-cutout/ImageCutout.vue +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - diff --git a/src/components/business/image-cutout/ImageCutout/index.vue b/src/components/business/image-cutout/ImageCutout/index.vue new file mode 100644 index 0000000..e80a965 --- /dev/null +++ b/src/components/business/image-cutout/ImageCutout/index.vue @@ -0,0 +1,202 @@ + + + + + + + + diff --git a/src/components/business/image-cutout/ImageCutout/method.ts b/src/components/business/image-cutout/ImageCutout/method.ts new file mode 100644 index 0000000..c7bcb44 --- /dev/null +++ b/src/components/business/image-cutout/ImageCutout/method.ts @@ -0,0 +1,68 @@ +/* + * @Author: Jeremy Yu + * @Date: 2024-03-03 19:00:00 + * @Description: 裁剪组件公共方法 + * @LastEditors: Jeremy Yu + * @Date: 2024-03-03 19:00:00 + */ + +import Qiniu from '@/common/methods/QiNiu' +import { TCommonUploadCb, TUploadErrorResult } from "@/api/ai" +import { TImageCutoutState } from "./index.vue" +import api from "@/api" +import { getImage } from '@/common/methods/getImgDetail' +import _config from '@/config' +import { Ref } from 'vue' + +/** 选择图片 */ +export const selectImageFile = async ( + state: TImageCutoutState, + raw: Ref, + file: File, + successCb?: (result: MediaSource, fileName: string) => void, + uploadCb?: TCommonUploadCb, +) => { + if (file.size > 1024 * 1024 * 2) { + alert('上传图片超出限制') + return false + } + if (!raw.value) return + // 显示选择的图片 + raw.value.addEventListener('load', () => { + state.offsetWidth = (raw.value as HTMLElement).offsetWidth + }) + state.rawImage = URL.createObjectURL(file) + + // 返回抠图结果 + const result = await api.ai.upload(file, (up: number, dp: number) => { + uploadCb && uploadCb(up, dp) + if (dp) { + state.progressText = dp === 100 ? '' : '导入中..' + state.progress = dp + } else { + state.progressText = up < 100 ? '上传中..' : '正在处理,请稍候..' + state.progress = up < 100 ? up : 0 + } + }) + if (typeof result == 'object' && (result as TUploadErrorResult).type !== 'application/json') { + successCb && successCb(result as MediaSource, file.name) + } else alert('服务器繁忙,请稍等下重新尝试~') +} + +export async function uploadCutPhotoToCloud(cutImage: string) { + try { + const response = await fetch(cutImage) + const buffer = await response.arrayBuffer() + const file = new File([buffer], `cut_image_${Math.random()}.png`) + // upload + const qnOptions = { bucket: 'xp-design', prePath: 'user' } + const result = await Qiniu.upload(file, qnOptions) + const { width, height } = await getImage(file) + const url = _config.IMG_URL + result.key + await api.material.addMyPhoto({ width, height, url }) + return url + } catch(e) { + console.error(`upload cut file error: msg: ${e}`) + return '' + } +} diff --git a/src/components/business/image-cutout/index.ts b/src/components/business/image-cutout/index.ts index 7c83cc4..61379c2 100644 --- a/src/components/business/image-cutout/index.ts +++ b/src/components/business/image-cutout/index.ts @@ -5,5 +5,5 @@ * @LastEditors: ShawnPhang * @LastEditTime: 2023-07-12 00:05:48 */ -import index from './ImageCutout.vue' +import index from './ImageCutout/index.vue' export default index diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 2306063..08e1e47 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -31,7 +31,10 @@ interface HTMLElementEventMap { } interface IQiniuSubscribeCb { - (result: { total: { percent: number }}): void + (result: { + total: { percent: number } + key: string + }): void } interface Window {