516 lines
17 KiB
Vue
Raw Normal View History

2025-06-06 03:08:19 +08:00
<template>
<view class="container">
<uni-nav-bar
:title="$t('MT Community')"
:right-text="$t('Add')"
right-icon="plus"
@clickRight="goto('/pages/im/index')"
: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"
>
<view style="padding: 20rpx;">
<image src="/static/im/bannar.png" style="width: 100%;" mode="widthFix"></image>
</view>
<uni-list>
<!-- 显示圆形头像 -->
<uni-list-item
:title="$t('Union customer service')"
thumb="/static/im/kefu.png"
thumbSize="lg"
showArrow
clickable
@click="show_kefu_popup"
></uni-list-item>
<uni-list-item
:title="$t('My team')"
thumb="/static/im/member.png"
thumbSize="lg"
showArrow
clickable
to="/pages/im/member"
></uni-list-item>
<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>
<uni-popup ref="kefuPopup" type="bottom"
border-radius="10px 10px 0 0"
backgroundColor="#fff"
>
<view class="" style="padding: 20rpx 20rpx 200rpx;">
<uni-section class="mb-10" :title="$t('Union customer service')">
<template v-slot:right>
<button type="default" @click="$refs.kefuPopup.close">
<uni-icons type="closeempty"></uni-icons>
</button>
</template>
</uni-section>
<uni-list>
<uni-list-chat
v-for="item in kefu_list"
:key="`kefu_${item.id}`"
:avatar-circle="true"
:title="item.nickname"
:avatar="getAvatarUrl(item)"
:note="(item.status === 'online' ? $t('Online') : $t('Offline')) + ' 10: 00-20: 00'"
clickable
link
@click="handleContactClick(item)"
></uni-list-chat>
</uni-list>
</view>
</uni-popup>
</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: {},
kefu_list: [],
// 聊天列表数据
ajax: {
limit: 20, // 每页数量
last_page: 1, // 总页数
page: 0, // 当前页码
flag: false, // 请求开关
loading: false // 加载状态
},
scrollView: {
refresherTriggered: false // 下拉刷新状态
},
// 固定的客服ID列表
kefu_ids: ['nps4r44g1fbwtn83cck18hn3iy', 'jj46siuqu3dzujk5uj3wekbqxe'],
// 固定频道ID
fixed_channel_id: 'd5j75u5r5id77jmysszdtrijcr'
}
},
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://q.sjqqzc.top";
}
// 检查 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_friend_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 recent_contacts) {
if (Object.prototype.hasOwnProperty.call(recent_contacts, key)) {
sortedContacts[key] = recent_contacts[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();
},
// 显示客服列表
async show_kefu_popup() {
try {
// 从服务端获取客服列表
const res = await this.$http.get('/api/chat/get_kefu_list');
console.log('客服列表数据:', res);
if (res.code === 0 && res.data.code === 0) {
// 获取实际的客服列表数据
const kefuData = res.data.data;
if (!Array.isArray(kefuData) || kefuData.length === 0) {
throw new Error('客服列表为空');
}
// 处理客服列表数据
this.kefu_list = kefuData;
// 获取每个客服的在线状态
const statusPromises = this.kefu_list.map(async (kefu) => {
try {
// 使用管理员 API 获取状态
const statusRes = await mattermost.request({
to_server:'admin',
url: `/api/v4/users/${kefu.id}/status`,
method: 'GET'
});
if (statusRes.statusCode === 200 && statusRes.data) {
return {
id: kefu.id,
status: statusRes.data.status || 'offline'
};
}
} catch (error) {
console.error(`获取客服 ${kefu.id} 状态失败:`, error);
}
return {
id: kefu.id,
status: 'offline'
};
});
// 等待所有状态请求完成
const statusResults = await Promise.all(statusPromises);
// 更新客服状态
this.kefu_list = this.kefu_list.map(kefu => {
const status = statusResults.find(s => s.id === kefu.id);
return {
...kefu,
status: status ? status.status : 'offline'
};
});
this.$refs.kefuPopup.open();
} else {
throw new Error(res.data.msg || res.msg || '获取客服列表失败');
}
} catch (error) {
console.error('获取客服列表失败:', error);
uni.showToast({
title: error.message || '获取客服列表失败',
icon: 'none'
});
}
},
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>