Browse Source

优化: 图片高度宽度显示优化

master
xsx 9 months ago
parent
commit
53bd7024a8
  1. 7
      im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java
  2. 6
      im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java
  3. 42
      im-uniapp/components/chat-message-item/chat-message-item.vue
  4. 23
      im-uniapp/pages/chat/chat-box.vue
  5. 28
      im-web/src/components/chat/ChatBox.vue
  6. 58
      im-web/src/components/chat/ChatMessageItem.vue
  7. 2
      im-web/src/store/chatStore.js

7
im-platform/src/main/java/com/bx/implatform/service/impl/FileServiceImpl.java

@ -25,6 +25,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils; import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.Objects; import java.util.Objects;
@ -101,6 +104,10 @@ public class FileServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> imple
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片格式不合法"); throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片格式不合法");
} }
UploadImageVO vo = new UploadImageVO(); UploadImageVO vo = new UploadImageVO();
// 获取图片长度和宽度
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
vo.setWidth(bufferedImage.getWidth());
vo.setHeight(bufferedImage.getHeight());
// 如果文件已存在,直接复用 // 如果文件已存在,直接复用
String md5 = DigestUtils.md5DigestAsHex(file.getInputStream()); String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
FileInfo fileInfo = findByMd5(md5, FileType.IMAGE.code()); FileInfo fileInfo = findByMd5(md5, FileType.IMAGE.code());

6
im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java

@ -12,4 +12,10 @@ public class UploadImageVO {
@Schema(description = "缩略图") @Schema(description = "缩略图")
private String thumbUrl; private String thumbUrl;
@Schema(description = "图片宽度")
private int width;
@Schema(description = "图片高度")
private int height;
} }

42
im-uniapp/components/chat-message-item/chat-message-item.vue

