完成图片、文件兼容修改语音icon 回复头像显示

This commit is contained in:
2025-07-05 14:25:53 +08:00
parent ad33895b6d
commit 179005822d
21 changed files with 2715 additions and 73 deletions

View File

@@ -0,0 +1,194 @@
<template>
<view class="file-message" @click="downloadFile">
<image
:src="fileIcon"
class="file-icon"
mode="aspectFit"
/>
<view class="file-info">
<text class="file-name">{{ fileName }}</text>
<text class="file-size">{{ fileSizeText }}</text>
</view>
<image
:src="downloadIcon"
class="download-icon"
mode="aspectFit"
/>
</view>
</template>
<script>
export default {
props: {
source: {
type: Object,
required: true
}
},
computed: {
fileName: function() {
return this.source && this.source.fileElem && this.source.fileElem.fileName || '未知文件';
},
fileSizeText: function() {
const size = this.source && this.source.fileElem && this.source.fileElem.fileSize;
if (!size) return '';
if (size < 1024) {
return size + ' B';
} else if (size < 1024 * 1024) {
return (size / 1024).toFixed(1) + ' KB';
} else {
return (size / (1024 * 1024)).toFixed(1) + ' MB';
}
},
fileIcon: function() {
const fileName = this.fileName.toLowerCase();
if (fileName.endsWith('.pdf')) {
return '/static/images/file_message/file_pdf.png';
} else if (fileName.endsWith('.doc') || fileName.endsWith('.docx')) {
return '/static/images/file_message/file_word.png';
} else if (fileName.endsWith('.xls') || fileName.endsWith('.xlsx')) {
return '/static/images/file_message/file_excel.png';
} else if (fileName.endsWith('.ppt') || fileName.endsWith('.pptx')) {
return '/static/images/file_message/file_ppt.png';
} else if (fileName.endsWith('.zip') || fileName.endsWith('.rar') || fileName.endsWith('.7z')) {
return '/static/images/file_message/file_zip.png';
} else if (fileName.endsWith('.jpg') || fileName.endsWith('.jpeg') || fileName.endsWith('.png') || fileName.endsWith('.gif')) {
return '/static/images/file_message/file_image.png';
} else {
return '/static/images/file_message/file_unknown.png';
}
}
},
data() {
return {
downloadIcon: '/static/images/file_message/file_download.png'
};
},
methods: {
getFixedSourceUrl: function(url) {
// 如果 url 以 http://47.117.71.33/api/object/ 开头,则替换为带端口的
if (typeof url === 'string' && url.startsWith('http://47.117.71.33/api/object/')) {
return url.replace('http://47.117.71.33/api/object/', 'http://47.117.71.33:15219/api/object/');
}
return url;
},
downloadFile: function() {
const sourceUrl = this.source && this.source.fileElem && this.source.fileElem.sourceUrl;
if (!sourceUrl) {
uni.showToast({
title: '文件链接无效',
icon: 'none'
});
return;
}
const fixedUrl = this.getFixedSourceUrl(sourceUrl);
// 在APP环境下直接下载
// #ifdef APP-PLUS
uni.downloadFile({
url: fixedUrl,
success: (res) => {
if (res.statusCode === 200) {
uni.saveFile({
tempFilePath: res.tempFilePath,
success: (saveRes) => {
uni.showToast({
title: '文件已保存',
icon: 'success'
});
},
fail: () => {
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
}
},
fail: () => {
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
});
// #endif
// 在其他环境下打开链接
// #ifndef APP-PLUS
uni.showModal({
title: '提示',
content: '是否打开文件链接?',
success: (res) => {
if (res.confirm) {
// #ifdef H5
window.open(fixedUrl);
// #endif
// #ifdef MP-WEIXIN
uni.setClipboardData({
data: fixedUrl,
success: () => {
uni.showToast({
title: '链接已复制',
icon: 'success'
});
}
});
// #endif
}
}
});
// #endif
}
}
};
</script>
<style scoped>
.file-message {
display: flex;
align-items: center;
background: #f5f6fa;
border-radius: 12px;
padding: 12px 16px;
margin: 4px 0;
min-width: 200px;
max-width: 300px;
}
.file-icon {
width: 40px;
height: 40px;
margin-right: 12px;
}
.file-info {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.file-name {
font-size: 14px;
color: #333;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 4px;
}
.file-size {
font-size: 12px;
color: #999;
}
.download-icon {
width: 24px;
height: 24px;
margin-left: 8px;
}
</style>

View File

@@ -10,7 +10,16 @@
@click="clickMediaItem"
>
<template v-slot:loading>
<u-loading-icon color="red"></u-loading-icon>
<view class="custom-loading" :style="{ width: '120px', height: maxHeight + 'px' }">
<view class="loading-content">
<view class="loading-spinner">
<view class="spinner-ring"></view>
<view class="spinner-ring"></view>
<view class="spinner-ring"></view>
</view>
<text class="loading-text">加载中...</text>
</view>
</view>
</template>
</u--image>
</view>
@@ -28,13 +37,22 @@ export default {
};
},
computed: {
getImgUrl() {
getImgUrl() {
if (!this.message || !this.message.pictureElem) {
return '';
}
return (
this.message.pictureElem.snapshotPicture?.url ??
this.message.pictureElem.sourcePath
);
this.message.pictureElem.snapshotPicture && this.message.pictureElem.snapshotPicture.url ?
this.getFixedSourceUrl(this.message.pictureElem.snapshotPicture.url) :
this.getFixedSourceUrl(this.message.pictureElem.sourcePicture.url)
);
},
maxHeight() {
if (!this.message || !this.message.pictureElem || !this.message.pictureElem.sourcePicture) {
return 120;
}
const imageHeight = this.message.pictureElem.sourcePicture.height;
const imageWidth = this.message.pictureElem.sourcePicture.width;
const aspectRatio = imageHeight / imageWidth;
@@ -42,10 +60,20 @@ export default {
},
},
methods: {
getFixedSourceUrl(url) {
if (typeof url === 'string' && url.startsWith('http://47.117.71.33/api/object/')) {
return url.replace('http://47.117.71.33/api/object/', 'http://47.117.71.33:15219/api/object/');
}
return url;
},
clickMediaItem() {
if (!this.message || !this.message.pictureElem || !this.message.pictureElem.sourcePicture) {
return;
}
uni.previewImage({
current: 0,
urls: [this.message.pictureElem.sourcePicture.url],
urls: [this.getFixedSourceUrl(this.message.pictureElem.sourcePicture.url)],
indicator: "none",
});
},
@@ -61,6 +89,7 @@ export default {
position: relative;
border-radius: 16rpx;
overflow: hidden;
display: inline-block;
.play_icon {
width: 48px;
@@ -78,4 +107,71 @@ export default {
color: #fff;
}
}
.custom-loading {
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(4px);
border-radius: 16rpx;
box-sizing: border-box;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loading-spinner {
position: relative;
width: 40px;
height: 40px;
margin-bottom: 8px;
}
.spinner-ring {
position: absolute;
width: 100%;
height: 100%;
border: 3px solid transparent;
border-top: 3px solid #4a9cfc;
border-radius: 50%;
animation: spin 1.2s linear infinite;
&:nth-child(2) {
width: 80%;
height: 80%;
top: 10%;
left: 10%;
border-top-color: #6bb6ff;
animation-delay: -0.4s;
}
&:nth-child(3) {
width: 60%;
height: 60%;
top: 20%;
left: 20%;
border-top-color: #8cc8ff;
animation-delay: -0.8s;
}
}
.loading-text {
font-size: 12px;
color: #666;
font-weight: 500;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@@ -20,8 +20,8 @@ export default {
},
computed: {
getContent() {
console.log(this.message);
return parseBr(this.message.textElem.content);
return this.message && this.message.textElem && this.message.textElem.content ?
parseBr(this.message.textElem.content) : '';
},
},
data() {

View File

@@ -2,7 +2,7 @@
<view class="voice-message" @click="playVoice">
<image
:src="isPlaying ? audioIcon : recordIcon"
class="audio-icon flipped"
:class="['audio-icon', { 'flipped': isSender }]"
mode="aspectFit"
/>
<text class="duration">{{ durationText }}</text>
@@ -15,11 +15,15 @@ export default {
source: {
type: Object,
required: true
},
isSender: {
type: Boolean,
default: false
}
},
computed: {
durationText() {
const d = this.source.soundElem && this.source.soundElem.duration;
const d = this.source && this.source.soundElem && this.source.soundElem.duration;
return d ? `${d}''` : '';
}
},
@@ -46,7 +50,7 @@ export default {
this.innerAudioContext = null;
}
this.innerAudioContext = uni.createInnerAudioContext();
const rawUrl = this.source.soundElem && this.source.soundElem.sourceUrl || '';
const rawUrl = this.source && this.source.soundElem && this.source.soundElem.sourceUrl || '';
this.innerAudioContext.src = this.getFixedSourceUrl(rawUrl);
this.isPlaying = true;
this.innerAudioContext.play();

View File

@@ -41,16 +41,16 @@
<TextMessageRender :message="source" />
</template>
<template v-else-if="source.contentType === 102">
<MediaMessageRender :source="source" />
<MediaMessageRender :message="source" />
</template>
<template v-else-if="source.contentType === 103">
<VoiceMessageRender :source="source" />
<VoiceMessageRender :source="source" :isSender="isSender" />
</template>
<template v-else-if="source.contentType === 104">
<view style="color:#999">[暂未实现] 视频消息</view>
</template>
<template v-else-if="source.contentType === 105">
<view style="color:#999">[暂未实现] 文件消息</view>
<FileMessageRender :source="source" />
</template>
<template v-else-if="source.contentType === 106">
<view style="color:#999">[暂未实现] @消息</view>
@@ -93,6 +93,7 @@ import TextMessageRender from "./TextMessageRender.vue";
import MediaMessageRender from "./MediaMessageRender.vue";
import ErrorMessageRender from "./ErrorMessageRender.vue";
import VoiceMessageRender from './VoiceMessageRender.vue'
import FileMessageRender from './FileMessageRender.vue'
import { noticeMessageTypes } from "@/constant";
import { tipMessaggeFormat, formatMessageTime } from "@/util/imCommon";
@@ -106,7 +107,8 @@ export default {
TextMessageRender,
MediaMessageRender,
ErrorMessageRender,
VoiceMessageRender
VoiceMessageRender,
FileMessageRender
},
props: {
source: Object,
@@ -131,15 +133,18 @@ export default {
);
},
formattedMessageTime() {
return formatMessageTime(this.source.sendTime);
return this.source && this.source.sendTime ? formatMessageTime(this.source.sendTime) : '';
},
showTextRender() {
return textRenderTypes.includes(this.source.contentType);
return this.source && this.source.contentType ? textRenderTypes.includes(this.source.contentType) : false;
},
showMediaRender() {
return mediaRenderTypes.includes(this.source.contentType);
return this.source && this.source.contentType ? mediaRenderTypes.includes(this.source.contentType) : false;
},
getNoticeContent() {
if (!this.source || !this.source.contentType) {
return "";
}
const isNoticeMessage = noticeMessageTypes.includes(
this.source.contentType
);
@@ -151,13 +156,13 @@ export default {
);
},
isSuccessMessage() {
return this.source.status === MessageStatus.Succeed;
return this.source && this.source.status === MessageStatus.Succeed;
},
isFailedMessage() {
return this.source.status === MessageStatus.Failed;
return this.source && this.source.status === MessageStatus.Failed;
},
showSending() {
return this.source.status === MessageStatus.Sending && !this.sendingDelay;
return this.source && this.source.status === MessageStatus.Sending && !this.sendingDelay;
},
},
mounted() {
@@ -166,7 +171,7 @@ export default {
},
methods: {
setSendingDelay() {
if (this.source.status === MessageStatus.Sending) {
if (this.source && this.source.status === MessageStatus.Sending) {
setTimeout(() => {
this.sendingDelay = false;
}, 2000);