Merge branch 'main' of http://103.44.237.87:3000/commiu/question_uniapp
This commit is contained in:
commit
265dcb2bf1
@ -546,6 +546,13 @@
|
|||||||
{
|
{
|
||||||
"navigationBarTitleText": "select"
|
"navigationBarTitleText": "select"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path" : "pages/im/member",
|
||||||
|
"style" :
|
||||||
|
{
|
||||||
|
"navigationBarTitleText" : ""
|
||||||
|
}
|
||||||
},{
|
},{
|
||||||
"path" : "pages/about/helpdetail",
|
"path" : "pages/about/helpdetail",
|
||||||
"style" :
|
"style" :
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</uni-nav-bar>
|
</uni-nav-bar>
|
||||||
|
|
||||||
<view class="box-1">
|
<view class="box-1" @click="emoji_show=addons_show=false">
|
||||||
<scroll-view scroll-y refresher-background="transparent" style="height: 100%;"
|
<scroll-view scroll-y refresher-background="transparent" style="height: 100%;"
|
||||||
@refresherrefresh="refresherrefresh" :refresher-enabled="hasMoreMessages" :scroll-with-animation="false"
|
@refresherrefresh="refresherrefresh" :refresher-enabled="hasMoreMessages" :scroll-with-animation="false"
|
||||||
:refresher-triggered="scrollView.refresherTriggered" :scroll-into-view="scrollView.intoView"
|
:refresher-triggered="scrollView.refresherTriggered" :scroll-into-view="scrollView.intoView"
|
||||||
@scrolltoupper="loadMoreMessages">
|
@scrolltoupper="loadMoreMessages">
|
||||||
<view class="talk-list">
|
<view class="talk-list">
|
||||||
<!-- 加载更多提示 -->
|
<!-- 加载更多提示 -->
|
||||||
<view v-if="isLoading" class="loading-more">
|
<view v-if="isLoading" class="loading-more">
|
||||||
@ -30,13 +30,11 @@
|
|||||||
@tap="showUserInfo(item.from_user)">
|
@tap="showUserInfo(item.from_user)">
|
||||||
</image>
|
</image>
|
||||||
<view class="body">
|
<view class="body">
|
||||||
<view class="nickname">{{ item.from_user.nickname || item.from_user.username ||
|
<view class="nickname">{{ item.from_user.nickname || item.from_user.username || 'Unknown'}}</view>
|
||||||
'Unknown'}}
|
|
||||||
</view>
|
|
||||||
<view class="content" :class="{ 'image-content': item.type === 'image' }">
|
<view class="content" :class="{ 'image-content': item.type === 'image' }">
|
||||||
<template v-if="item.type === 'image'">
|
<template v-if="item.type === 'image'">
|
||||||
<image :src="getImageUrl(item.content)" mode="widthFix"
|
<image :src="item.message.thumbnail" mode="widthFix"
|
||||||
style="max-width: 400rpx;" @tap="previewImage(item.content)"
|
style="max-width: 400rpx;" @tap="previewImage(item)"
|
||||||
@load="onImageLoad(item.id)"></image>
|
@load="onImageLoad(item.id)"></image>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -69,10 +67,10 @@
|
|||||||
@blur="onInputBlur"></uni-easyinput>
|
@blur="onInputBlur"></uni-easyinput>
|
||||||
</view>
|
</view>
|
||||||
<view class="action-buttons">
|
<view class="action-buttons">
|
||||||
<button @tap="showemoji">
|
<button @tap="showemoji" style="display: flex;align-items: center;">
|
||||||
<uni-icons type="checkbox-filled" size="24" color="#666"></uni-icons>
|
<img src="@/static/im/emoji.png" style="height:48rpx" mode="widthFix" />
|
||||||
</button>
|
</button>
|
||||||
<button @tap="showaddons">
|
<button @tap="showaddons" style="display: flex;align-items: center;">
|
||||||
<uni-icons type="plus" size="24" color="#666"></uni-icons>
|
<uni-icons type="plus" size="24" color="#666"></uni-icons>
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
@ -165,7 +163,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
init: {
|
init: {
|
||||||
cdnurl: 'http://q.sjqqzc.top'
|
cdnurl: 'http://api.dxmt.io'
|
||||||
},
|
},
|
||||||
channel:{},
|
channel:{},
|
||||||
// 滚动容器
|
// 滚动容器
|
||||||
@ -197,6 +195,11 @@ export default {
|
|||||||
emojiList:emojiList,
|
emojiList:emojiList,
|
||||||
emoji_show:false,
|
emoji_show:false,
|
||||||
addons_show:false,
|
addons_show:false,
|
||||||
|
// 图片加载队列
|
||||||
|
imageLoadQueue: [],
|
||||||
|
isProcessingQueue: false,
|
||||||
|
// 占位图
|
||||||
|
placeholderImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -254,7 +257,21 @@ export default {
|
|||||||
async initChannel(options) {
|
async initChannel(options) {
|
||||||
let channel = await mattermost.getChannelById(options.target_id);
|
let channel = await mattermost.getChannelById(options.target_id);
|
||||||
if(channel.type=='O'){
|
if(channel.type=='O'){
|
||||||
channel.name = channel.display_name || channel.name
|
channel.name = channel.display_name || channel.name;
|
||||||
|
//一次性获取全部用户的昵称
|
||||||
|
// const members = await mattermost.getChannelMembers(channel.id);
|
||||||
|
// console.log('获取到的频道成员:', members);
|
||||||
|
// const users_ids = [];
|
||||||
|
// for (const member of members) {
|
||||||
|
// if (member.user_id && member.user_id !== mattermost.getCurrentUserId()) {
|
||||||
|
// users_ids.push(member.user_id);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// const users = await mattermost.getUsersByIds(users_ids);
|
||||||
|
// console.log('获取到的频道成员:', users);
|
||||||
|
// for (const user of users.data) {
|
||||||
|
// mattermost.kown_users[user.id] = user;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
if(channel.type=='D'){
|
if(channel.type=='D'){
|
||||||
var target_user_id = channel.name.replace(mattermost.getCurrentUserId(),'').replace('__','')
|
var target_user_id = channel.name.replace(mattermost.getCurrentUserId(),'').replace('__','')
|
||||||
@ -297,30 +314,95 @@ export default {
|
|||||||
|
|
||||||
|
|
||||||
// 处理消息数据
|
// 处理消息数据
|
||||||
processMessageData(post, senderName = '') {
|
async processMessageData(post, senderName = '') {
|
||||||
|
console.log('处理消息数据:', post);
|
||||||
// 构建用户信息
|
// 构建用户信息
|
||||||
const fromUser = mattermost.kown_users[post.user_id];
|
const fromUser = await mattermost.getUserById(post.user_id);
|
||||||
|
|
||||||
// 处理消息类型
|
// 处理消息类型
|
||||||
let messageType = 'text';
|
let messageType = 'text';
|
||||||
let messageContent = post.message;
|
let messageContent = post.message;
|
||||||
let imageUrl = '';
|
let imageUrl = '';
|
||||||
|
|
||||||
if (post.props && post.props.attachments) {
|
let imageInfo = null;
|
||||||
const attachment = post.props.attachments[0];
|
// 检查是否是图片消息
|
||||||
if (attachment.type === 'image') {
|
if (post.file_ids && post.file_ids.length > 0) {
|
||||||
messageType = 'image';
|
messageType = 'image';
|
||||||
imageUrl = attachment.image_url;
|
// 从 metadata.files 中获取图片信息
|
||||||
|
if (post.metadata && post.metadata.files && post.metadata.files.length > 0) {
|
||||||
|
const file = post.metadata.files[0];
|
||||||
|
if (file.mime_type && file.mime_type.startsWith('image/')) {
|
||||||
|
// 构建图片信息对象
|
||||||
|
imageInfo = {
|
||||||
|
id: file.id,
|
||||||
|
width: file.width,
|
||||||
|
height: file.height,
|
||||||
|
mimeType: file.mime_type
|
||||||
|
};
|
||||||
|
console.log(file)
|
||||||
|
|
||||||
|
messageContent = {
|
||||||
|
id: file.id,
|
||||||
|
//thumbnail: this.placeholderImage, // 先使用占位图
|
||||||
|
thumbnail: `${mattermost.adminBaseUrl}/api/v4/files/${file.id}/thumbnail`,
|
||||||
|
preview: null,
|
||||||
|
original: `${mattermost.adminBaseUrl}/api/v4/files/${file.id}/preview`
|
||||||
|
};
|
||||||
|
|
||||||
|
// 将图片添加到加载队列
|
||||||
|
//this.addToImageLoadQueue(post.id, file.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...post,
|
...post,
|
||||||
|
id: post.id,
|
||||||
|
user_id: post.user_id,
|
||||||
|
message: messageContent,
|
||||||
|
type: messageType,
|
||||||
|
create_time: post.create_at,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
from_user: fromUser
|
from_user: fromUser,
|
||||||
|
imageInfo: imageInfo
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 添加图片到加载队列
|
||||||
|
addToImageLoadQueue(messageId, fileId) {
|
||||||
|
this.imageLoadQueue.push({ messageId, fileId });
|
||||||
|
if (!this.isProcessingQueue) {
|
||||||
|
this.processImageLoadQueue();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理图片加载队列
|
||||||
|
async processImageLoadQueue() {
|
||||||
|
if (this.isProcessingQueue || this.imageLoadQueue.length === 0) return;
|
||||||
|
|
||||||
|
this.isProcessingQueue = true;
|
||||||
|
const { messageId, fileId } = this.imageLoadQueue.shift();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取缩略图
|
||||||
|
const thumbnailData = await mattermost.getThumbnail(fileId);
|
||||||
|
|
||||||
|
// 更新消息中的图片
|
||||||
|
const messageIndex = this.talkList.findIndex(msg => msg.id === messageId);
|
||||||
|
if (messageIndex !== -1) {
|
||||||
|
this.$set(this.talkList[messageIndex].message, 'thumbnail', thumbnailData);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载缩略图失败:', error);
|
||||||
|
} finally {
|
||||||
|
this.isProcessingQueue = false;
|
||||||
|
// 继续处理队列中的下一个图片
|
||||||
|
if (this.imageLoadQueue.length > 0) {
|
||||||
|
setTimeout(() => this.processImageLoadQueue(), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 加载消息
|
// 加载消息
|
||||||
async loadMessages() {
|
async loadMessages() {
|
||||||
if (this.ajax.flag || this.ajax.loading) return;
|
if (this.ajax.flag || this.ajax.loading) return;
|
||||||
@ -355,7 +437,8 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (var k in posts) {
|
for (var k in posts) {
|
||||||
posts[k]['from_user'] = mattermost.kown_users[posts[k]['user_id']];//users[posts[k]['user_id']];
|
posts[k] = await this.processMessageData(posts[k]);
|
||||||
|
//posts[k]['from_user'] = await mattermost.getUserById(posts[k]['user_id']);//users[posts[k]['user_id']];
|
||||||
}
|
}
|
||||||
// 更新消息列表
|
// 更新消息列表
|
||||||
if (this.ajax.page === 0) {
|
if (this.ajax.page === 0) {
|
||||||
@ -405,15 +488,14 @@ export default {
|
|||||||
//console.log('发送消息:', this.content);
|
//console.log('发送消息:', this.content);
|
||||||
// 发送消息
|
// 发送消息
|
||||||
const result = await mattermost.sendTextMessage(this.channel.id, this.content);
|
const result = await mattermost.sendTextMessage(this.channel.id, this.content);
|
||||||
|
//console.log('发送消息:', this.content);
|
||||||
if (result && result.id) {
|
if (result && result.id) {
|
||||||
this.content = '';
|
this.content = '';
|
||||||
result['from_user'] = await mattermost.getUserById(result.user_id);
|
//result['from_user'] = await mattermost.getUserById(result.user_id);
|
||||||
this.talkList.push(result);
|
if(this.channel.type !='O'){
|
||||||
console.log('发送成功:', result,this.talkList);
|
this.handleNewMessage({post:result});
|
||||||
// 更新消息状态
|
}
|
||||||
//result.from_user = this.im_user;
|
//console.log('发送成功:', result,this.talkList);
|
||||||
//this.talkList[result.id] = result;
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('发送失败');
|
throw new Error('发送失败');
|
||||||
}
|
}
|
||||||
@ -428,30 +510,29 @@ export default {
|
|||||||
|
|
||||||
// 发送图片消息
|
// 发送图片消息
|
||||||
async sendImageMessage(tempFilePath) {
|
async sendImageMessage(tempFilePath) {
|
||||||
try {
|
try {
|
||||||
// 上传图片
|
// 上传图片
|
||||||
const result = await mattermost.sendImageMessage(this.channel.id, tempFilePath);
|
const result = await mattermost.sendImageMessage(this.channel.id, tempFilePath);
|
||||||
|
|
||||||
if (!result || !result.id) {
|
|
||||||
throw new Error('发送失败');
|
|
||||||
}
|
|
||||||
result['from_user'] = await mattermost.getUserById(result.user_id);
|
|
||||||
this.talkList.push(result);
|
|
||||||
//this.content = '';
|
|
||||||
|
|
||||||
//console.log('发送图片消息成功:', result);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('发送图片消息失败:', error);
|
|
||||||
uni.showToast({
|
|
||||||
title: '发送失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
if (!result || !result.id) {
|
||||||
|
throw new Error('发送失败');
|
||||||
|
}
|
||||||
|
if(this.channel.type !='O'){
|
||||||
|
this.handleNewMessage({post:result});
|
||||||
|
}
|
||||||
|
//this.content = '';
|
||||||
|
|
||||||
|
//console.log('发送图片消息成功:', result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发送图片消息失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '发送失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
// 处理新消息
|
// 处理新消息
|
||||||
handleNewMessage(message) {
|
async handleNewMessage(message) {
|
||||||
console.log('收到新消息:', message);
|
console.log('收到新消息:', message);
|
||||||
|
|
||||||
// 检查消息格式
|
// 检查消息格式
|
||||||
@ -485,8 +566,7 @@ export default {
|
|||||||
console.log('消息已存在,忽略:', post.id);
|
console.log('消息已存在,忽略:', post.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const newMessage = await this.processMessageData(post, message.sender_name);
|
||||||
const newMessage = this.processMessageData(post, message.sender_name);
|
|
||||||
|
|
||||||
console.log('添加新消息到列表:', newMessage);
|
console.log('添加新消息到列表:', newMessage);
|
||||||
this.talkList.push(newMessage);
|
this.talkList.push(newMessage);
|
||||||
@ -642,13 +722,27 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 预览图片
|
// 预览图片
|
||||||
previewImage(url) {
|
previewImage(message) {
|
||||||
|
console
|
||||||
|
.warn('previewImage', message);
|
||||||
|
if (!message.message || !message.message.id) return;
|
||||||
|
const file_id = message.imageInfo.id;
|
||||||
|
// 获取所有图片消息的原图URL
|
||||||
const images = this.talkList
|
const images = this.talkList
|
||||||
.filter(msg => msg.type === 'image')
|
.filter(msg => msg.type === 'image' && msg.imageInfo && msg.imageInfo.id)
|
||||||
.map(msg => this.getImageUrl(msg.content));
|
.map(msg => {
|
||||||
|
return `${mattermost.adminBaseUrl}/api/v4/files/${msg.imageInfo.id}`;
|
||||||
|
});
|
||||||
|
|
||||||
uni.previewImage({
|
uni.previewImage({
|
||||||
urls: images,
|
urls: images,
|
||||||
current: this.getImageUrl(url)
|
current: `${mattermost.adminBaseUrl}/api/v4/files/${message.imageInfo.id}`,
|
||||||
|
success: () => {
|
||||||
|
console.log('预览图片成功');
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('预览图片失败:', err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -762,7 +856,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
.container {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
390
pages/im/member.vue
Normal file
390
pages/im/member.vue
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<uni-nav-bar
|
||||||
|
:title="$t('MT Team')"
|
||||||
|
left-icon="left"
|
||||||
|
@clickLeft="goto('/pages/im/index',2)"
|
||||||
|
:border="false"
|
||||||
|
backgroundColor="F5f5f5">
|
||||||
|
</uni-nav-bar>
|
||||||
|
<view class="box-1">
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
refresher-background="transparent"
|
||||||
|
style="height: 100%;"
|
||||||
|
@refresherrefresh="refresherrefresh"
|
||||||
|
:refresher-enabled="ajax.last_page > ajax.page"
|
||||||
|
:scroll-with-animation="false"
|
||||||
|
:refresher-triggered="scrollView.refresherTriggered"
|
||||||
|
>
|
||||||
|
<uni-list>
|
||||||
|
<template v-for="item in friend_list" >
|
||||||
|
<uni-list-chat
|
||||||
|
v-if="item.id"
|
||||||
|
:key="`${item.id}`"
|
||||||
|
:avatar-circle="true"
|
||||||
|
:title="item.display_name || item.name || item.nickname || item.email"
|
||||||
|
:avatar="getAvatarUrl(item)"
|
||||||
|
:note="item.last_msg ? (item.last_msg_type === 'image' ? '[图片]' : item.last_msg) : '暂无消息'"
|
||||||
|
:time="item.last_post_at ? date(item.last_post_at) : ''"
|
||||||
|
:badge-text="item.unread_count > 0 ? (item.unread_count > 99 ? '99+' : item.unread_count) : ''"
|
||||||
|
clickable
|
||||||
|
@click="handleContactClick(item)"
|
||||||
|
></uni-list-chat>
|
||||||
|
</template>
|
||||||
|
<!--这里显示最近联系人-->
|
||||||
|
</uni-list>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
mapState,
|
||||||
|
mapMutations
|
||||||
|
} from 'vuex';
|
||||||
|
import mattermost from '@/static/im/mattermost.js';
|
||||||
|
import base from '@/config/baseUrl'; // 导入base变量
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 初始化数据
|
||||||
|
config: {
|
||||||
|
cdnurl: '',
|
||||||
|
// 其他初始化数据
|
||||||
|
},
|
||||||
|
friend_list: {},
|
||||||
|
// 聊天列表数据
|
||||||
|
ajax: {
|
||||||
|
limit: 20, // 每页数量
|
||||||
|
last_page: 1, // 总页数
|
||||||
|
page: 0, // 当前页码
|
||||||
|
flag: false, // 请求开关
|
||||||
|
loading: false // 加载状态
|
||||||
|
},
|
||||||
|
scrollView: {
|
||||||
|
refresherTriggered: false // 下拉刷新状态
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['userInfo']),
|
||||||
|
// 添加计算属性来安全访问 ajax 数据
|
||||||
|
hasMoreData() {
|
||||||
|
return this.ajax && this.ajax.last_page > this.ajax.page;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
// 移除消息事件监听
|
||||||
|
mattermost.off('posted', this.handleNewMessage);
|
||||||
|
mattermost.off('post_deleted', this.handleMessageDeleted);
|
||||||
|
mattermost.off('post_edited', this.handleMessageEdited);
|
||||||
|
},
|
||||||
|
onPullDownRefresh() {
|
||||||
|
this.loadContactList().finally(() => {
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations(['setUserInfo']),
|
||||||
|
async init(){
|
||||||
|
try {
|
||||||
|
if(!this.userInfo.token || !this.userInfo.im_token){
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
// 获取用户信息
|
||||||
|
//const userinfo = await this.$http.get('/api/user/profile');
|
||||||
|
//this.setUserInfo(userinfo.data);
|
||||||
|
//console.log('用户信息:', this.userInfo);
|
||||||
|
|
||||||
|
// 获取初始化数据
|
||||||
|
const initRes = await this.$http.get('/api/common/init?lang=' + this.$i18n.locale);
|
||||||
|
if (initRes.code === 0) {
|
||||||
|
this.config = initRes.data;
|
||||||
|
this.config.cdnurl = initRes.data.cdnurl || "http://www.dxmt.io";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 Mattermost 连接状态
|
||||||
|
await mattermost.init();
|
||||||
|
// 加载联系人列表
|
||||||
|
this.loadContactList();
|
||||||
|
// 注册消息事件监听
|
||||||
|
mattermost.on('posted', this.handleNewMessage);
|
||||||
|
mattermost.on('post_deleted', this.handleMessageDeleted);
|
||||||
|
mattermost.on('post_edited', this.handleMessageEdited);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('页面加载失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载联系人列表
|
||||||
|
async loadContactList() {
|
||||||
|
if (this.ajax.flag || this.ajax.loading) return;
|
||||||
|
this.ajax.flag = true;
|
||||||
|
this.ajax.loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取固定联系人列表
|
||||||
|
const fixedContactsRes = await this.$http.get('/api/chat/get_team_member_list');
|
||||||
|
let fixedContacts = [];
|
||||||
|
|
||||||
|
if (fixedContactsRes.code === 0 && Array.isArray(fixedContactsRes.data)) {
|
||||||
|
fixedContacts = fixedContactsRes.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取私聊列表
|
||||||
|
const recent_contacts = await mattermost.getRecentContacts();
|
||||||
|
|
||||||
|
const sortedContacts = {};
|
||||||
|
for (const key in fixedContacts) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(fixedContacts, key)) {
|
||||||
|
sortedContacts[fixedContacts[key].id] = fixedContacts[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const key in sortedContacts) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(sortedContacts, key)) {
|
||||||
|
sortedContacts[key].unread_count = sortedContacts[key].unread_count || 0;
|
||||||
|
this.friend_list[key] = sortedContacts[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('联系人列表:',this.friend_list);
|
||||||
|
// 更新列表
|
||||||
|
if (this.ajax.page === 0) {
|
||||||
|
//this.friend_list = sortedContacts;
|
||||||
|
} else {
|
||||||
|
//this.friend_list = [...this.friend_list, ...sortedContacts];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新分页信息
|
||||||
|
this.ajax.page++;
|
||||||
|
this.ajax.last_page = Math.ceil(sortedContacts.length / this.ajax.limit);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载联系人列表失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.ajax.flag = false;
|
||||||
|
this.ajax.loading = false;
|
||||||
|
this.scrollView.refresherTriggered = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理联系人点击
|
||||||
|
async handleContactClick(item) {
|
||||||
|
try {
|
||||||
|
// 如果是私聊类型,需要先查询直接消息频道ID
|
||||||
|
var url = await mattermost.talk_to_user(item); // 跳转到聊天页面
|
||||||
|
uni.navigateTo({
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理联系人点击失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '操作失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理新消息
|
||||||
|
async handleNewMessage(message) {
|
||||||
|
// 更新联系人列表中的消息
|
||||||
|
const updateContact = async(list) => {
|
||||||
|
let is_exist = false;
|
||||||
|
let post = JSON.parse(message.post);
|
||||||
|
for(var k in list){
|
||||||
|
const contact = list[k];
|
||||||
|
if(message.channel_type == 'D'){
|
||||||
|
//私聊消息
|
||||||
|
if(contact.id === post.user_id) {
|
||||||
|
is_exist = true;
|
||||||
|
list[k]= contact;
|
||||||
|
list[k]['last_msg']=post.message;
|
||||||
|
list[k]['last_msg_type']= post.type || 'text';
|
||||||
|
list[k]['last_msg_time']= post.create_at;
|
||||||
|
list[k]['unread_count']= contact.unread_count+=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(message.channel_type == 'O'){
|
||||||
|
if(contact.id === post.channel_id) {
|
||||||
|
is_exist = true;
|
||||||
|
list[k]= contact;
|
||||||
|
list[k]['last_msg']=post.message;
|
||||||
|
list[k]['last_msg_type']= post.type || 'text';
|
||||||
|
list[k]['last_msg_time']= post.create_at;
|
||||||
|
list[k]['unread_count']= contact.unread_count+=1;
|
||||||
|
}
|
||||||
|
//群组消息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(message.channel_type == 'D' && !is_exist){
|
||||||
|
console.log('新消息:', message,'不存在');
|
||||||
|
let send_user = await mattermost.getUserInfo(post.user_id);
|
||||||
|
//console.log('send_user',send_user);
|
||||||
|
let contact = send_user.data;
|
||||||
|
contact['last_msg']=post.message;
|
||||||
|
contact['last_msg_type']= post.type || 'text';
|
||||||
|
contact['last_msg_time']= post.create_at;
|
||||||
|
contact['unread_count']= 1;
|
||||||
|
list[k]= contact;
|
||||||
|
}
|
||||||
|
let _keys = Object.keys(list).sort((a, b) => {
|
||||||
|
const timeA = a.last_msg_time ? new Date(a.last_msg_time).getTime() : 0;
|
||||||
|
const timeB = b.last_msg_time ? new Date(b.last_msg_time).getTime() : 0;
|
||||||
|
return timeB - timeA;
|
||||||
|
});
|
||||||
|
|
||||||
|
let _list = {};
|
||||||
|
for (let index = 0; index < _keys.length; index++) {
|
||||||
|
_list[_keys[index]] = list[_keys[index]];
|
||||||
|
}
|
||||||
|
return _list;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.friend_list = await updateContact(this.friend_list);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理消息删除
|
||||||
|
handleMessageDeleted(message) {
|
||||||
|
const updateContact = (list) => {
|
||||||
|
return list.map(contact => {
|
||||||
|
if(contact.id === message.channel_id) {
|
||||||
|
return {
|
||||||
|
...contact,
|
||||||
|
last_msg: '此消息已删除',
|
||||||
|
last_msg_type: 'text',
|
||||||
|
last_msg_time: message.create_at
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return contact;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.friend_list = updateContact(this.friend_list);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理消息编辑
|
||||||
|
handleMessageEdited(message) {
|
||||||
|
const updateContact = (list) => {
|
||||||
|
return list.map(contact => {
|
||||||
|
if(contact.id === message.channel_id) {
|
||||||
|
return {
|
||||||
|
...contact,
|
||||||
|
last_msg: message.message,
|
||||||
|
last_msg_time: message.update_at
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return contact;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.friend_list = updateContact(this.friend_list);
|
||||||
|
},
|
||||||
|
|
||||||
|
refresherrefresh() {
|
||||||
|
this.scrollView.refresherTriggered = true;
|
||||||
|
this.ajax.page = 0;
|
||||||
|
this.loadContactList();
|
||||||
|
},
|
||||||
|
goto(url,type){
|
||||||
|
if(type==2){
|
||||||
|
return uni.switchTab({url:url})
|
||||||
|
}
|
||||||
|
if(type==1){
|
||||||
|
return uni.navigateBack({delta:url});
|
||||||
|
}
|
||||||
|
uni.navigateTo({
|
||||||
|
url:url
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JavaScript 仿 PHP date() 函数
|
||||||
|
* @param {string} format - 格式字符串
|
||||||
|
* @param {number|Date} [timestamp] - 可选的时间戳或Date对象,默认为当前时间
|
||||||
|
* @return {string} 格式化后的日期时间字符串
|
||||||
|
*/
|
||||||
|
date(format, timestamp) {
|
||||||
|
// 处理时间参数
|
||||||
|
if(!timestamp){
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
let date = timestamp;
|
||||||
|
if(!(timestamp instanceof Date)){
|
||||||
|
date = Date(timestamp * 1000);
|
||||||
|
if(timestamp > 1749000000){
|
||||||
|
date = Date(timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义替换规则
|
||||||
|
const replacements = {
|
||||||
|
// 日
|
||||||
|
'd': () => String(date.getDate()).padStart(2, '0'),
|
||||||
|
'j': () => date.getDate(),
|
||||||
|
// 月
|
||||||
|
'm': () => String(date.getMonth() + 1).padStart(2, '0'),
|
||||||
|
'n': () => date.getMonth() + 1,
|
||||||
|
// 年
|
||||||
|
'Y': () => date.getFullYear(),
|
||||||
|
'y': () => String(date.getFullYear()).slice(-2),
|
||||||
|
// 时间
|
||||||
|
'H': () => String(date.getHours()).padStart(2, '0'),
|
||||||
|
'i': () => String(date.getMinutes()).padStart(2, '0'),
|
||||||
|
's': () => String(date.getSeconds()).padStart(2, '0'),
|
||||||
|
// 其他
|
||||||
|
'w': () => date.getDay(), // 星期几 (0-6)
|
||||||
|
'A': () => date.getHours() >= 12 ? 'PM' : 'AM',
|
||||||
|
'a': () => date.getHours() >= 12 ? 'pm' : 'am',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 执行替换
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < format.length; i++) {
|
||||||
|
const char = format[i];
|
||||||
|
if (char in replacements) {
|
||||||
|
result += replacements[char]();
|
||||||
|
} else {
|
||||||
|
result += char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
getAvatarUrl(item) {
|
||||||
|
if(!item.avatar){
|
||||||
|
return this.config.cdnurl+'/static/img/avatar.png';
|
||||||
|
}
|
||||||
|
return (item.avatar.startsWith('http') ? '' : this.config.cdnurl) + item.avatar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
font-family: "poppins";
|
||||||
|
.box-1 {
|
||||||
|
flex: 1;
|
||||||
|
height: calc(100vh - var(--window-top) - 44px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
BIN
static/im/emoji.png
Normal file
BIN
static/im/emoji.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
@ -73,7 +73,7 @@ class MattermostClient {
|
|||||||
const protocol = process.env.NODE_ENV === 'development' ? 'ws' : 'wss';
|
const protocol = process.env.NODE_ENV === 'development' ? 'ws' : 'wss';
|
||||||
// 构建 WebSocket URL,使用用户 API 域名
|
// 构建 WebSocket URL,使用用户 API 域名
|
||||||
const connectionId = Date.now().toString(36) + Math.random().toString(36).substr(2);
|
const connectionId = Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||||
this.wsUrl = this.userBaseUrl.replace('https',protocol) + `/api/v4/websocket?connection_id=${connectionId}&sequence_number=0`;
|
this.wsUrl = this.userBaseUrl.replace('https',protocol)+`/api/v4/websocket?connection_id=${connectionId}&sequence_number=0`;
|
||||||
// 重置状态
|
// 重置状态
|
||||||
this.sequence = 1;
|
this.sequence = 1;
|
||||||
this.reconnectAttempts = 0;
|
this.reconnectAttempts = 0;
|
||||||
@ -591,32 +591,40 @@ class MattermostClient {
|
|||||||
|
|
||||||
// 文件上传封装
|
// 文件上传封装
|
||||||
async uploadFile(options) {
|
async uploadFile(options) {
|
||||||
const { url, filePath, name = 'files', headers = {} } = options;
|
const { channelId, filePath, name = 'files', headers = {} } = options;
|
||||||
|
|
||||||
|
if (!channelId) {
|
||||||
|
throw new Error('channelId 是必需的');
|
||||||
|
}
|
||||||
|
|
||||||
// 添加认证头
|
// 添加认证头
|
||||||
const requestHeaders = {
|
const requestHeaders = {
|
||||||
'Authorization': `Bearer ${this.userToken}`,
|
'Authorization': `Bearer ${this.userToken}`,
|
||||||
...headers
|
...headers
|
||||||
};
|
};
|
||||||
|
|
||||||
const [error, response] = await uni.uploadFile({
|
// 直接上传文件
|
||||||
url,
|
const [uploadError, uploadResponse] = await uni.uploadFile({
|
||||||
|
url: `${this.userBaseUrl}/api/v4/files`,
|
||||||
filePath,
|
filePath,
|
||||||
name,
|
name,
|
||||||
header: requestHeaders
|
header: requestHeaders,
|
||||||
|
formData: {
|
||||||
|
channel_id: channelId
|
||||||
|
}
|
||||||
}).catch(err => [err, null]);
|
}).catch(err => [err, null]);
|
||||||
|
|
||||||
if (error) {
|
if (uploadError) {
|
||||||
console.error(`文件上传失败 [${url}]:`, error);
|
console.error('文件上传失败:', uploadError);
|
||||||
throw error;
|
throw uploadError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
if (uploadResponse.statusCode >= 200 && uploadResponse.statusCode < 300) {
|
||||||
return response.data;
|
return JSON.parse(uploadResponse.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(`文件上传失败 [${url}]:`, response);
|
console.error('文件上传失败:', uploadResponse);
|
||||||
throw new Error(`文件上传失败: ${response.statusCode}`);
|
throw new Error(`文件上传失败: ${uploadResponse.statusCode}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取频道消息
|
// 获取频道消息
|
||||||
@ -664,16 +672,15 @@ class MattermostClient {
|
|||||||
try {
|
try {
|
||||||
// 1. 上传文件
|
// 1. 上传文件
|
||||||
const fileData = await this.uploadFile({
|
const fileData = await this.uploadFile({
|
||||||
url: `${this.userBaseUrl}/api/v4/files`,
|
channelId,
|
||||||
filePath
|
filePath
|
||||||
});
|
});
|
||||||
|
|
||||||
const fileInfo = JSON.parse(fileData);
|
const fileId = fileData.file_infos[0].id;
|
||||||
const fileId = fileInfo.file_infos[0].id;
|
|
||||||
|
|
||||||
// 2. 发送带图片的消息
|
// 2. 发送带图片的消息
|
||||||
return this.request({
|
return this.request({
|
||||||
url: `${this.userBaseUrl}/api/v4/posts`,
|
url: `/api/v4/posts`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
@ -687,6 +694,40 @@ class MattermostClient {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async getThumbnail(file_id, width = 200, height = 200) {
|
||||||
|
return this.adminBaseUrl+'/api/v4/files/${file_id}/thumbnail?width=${width}&height=${height}';
|
||||||
|
try {
|
||||||
|
const response = await this.request({
|
||||||
|
url: `/api/v4/files/${file_id}/thumbnail`,
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取缩略图失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPreview(file_id, width = 800, height = 800) {
|
||||||
|
try {
|
||||||
|
const response = await this.request({
|
||||||
|
url: `/api/v4/files/${file_id}/preview`,
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取预览图失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 标记消息为已读
|
// 标记消息为已读
|
||||||
async markChannelAsViewed(channelId) {
|
async markChannelAsViewed(channelId) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user