@ -27,9 +27,8 @@
<view class="message-image" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE"> <view class="message-image" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE">
<long-press-menu :items="menuItems" @select="onSelectMenu"> <long-press-menu :items="menuItems" @select="onSelectMenu">
<view class="image-box"> <view class="image-box">
<image class="send-image" mode="heightFix" <image class="send-image" :style="imageStyle" mode="aspectFill"
:src="JSON.parse(msgInfo.content).thumbUrl" lazy-load="true" :src="contentData.thumbUrl" lazy-load="true" @click.stop="onShowFullImage()">
@click.stop="onShowFullImage()">
</image> </image>
<loading v-if="sending"></loading> <loading v-if="sending"></loading>
</view> </view>
@ -39,8 +38,8 @@
<long-press-menu :items="menuItems" @select="onSelectMenu"> <long-press-menu :items="menuItems" @select="onSelectMenu">
<view class="file-box"> <view class="file-box">
<view class="file-info"> <view class="file-info">
<uni-link class="file-name" :text="data.name" showUnderLine="true" <uni-link class="file-name" :text="contentData.name" showUnderLine="true"
color="#007BFF" :href="data.url"></uni-link> color="#007BFF" :href="contentData.url"></uni-link>
<view class="file-size">{{ fileSize }}</view> <view class="file-size">{{ fileSize }}</view>
</view> </view>
<view class="file-icon iconfont icon-file"></view> <view class="file-icon iconfont icon-file"></view>
@ -52,7 +51,7 @@
@select="onSelectMenu"> @select="onSelectMenu">
<view class="message-audio message-text" @click="onPlayAudio()"> <view class="message-audio message-text" @click="onPlayAudio()">
<text class="iconfont icon-voice-play"></text> <text class="iconfont icon-voice-play"></text>
<text class="chat-audio-text">{{ JSON.parse(msgInfo.content).duration + '"' }}</text> <text class="chat-audio-text">{{ contentData.duration + '"' }}</text>
<text v-if="audioPlayState == 'PAUSE'" class="iconfont icon-play"></text> <text v-if="audioPlayState == 'PAUSE'" class="iconfont icon-play"></text>
<text v-if="audioPlayState == 'PLAYING'" class="iconfont icon-pause"></text> <text v-if="audioPlayState == 'PLAYING'" class="iconfont icon-pause"></text>
</view> </view>
@ -128,7 +127,7 @@ export default {
// //
if (!this.innerAudioContext) { if (!this.innerAudioContext) {
this.innerAudioContext = uni.createInnerAudioContext(); this.innerAudioContext = uni.createInnerAudioContext();
let url = JSON.parse(this.msgInfo.content).url; let url = this.contentData.url;
this.innerAudioContext.src = url; this.innerAudioContext.src = url;
this.innerAudioContext.onEnded((e) => { this.innerAudioContext.onEnded((e) => {
console.log('停止') console.log('停止')
@ -159,7 +158,7 @@ export default {
this.menu.show = false; this.menu.show = false;
}, },
onShowFullImage() { onShowFullImage() {
let imageUrl = JSON.parse(this.msgInfo.content).originUrl; let imageUrl = this.contentData.originUrl;
uni.previewImage({ uni.previewImage({
urls: [imageUrl] urls: [imageUrl]
}) })
@ -185,11 +184,11 @@ export default {
sendFail() { sendFail() {
return this.msgInfo.status == this.$enums.MESSAGE_STATUS.FAILED; return this.msgInfo.status == this.$enums.MESSAGE_STATUS.FAILED;
}, },
data() { contentData() {
return JSON.parse(this.msgInfo.content) return JSON.parse(this.msgInfo.content)
}, },
fileSize() { fileSize() {
let size = this.data.size; let size = this.contentData.size;
if (size > 1024 * 1024) { if (size > 1024 * 1024) {
return Math.round(size / 1024 / 1024) + "M"; return Math.round(size / 1024 / 1024) + "M";
} }
@ -244,6 +243,22 @@ export default {
let text = this.$str.html2Escape(this.msgInfo.content) let text = this.$str.html2Escape(this.msgInfo.content)
text = this.$url.replaceURLWithHTMLLinks(text, color) text = this.$url.replaceURLWithHTMLLinks(text, color)
return this.$emo.transform(text, 'emoji-normal') return this.$emo.transform(text, 'emoji-normal')
},
imageStyle() {
console.log(uni.getSystemInfo())
let maxSize = uni.getSystemInfoSync().windowWidth * 0.6;
let minSize = uni.getSystemInfoSync().windowWidth * 0.2;
let width = this.contentData.width;
let height = this.contentData.height;
if (width && height) {
let ratio = Math.min(width, height) / Math.max(width, height);
let w = Math.max(width > height ? maxSize : ratio * maxSize, minSize);
let h = Math.max(width > height ? ratio * maxSize : maxSize, minSize);
return `width: ${w}px;height:${h}px;`
} else {
//
return `max-width: ${maxSize}px;min-width:100px;max-height: ${maxSize}px;min-height:100px;`
}
} }
} }
} }
@ -348,11 +363,10 @@ export default {
position: relative; position: relative;
.send-image { .send-image {
min-width: 200rpx;
max-width: 420rpx;
height: 350rpx;
cursor: pointer; cursor: pointer;
border-radius: 4px; border-radius: 10rpx;
background: $im-bg;
border: 6rpx solid $im-color-primary-light-5;
} }
} }

23
im-uniapp/pages/chat/chat-box.vue

@ -453,6 +453,16 @@ export default {
file.chat = this.chat; file.chat = this.chat;
// //
this.scrollToBottom(); this.scrollToBottom();
//
let chat = this.chat;
this.getImageSize(file).then(size => {
msgInfo = JSON.parse(JSON.stringify(msgInfo))
data.width = size.width;
data.height = size.height;
msgInfo.content = JSON.stringify(data)
this.chatStore.insertMessage(msgInfo, chat);
this.scrollToBottom();
})
return true; return true;
}, },
onUploadImageSuccess(file, res) { onUploadImageSuccess(file, res) {
@ -904,6 +914,19 @@ export default {
} }
return message; return message;
}, },
getImageSize(file) {
return new Promise((resolve) => {
uni.getImageInfo({
src: file.path,
success: (res) => {
resolve(res);
},
fail: (err) => {
console.error('获取图片信息失败', err);
}
});
});
},
generateId() { generateId() {
// id // id
return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000));

28
im-web/src/components/chat/ChatBox.vue

@ -203,6 +203,15 @@ export default {
// file // file
file.msgInfo = msgInfo; file.msgInfo = msgInfo;
file.chat = this.chat; file.chat = this.chat;
//
let chat = this.chat;
this.getImageSize(file).then(size => {
data.width = size.width;
data.height = size.height;
msgInfo.content = JSON.stringify(data)
this.chatStore.insertMessage(msgInfo, chat);
this.scrollToBottom();
})
}, },
onFileSuccess(url, file) { onFileSuccess(url, file) {
let data = { let data = {
@ -681,6 +690,25 @@ export default {
} }
return message; return message;
}, },
getImageSize(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function (event) {
const img = new Image();
img.onload = function () {
resolve({ width: img.width, height: img.height });
};
img.onerror = function () {
reject(new Error('无法加载图片'));
};
img.src = event.target.result;
};
reader.onerror = function () {
reject(new Error('无法读取文件'));
};
reader.readAsDataURL(file);
});
},
generateId() { generateId() {
// id // id
return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000)); return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000));

