mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-15 11:18:12 +00:00
perf: 优化移动端图片预览
This commit is contained in:
parent
022499300e
commit
1f8198b36b
@ -758,14 +758,19 @@ class FileController extends AbstractController
|
||||
$file->saveBeforePids();
|
||||
//
|
||||
$data = Base::uploadMove($data, "uploads/file/" . $file->type . "/" . date("Ym") . "/" . $file->id . "/");
|
||||
$content = [
|
||||
'from' => '',
|
||||
'type' => $type,
|
||||
'ext' => $data['ext'],
|
||||
'url' => $data['path'],
|
||||
];
|
||||
if (isset($data['width'])) {
|
||||
$content['width'] = $data['width'];
|
||||
$content['height'] = $data['height'];
|
||||
}
|
||||
$content = FileContent::createInstance([
|
||||
'fid' => $file->id,
|
||||
'content' => [
|
||||
'from' => '',
|
||||
'type' => $type,
|
||||
'ext' => $data['ext'],
|
||||
'url' => $data['path']
|
||||
],
|
||||
'content' => $content,
|
||||
'text' => '',
|
||||
'size' => $file->size,
|
||||
'userid' => $user->userid,
|
||||
|
||||
@ -341,6 +341,8 @@ class File extends AbstractModel
|
||||
$content = Base::json2array(FileContent::whereFid($item['id'])->orderByDesc('id')->value('content'));
|
||||
if ($content) {
|
||||
$item['image_url'] = Base::fillUrl($content['url']);
|
||||
$item['image_width'] = intval($content['width']);
|
||||
$item['image_height'] = intval($content['height']);
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
"moment": "^2.29.1",
|
||||
"node-sass": "^6.0.1",
|
||||
"notification-koro1": "^1.1.1",
|
||||
"photoswipe": "^5.2.7",
|
||||
"postcss": "^8.4.5",
|
||||
"quill": "^1.3.7",
|
||||
"quill-mention-hi": "^3.1.0-1",
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<style lang="scss">
|
||||
body {
|
||||
.preview-image-swipe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
> img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||
import 'photoswipe/style.css';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
urlList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
initialIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lightbox: null,
|
||||
};
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.lightbox?.destroy();
|
||||
},
|
||||
watch: {
|
||||
urlList: {
|
||||
handler(array) {
|
||||
this.lightbox?.destroy();
|
||||
const dataSource = array.map(item => {
|
||||
if ($A.isJson(item)) {
|
||||
if (item.src) {
|
||||
item.src = $A.rightDelete(item.src, "_thumb.jpg");
|
||||
}
|
||||
return item
|
||||
}
|
||||
return {
|
||||
html: `<div class="preview-image-swipe"><img src="${$A.rightDelete(item, "_thumb.jpg")}"/></div>`,
|
||||
}
|
||||
})
|
||||
this.lightbox = new PhotoSwipeLightbox({
|
||||
dataSource,
|
||||
escKey: false,
|
||||
showHideAnimationType: 'none',
|
||||
pswpModule: () => import('photoswipe'),
|
||||
});
|
||||
this.lightbox.on('close', () => {
|
||||
this.$emit("on-close")
|
||||
});
|
||||
this.lightbox.on('destroy', () => {
|
||||
this.$emit("on-destroy")
|
||||
});
|
||||
this.lightbox.init();
|
||||
this.lightbox.loadAndOpen(this.initialIndex);
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
initialIndex(index) {
|
||||
this.lightbox?.loadAndOpen(index);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -72,6 +72,10 @@
|
||||
z-index: 2;
|
||||
transform: translateY(-50%);
|
||||
|
||||
@media (max-width: 640px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
cursor: no-drop;
|
||||
|
||||
@ -208,7 +212,11 @@ export default {
|
||||
return this.index === this.urlList.length - 1;
|
||||
},
|
||||
currentImg() {
|
||||
return $A.rightDelete(this.urlList[this.index], "_thumb.jpg");
|
||||
let item = this.urlList[this.index];
|
||||
if ($A.isJson(item)) {
|
||||
item = item.src;
|
||||
}
|
||||
return $A.rightDelete(item, "_thumb.jpg");
|
||||
},
|
||||
imgStyle() {
|
||||
const {scale, deg, offsetX, offsetY, enableTransition} = this.transform;
|
||||
@ -5,16 +5,19 @@
|
||||
:mask-closable="false"
|
||||
:footer-hide="true"
|
||||
:transition-names="['', '']"
|
||||
fullscreen
|
||||
class-name="common-preview-image">
|
||||
<PreviewImageView v-if="list.length > 0" :initial-index="index" :url-list="list" infinite/>
|
||||
:class-name="mode === 'desktop' ? 'common-preview-image-view' : 'common-preview-image-swipe'"
|
||||
fullscreen>
|
||||
<template v-if="list.length > 0">
|
||||
<PreviewImageView v-if="mode === 'desktop'" :initial-index="index" :url-list="list" infinite/>
|
||||
<PreviewImageSwipe v-if="mode === 'mobile'" :initial-index="index" :url-list="list" @on-destroy="show=false"/>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
body {
|
||||
.ivu-modal-wrap {
|
||||
&.common-preview-image {
|
||||
&.common-preview-image-view {
|
||||
.ivu-modal {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -34,6 +37,11 @@ body {
|
||||
top: 40px;
|
||||
width: 40px;
|
||||
|
||||
@media (max-width: 640px) {
|
||||
right: 24px;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
.ivu-icon-ios-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
@ -48,16 +56,20 @@ body {
|
||||
}
|
||||
}
|
||||
}
|
||||
&.common-preview-image-swipe {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import PreviewImageView from "./view";
|
||||
const PreviewImageView = () => import('./components/view');
|
||||
const PreviewImageSwipe = () => import('./components/swipe');
|
||||
|
||||
export default {
|
||||
name: 'PreviewImage',
|
||||
components: {PreviewImageView},
|
||||
components: {PreviewImageSwipe, PreviewImageView},
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
@ -72,6 +84,12 @@ export default {
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return $A.isDesktop ? 'desktop' : 'mobile';
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@ -375,7 +375,7 @@ export default {
|
||||
// 直接执行会导致输入框滚到底部被遮挡
|
||||
} else {
|
||||
this.timerScroll = setInterval(() => {
|
||||
if (this.quill.hasFocus()) {
|
||||
if (this.quill?.hasFocus()) {
|
||||
$A.scrollToView(this.$refs.editor, true)
|
||||
} else {
|
||||
clearInterval(this.timerScroll);
|
||||
|
||||
@ -152,7 +152,7 @@ export default {
|
||||
|
||||
contentClass() {
|
||||
const {type, msg} = this.msgData;
|
||||
let classArray = [];
|
||||
const classArray = [];
|
||||
if (type === 'text') {
|
||||
if (/^<img\s+class="emoticon"[^>]*?>$/.test(msg.text)) {
|
||||
classArray.push('an-emoticon')
|
||||
@ -243,15 +243,19 @@ export default {
|
||||
// 处理图片显示尺寸
|
||||
const array = text.match(/<img\s+[^>]*?>/g);
|
||||
if (array) {
|
||||
const widthReg = new RegExp("width=\"(\\d+)\"")
|
||||
const heightReg = new RegExp("height=\"(\\d+)\"")
|
||||
const widthReg = new RegExp("width=\"(\\d+)\""),
|
||||
heightReg = new RegExp("height=\"(\\d+)\"")
|
||||
array.some(res => {
|
||||
if (widthReg.test(res) && heightReg.test(res)) {
|
||||
let width = parseInt(res.match(widthReg)[1]),
|
||||
height = parseInt(res.match(heightReg)[1]),
|
||||
const widthMatch = res.match(widthReg),
|
||||
heightMatch = res.match(heightReg);
|
||||
if (widthMatch && heightMatch) {
|
||||
const width = parseInt(widthMatch[1]),
|
||||
height = parseInt(heightMatch[1]),
|
||||
maxSize = res.indexOf("emoticon") > -1 ? 150 : 220;
|
||||
let scale = $A.scaleToScale(width, height, maxSize, maxSize);
|
||||
let value = res.replace(widthReg, `width=${scale.width}`).replace(heightReg, `height=${scale.height}`)
|
||||
const scale = $A.scaleToScale(width, height, maxSize, maxSize);
|
||||
const value = res
|
||||
.replace(widthReg, `original-width="${width}" width="${scale.width}"`)
|
||||
.replace(heightReg, `original-height="${height}" height="${scale.height}"`)
|
||||
text = text.replace(res, value)
|
||||
}
|
||||
})
|
||||
@ -261,14 +265,14 @@ export default {
|
||||
|
||||
recordStyle(info) {
|
||||
const {duration} = info;
|
||||
let width = 50 + Math.min(180, Math.floor(duration / 150));
|
||||
const width = 50 + Math.min(180, Math.floor(duration / 150));
|
||||
return {
|
||||
width: width + 'px',
|
||||
};
|
||||
},
|
||||
|
||||
recordDuration(duration) {
|
||||
let minute = Math.floor(duration / 60000),
|
||||
const minute = Math.floor(duration / 60000),
|
||||
seconds = Math.floor(duration / 1000) % 60;
|
||||
if (minute > 0) {
|
||||
return `${minute}:${seconds}″`
|
||||
@ -340,7 +344,7 @@ export default {
|
||||
this.viewPicture(target.currentSrc);
|
||||
} else {
|
||||
this.$store.state.previewImageIndex = 0;
|
||||
this.$store.state.previewImageList = [target.currentSrc];
|
||||
this.$store.state.previewImageList = this.getTextImageInfos(target.outerHTML);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -400,20 +404,20 @@ export default {
|
||||
return a.id - b.id;
|
||||
});
|
||||
//
|
||||
let list = [];
|
||||
const list = [];
|
||||
data.some(({type, msg}) => {
|
||||
if (type === 'file') {
|
||||
list.push(msg.path)
|
||||
} else if (type === 'text') {
|
||||
const baseUrl = $A.apiUrl('../');
|
||||
const array = msg.text.match(/<img\s+class="browse"[^>]*?src="(.*?)"[^>]*?>/g);
|
||||
array && array.some(res => {
|
||||
list.push(res.match(/<img\s+class="browse"[^>]*?src="(.*?)"[^>]*?>/)[1].replace(/\{\{RemoteURL\}\}/g, baseUrl))
|
||||
list.push({
|
||||
src: msg.path,
|
||||
width: msg.width,
|
||||
height: msg.height,
|
||||
})
|
||||
} else if (type === 'text') {
|
||||
list.push(...this.getTextImageInfos(msg.text))
|
||||
}
|
||||
})
|
||||
//
|
||||
let index = list.findIndex(item => item === currentUrl);
|
||||
const index = list.findIndex(({src}) => src === currentUrl);
|
||||
if (index > -1) {
|
||||
this.$store.state.previewImageIndex = index;
|
||||
this.$store.state.previewImageList = list;
|
||||
@ -423,6 +427,30 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
getTextImageInfos(text) {
|
||||
const baseUrl = $A.apiUrl('../');
|
||||
const array = text.match(new RegExp(`<img[^>]*?>`, "g"));
|
||||
const list = [];
|
||||
if (array) {
|
||||
const srcReg = new RegExp("src=\"(.*?)\""),
|
||||
widthReg = new RegExp("(original-)?width=\"(\\d+)\""),
|
||||
heightReg = new RegExp("(original-)?height=\"(\\d+)\"")
|
||||
array.some(res => {
|
||||
const srcMatch = res.match(srcReg),
|
||||
widthMatch = res.match(widthReg),
|
||||
heightMatch = res.match(heightReg);
|
||||
if (srcMatch && widthMatch && heightMatch) {
|
||||
list.push({
|
||||
src: srcMatch[1].replace(/\{\{RemoteURL\}\}/g, baseUrl),
|
||||
width: widthMatch[2],
|
||||
height: heightMatch[2],
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
return list;
|
||||
},
|
||||
|
||||
downFile() {
|
||||
$A.modalConfirm({
|
||||
title: '下载文件',
|
||||
|
||||
@ -951,7 +951,16 @@ export default {
|
||||
const list = this.fileList.filter(({image_url}) => !!image_url)
|
||||
if (list.length > 0) {
|
||||
this.imageIndex = list.findIndex(({id}) => item.id === id)
|
||||
this.imageList = list.map(item => item.image_url)
|
||||
this.imageList = list.map(item => {
|
||||
if (item.image_width) {
|
||||
return {
|
||||
src: item.image_url,
|
||||
width: item.image_width,
|
||||
height: item.image_height,
|
||||
}
|
||||
}
|
||||
return item.image_url;
|
||||
})
|
||||
this.imageShow = true
|
||||
return;
|
||||
}
|
||||
|
||||
3
resources/assets/sass/pages/page-file.scss
vendored
3
resources/assets/sass/pages/page-file.scss
vendored
@ -676,5 +676,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.file-upload-list {
|
||||
bottom: 86px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user