From 8db34c6ee62c807b2f68d5d0e194d76f02a04581 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Wed, 30 Oct 2024 11:23:47 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=BC=A9=E7=95=A5?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Exceptions/Handler.php | 25 ++- resources/assets/js/functions/common.js | 27 ++++ resources/assets/js/functions/web.js | 153 +++++++++--------- .../manage/components/DialogView/file.vue | 4 +- routes/web.php | 4 +- 5 files changed, 127 insertions(+), 86 deletions(-) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 8c55b826c..32acf7725 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -96,14 +96,25 @@ class Handler extends ExceptionHandler $path = $request->path(); // 处理图片 - $pattern = '/^(uploads\/.*\.(png|jpg|jpeg))\/crop\/([^\/]+)$/'; - if (preg_match($pattern, $path, $matches)) { + $patternCrop = '/^(uploads\/.*\.(png|jpg|jpeg))\/crop\/([^\/]+)$/'; + $patternThumb = '/^(uploads\/.*)_thumb\.(png|jpg|jpeg)$/'; + $matchesCrop = null; + $matchesThumb = null; + if (preg_match($patternCrop, $path, $matchesCrop) || preg_match($patternThumb, $path, $matchesThumb)) { // 获取参数 - $file = $matches[1]; - $ext = $matches[2]; - $rules = preg_replace('/\s+/', '', $matches[3]); - $rules = str_replace(['=', '&'], [':', ','], $rules); - $rules = explode(',', $rules); + if ($matchesCrop) { + $file = $matchesCrop[1]; + $ext = $matchesCrop[2]; + $rules = preg_replace('/\s+/', '', $matchesCrop[3]); + $rules = str_replace(['=', '&'], [':', ','], $rules); + $rules = explode(',', $rules); + } elseif ($matchesThumb) { + $file = $matchesThumb[1]; + $ext = $matchesThumb[2]; + $rules = ['percentage:320x0']; + } else { + return null; + } if (empty($rules)) { return null; } diff --git a/resources/assets/js/functions/common.js b/resources/assets/js/functions/common.js index b1bd05716..aa106676d 100755 --- a/resources/assets/js/functions/common.js +++ b/resources/assets/js/functions/common.js @@ -1199,6 +1199,33 @@ const timezone = require("dayjs/plugin/timezone"); } return result; }, {}); + }, + + /** + * 从HTML中提取图片参数 + * @param imgTag + * @returns {{original, src: (*|null), width: (number|*), height: (number|*)}} + */ + extractImageParameter(imgTag) { + const srcMatch = imgTag.match(/\s+src=(["'])([^'"]*)\1/i); + const widthMatch = imgTag.match(/\s+width=(["'])([^'"]*)\1/i); + const heightMatch = imgTag.match(/\s+height=(["'])([^'"]*)\1/i); + return { + src: srcMatch ? srcMatch[2] : null, + width: $A.runNum(widthMatch ? widthMatch[2] : 0), + height: $A.runNum(heightMatch ? heightMatch[2] : 0), + original: imgTag, + } + }, + + /** + * 从HTML中提取所有图片参数 + * @param html + * @returns {{original, src: (*|null), width: (number|*), height: (number|*)}[]} + */ + extractImageParameterAll(html) { + const imgTags = html.match(/]*?>/g) || []; + return imgTags.map(imgTag => this.extractImageParameter(imgTag)); } }); diff --git a/resources/assets/js/functions/web.js b/resources/assets/js/functions/web.js index b30e5ef0f..b91e95f99 100755 --- a/resources/assets/js/functions/web.js +++ b/resources/assets/js/functions/web.js @@ -249,26 +249,17 @@ import {MarkdownPreview} from "../store/markdown"; text = text.replace(/]*?>/g, `[${$A.L('动画表情')}]`) if (imgClassName) { text = text.replace(/]*?src="(\S+)"[^>]*?>/g, function (res, src) { - const widthMatch = res.match("width=\"(\\d+)\""), - heightMatch = res.match("height=\"(\\d+)\""); - if (widthMatch && heightMatch) { - const data = { - width: parseInt(widthMatch[1]), - height: parseInt(heightMatch[1]), - maxSize: 40, - src - } - const ratioExceed = $A.imageRatioExceed(data.width, data.height, 2) - if (ratioExceed > 0 && /\.(png|jpg|jpeg)$/.test(data.src)) { - src = $A.thumbRestore(data.src) + `/crop/ratio:${ratioExceed},percentage:80x0` - if (data.width > data.height) { - data.width = data.height * ratioExceed; - } else { - data.height = data.width * ratioExceed; - } - } - const scale = $A.scaleToScale(data.width, data.height, data.maxSize); - imgClassName = `${imgClassName}" style="width:${scale.width}px;height:${scale.height}px` + const item = $A.extractImageParameter(res); + if (item.width && item.height) { + const data = $A.imageRatioHandle({ + src: item.src, + width: item.width, + height: item.height, + crops: {ratio: 2, percentage: '80x0'}, + scaleSize: 40, + }) + src = data.src + imgClassName = `${imgClassName}" style="width:${data.width}px;height:${data.height}px` } return `[image:${src}]` }) @@ -344,44 +335,25 @@ import {MarkdownPreview} from "../store/markdown"; }).join("") } // 处理图片显示尺寸 - const array = text.match(/]*?>/g); - if (array) { - const widthReg = new RegExp("width=\"(\\d+)\""), - heightReg = new RegExp("height=\"(\\d+)\"") - array.some(res => { - const widthMatch = res.match(widthReg), - heightMatch = res.match(heightReg); - if (widthMatch && heightMatch) { - const data = { - res, - width: parseInt(widthMatch[1]), - height: parseInt(heightMatch[1]), - maxSize: res.indexOf("emoticon") > -1 ? 150 : 220, // 跟css中的设置一致 - } - if (data.maxSize === 220) { - const ratioExceed = $A.imageRatioExceed(data.width, data.height) - if (ratioExceed > 0) { - const srcMatch = res.match(/src=(["'])(([^'"]*)\.(png|jpg|jpeg))\1/); - if (srcMatch) { - data.res = data.res.replace(srcMatch[2], $A.thumbRestore(srcMatch[2]) + `/crop/ratio:${ratioExceed},percentage:320x0`) - if (data.width > data.height) { - data.width = data.height * ratioExceed; - } else { - data.height = data.width * ratioExceed; - } - } - } - } - const scale = $A.scaleToScale(data.width, data.height, data.maxSize); - const value = data.res - .replace(widthReg, `original-width="${data.width}"`) - .replace(heightReg, `original-height="${data.height}" style="width:${scale.width}px;height:${scale.height}px"`) - text = text.replace(res, value) - } else { - text = text.replace(res, `
${res}
`); - } - }) - } + const items = $A.extractImageParameterAll(text); + items.some(item => { + if (item.src && item.width && item.height) { + const data = $A.imageRatioHandle({ + src: item.src, + width: item.width, + height: item.height, + crops: {ratio: 3, percentage: '320x0'}, + scaleSize: item.original.indexOf("emoticon") > -1 ? 150 : 220, + }) + const value = item.original + .replace(/\s+width=/, ` original-width=`) + .replace(/\s+height=/, ` original-height=`) + .replace(/\s+src=(["'])([^'"]*)\1/i, ` style="width:${data.width}px;height:${data.height}px" src="${data.src}"`) + text = text.replace(item.original, value) + } else { + text = text.replace(item.original, `
${item.original}
`); + } + }) return text; }, @@ -462,23 +434,14 @@ import {MarkdownPreview} from "../store/markdown"; if (msg.type == 'img') { if (imgClassName) { // 缩略图,主要用于回复消息预览 - const data = { + const data = $A.imageRatioHandle({ + src: msg.thumb, width: parseInt(msg.width), height: parseInt(msg.height), - maxSize: 40, - thumb: msg.thumb - } - const ratioExceed = $A.imageRatioExceed(data.width, data.height, 2) - if (ratioExceed > 0 && /\.(png|jpg|jpeg)$/.test(data.thumb)) { - data.thumb = $A.thumbRestore(data.thumb) + `/crop/ratio:${ratioExceed},percentage:80x0` - if (data.width > data.height) { - data.width = data.height * ratioExceed; - } else { - data.height = data.width * ratioExceed; - } - } - const scale = $A.scaleToScale(data.width, data.height, data.maxSize); - return `` + crops: {ratio: 2, percentage: '80x0'}, + scaleSize: 40, + }) + return `` } return `[${$A.L('图片')}]` } else if (msg.ext == 'mp4') { @@ -564,6 +527,46 @@ import {MarkdownPreview} from "../store/markdown"; return false; }, + /** + * 图片尺寸比例超出 + * @param params {{src: *, width: (number|*), height: (number|*), crops: {percentage: string, ratio: number}, scaleSize: (number)}} + * src, // 原图地址 + * width, // 原图宽度 + * height, // 原图高度 + * crops, // 裁剪参数,如:{ratio:3, percentage:"80x0"} + * scaleSize, // 返回尺寸缩放最高尺寸 + */ + imageRatioHandle(params) { + if (!/\.(png|jpg|jpeg)$/.test(params.src)) { + return params + } + + if (!$A.isJson(params.crops)) { + return params + } + + params.src = $A.thumbRestore(params.src) + "/crop/" + Object.keys(params.crops).map(key => { + return `${key}:${params.crops[key]}` + }).join(",") + + const ratio = $A.imageRatioExceed(params.width, params.height, params.crops.ratio) + if (ratio > 0) { + if (params.width > params.height) { + params.width = params.height * ratio; + } else { + params.height = params.width * ratio; + } + } + + if (params.scaleSize) { + const scale = $A.scaleToScale(params.width, params.height, params.scaleSize); + params.width = scale.width; + params.height = scale.height; + } + + return params; + }, + /** * 图片尺寸比例超出 * @param width @@ -572,8 +575,8 @@ import {MarkdownPreview} from "../store/markdown"; * @param float * @returns {number} */ - imageRatioExceed(width, height, ratio = 3, float = 0.5) { - if (width && height) { + imageRatioExceed(width, height, ratio, float = 0.5) { + if (width && height && ratio) { if (width / height > (ratio + float) || height / width > (ratio + float)) { return ratio } diff --git a/resources/assets/js/pages/manage/components/DialogView/file.vue b/resources/assets/js/pages/manage/components/DialogView/file.vue index bc2088468..5790fac91 100644 --- a/resources/assets/js/pages/manage/components/DialogView/file.vue +++ b/resources/assets/js/pages/manage/components/DialogView/file.vue @@ -51,7 +51,7 @@ export default { imageStyle({width, height, ext}, type = 'style') { if (width && height) { - const ratioExceed = $A.imageRatioExceed(width, height) + const ratioExceed = $A.imageRatioExceed(width, height, 3) if (['png', 'jpg', 'jpeg'].includes(ext) && ratioExceed > 0) { if (width > height) { width = height * ratioExceed; @@ -90,7 +90,7 @@ export default { }, imageSrc({width, height, ext, thumb}) { - const ratioExceed = $A.imageRatioExceed(width, height) + const ratioExceed = $A.imageRatioExceed(width, height, 3) if (['png', 'jpg', 'jpeg'].includes(ext) && ratioExceed > 0) { thumb = $A.thumbRestore(thumb) + `/crop/ratio:${ratioExceed},percentage:320x0`; } diff --git a/routes/web.php b/routes/web.php index 1c3f20805..42dba4179 100644 --- a/routes/web.php +++ b/routes/web.php @@ -69,6 +69,6 @@ Route::middleware(['webapi'])->group(function () { Route::any('/{method}', IndexController::class); Route::any('/{method}/{action}', IndexController::class); Route::any('/{method}/{action}/{child}', IndexController::class); - Route::any('/{method}/{action}/{child}/{n}', IndexController::class); - Route::any('/{method}/{action}/{child}/{n}/{c}', IndexController::class); + Route::any('/{method}/{action}/{child}/{n}', IndexController::class)->where('method', '^(?!uploads).*'); + Route::any('/{method}/{action}/{child}/{n}/{c}', IndexController::class)->where('method', '^(?!uploads).*'); });