58
im-web/src/components/chat/ChatMessageItem.vue

@ -21,18 +21,19 @@
<div class="message-bottom" @contextmenu.prevent="showRightMenu($event)"> <div class="message-bottom" @contextmenu.prevent="showRightMenu($event)">
<div ref="chatMsgBox" class="message-content-wrapper"> <div ref="chatMsgBox" class="message-content-wrapper">
<span class="message-text" v-if="isTextMessage" v-html="htmlText"></span> <span class="message-text" v-if="isTextMessage" v-html="htmlText"></span>
<div class="message-image" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE"> <div class="message-image" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE"
<div class="img-load-box" v-loading="sending" element-loading-text="发送中.." @click="showFullImageBox()">
<div v-loading="sending" element-loading-text="发送中.."
element-loading-background="rgba(0, 0, 0, 0.4)"> element-loading-background="rgba(0, 0, 0, 0.4)">
<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl" <img :style="imageStyle" :src="contentData.thumbUrl" loading="lazy" />
@click="showFullImageBox()" loading="lazy" />
</div> </div>
</div> </div>
<div class="message-file" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.FILE"> <div class="message-file" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.FILE">
<div class="chat-file-box" v-loading="sending"> <div class="chat-file-box" v-loading="sending">
<div class="chat-file-info"> <div class="chat-file-info">
<el-link class="chat-file-name" :underline="true" target="_blank" type="primary" <el-link class="chat-file-name" :underline="true" target="_blank" type="primary"
:href="data.url" :download="data.name">{{ data.name }}</el-link> :href="contentData.url" :download="contentData.name">{{ contentData.name
}}</el-link>
<div class="chat-file-size">{{ fileSize }}</div> <div class="chat-file-size">{{ fileSize }}</div>
</div> </div>
<div class="chat-file-icon"> <div class="chat-file-icon">
@ -44,7 +45,7 @@
@click="onPlayVoice()"> @click="onPlayVoice()">
<audio controls :src="JSON.parse(msgInfo.content).url"></audio> <audio controls :src="JSON.parse(msgInfo.content).url"></audio>
</div> </div>
<div title="发送中" v-if="sending && isTextMessage" class="sending" v-loading="'true'"></div> <div title="发送中" v-if="sending" class="sending" v-loading="'true'"></div>
<div title="发送失败" v-else-if="sendFail" @click="onSendFail" class="send-fail el-icon-warning"> <div title="发送失败" v-else-if="sendFail" @click="onSendFail" class="send-fail el-icon-warning">
</div> </div>
</div> </div>
@ -152,11 +153,11 @@ export default {
sendFail() { sendFail() {
return this.msgInfo.status == this.$enums.MESSAGE_STATUS.FAILED; return this.msgInfo.status == this.$enums.MESSAGE_STATUS.FAILED;
}, },
data() { contentData() {
return JSON.parse(this.msgInfo.content) return JSON.parse(this.msgInfo.content)
}, },
fileSize() { fileSize() {
let size = this.data.size; let size = this.contentData.size;
if (size > 1024 * 1024) { if (size > 1024 * 1024) {
return Math.round(size / 1024 / 1024) + "M"; return Math.round(size / 1024 / 1024) + "M";
} }
@ -199,6 +200,22 @@ export default {
}, },
isGroupMessage() { isGroupMessage() {
return !!this.msgInfo.groupId; return !!this.msgInfo.groupId;
},
imageStyle() {
// 360px,60px,
let maxSize = this.configStore.fullScreen ? 360 : 240;
let minSize = 60;
let width = this.contentData.width;
let height = this.contentData.height;
if (width && height) {
let ratio = Math.min(width, height) / Math.max(width, height);
let w = Math.max(Math.min(width > height ? maxSize : ratio * maxSize, width), minSize);
let h = Math.max(Math.min(width > height ? ratio * maxSize : maxSize, height), minSize);
return `width: ${w}px;height:${h}px;object-fit: cover;`
} else {
//
return `max-width: ${maxSize}px;min-width:60px;max-height: ${maxSize}px;min-height:60px;`
}
} }
} }
} }
@ -206,7 +223,7 @@ export default {
<style lang="scss"> <style lang="scss">
.chat-message-item { .chat-message-item {
padding: 2px 10px; padding: 3px 10px;
border-radius: 10px; border-radius: 10px;
.message-tip { .message-tip {
@ -254,7 +271,7 @@ export default {
.message-content-wrapper { .message-content-wrapper {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: end;
.sending { .sending {
width: 25px; width: 25px;
@ -270,7 +287,7 @@ export default {
color: #e45050; color: #e45050;
font-size: 30px; font-size: 30px;
cursor: pointer; cursor: pointer;
margin: 0 3px; margin: 0 5px;
} }
} }
@ -302,20 +319,10 @@ export default {
} }
.message-image { .message-image {
display: flex; border-radius: 8px;
flex-wrap: nowrap; border: 3px solid var(--im-color-primary-light-8);
flex-direction: row; overflow: hidden;
align-items: center; cursor: pointer;
.send-image {
min-width: 200px;
min-height: 150px;
max-width: 400px;
max-height: 300px;
border-radius: 8px;
cursor: pointer;
}
} }
.message-file { .message-file {
@ -460,7 +467,6 @@ export default {
} }
.message-text { .message-text {
margin-left: 10px;
background-color: var(--im-color-primary-light-2); background-color: var(--im-color-primary-light-2);
color: #fff; color: #fff;

2
im-web/src/store/chatStore.js

@ -154,7 +154,6 @@ export default defineStore('chatStore', {
} }
}, },
insertMessage(msgInfo, chatInfo) { insertMessage(msgInfo, chatInfo) {
let time = new Date().getTime()
let type = chatInfo.type; let type = chatInfo.type;
// 记录消息的最大id // 记录消息的最大id
if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) { if (msgInfo.id && type == "PRIVATE" && msgInfo.id > this.privateMsgMaxId) {
@ -230,7 +229,6 @@ export default defineStore('chatStore', {
chat.messages.splice(insertPos, 0, msgInfo); chat.messages.splice(insertPos, 0, msgInfo);
chat.stored = false; chat.stored = false;
this.saveToStorage(); this.saveToStorage();
console.log("耗时:", new Date().getTime() - time)
}, },
updateMessage(msgInfo, chatInfo) { updateMessage(msgInfo, chatInfo) {
// 获取对方id或群id // 获取对方id或群id

Loading…
Cancel
Save