committed by
Gitee
67 changed files with 2838 additions and 1773 deletions
@ -0,0 +1,89 @@ |
|||||
|
package com.bx.imcommon.util; |
||||
|
|
||||
|
|
||||
|
|
||||
|
import cn.hutool.core.collection.CollUtil; |
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.Collection; |
||||
|
import java.util.LinkedList; |
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
/** |
||||
|
* 逗号分格文本处理工具类 |
||||
|
* |
||||
|
* @author: blue |
||||
|
* @date: 2023-11-09 09:52:49 |
||||
|
* @version: 1.0 |
||||
|
*/ |
||||
|
public class CommaTextUtils { |
||||
|
|
||||
|
/** |
||||
|
* 文本转列表 |
||||
|
* |
||||
|
* @param strText 文件 |
||||
|
* @return 列表 |
||||
|
*/ |
||||
|
public static List<String> asList(String strText) { |
||||
|
if (StrUtil.isEmpty(strText)) { |
||||
|
return new LinkedList<>(); |
||||
|
} |
||||
|
return new LinkedList<>(Arrays.asList(strText.split(","))); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 列表转字符串,并且自动清空、去重、排序 |
||||
|
* |
||||
|
* @param texts 列表 |
||||
|
* @return 文本 |
||||
|
*/ |
||||
|
public static <T> String asText(Collection<T> texts) { |
||||
|
if (CollUtil.isEmpty(texts)) { |
||||
|
return StrUtil.EMPTY; |
||||
|
} |
||||
|
return texts.stream().map(text -> StrUtil.toString(text)).filter(StrUtil::isNotEmpty).distinct().sorted().collect(Collectors.joining(",")); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 追加一个单词 |
||||
|
* |
||||
|
* @param strText 文本 |
||||
|
* @param word 单词 |
||||
|
* @return 文本 |
||||
|
*/ |
||||
|
public static <T> String appendWord(String strText, T word) { |
||||
|
List<String> texts = asList(strText); |
||||
|
texts.add(StrUtil.toString(word)); |
||||
|
return asText(texts); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除一个单词 |
||||
|
* |
||||
|
* @param strText 文本 |
||||
|
* @param word 单词 |
||||
|
* @return 文本 |
||||
|
*/ |
||||
|
public static <T> String removeWord(String strText, T word) { |
||||
|
List<String> texts = asList(strText); |
||||
|
texts.remove(StrUtil.toString(word)); |
||||
|
return asText(texts); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 合并 |
||||
|
* |
||||
|
* @param strText1 文本1 |
||||
|
* @param strText2 文本2 |
||||
|
* @return 文本 |
||||
|
*/ |
||||
|
public static String merge(String strText1, String strText2) { |
||||
|
List<String> texts = asList(strText1); |
||||
|
texts.addAll(asList(strText2)); |
||||
|
return asText(texts); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,67 @@ |
|||||
|
<template> |
||||
|
<div class="chat-group-member" :style="{'height':height+'px'}"> |
||||
|
<div class="member-avatar"> |
||||
|
<head-image :size="headImageSize" :name="member.aliasName" :url="member.headImage"> </head-image> |
||||
|
</div> |
||||
|
<div class="member-name" :style="{'line-height':height+'px'}"> |
||||
|
<div>{{ member.aliasName }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HeadImage from "../common/HeadImage.vue"; |
||||
|
export default { |
||||
|
name: "groupMember", |
||||
|
components: { HeadImage }, |
||||
|
data() { |
||||
|
return {}; |
||||
|
}, |
||||
|
props: { |
||||
|
member: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
}, |
||||
|
height:{ |
||||
|
type: Number, |
||||
|
default: 50 |
||||
|
} |
||||
|
}, |
||||
|
computed:{ |
||||
|
headImageSize(){ |
||||
|
return Math.ceil(this.height * 0.75) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.chat-group-member { |
||||
|
display: flex; |
||||
|
margin-bottom: 1px; |
||||
|
position: relative; |
||||
|
padding: 0 5px; |
||||
|
align-items: center; |
||||
|
background-color: #fafafa; |
||||
|
white-space: nowrap; |
||||
|
box-sizing: border-box; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: #eeeeee; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
background-color: #eeeeee; |
||||
|
} |
||||
|
|
||||
|
.member-name { |
||||
|
padding-left: 10px; |
||||
|
height: 100%; |
||||
|
text-align: left; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
font-size: 14px; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,187 @@ |
|||||
|
<template> |
||||
|
<div v-show="show"> |
||||
|
<div class="chat-group-readed-mask" @click.self="close()"> |
||||
|
<div class="chat-group-readed" :style="{ 'left': pos.x + 'px', 'top': pos.y + 'px' }" @click.prevent=""> |
||||
|
<el-tabs type="border-card" :stretch="true"> |
||||
|
<el-tab-pane :label="`已读(${readedMembers.length})`"> |
||||
|
<el-scrollbar class="scroll-box"> |
||||
|
<div v-for="(member) in readedMembers" :key="member.id"> |
||||
|
<chat-group-member :member="member"></chat-group-member> |
||||
|
</div> |
||||
|
</el-scrollbar> |
||||
|
</el-tab-pane> |
||||
|
<el-tab-pane :label="`未读(${unreadMembers.length})`"> |
||||
|
<el-scrollbar class="scroll-box"> |
||||
|
<div v-for="(member) in unreadMembers" :key="member.id"> |
||||
|
<chat-group-member :member="member"></chat-group-member> |
||||
|
</div> |
||||
|
</el-scrollbar> |
||||
|
</el-tab-pane> |
||||
|
</el-tabs> |
||||
|
<div v-show="msgInfo.selfSend" class="arrow-right" :style="{ 'top': pos.arrowY + 'px' }"> |
||||
|
<div class="arrow-right-inner"> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div v-show="!msgInfo.selfSend" class="arrow-left" :style="{ 'top': pos.arrowY + 'px' }"> |
||||
|
<div class="arrow-left-inner"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
|
||||
|
<script> |
||||
|
import ChatGroupMember from "./ChatGroupMember.vue"; |
||||
|
|
||||
|
export default { |
||||
|
name: "chatGroupReaded", |
||||
|
components: { |
||||
|
ChatGroupMember |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
show: false, |
||||
|
pos: { |
||||
|
x: 0, |
||||
|
y: 0, |
||||
|
arrowY: 0 |
||||
|
}, |
||||
|
readedMembers: [], |
||||
|
unreadMembers: [] |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
groupMembers: { |
||||
|
type: Array |
||||
|
}, |
||||
|
msgInfo: { |
||||
|
type: Object |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
close() { |
||||
|
this.show = false; |
||||
|
}, |
||||
|
open(rect) { |
||||
|
this.show = true; |
||||
|
this.pos.arrowY = 200; |
||||
|
// 计算窗口位置 |
||||
|
if (this.msgInfo.selfSend) { |
||||
|
// 自己发的消息弹出在消息的左边 |
||||
|
this.pos.x = rect.left - 310; |
||||
|
} else { |
||||
|
// 别人发的消息弹窗在消息右边 |
||||
|
this.pos.x = rect.right + 20; |
||||
|
} |
||||
|
this.pos.y = rect.top + rect.height / 2 - 215; |
||||
|
// 防止窗口溢出 |
||||
|
if (this.pos.y < 0) { |
||||
|
this.pos.arrowY += this.pos.y |
||||
|
this.pos.y = 0; |
||||
|
} |
||||
|
this.loadReadedUser() |
||||
|
}, |
||||
|
loadReadedUser() { |
||||
|
this.readedMembers = []; |
||||
|
this.unreadMembers = []; |
||||
|
this.$http({ |
||||
|
url: "/message/group/findReadedUsers", |
||||
|
method: 'get', |
||||
|
params: { groupId: this.msgInfo.groupId, messageId: this.msgInfo.id } |
||||
|
}).then(userIds => { |
||||
|
this.groupMembers.forEach(member => { |
||||
|
// 发送者和已退群的不显示 |
||||
|
if (member.userId == this.msgInfo.sendId || member.quit) { |
||||
|
return; |
||||
|
} |
||||
|
// 区分已读还是未读 |
||||
|
if (userIds.find(userId => member.userId == userId)) { |
||||
|
this.readedMembers.push(member); |
||||
|
} else { |
||||
|
this.unreadMembers.push(member); |
||||
|
} |
||||
|
}) |
||||
|
// 更新已读人数 |
||||
|
this.$store.commit("updateMessage", { |
||||
|
id: this.msgInfo.id, |
||||
|
groupId: this.msgInfo.groupId, |
||||
|
readedCount: this.readedMembers.length |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.chat-group-readed-mask { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
z-index: 9999; |
||||
|
} |
||||
|
|
||||
|
.chat-group-readed { |
||||
|
position: fixed; |
||||
|
box-shadow: 0px 0px 10px #ccc; |
||||
|
width: 300px; |
||||
|
background-color: #fafafa; |
||||
|
border-radius: 8px; |
||||
|
|
||||
|
.scroll-box { |
||||
|
height: 400px; |
||||
|
} |
||||
|
|
||||
|
.arrow-left { |
||||
|
|
||||
|
position: absolute; |
||||
|
left: -15px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-top: 15px solid transparent; |
||||
|
border-bottom: 15px solid transparent; |
||||
|
border-right: 15px solid #ccc; |
||||
|
|
||||
|
.arrow-left-inner { |
||||
|
position: absolute; |
||||
|
top: -12px; |
||||
|
left: 3px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
overflow: hidden; |
||||
|
border-top: 12px solid transparent; |
||||
|
border-bottom: 12px solid transparent; |
||||
|
border-right: 12px solid white; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.arrow-right { |
||||
|
position: absolute; |
||||
|
right: -15px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-top: 15px solid transparent; |
||||
|
border-bottom: 15px solid transparent; |
||||
|
border-left: 15px solid #ccc; |
||||
|
|
||||
|
.arrow-right-inner { |
||||
|
position: absolute; |
||||
|
top: -12px; |
||||
|
right: 3px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
overflow: hidden; |
||||
|
border-top: 12px solid transparent; |
||||
|
border-bottom: 12px solid transparent; |
||||
|
border-left: 12px solid white; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,16 @@ |
|||||
|
//设置环境(打包前修改此变量)
|
||||
|
const ENV = "DEV"; |
||||
|
const UNI_APP = {} |
||||
|
if(ENV=="DEV"){ |
||||
|
UNI_APP.BASE_URL = "http://127.0.0.1:8888"; |
||||
|
UNI_APP.WS_URL = "ws://127.0.0.1:8878/im"; |
||||
|
// H5 走本地代理解决跨域问题
|
||||
|
// #ifdef H5
|
||||
|
UNI_APP.BASE_URL = "/api"; |
||||
|
// #endif
|
||||
|
} |
||||
|
if(ENV=="PROD"){ |
||||
|
UNI_APP.BASE_URL = "https://www.boxim.online/api"; |
||||
|
UNI_APP.WS_URL = "wss://www.boxim.online:81/im"; |
||||
|
} |
||||
|
export default UNI_APP |
||||
@ -0,0 +1,131 @@ |
|||||
|
<template> |
||||
|
<uni-popup ref="popup" type="bottom"> |
||||
|
<view class="chat-group-readed"> |
||||
|
<view class="uni-padding-wrap uni-common-mt"> |
||||
|
<uni-segmented-control :current="current" :values="items" style-type="button" active-color="#587ff0" @clickItem="onClickItem"/> |
||||
|
</view> |
||||
|
<view class="content"> |
||||
|
<view v-if="current === 0"> |
||||
|
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> |
||||
|
<view v-for="m in readedMembers" :key="m.userId"> |
||||
|
<view class="member-item"> |
||||
|
<head-image :name="m.aliasName" :online="m.online" :url="m.headImage" |
||||
|
:size="90"></head-image> |
||||
|
<view class="member-name">{{ m.aliasName}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<view v-if="current === 1"> |
||||
|
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> |
||||
|
<view v-for="m in unreadMembers" :key="m.userId"> |
||||
|
<view class="member-item"> |
||||
|
<head-image :name="m.aliasName" :online="m.online" :url="m.headImage" |
||||
|
:size="90"></head-image> |
||||
|
<view class="member-name">{{ m.aliasName}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</uni-popup> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "chat-group-readed", |
||||
|
data() { |
||||
|
return { |
||||
|
items: ['已读', '未读'], |
||||
|
current: 0, |
||||
|
readedMembers: [], |
||||
|
unreadMembers: [] |
||||
|
}; |
||||
|
}, |
||||
|
props: { |
||||
|
msgInfo: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
}, |
||||
|
groupMembers: { |
||||
|
type: Array |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
open() { |
||||
|
this.$refs.popup.open(); |
||||
|
this.loadReadedUser(); |
||||
|
}, |
||||
|
loadReadedUser() { |
||||
|
this.readedMembers = []; |
||||
|
this.unreadMembers = []; |
||||
|
this.$http({ |
||||
|
url: `/message/group/findReadedUsers?groupId=${this.msgInfo.groupId}&messageId=${this.msgInfo.id}`, |
||||
|
method: 'Get' |
||||
|
}).then(userIds => { |
||||
|
this.groupMembers.forEach(member => { |
||||
|
// 发送者和已退群的不显示 |
||||
|
if (member.userId == this.msgInfo.sendId || member.quit) { |
||||
|
return; |
||||
|
} |
||||
|
// 区分已读还是未读 |
||||
|
if (userIds.find(userId => member.userId == userId)) { |
||||
|
this.readedMembers.push(member); |
||||
|
} else { |
||||
|
this.unreadMembers.push(member); |
||||
|
} |
||||
|
}) |
||||
|
this.items[0] = `已读(${this.readedMembers.length})`; |
||||
|
this.items[1] = `未读(${this.unreadMembers.length})`; |
||||
|
// 更新已读人数 |
||||
|
this.$store.commit("updateMessage", { |
||||
|
id: this.msgInfo.id, |
||||
|
groupId: this.msgInfo.groupId, |
||||
|
readedCount: this.readedMembers.length |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
onClickItem(e){ |
||||
|
this.current = e.currentIndex; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.chat-group-readed { |
||||
|
position: relative; |
||||
|
border: #dddddd solid 1rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
background-color: white; |
||||
|
padding: 10rpx; |
||||
|
border-radius: 15rpx; |
||||
|
.scroll-bar { |
||||
|
height: 800rpx; |
||||
|
} |
||||
|
|
||||
|
.member-item { |
||||
|
height: 120rpx; |
||||
|
display: flex; |
||||
|
position: relative; |
||||
|
padding: 0 30rpx; |
||||
|
align-items: center; |
||||
|
background-color: white; |
||||
|
white-space: nowrap; |
||||
|
|
||||
|
.member-name { |
||||
|
flex: 1; |
||||
|
padding-left: 20rpx; |
||||
|
font-size: 30rpx; |
||||
|
font-weight: 600; |
||||
|
line-height: 60rpx; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
</style> |
||||
@ -1,40 +1,6 @@ |
|||||
{ |
{ |
||||
"uni-app": { |
"uni-app": { |
||||
"scripts": { |
"scripts": { |
||||
"dev-h5": { |
|
||||
"title": "开发环境-H5", |
|
||||
"browser":"chrome", |
|
||||
"env": { |
|
||||
"UNI_PLATFORM": "h5", |
|
||||
"BASE_URL": "/api", |
|
||||
"WS_URL": "ws://127.0.0.1:8878/im" |
|
||||
} |
|
||||
}, |
|
||||
"dev-wx-mini": { |
|
||||
"title": "开发环境-微信小程序", |
|
||||
"env": { |
|
||||
"UNI_PLATFORM": "mp-weixin", |
|
||||
"BASE_URL": "http://127.0.0.1:8888", |
|
||||
"WS_URL": "ws://127.0.0.1:8878/im" |
|
||||
} |
|
||||
}, |
|
||||
"prod-h5": { |
|
||||
"title": "正式环境-H5", |
|
||||
"browser":"chrome", |
|
||||
"env": { |
|
||||
"UNI_PLATFORM": "h5", |
|
||||
"BASE_URL": "https://www.boxim.online/api", |
|
||||
"WS_URL": "wss://www.boxim.online:81/im" |
|
||||
} |
|
||||
}, |
|
||||
"prod-wx-mini": { |
|
||||
"title": "正式环境-微信小程序", |
|
||||
"env": { |
|
||||
"UNI_PLATFORM": "mp-weixin", |
|
||||
"BASE_URL": "https://www.boxim.online/api", |
|
||||
"WS_URL": "wss://www.boxim.online:81/im" |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Loading…
Reference in new issue